Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/app/breakpad_win.h" | 5 #include "chrome/app/breakpad_win.h" |
| 6 | 6 |
| 7 #include <shellapi.h> | 7 #include <shellapi.h> |
| 8 #include <tchar.h> | 8 #include <tchar.h> |
| 9 #include <userenv.h> | 9 #include <userenv.h> |
| 10 #include <windows.h> | 10 #include <windows.h> |
| 11 #include <winnt.h> | 11 #include <winnt.h> |
| 12 | 12 |
| 13 #include <algorithm> | 13 #include <algorithm> |
| 14 #include <vector> | 14 #include <vector> |
| 15 | 15 |
| 16 #include "base/atomicops.h" | |
| 16 #include "base/basictypes.h" | 17 #include "base/basictypes.h" |
| 17 #include "base/base_switches.h" | 18 #include "base/base_switches.h" |
| 18 #include "base/command_line.h" | 19 #include "base/command_line.h" |
| 19 #include "base/debug/crash_logging.h" | 20 #include "base/debug/crash_logging.h" |
| 20 #include "base/environment.h" | 21 #include "base/environment.h" |
| 21 #include "base/memory/scoped_ptr.h" | 22 #include "base/memory/scoped_ptr.h" |
| 23 #include "base/strings/safe_sprintf.h" | |
| 22 #include "base/strings/string16.h" | 24 #include "base/strings/string16.h" |
| 23 #include "base/strings/string_split.h" | 25 #include "base/strings/string_split.h" |
| 24 #include "base/strings/string_util.h" | 26 #include "base/strings/string_util.h" |
| 25 #include "base/strings/stringprintf.h" | 27 #include "base/strings/stringprintf.h" |
| 26 #include "base/strings/utf_string_conversions.h" | 28 #include "base/strings/utf_string_conversions.h" |
| 27 #include "base/win/metro.h" | 29 #include "base/win/metro.h" |
| 28 #include "base/win/pe_image.h" | 30 #include "base/win/pe_image.h" |
| 29 #include "base/win/registry.h" | 31 #include "base/win/registry.h" |
| 30 #include "base/win/win_util.h" | 32 #include "base/win/win_util.h" |
| 31 #include "breakpad/src/client/windows/handler/exception_handler.h" | 33 #include "breakpad/src/client/windows/handler/exception_handler.h" |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 112 DynamicEntriesMap; | 114 DynamicEntriesMap; |
| 113 DynamicEntriesMap* g_dynamic_entries = NULL; | 115 DynamicEntriesMap* g_dynamic_entries = NULL; |
| 114 // Allow for 128 entries. POSIX uses 64 entries of 256 bytes, so Windows needs | 116 // Allow for 128 entries. POSIX uses 64 entries of 256 bytes, so Windows needs |
| 115 // 256 entries of 64 bytes to match. See CustomInfoEntry::kValueMaxLength in | 117 // 256 entries of 64 bytes to match. See CustomInfoEntry::kValueMaxLength in |
| 116 // Breakpad. | 118 // Breakpad. |
| 117 const size_t kMaxDynamicEntries = 256; | 119 const size_t kMaxDynamicEntries = 256; |
| 118 | 120 |
| 119 // Maximum length for plugin path to include in plugin crash reports. | 121 // Maximum length for plugin path to include in plugin crash reports. |
| 120 const size_t kMaxPluginPathLength = 256; | 122 const size_t kMaxPluginPathLength = 256; |
| 121 | 123 |
| 122 // These values track the browser crash dump registry key and pre-computed | 124 // The value name prefix will be of the form {chrome-version}-{pid}-{timestamp} |
| 123 // registry value name, which we use as a "smoke-signal" for counting dumps. | 125 // (i.e., "#####.#####.#####.#####-########-########") which easily fits into a |
| 124 static HKEY g_browser_crash_dump_regkey = NULL; | 126 // 63 character buffer. |
| 125 static const wchar_t kBrowserCrashDumpValueFormatStr[] = L"%08x-%08x"; | 127 const char kBrowserCrashDumpPrefixTemplate[] = "%s-%08x-%08x"; |
| 126 static const int kBrowserCrashDumpValueLength = 17; | 128 const size_t kBrowserCrashDumpPrefixLength = 63; |
| 127 static wchar_t g_browser_crash_dump_value[kBrowserCrashDumpValueLength+1] = {0}; | 129 char g_browser_crash_dump_prefix[kBrowserCrashDumpPrefixLength + 1] = {}; |
| 130 | |
| 131 // These registry key to which we'll write a value for each crash dump attempt. | |
| 132 HKEY g_browser_crash_dump_regkey = NULL; | |
| 133 | |
| 134 // A atomic counter to make each crash dump value name unique. | |
| 135 base::subtle::Atomic32 g_browser_crash_dump_count = 0; | |
| 128 | 136 |
| 129 void InitBrowserCrashDumpsRegKey() { | 137 void InitBrowserCrashDumpsRegKey() { |
| 130 DCHECK(g_browser_crash_dump_regkey == NULL); | 138 DCHECK(g_browser_crash_dump_regkey == NULL); |
| 131 | 139 |
| 132 base::string16 key_str(chrome::kBrowserCrashDumpAttemptsRegistryPath); | |
| 133 key_str += L"\\"; | |
| 134 key_str += UTF8ToWide(chrome::kChromeVersion); | |
| 135 | |
| 136 base::win::RegKey regkey; | 140 base::win::RegKey regkey; |
| 137 if (regkey.Create(HKEY_CURRENT_USER, | 141 if (regkey.Create(HKEY_CURRENT_USER, |
| 138 key_str.c_str(), | 142 chrome::kBrowserCrashDumpAttemptsRegistryPath, |
| 139 KEY_ALL_ACCESS) != ERROR_SUCCESS) { | 143 KEY_ALL_ACCESS) != ERROR_SUCCESS) { |
| 140 return; | 144 return; |
| 141 } | 145 } |
| 142 | 146 |
| 143 g_browser_crash_dump_regkey = regkey.Take(); | 147 // We use the current process id and the current tick count as a (hopefully) |
| 144 | |
| 145 // We use the current process id and the curren tick count as a (hopefully) | |
| 146 // unique combination for the crash dump value. There's a small chance that | 148 // unique combination for the crash dump value. There's a small chance that |
| 147 // across a reboot we might have a crash dump signal written, and the next | 149 // across a reboot we might have a crash dump signal written, and the next |
| 148 // browser process might have the same process id and tick count, but crash | 150 // browser process might have the same process id and tick count, but crash |
| 149 // before consuming the signal (overwriting the signal with an identical one). | 151 // before consuming the signal (overwriting the signal with an identical one). |
| 150 // For now, we're willing to live with that risk. | 152 // For now, we're willing to live with that risk. |
| 151 int length = swprintf(g_browser_crash_dump_value, | 153 int length = base::strings::SafeSPrintf(g_browser_crash_dump_prefix, |
| 152 arraysize(g_browser_crash_dump_value), | 154 kBrowserCrashDumpPrefixTemplate, |
| 153 kBrowserCrashDumpValueFormatStr, | 155 chrome::kChromeVersion, |
| 154 ::GetCurrentProcessId(), | 156 ::GetCurrentProcessId(), |
| 155 ::GetTickCount()); | 157 ::GetTickCount()); |
| 156 DCHECK_EQ(kBrowserCrashDumpValueLength, length); | 158 if (length <= 0) { |
| 159 NOTREACHED(); | |
| 160 g_browser_crash_dump_prefix[0] = '\0'; | |
| 161 return; | |
| 162 } | |
| 163 | |
| 164 // Hold the registry key in a global for update on crash dump. | |
| 165 g_browser_crash_dump_regkey = regkey.Take(); | |
| 157 } | 166 } |
| 158 | 167 |
| 159 void SendSmokeSignalForCrashDump() { | 168 void RecordCrashDumpAttempt(bool is_real_crash) { |
| 160 if (g_browser_crash_dump_regkey != NULL) { | 169 // If we're not a browser (or the registry is unavailable to us for some |
| 161 base::win::RegKey regkey(g_browser_crash_dump_regkey); | 170 // reason) then there's nothing to do. |
| 162 regkey.WriteValue(g_browser_crash_dump_value, 1); | 171 if (g_browser_crash_dump_regkey == NULL) |
| 163 g_browser_crash_dump_regkey = NULL; | 172 return; |
| 173 | |
| 174 // Generate the final value name we'll use (appends the crash number to the | |
| 175 // base value name). | |
| 176 const size_t kMaxValueSize = 2 * kBrowserCrashDumpPrefixLength; | |
| 177 char value_name[kMaxValueSize + 1] = {}; | |
| 178 int length = base::strings::SafeSPrintf( | |
| 179 value_name, | |
| 180 "%s-%x", | |
| 181 g_browser_crash_dump_prefix, | |
| 182 base::subtle::NoBarrier_AtomicIncrement(&g_browser_crash_dump_count, 1)); | |
| 183 | |
| 184 if (length > 0) { | |
| 185 DWORD value_dword = is_real_crash ? 1 : 0; | |
| 186 ::RegSetValueExA(g_browser_crash_dump_regkey, value_name, 0, REG_DWORD, | |
| 187 reinterpret_cast<BYTE*>(&value_dword), | |
| 188 sizeof(value_dword)); | |
| 164 } | 189 } |
| 165 } | 190 } |
| 166 | 191 |
| 167 // Dumps the current process memory. | 192 // Dumps the current process memory. |
| 168 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { | 193 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { |
| 169 if (g_breakpad) { | 194 if (g_breakpad) { |
| 170 SendSmokeSignalForCrashDump(); | |
| 171 g_breakpad->WriteMinidump(); | 195 g_breakpad->WriteMinidump(); |
| 172 } | 196 } |
| 173 } | 197 } |
| 174 | 198 |
| 175 // Used for dumping a process state when there is no crash. | 199 // Used for dumping a process state when there is no crash. |
| 176 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { | 200 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { |
| 177 if (g_dumphandler_no_crash) { | 201 if (g_dumphandler_no_crash) { |
| 178 SendSmokeSignalForCrashDump(); | |
| 179 g_dumphandler_no_crash->WriteMinidump(); | 202 g_dumphandler_no_crash->WriteMinidump(); |
| 180 } | 203 } |
| 181 } | 204 } |
| 182 | 205 |
| 183 // We need to prevent ICF from folding DumpForHangDebuggingThread() and | 206 // We need to prevent ICF from folding DumpForHangDebuggingThread() and |
| 184 // DumpProcessWithoutCrashThread() together, since that makes them | 207 // DumpProcessWithoutCrashThread() together, since that makes them |
| 185 // indistinguishable in crash dumps. We do this by making the function | 208 // indistinguishable in crash dumps. We do this by making the function |
| 186 // bodies unique, and prevent optimization from shuffling things around. | 209 // bodies unique, and prevent optimization from shuffling things around. |
| 187 MSVC_DISABLE_OPTIMIZE() | 210 MSVC_DISABLE_OPTIMIZE() |
| 188 MSVC_PUSH_DISABLE_WARNING(4748) | 211 MSVC_PUSH_DISABLE_WARNING(4748) |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 215 | 238 |
| 216 extern "C" HANDLE __declspec(dllexport) __cdecl | 239 extern "C" HANDLE __declspec(dllexport) __cdecl |
| 217 InjectDumpForHangDebugging(HANDLE process) { | 240 InjectDumpForHangDebugging(HANDLE process) { |
| 218 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, | 241 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, |
| 219 0, 0, NULL); | 242 0, 0, NULL); |
| 220 } | 243 } |
| 221 | 244 |
| 222 extern "C" void DumpProcessAbnormalSignature() { | 245 extern "C" void DumpProcessAbnormalSignature() { |
| 223 if (!g_breakpad) | 246 if (!g_breakpad) |
| 224 return; | 247 return; |
| 225 SendSmokeSignalForCrashDump(); | |
| 226 g_custom_entries->push_back( | 248 g_custom_entries->push_back( |
| 227 google_breakpad::CustomInfoEntry(L"unusual-crash-signature", L"")); | 249 google_breakpad::CustomInfoEntry(L"unusual-crash-signature", L"")); |
| 228 g_breakpad->WriteMinidump(); | 250 g_breakpad->WriteMinidump(); |
| 229 } | 251 } |
| 230 | 252 |
| 231 // Reduces the size of the string |str| to a max of 64 chars. Required because | 253 // Reduces the size of the string |str| to a max of 64 chars. Required because |
| 232 // breakpad's CustomInfoEntry raises an invalid_parameter error if the string | 254 // breakpad's CustomInfoEntry raises an invalid_parameter error if the string |
| 233 // we want to set is longer. | 255 // we want to set is longer. |
| 234 std::wstring TrimToBreakpadMax(const std::wstring& str) { | 256 std::wstring TrimToBreakpadMax(const std::wstring& str) { |
| 235 std::wstring shorter(str); | 257 std::wstring shorter(str); |
| (...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 581 } | 603 } |
| 582 | 604 |
| 583 // flag to indicate that we are already handling an exception. | 605 // flag to indicate that we are already handling an exception. |
| 584 volatile LONG handling_exception = 0; | 606 volatile LONG handling_exception = 0; |
| 585 | 607 |
| 586 // This callback is used when there is no crash. Note: Unlike the | 608 // This callback is used when there is no crash. Note: Unlike the |
| 587 // |FilterCallback| below this does not do dupe detection. It is upto the caller | 609 // |FilterCallback| below this does not do dupe detection. It is upto the caller |
| 588 // to implement it. | 610 // to implement it. |
| 589 bool FilterCallbackWhenNoCrash( | 611 bool FilterCallbackWhenNoCrash( |
| 590 void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { | 612 void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { |
| 613 RecordCrashDumpAttempt(false); | |
| 591 return true; | 614 return true; |
| 592 } | 615 } |
| 593 | 616 |
| 594 // This callback is executed when the Chrome process has crashed and *before* | 617 // This callback is executed when the Chrome process has crashed and *before* |
| 595 // the crash dump is created. To prevent duplicate crash reports we | 618 // the crash dump is created. To prevent duplicate crash reports we |
| 596 // make every thread calling this method, except the very first one, | 619 // make every thread calling this method, except the very first one, |
| 597 // go to sleep. | 620 // go to sleep. |
| 598 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { | 621 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { |
| 599 // Capture every thread except the first one in the sleep. We don't | 622 // Capture every thread except the first one in the sleep. We don't |
| 600 // want multiple threads to concurrently report exceptions. | 623 // want multiple threads to concurrently report exceptions. |
| 601 if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) { | 624 if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) { |
| 602 ::Sleep(INFINITE); | 625 ::Sleep(INFINITE); |
| 603 } | 626 } |
| 627 RecordCrashDumpAttempt(true); | |
|
cpu_(ooo_6.6-7.5)
2013/09/13 21:04:28
Is this thing here temporary? I want to keep the c
Roger McFarlane (Chromium)
2013/09/13 21:30:46
It's here for as long as want to keep these histog
| |
| 604 return true; | 628 return true; |
| 605 } | 629 } |
| 606 | 630 |
| 607 // Previous unhandled filter. Will be called if not null when we | 631 // Previous unhandled filter. Will be called if not null when we |
| 608 // intercept a crash. | 632 // intercept a crash. |
| 609 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL; | 633 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL; |
| 610 | 634 |
| 611 // Exception filter used when breakpad is not enabled. We just display | 635 // Exception filter used when breakpad is not enabled. We just display |
| 612 // the "Do you want to restart" message and then we call the previous filter. | 636 // the "Do you want to restart" message and then we call the previous filter. |
| 613 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) { | 637 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) { |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 766 | 790 |
| 767 // Crashes the process after generating a dump for the provided exception. Note | 791 // Crashes the process after generating a dump for the provided exception. Note |
| 768 // that the crash reporter should be initialized before calling this function | 792 // that the crash reporter should be initialized before calling this function |
| 769 // for it to do anything. | 793 // for it to do anything. |
| 770 // NOTE: This function is used by SyzyASAN to invoke a crash. If you change the | 794 // NOTE: This function is used by SyzyASAN to invoke a crash. If you change the |
| 771 // the name or signature of this function you will break SyzyASAN instrumented | 795 // the name or signature of this function you will break SyzyASAN instrumented |
| 772 // releases of Chrome. Please contact syzygy-team@chromium.org before doing so! | 796 // releases of Chrome. Please contact syzygy-team@chromium.org before doing so! |
| 773 extern "C" int __declspec(dllexport) CrashForException( | 797 extern "C" int __declspec(dllexport) CrashForException( |
| 774 EXCEPTION_POINTERS* info) { | 798 EXCEPTION_POINTERS* info) { |
| 775 if (g_breakpad) { | 799 if (g_breakpad) { |
| 776 SendSmokeSignalForCrashDump(); | |
| 777 g_breakpad->WriteMinidumpForException(info); | 800 g_breakpad->WriteMinidumpForException(info); |
| 778 // Patched stub exists based on conditions (See InitCrashReporter). | 801 // Patched stub exists based on conditions (See InitCrashReporter). |
| 779 // As a side note this function also gets called from | 802 // As a side note this function also gets called from |
| 780 // WindowProcExceptionFilter. | 803 // WindowProcExceptionFilter. |
| 781 if (g_real_terminate_process_stub == NULL) { | 804 if (g_real_terminate_process_stub == NULL) { |
| 782 ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED); | 805 ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED); |
| 783 } else { | 806 } else { |
| 784 NtTerminateProcessPtr real_terminate_proc = | 807 NtTerminateProcessPtr real_terminate_proc = |
| 785 reinterpret_cast<NtTerminateProcessPtr>( | 808 reinterpret_cast<NtTerminateProcessPtr>( |
| 786 static_cast<char*>(g_real_terminate_process_stub)); | 809 static_cast<char*>(g_real_terminate_process_stub)); |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1025 previous_filter = SetUnhandledExceptionFilter(filter); | 1048 previous_filter = SetUnhandledExceptionFilter(filter); |
| 1026 } | 1049 } |
| 1027 | 1050 |
| 1028 void StringVectorToCStringVector(const std::vector<std::wstring>& wstrings, | 1051 void StringVectorToCStringVector(const std::vector<std::wstring>& wstrings, |
| 1029 std::vector<const wchar_t*>* cstrings) { | 1052 std::vector<const wchar_t*>* cstrings) { |
| 1030 cstrings->clear(); | 1053 cstrings->clear(); |
| 1031 cstrings->reserve(wstrings.size()); | 1054 cstrings->reserve(wstrings.size()); |
| 1032 for (size_t i = 0; i < wstrings.size(); ++i) | 1055 for (size_t i = 0; i < wstrings.size(); ++i) |
| 1033 cstrings->push_back(wstrings[i].c_str()); | 1056 cstrings->push_back(wstrings[i].c_str()); |
| 1034 } | 1057 } |
| OLD | NEW |