| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "components/crash/content/app/crashpad.h" | 5 #include "components/crash/content/app/crashpad.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <string.h> | 8 #include <string.h> |
| 9 | 9 |
| 10 #if BUILDFLAG(ENABLE_KASKO) | |
| 11 #include <psapi.h> | |
| 12 #endif // BUILDFLAG(ENABLE_KASKO) | |
| 13 | |
| 14 #include <algorithm> | 10 #include <algorithm> |
| 15 #include <map> | 11 #include <map> |
| 16 #include <vector> | 12 #include <vector> |
| 17 | 13 |
| 18 #include "base/auto_reset.h" | 14 #include "base/auto_reset.h" |
| 19 #include "base/base_paths.h" | 15 #include "base/base_paths.h" |
| 20 #include "base/command_line.h" | 16 #include "base/command_line.h" |
| 21 #include "base/debug/crash_logging.h" | 17 #include "base/debug/crash_logging.h" |
| 22 #include "base/debug/dump_without_crashing.h" | 18 #include "base/debug/dump_without_crashing.h" |
| 23 #include "base/logging.h" | 19 #include "base/logging.h" |
| 24 #include "base/macros.h" | 20 #include "base/macros.h" |
| 25 #include "base/strings/string_number_conversions.h" | 21 #include "base/strings/string_number_conversions.h" |
| 26 #include "base/strings/string_piece.h" | 22 #include "base/strings/string_piece.h" |
| 27 #include "base/strings/stringprintf.h" | 23 #include "base/strings/stringprintf.h" |
| 28 #include "base/strings/sys_string_conversions.h" | 24 #include "base/strings/sys_string_conversions.h" |
| 29 #include "base/strings/utf_string_conversions.h" | 25 #include "base/strings/utf_string_conversions.h" |
| 30 #include "build/build_config.h" | 26 #include "build/build_config.h" |
| 31 #include "components/crash/content/app/crash_reporter_client.h" | 27 #include "components/crash/content/app/crash_reporter_client.h" |
| 32 #include "third_party/crashpad/crashpad/client/crash_report_database.h" | 28 #include "third_party/crashpad/crashpad/client/crash_report_database.h" |
| 33 #include "third_party/crashpad/crashpad/client/crashpad_client.h" | 29 #include "third_party/crashpad/crashpad/client/crashpad_client.h" |
| 34 #include "third_party/crashpad/crashpad/client/crashpad_info.h" | 30 #include "third_party/crashpad/crashpad/client/crashpad_info.h" |
| 35 #include "third_party/crashpad/crashpad/client/settings.h" | 31 #include "third_party/crashpad/crashpad/client/settings.h" |
| 36 #include "third_party/crashpad/crashpad/client/simple_string_dictionary.h" | 32 #include "third_party/crashpad/crashpad/client/simple_string_dictionary.h" |
| 37 #include "third_party/crashpad/crashpad/client/simulate_crash.h" | 33 #include "third_party/crashpad/crashpad/client/simulate_crash.h" |
| 38 | 34 |
| 39 #if defined(OS_POSIX) | 35 #if defined(OS_POSIX) |
| 40 #include <unistd.h> | 36 #include <unistd.h> |
| 41 #endif // OS_POSIX | 37 #endif // OS_POSIX |
| 42 | 38 |
| 43 #if BUILDFLAG(ENABLE_KASKO) | |
| 44 #include "base/win/scoped_handle.h" | |
| 45 #include "third_party/crashpad/crashpad/snapshot/api/module_annotations_win.h" | |
| 46 #endif | |
| 47 | |
| 48 namespace crash_reporter { | 39 namespace crash_reporter { |
| 49 | 40 |
| 50 namespace { | 41 namespace { |
| 51 | 42 |
| 52 crashpad::SimpleStringDictionary* g_simple_string_dictionary; | 43 crashpad::SimpleStringDictionary* g_simple_string_dictionary; |
| 53 crashpad::CrashReportDatabase* g_database; | 44 crashpad::CrashReportDatabase* g_database; |
| 54 | 45 |
| 55 void SetCrashKeyValue(const base::StringPiece& key, | 46 void SetCrashKeyValue(const base::StringPiece& key, |
| 56 const base::StringPiece& value) { | 47 const base::StringPiece& value) { |
| 57 g_simple_string_dictionary->SetKeyValue(key.data(), value.data()); | 48 g_simple_string_dictionary->SetKeyValue(key.data(), value.data()); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 96 | 87 |
| 97 // Rather than including the code to force the crash here, allow the caller to | 88 // Rather than including the code to force the crash here, allow the caller to |
| 98 // do it. | 89 // do it. |
| 99 return false; | 90 return false; |
| 100 } | 91 } |
| 101 | 92 |
| 102 void DumpWithoutCrashing() { | 93 void DumpWithoutCrashing() { |
| 103 CRASHPAD_SIMULATE_CRASH(); | 94 CRASHPAD_SIMULATE_CRASH(); |
| 104 } | 95 } |
| 105 | 96 |
| 106 #if BUILDFLAG(ENABLE_KASKO) | |
| 107 // TODO(ananta) | |
| 108 // We cannot depend on functionality in base which pulls in dependencies on | |
| 109 // user32 directly or indirectly. The GetLoadedModulesSnapshot is a copy of the | |
| 110 // function in base/win/win_util.cc. Depending on the base function pulls in | |
| 111 // dependencies on user32 due to other functionality in win_util.cc. This | |
| 112 // function should be removed when KASKO is removed. | |
| 113 bool GetLoadedModulesSnapshot(HANDLE process, std::vector<HMODULE>* snapshot) { | |
| 114 DCHECK(snapshot); | |
| 115 DCHECK_EQ(0u, snapshot->size()); | |
| 116 snapshot->resize(128); | |
| 117 | |
| 118 // We will retry at least once after first determining |bytes_required|. If | |
| 119 // the list of modules changes after we receive |bytes_required| we may retry | |
| 120 // more than once. | |
| 121 int retries_remaining = 5; | |
| 122 do { | |
| 123 DWORD bytes_required = 0; | |
| 124 // EnumProcessModules returns 'success' even if the buffer size is too | |
| 125 // small. | |
| 126 DCHECK_GE(std::numeric_limits<DWORD>::max(), | |
| 127 snapshot->size() * sizeof(HMODULE)); | |
| 128 if (!::EnumProcessModules( | |
| 129 process, &(*snapshot)[0], | |
| 130 static_cast<DWORD>(snapshot->size() * sizeof(HMODULE)), | |
| 131 &bytes_required)) { | |
| 132 DPLOG(ERROR) << "::EnumProcessModules failed."; | |
| 133 return false; | |
| 134 } | |
| 135 DCHECK_EQ(0u, bytes_required % sizeof(HMODULE)); | |
| 136 size_t num_modules = bytes_required / sizeof(HMODULE); | |
| 137 if (num_modules <= snapshot->size()) { | |
| 138 // Buffer size was too big, presumably because a module was unloaded. | |
| 139 snapshot->erase(snapshot->begin() + num_modules, snapshot->end()); | |
| 140 return true; | |
| 141 } else if (num_modules == 0) { | |
| 142 DLOG(ERROR) << "Can't determine the module list size."; | |
| 143 return false; | |
| 144 } else { | |
| 145 // Buffer size was too small. Try again with a larger buffer. A little | |
| 146 // more room is given to avoid multiple expensive calls to | |
| 147 // ::EnumProcessModules() just because one module has been added. | |
| 148 snapshot->resize(num_modules + 8, NULL); | |
| 149 } | |
| 150 } while (--retries_remaining); | |
| 151 | |
| 152 DLOG(ERROR) << "Failed to enumerate modules."; | |
| 153 return false; | |
| 154 } | |
| 155 | |
| 156 HMODULE GetModuleInProcess(base::ProcessHandle process, | |
| 157 const wchar_t* module_name) { | |
| 158 std::vector<HMODULE> modules_snapshot; | |
| 159 if (!GetLoadedModulesSnapshot(process, &modules_snapshot)) | |
| 160 return nullptr; | |
| 161 | |
| 162 for (HMODULE module : modules_snapshot) { | |
| 163 wchar_t current_module_name[MAX_PATH]; | |
| 164 if (!::GetModuleBaseName(process, module, current_module_name, MAX_PATH)) | |
| 165 continue; | |
| 166 | |
| 167 if (std::wcscmp(module_name, current_module_name) == 0) | |
| 168 return module; | |
| 169 } | |
| 170 return nullptr; | |
| 171 } | |
| 172 #endif // BUILDFLAG(ENABLE_KASKO) | |
| 173 | |
| 174 void InitializeCrashpadImpl(bool initial_client, | 97 void InitializeCrashpadImpl(bool initial_client, |
| 175 const std::string& process_type, | 98 const std::string& process_type, |
| 176 bool embedded_handler) { | 99 bool embedded_handler) { |
| 177 static bool initialized = false; | 100 static bool initialized = false; |
| 178 DCHECK(!initialized); | 101 DCHECK(!initialized); |
| 179 initialized = true; | 102 initialized = true; |
| 180 | 103 |
| 181 const bool browser_process = process_type.empty(); | 104 const bool browser_process = process_type.empty(); |
| 182 CrashReporterClient* crash_reporter_client = GetCrashReporterClient(); | 105 CrashReporterClient* crash_reporter_client = GetCrashReporterClient(); |
| 183 | 106 |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 356 report.state = ReportUploadState::Pending; | 279 report.state = ReportUploadState::Pending; |
| 357 reports->push_back(report); | 280 reports->push_back(report); |
| 358 } | 281 } |
| 359 | 282 |
| 360 std::sort(reports->begin(), reports->end(), | 283 std::sort(reports->begin(), reports->end(), |
| 361 [](const Report& a, const Report& b) { | 284 [](const Report& a, const Report& b) { |
| 362 return a.capture_time > b.capture_time; | 285 return a.capture_time > b.capture_time; |
| 363 }); | 286 }); |
| 364 } | 287 } |
| 365 | 288 |
| 366 #if BUILDFLAG(ENABLE_KASKO) | |
| 367 | |
| 368 void GetCrashKeysForKasko(std::vector<kasko::api::CrashKey>* crash_keys) { | |
| 369 // Get the platform annotations. | |
| 370 std::map<std::string, std::string> annotations; | |
| 371 internal::GetPlatformCrashpadAnnotations(&annotations); | |
| 372 | |
| 373 // Reserve room for the GUID and the platform annotations. | |
| 374 crash_keys->clear(); | |
| 375 crash_keys->reserve( | |
| 376 g_simple_string_dictionary->GetCount() + 1 + annotations.size()); | |
| 377 | |
| 378 // Set the Crashpad client ID in the crash keys. | |
| 379 bool got_guid = false; | |
| 380 if (g_database) { | |
| 381 crashpad::Settings* settings = g_database->GetSettings(); | |
| 382 crashpad::UUID uuid; | |
| 383 if (settings->GetClientID(&uuid)) { | |
| 384 kasko::api::CrashKey kv; | |
| 385 wcsncpy_s(kv.name, L"guid", _TRUNCATE); | |
| 386 wcsncpy_s(kv.value, base::UTF8ToWide(uuid.ToString()).c_str(), _TRUNCATE); | |
| 387 crash_keys->push_back(kv); | |
| 388 got_guid = true; | |
| 389 } | |
| 390 } | |
| 391 | |
| 392 crashpad::SimpleStringDictionary::Iterator iter(*g_simple_string_dictionary); | |
| 393 for (;;) { | |
| 394 const auto* entry = iter.Next(); | |
| 395 if (!entry) | |
| 396 break; | |
| 397 | |
| 398 // Skip the 'guid' key if it was already set. | |
| 399 static const char kGuid[] = "guid"; | |
| 400 if (got_guid && ::strncmp(entry->key, kGuid, arraysize(kGuid)) == 0) | |
| 401 continue; | |
| 402 | |
| 403 // Skip any platform annotations as they'll be set below. | |
| 404 if (annotations.count(entry->key)) | |
| 405 continue; | |
| 406 | |
| 407 kasko::api::CrashKey kv; | |
| 408 wcsncpy_s(kv.name, base::UTF8ToWide(entry->key).c_str(), _TRUNCATE); | |
| 409 wcsncpy_s(kv.value, base::UTF8ToWide(entry->value).c_str(), _TRUNCATE); | |
| 410 crash_keys->push_back(kv); | |
| 411 } | |
| 412 | |
| 413 // Merge in the platform annotations. | |
| 414 for (const auto& entry : annotations) { | |
| 415 kasko::api::CrashKey kv; | |
| 416 wcsncpy_s(kv.name, base::UTF8ToWide(entry.first).c_str(), _TRUNCATE); | |
| 417 wcsncpy_s(kv.value, base::UTF8ToWide(entry.second).c_str(), _TRUNCATE); | |
| 418 crash_keys->push_back(kv); | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 void ReadMainModuleAnnotationsForKasko( | |
| 423 const base::Process& process, | |
| 424 std::vector<kasko::api::CrashKey>* crash_keys) { | |
| 425 // Reopen process with necessary access. | |
| 426 base::win::ScopedHandle process_handle(::OpenProcess( | |
| 427 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process.Pid())); | |
| 428 if (!process_handle.IsValid()) | |
| 429 return; | |
| 430 | |
| 431 // The executable name is the same for the browser process and the crash | |
| 432 // reporter. | |
| 433 wchar_t exe_file[MAX_PATH] = {}; | |
| 434 CHECK(::GetModuleFileName(nullptr, exe_file, arraysize(exe_file))); | |
| 435 | |
| 436 base::FilePath exe_path(exe_file); | |
| 437 | |
| 438 HMODULE module = GetModuleInProcess(process_handle.Get(), | |
| 439 exe_path.BaseName().value().c_str()); | |
| 440 if (!module) | |
| 441 return; | |
| 442 | |
| 443 std::map<std::string, std::string> annotations; | |
| 444 crashpad::ReadModuleAnnotations(process_handle.Get(), module, &annotations); | |
| 445 | |
| 446 // Append the annotations to the crash keys. | |
| 447 for (const auto& entry : annotations) { | |
| 448 kasko::api::CrashKey kv; | |
| 449 wcsncpy_s(kv.name, base::UTF8ToWide(entry.first).c_str(), _TRUNCATE); | |
| 450 wcsncpy_s(kv.value, base::UTF8ToWide(entry.second).c_str(), _TRUNCATE); | |
| 451 crash_keys->push_back(kv); | |
| 452 } | |
| 453 } | |
| 454 | |
| 455 #endif // BUILDFLAG(ENABLE_KASKO) | |
| 456 | |
| 457 } // namespace crash_reporter | 289 } // namespace crash_reporter |
| 458 | 290 |
| 459 #if defined(OS_WIN) | 291 #if defined(OS_WIN) |
| 460 | 292 |
| 461 extern "C" { | 293 extern "C" { |
| 462 | 294 |
| 463 // NOTE: This function is used by SyzyASAN to annotate crash reports. If you | 295 // NOTE: This function is used by SyzyASAN to annotate crash reports. If you |
| 464 // change the name or signature of this function you will break SyzyASAN | 296 // change the name or signature of this function you will break SyzyASAN |
| 465 // instrumented releases of Chrome. Please contact syzygy-team@chromium.org | 297 // instrumented releases of Chrome. Please contact syzygy-team@chromium.org |
| 466 // before doing so! See also http://crbug.com/567781. | 298 // before doing so! See also http://crbug.com/567781. |
| 467 void __declspec(dllexport) __cdecl SetCrashKeyValueImpl(const wchar_t* key, | 299 void __declspec(dllexport) __cdecl SetCrashKeyValueImpl(const wchar_t* key, |
| 468 const wchar_t* value) { | 300 const wchar_t* value) { |
| 469 crash_reporter::SetCrashKeyValue(base::UTF16ToUTF8(key), | 301 crash_reporter::SetCrashKeyValue(base::UTF16ToUTF8(key), |
| 470 base::UTF16ToUTF8(value)); | 302 base::UTF16ToUTF8(value)); |
| 471 } | 303 } |
| 472 | 304 |
| 473 void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl(const wchar_t* key) { | 305 void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl(const wchar_t* key) { |
| 474 crash_reporter::ClearCrashKey(base::UTF16ToUTF8(key)); | 306 crash_reporter::ClearCrashKey(base::UTF16ToUTF8(key)); |
| 475 } | 307 } |
| 476 | 308 |
| 477 } // extern "C" | 309 } // extern "C" |
| 478 | 310 |
| 479 #endif // OS_WIN | 311 #endif // OS_WIN |
| OLD | NEW |