| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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"); |
| OLD | NEW |