Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: chrome/chrome_watcher/kasko_util.cc

Issue 1834463002: Identify the hung thread using the Wait Chain Traversal API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: BASE_EXPORT Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/chrome_watcher/kasko_util.h" 5 #include "chrome/chrome_watcher/kasko_util.h"
6 6
7 #include <sddl.h> 7 #include <sddl.h>
8 8
9 #include <memory> 9 #include <memory>
10 #include <string> 10 #include <string>
11 #include <utility>
11 #include <vector> 12 #include <vector>
12 13
13 #include "base/base_paths.h" 14 #include "base/base_paths.h"
14 #include "base/bind.h" 15 #include "base/bind.h"
15 #include "base/callback_helpers.h" 16 #include "base/callback_helpers.h"
16 #include "base/environment.h" 17 #include "base/environment.h"
17 #include "base/files/file_path.h" 18 #include "base/files/file_path.h"
19 #include "base/format_macros.h"
18 #include "base/macros.h" 20 #include "base/macros.h"
19 #include "base/path_service.h" 21 #include "base/path_service.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
20 #include "base/strings/utf_string_conversions.h" 24 #include "base/strings/utf_string_conversions.h"
25 #include "base/win/wait_chain.h"
21 #include "base/win/win_util.h" 26 #include "base/win/win_util.h"
22 27
23 #include "chrome/chrome_watcher/chrome_watcher_main_api.h" 28 #include "chrome/chrome_watcher/chrome_watcher_main_api.h"
24 #include "components/crash/content/app/crashpad.h" 29 #include "components/crash/content/app/crashpad.h"
25 #include "syzygy/kasko/api/reporter.h" 30 #include "syzygy/kasko/api/reporter.h"
26 31
27 namespace { 32 namespace {
28 33
29 // Helper function for determining the crash server to use. Defaults to the 34 // Helper function for determining the crash server to use. Defaults to the
30 // standard crash server, but can be overridden via an environment variable. 35 // standard crash server, but can be overridden via an environment variable.
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 DPLOG(ERROR); 125 DPLOG(ERROR);
121 } 126 }
122 } 127 }
123 128
124 void AddCrashKey(const wchar_t *key, const wchar_t *value, 129 void AddCrashKey(const wchar_t *key, const wchar_t *value,
125 std::vector<kasko::api::CrashKey> *crash_keys) { 130 std::vector<kasko::api::CrashKey> *crash_keys) {
126 DCHECK(key); 131 DCHECK(key);
127 DCHECK(value); 132 DCHECK(value);
128 DCHECK(crash_keys); 133 DCHECK(crash_keys);
129 134
130 kasko::api::CrashKey crash_key; 135 crash_keys->resize(crash_keys->size() + 1);
131 std::wcsncpy(crash_key.name, key, kasko::api::CrashKey::kNameMaxLength - 1); 136 kasko::api::CrashKey& crash_key = crash_keys->back();
132 std::wcsncpy(crash_key.value, value, 137 base::wcslcpy(crash_key.name, key, kasko::api::CrashKey::kNameMaxLength);
133 kasko::api::CrashKey::kValueMaxLength - 1); 138 base::wcslcpy(crash_key.value, value, kasko::api::CrashKey::kValueMaxLength);
134 crash_keys->push_back(crash_key); 139 }
140
141 // Get the |process| and the |thread_id| of the node inside the |wait_chain|
142 // that is of type ThreadType and belongs to a process that is valid for the
143 // capture of a crash dump. Returns if such a node was found.
144 bool GetLastValidNodeInfo(const base::win::WaitChainNodeVector& wait_chain,
145 base::Process* process,
146 DWORD* thread_id) {
147 // The last thread in the wait chain is nominated as the hung thread.
148 base::win::WaitChainNodeVector::const_reverse_iterator it;
149 for (it = wait_chain.rbegin(); it != wait_chain.rend(); ++it) {
150 if (it->ObjectType != WctThreadType)
151 continue;
152
153 auto current_process = base::Process::Open(it->ThreadObject.ProcessId);
154 if (EnsureTargetProcessValidForCapture(current_process)) {
155 *process = std::move(current_process);
156 *thread_id = it->ThreadObject.ThreadId;
157 return true;
158 }
159 }
160 return false;
135 } 161 }
136 162
137 } // namespace 163 } // namespace
138 164
139 bool InitializeKaskoReporter(const base::string16& endpoint, 165 bool InitializeKaskoReporter(const base::string16& endpoint,
140 const base::char16* browser_data_directory) { 166 const base::char16* browser_data_directory) {
141 base::string16 crash_server = GetKaskoCrashServerUrl(); 167 base::string16 crash_server = GetKaskoCrashServerUrl();
142 base::FilePath crash_reports_base_dir = 168 base::FilePath crash_reports_base_dir =
143 GetKaskoCrashReportsBaseDir(browser_data_directory); 169 GetKaskoCrashReportsBaseDir(browser_data_directory);
144 170
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 return false; 211 return false;
186 } 212 }
187 213
188 void DumpHungProcess(DWORD main_thread_id, const base::string16& channel, 214 void DumpHungProcess(DWORD main_thread_id, const base::string16& channel,
189 const base::char16* key, const base::Process& process) { 215 const base::char16* key, const base::Process& process) {
190 // Read the Crashpad module annotations for the process. 216 // Read the Crashpad module annotations for the process.
191 std::vector<kasko::api::CrashKey> annotations; 217 std::vector<kasko::api::CrashKey> annotations;
192 crash_reporter::ReadMainModuleAnnotationsForKasko(process, &annotations); 218 crash_reporter::ReadMainModuleAnnotationsForKasko(process, &annotations);
193 AddCrashKey(key, L"1", &annotations); 219 AddCrashKey(key, L"1", &annotations);
194 220
221 // Use the Wait Chain Traversal API to determine the hung thread. Defaults to
222 // UI thread on error. The wait chain may point to a different thread in a
223 // different process for the hung thread.
224 DWORD hung_thread_id = main_thread_id;
225 base::Process hung_process = process.Duplicate();
226
227 base::win::WaitChainNodeVector wait_chain;
228 bool is_deadlock = false;
229 if (base::win::GetThreadWaitChain(main_thread_id, &wait_chain,
230 &is_deadlock)) {
231 bool found_valid_node =
232 GetLastValidNodeInfo(wait_chain, &hung_process, &hung_thread_id);
233 DCHECK(found_valid_node);
234
235 // The entire wait chain is added to the crash report via crash keys.
236 //
237 // As an example (key : value):
238 // hung-process-is-deadlock : false
239 // hung-process-wait-chain-00 : Thread #10242 with status Blocked
240 // hung-process-wait-chain-01 : Lock of type ThreadWait with status Owned
241 // hung-process-wait-chain-02 : Thread #77221 with status Blocked
242 //
243 AddCrashKey(L"hung-process-is-deadlock", is_deadlock ? L"true" : L"false",
244 &annotations);
245 for (size_t i = 0; i < wait_chain.size(); i++) {
246 AddCrashKey(
247 base::StringPrintf(L"hung-process-wait-chain-%02" PRIuS, i).c_str(),
248 base::win::WaitChainNodeToString(wait_chain[i]).c_str(),
249 &annotations);
250 }
251 }
252
195 std::vector<const base::char16*> key_buffers; 253 std::vector<const base::char16*> key_buffers;
196 std::vector<const base::char16*> value_buffers; 254 std::vector<const base::char16*> value_buffers;
197 for (const auto& crash_key : annotations) { 255 for (const auto& crash_key : annotations) {
198 key_buffers.push_back(crash_key.name); 256 key_buffers.push_back(crash_key.name);
199 value_buffers.push_back(crash_key.value); 257 value_buffers.push_back(crash_key.value);
200 } 258 }
201 key_buffers.push_back(nullptr); 259 key_buffers.push_back(nullptr);
202 value_buffers.push_back(nullptr); 260 value_buffers.push_back(nullptr);
203 261
204 // Synthesize an exception for the main thread. Populate the record with the 262 // Synthesize an exception for the hung thread. Populate the record with the
205 // current context of the thread to get the stack trace bucketed on the crash 263 // current context of the thread to get the stack trace bucketed on the crash
206 // backend. 264 // backend.
207 CONTEXT thread_context = {}; 265 CONTEXT thread_context = {};
208 EXCEPTION_RECORD exception_record = {}; 266 EXCEPTION_RECORD exception_record = {};
209 exception_record.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED; 267 exception_record.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
210 EXCEPTION_POINTERS exception_pointers = {&exception_record, &thread_context}; 268 EXCEPTION_POINTERS exception_pointers = {&exception_record, &thread_context};
211 269
212 base::win::ScopedHandle main_thread(::OpenThread( 270 base::win::ScopedHandle hung_thread(::OpenThread(
213 THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, 271 THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION,
214 FALSE, main_thread_id)); 272 FALSE, hung_thread_id));
215 273
216 bool have_context = false; 274 bool have_context = false;
217 if (main_thread.IsValid()) { 275 if (hung_thread.IsValid()) {
218 DWORD suspend_count = ::SuspendThread(main_thread.Get()); 276 DWORD suspend_count = ::SuspendThread(hung_thread.Get());
219 const DWORD kSuspendFailed = static_cast<DWORD>(-1); 277 const DWORD kSuspendFailed = static_cast<DWORD>(-1);
220 if (suspend_count != kSuspendFailed) { 278 if (suspend_count != kSuspendFailed) {
221 // Best effort capture of the context. 279 // Best effort capture of the context.
222 thread_context.ContextFlags = CONTEXT_FLOATING_POINT | CONTEXT_SEGMENTS | 280 thread_context.ContextFlags = CONTEXT_FLOATING_POINT | CONTEXT_SEGMENTS |
223 CONTEXT_INTEGER | CONTEXT_CONTROL; 281 CONTEXT_INTEGER | CONTEXT_CONTROL;
224 if (::GetThreadContext(main_thread.Get(), &thread_context) == TRUE) 282 if (::GetThreadContext(hung_thread.Get(), &thread_context) == TRUE)
225 have_context = true; 283 have_context = true;
226 284
227 ::ResumeThread(main_thread.Get()); 285 ::ResumeThread(hung_thread.Get());
228 } 286 }
229 } 287 }
230 288
231 // TODO(manzagop): consider making the dump-type channel-dependent. 289 // TODO(manzagop): consider making the dump-type channel-dependent.
232 if (have_context) { 290 if (have_context) {
233 kasko::api::SendReportForProcess( 291 kasko::api::SendReportForProcess(
234 process.Handle(), main_thread_id, &exception_pointers, 292 hung_process.Handle(), hung_thread_id, &exception_pointers,
235 kasko::api::LARGER_DUMP_TYPE, key_buffers.data(), value_buffers.data()); 293 kasko::api::LARGER_DUMP_TYPE, key_buffers.data(), value_buffers.data());
236 } else { 294 } else {
237 kasko::api::SendReportForProcess(process.Handle(), 0, nullptr, 295 kasko::api::SendReportForProcess(hung_process.Handle(), 0, nullptr,
238 kasko::api::LARGER_DUMP_TYPE, 296 kasko::api::LARGER_DUMP_TYPE,
239 key_buffers.data(), value_buffers.data()); 297 key_buffers.data(), value_buffers.data());
240 } 298 }
241 } 299 }
OLDNEW
« no previous file with comments | « chrome/chrome_watcher/chrome_watcher_main.cc ('k') | chrome/test/kasko/hang_watcher_integration_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698