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

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