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 |