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 |