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

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

Issue 1844023002: Capture a report on failed browser rendez-vous. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: GYP fixup 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
« no previous file with comments | « chrome/chrome_watcher/chrome_watcher.gypi ('k') | chrome/chrome_watcher/kasko_util.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2014 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 <windows.h> 5 #include <windows.h>
6 #include <sddl.h> 6 #include <sddl.h>
7 7
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/at_exit.h" 10 #include "base/at_exit.h"
(...skipping 16 matching lines...) Expand all
27 #include "base/strings/string_piece.h" 27 #include "base/strings/string_piece.h"
28 #include "base/strings/utf_string_conversions.h" 28 #include "base/strings/utf_string_conversions.h"
29 #include "base/synchronization/waitable_event.h" 29 #include "base/synchronization/waitable_event.h"
30 #include "base/thread_task_runner_handle.h" 30 #include "base/thread_task_runner_handle.h"
31 #include "base/threading/thread.h" 31 #include "base/threading/thread.h"
32 #include "base/time/time.h" 32 #include "base/time/time.h"
33 #include "base/win/scoped_handle.h" 33 #include "base/win/scoped_handle.h"
34 #include "base/win/win_util.h" 34 #include "base/win/win_util.h"
35 35
36 #include "chrome/chrome_watcher/chrome_watcher_main_api.h" 36 #include "chrome/chrome_watcher/chrome_watcher_main_api.h"
37 #include "chrome/chrome_watcher/kasko_util.h"
37 #include "chrome/installer/util/util_constants.h" 38 #include "chrome/installer/util/util_constants.h"
38 #include "components/browser_watcher/endsession_watcher_window_win.h" 39 #include "components/browser_watcher/endsession_watcher_window_win.h"
39 #include "components/browser_watcher/exit_code_watcher_win.h" 40 #include "components/browser_watcher/exit_code_watcher_win.h"
40 #include "components/browser_watcher/window_hang_monitor_win.h" 41 #include "components/browser_watcher/window_hang_monitor_win.h"
41 #include "third_party/kasko/kasko_features.h" 42 #include "third_party/kasko/kasko_features.h"
42 43
43 #if BUILDFLAG(ENABLE_KASKO)
44 #include "components/crash/content/app/crashpad.h"
45 #include "syzygy/kasko/api/reporter.h"
46 #endif
47
48 namespace { 44 namespace {
49 45
50 // Use the same log facility as Chrome for convenience. 46 // Use the same log facility as Chrome for convenience.
51 // {7FE69228-633E-4f06-80C1-527FEA23E3A7} 47 // {7FE69228-633E-4f06-80C1-527FEA23E3A7}
52 const GUID kChromeWatcherTraceProviderName = { 48 const GUID kChromeWatcherTraceProviderName = {
53 0x7fe69228, 0x633e, 0x4f06, 49 0x7fe69228, 0x633e, 0x4f06,
54 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } }; 50 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
55 51
56 // The amount of time we wait around for a WM_ENDSESSION or a process exit. 52 // The amount of time we wait around for a WM_ENDSESSION or a process exit.
57 const int kDelayTimeSeconds = 30; 53 const int kDelayTimeSeconds = 30;
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 const base::string16& registry_path, 186 const base::string16& registry_path,
191 base::Process process, 187 base::Process process,
192 const base::Callback<void(const base::Process&)>& on_hung_callback, 188 const base::Callback<void(const base::Process&)>& on_hung_callback,
193 browser_watcher::WindowHangMonitor::WindowEvent window_event) { 189 browser_watcher::WindowHangMonitor::WindowEvent window_event) {
194 if (window_event == browser_watcher::WindowHangMonitor::WINDOW_HUNG && 190 if (window_event == browser_watcher::WindowHangMonitor::WINDOW_HUNG &&
195 !on_hung_callback.is_null()) { 191 !on_hung_callback.is_null()) {
196 on_hung_callback.Run(process); 192 on_hung_callback.Run(process);
197 } 193 }
198 } 194 }
199 195
200 #if BUILDFLAG(ENABLE_KASKO)
201 // Helper function for determining the crash server to use. Defaults to the
202 // standard crash server, but can be overridden via an environment variable.
203 // Enables easy integration testing.
204 void GetKaskoCrashServerUrl(base::string16* crash_server) {
205 const char kKaskoCrashServerUrl[] = "KASKO_CRASH_SERVER_URL";
206 static const wchar_t kDefaultKaskoCrashServerUrl[] =
207 L"https://clients2.google.com/cr/report";
208
209 auto env = base::Environment::Create();
210 std::string env_var;
211 if (env->GetVar(kKaskoCrashServerUrl, &env_var)) {
212 base::UTF8ToWide(env_var.c_str(), env_var.size(), crash_server);
213 } else {
214 *crash_server = kDefaultKaskoCrashServerUrl;
215 }
216 }
217
218 // Helper function for determining the crash reports directory to use. Defaults
219 // to the browser data directory, but can be overridden via an environment
220 // variable. Enables easy integration testing.
221 void GetKaskoCrashReportsBaseDir(const base::char16* browser_data_directory,
222 base::FilePath* base_dir) {
223 const char kKaskoCrashReportBaseDir[] = "KASKO_CRASH_REPORTS_BASE_DIR";
224 auto env = base::Environment::Create();
225 std::string env_var;
226 if (env->GetVar(kKaskoCrashReportBaseDir, &env_var)) {
227 base::string16 wide_env_var;
228 base::UTF8ToWide(env_var.c_str(), env_var.size(), &wide_env_var);
229 *base_dir = base::FilePath(wide_env_var);
230 } else {
231 *base_dir = base::FilePath(browser_data_directory);
232 }
233 }
234
235 void DumpHungBrowserProcess(DWORD main_thread_id,
236 const base::string16& channel,
237 const base::Process& process) {
238 // Read the Crashpad module annotations for the process.
239 std::vector<kasko::api::CrashKey> annotations;
240 crash_reporter::ReadMainModuleAnnotationsForKasko(process, &annotations);
241
242 // Add a special crash key to distinguish reports generated for a hung
243 // process.
244 annotations.push_back(kasko::api::CrashKey{L"hung-process", L"1"});
245
246 std::vector<const base::char16*> key_buffers;
247 std::vector<const base::char16*> value_buffers;
248 for (const auto& crash_key : annotations) {
249 key_buffers.push_back(crash_key.name);
250 value_buffers.push_back(crash_key.value);
251 }
252 key_buffers.push_back(nullptr);
253 value_buffers.push_back(nullptr);
254
255 // Synthesize an exception for the main thread. Populate the record with the
256 // current context of the thread to get the stack trace bucketed on the crash
257 // backend.
258 CONTEXT thread_context = {};
259 EXCEPTION_RECORD exception_record = {};
260 exception_record.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
261 EXCEPTION_POINTERS exception_pointers = {&exception_record, &thread_context};
262
263 base::win::ScopedHandle main_thread(::OpenThread(
264 THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION,
265 FALSE, main_thread_id));
266
267 bool have_context = false;
268 if (main_thread.IsValid()) {
269 DWORD suspend_count = ::SuspendThread(main_thread.Get());
270 const DWORD kSuspendFailed = static_cast<DWORD>(-1);
271 if (suspend_count != kSuspendFailed) {
272 // Best effort capture of the context.
273 thread_context.ContextFlags = CONTEXT_FLOATING_POINT | CONTEXT_SEGMENTS |
274 CONTEXT_INTEGER | CONTEXT_CONTROL;
275 if (::GetThreadContext(main_thread.Get(), &thread_context) == TRUE)
276 have_context = true;
277
278 ::ResumeThread(main_thread.Get());
279 }
280 }
281
282 // TODO(erikwright): Make the dump-type channel-dependent.
283 if (have_context) {
284 kasko::api::SendReportForProcess(
285 process.Handle(), main_thread_id, &exception_pointers,
286 kasko::api::LARGER_DUMP_TYPE, key_buffers.data(), value_buffers.data());
287 } else {
288 kasko::api::SendReportForProcess(process.Handle(), 0, nullptr,
289 kasko::api::LARGER_DUMP_TYPE,
290 key_buffers.data(), value_buffers.data());
291 }
292 }
293
294 void LoggedDeregisterEventSource(HANDLE event_source_handle) {
295 if (!::DeregisterEventSource(event_source_handle))
296 DPLOG(ERROR) << "DeregisterEventSource";
297 }
298
299 void LoggedLocalFree(PSID sid) {
300 if (::LocalFree(sid) != nullptr)
301 DPLOG(ERROR) << "LocalFree";
302 }
303
304 void OnCrashReportUpload(void* context,
305 const base::char16* report_id,
306 const base::char16* minidump_path,
307 const base::char16* const* keys,
308 const base::char16* const* values) {
309 // Open the event source.
310 HANDLE event_source_handle = ::RegisterEventSource(NULL, L"Chrome");
311 if (!event_source_handle) {
312 PLOG(ERROR) << "RegisterEventSource";
313 return;
314 }
315 // Ensure cleanup on scope exit.
316 base::ScopedClosureRunner deregister_event_source(
317 base::Bind(&LoggedDeregisterEventSource, event_source_handle));
318
319 // Get the user's SID for the log record.
320 base::string16 sid_string;
321 PSID sid = nullptr;
322 if (base::win::GetUserSidString(&sid_string)) {
323 if (!sid_string.empty()) {
324 if (!::ConvertStringSidToSid(sid_string.c_str(), &sid))
325 DPLOG(ERROR) << "ConvertStringSidToSid";
326 DCHECK(sid);
327 }
328 }
329 // Ensure cleanup on scope exit.
330 base::ScopedClosureRunner free_sid(
331 base::Bind(&LoggedLocalFree, base::Unretained(sid)));
332
333 // Generate the message.
334 // Note that the format of this message must match the consumer in
335 // chrome/browser/crash_upload_list_win.cc.
336 base::string16 message =
337 L"Crash uploaded. Id=" + base::string16(report_id) + L".";
338
339 // Matches Omaha.
340 const int kCrashUploadEventId = 2;
341
342 // Report the event.
343 const base::char16* strings[] = {message.c_str()};
344 if (!::ReportEvent(event_source_handle, EVENTLOG_INFORMATION_TYPE,
345 0, // category
346 kCrashUploadEventId, sid,
347 1, // count
348 0, strings, nullptr)) {
349 DPLOG(ERROR);
350 }
351 }
352
353 #endif // BUILDFLAG(ENABLE_KASKO)
354
355 } // namespace 196 } // namespace
356 197
357 // The main entry point to the watcher, declared as extern "C" to avoid name 198 // The main entry point to the watcher, declared as extern "C" to avoid name
358 // mangling. 199 // mangling.
359 extern "C" int WatcherMain(const base::char16* registry_path, 200 extern "C" int WatcherMain(const base::char16* registry_path,
360 HANDLE process_handle, 201 HANDLE process_handle,
361 DWORD main_thread_id, 202 DWORD main_thread_id,
362 HANDLE on_initialized_event_handle, 203 HANDLE on_initialized_event_handle,
363 const base::char16* browser_data_directory, 204 const base::char16* browser_data_directory,
364 const base::char16* channel_name) { 205 const base::char16* channel_name) {
365 base::Process process(process_handle); 206 base::Process process(process_handle);
366 base::win::ScopedHandle on_initialized_event(on_initialized_event_handle); 207 base::win::ScopedHandle on_initialized_event(on_initialized_event_handle);
367 208
368 // The exit manager is in charge of calling the dtors of singletons. 209 // The exit manager is in charge of calling the dtors of singletons.
369 base::AtExitManager exit_manager; 210 base::AtExitManager exit_manager;
370 // Initialize the commandline singleton from the environment. 211 // Initialize the commandline singleton from the environment.
371 base::CommandLine::Init(0, nullptr); 212 base::CommandLine::Init(0, nullptr);
372 213
373 logging::LogEventProvider::Initialize(kChromeWatcherTraceProviderName); 214 logging::LogEventProvider::Initialize(kChromeWatcherTraceProviderName);
374 215
375 // Arrange to be shut down as late as possible, as we want to outlive 216 // Arrange to be shut down as late as possible, as we want to outlive
376 // chrome.exe in order to report its exit status. 217 // chrome.exe in order to report its exit status.
377 ::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY); 218 ::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);
378 219
379 base::Callback<void(const base::Process&)> on_hung_callback; 220 base::Callback<void(const base::Process&)> on_hung_callback;
380 221
381 #if BUILDFLAG(ENABLE_KASKO) 222 #if BUILDFLAG(ENABLE_KASKO)
382 base::string16 crash_server; 223 bool launched_kasko = InitializeKaskoReporter(GetKaskoEndpoint(process.Pid()),
383 GetKaskoCrashServerUrl(&crash_server); 224 browser_data_directory);
384 225
385 base::FilePath crash_reports_base_dir;
386 GetKaskoCrashReportsBaseDir(browser_data_directory, &crash_reports_base_dir);
387 bool launched_kasko = kasko::api::InitializeReporter(
388 GetKaskoEndpoint(process.Pid()).c_str(),
389 crash_server.c_str(),
390 crash_reports_base_dir
391 .Append(L"Crash Reports")
392 .value()
393 .c_str(),
394 crash_reports_base_dir
395 .Append(kPermanentlyFailedReportsSubdir)
396 .value()
397 .c_str(),
398 &OnCrashReportUpload, nullptr);
399 #if BUILDFLAG(ENABLE_KASKO_HANG_REPORTS) 226 #if BUILDFLAG(ENABLE_KASKO_HANG_REPORTS)
400 // Only activate hang reports for the canary channel. For testing purposes, 227 // Only activate hang reports for the canary channel. For testing purposes,
401 // Chrome instances with no channels will also report hangs. 228 // Chrome instances with no channels will also report hangs.
402 if (launched_kasko && 229 if (launched_kasko &&
403 (base::StringPiece16(channel_name) == L"" || 230 (base::StringPiece16(channel_name) == L"" ||
404 base::StringPiece16(channel_name) == installer::kChromeChannelCanary)) { 231 base::StringPiece16(channel_name) == installer::kChromeChannelCanary)) {
405 on_hung_callback = 232 on_hung_callback = base::Bind(&DumpHungProcess, main_thread_id,
406 base::Bind(&DumpHungBrowserProcess, main_thread_id, channel_name); 233 channel_name, L"hung-process");
407 } 234 }
408 #endif // BUILDFLAG(ENABLE_KASKO_HANG_REPORTS) 235 #endif // BUILDFLAG(ENABLE_KASKO_HANG_REPORTS)
409 #endif // BUILDFLAG(ENABLE_KASKO) 236 #endif // BUILDFLAG(ENABLE_KASKO)
410 237
411 // Run a UI message loop on the main thread. 238 // Run a UI message loop on the main thread.
412 base::MessageLoop msg_loop(base::MessageLoop::TYPE_UI); 239 base::MessageLoop msg_loop(base::MessageLoop::TYPE_UI);
413 msg_loop.set_thread_name("WatcherMainThread"); 240 msg_loop.set_thread_name("WatcherMainThread");
414 241
415 base::RunLoop run_loop; 242 base::RunLoop run_loop;
416 BrowserMonitor monitor(&run_loop, registry_path); 243 BrowserMonitor monitor(&run_loop, registry_path);
417 if (!monitor.StartWatching(registry_path, process.Duplicate(), 244 if (!monitor.StartWatching(registry_path, process.Duplicate(),
418 std::move(on_initialized_event))) { 245 std::move(on_initialized_event))) {
419 return 1; 246 return 1;
420 } 247 }
421 248
422 { 249 {
423 // Scoped to force |hang_monitor| destruction before Kasko is shut down. 250 // Scoped to force |hang_monitor| destruction before Kasko is shut down.
424 browser_watcher::WindowHangMonitor hang_monitor( 251 browser_watcher::WindowHangMonitor hang_monitor(
425 base::TimeDelta::FromSeconds(60), base::TimeDelta::FromSeconds(20), 252 base::TimeDelta::FromSeconds(60), base::TimeDelta::FromSeconds(20),
426 base::Bind(&OnWindowEvent, registry_path, 253 base::Bind(&OnWindowEvent, registry_path,
427 base::Passed(process.Duplicate()), on_hung_callback)); 254 base::Passed(process.Duplicate()), on_hung_callback));
428 hang_monitor.Initialize(process.Duplicate()); 255 hang_monitor.Initialize(process.Duplicate());
429 256
430 run_loop.Run(); 257 run_loop.Run();
431 } 258 }
432 259
433 #if BUILDFLAG(ENABLE_KASKO) 260 #if BUILDFLAG(ENABLE_KASKO)
434 if (launched_kasko) 261 if (launched_kasko)
435 kasko::api::ShutdownReporter(); 262 ShutdownKaskoReporter();
436 #endif // BUILDFLAG(ENABLE_KASKO) 263 #endif // BUILDFLAG(ENABLE_KASKO)
437 264
438 // Wind logging down. 265 // Wind logging down.
439 logging::LogEventProvider::Uninitialize(); 266 logging::LogEventProvider::Uninitialize();
440 267
441 return 0; 268 return 0;
442 } 269 }
443 270
444 static_assert( 271 static_assert(
445 std::is_same<decltype(&WatcherMain), ChromeWatcherMainFunction>::value, 272 std::is_same<decltype(&WatcherMain), ChromeWatcherMainFunction>::value,
446 "WatcherMain() has wrong type"); 273 "WatcherMain() has wrong type");
OLDNEW
« no previous file with comments | « chrome/chrome_watcher/chrome_watcher.gypi ('k') | chrome/chrome_watcher/kasko_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698