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" |
22 #include "base/strings/string16.h" | 23 #include "base/strings/string16.h" |
23 #include "base/strings/string_split.h" | 24 #include "base/strings/string_split.h" |
24 #include "base/strings/string_util.h" | 25 #include "base/strings/string_util.h" |
25 #include "base/strings/stringprintf.h" | 26 #include "base/strings/stringprintf.h" |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
114 DynamicEntriesMap; | 115 DynamicEntriesMap; |
115 DynamicEntriesMap* g_dynamic_entries = NULL; | 116 DynamicEntriesMap* g_dynamic_entries = NULL; |
116 // Allow for 128 entries. POSIX uses 64 entries of 256 bytes, so Windows needs | 117 // Allow for 128 entries. POSIX uses 64 entries of 256 bytes, so Windows needs |
117 // 256 entries of 64 bytes to match. See CustomInfoEntry::kValueMaxLength in | 118 // 256 entries of 64 bytes to match. See CustomInfoEntry::kValueMaxLength in |
118 // Breakpad. | 119 // Breakpad. |
119 const size_t kMaxDynamicEntries = 256; | 120 const size_t kMaxDynamicEntries = 256; |
120 | 121 |
121 // Maximum length for plugin path to include in plugin crash reports. | 122 // Maximum length for plugin path to include in plugin crash reports. |
122 const size_t kMaxPluginPathLength = 256; | 123 const size_t kMaxPluginPathLength = 256; |
123 | 124 |
124 // These values track the browser crash dump registry key and pre-computed | 125 // The value name prefix will be of the form {chrome-version}-{pid}-{timestamp} |
125 // registry value name, which we use as a "smoke-signal" for counting dumps. | 126 // (i.e., "#####.#####.#####.#####-########-########") which easily fits into a |
126 static HKEY g_browser_crash_dump_regkey = NULL; | 127 // 63 character buffer. |
127 static const wchar_t kBrowserCrashDumpValueFormatStr[] = L"%08x-%08x"; | 128 const wchar_t kBrowserCrashDumpPrefixTemplate[] = L"%hs-%08x-%08x"; |
128 static const int kBrowserCrashDumpValueLength = 17; | 129 const int kBrowserCrashDumpPrefixLength = 63; |
129 static wchar_t g_browser_crash_dump_value[kBrowserCrashDumpValueLength+1] = {0}; | 130 wchar_t g_browser_crash_dump_prefix[kBrowserCrashDumpPrefixLength + 1] = { 0 }; |
grt (UTC plus 2)
2013/09/06 15:53:23
grt's pet peeve: "{ 0 }" -> "{}"
Roger McFarlane (Chromium)
2013/09/10 19:43:44
Done.
| |
131 | |
132 // These registry key to which we'll write a value for each crash dump attempt. | |
133 HKEY g_browser_crash_dump_regkey = NULL; | |
134 | |
135 // A atomic counter to make each crash dump value name unique. | |
136 base::subtle::Atomic32 g_browser_crash_dump_count = 0; | |
130 | 137 |
131 void InitBrowserCrashDumpsRegKey() { | 138 void InitBrowserCrashDumpsRegKey() { |
132 DCHECK(g_browser_crash_dump_regkey == NULL); | 139 DCHECK(g_browser_crash_dump_regkey == NULL); |
133 | 140 |
134 base::string16 key_str(chrome::kBrowserCrashDumpAttemptsRegistryPath); | |
135 key_str += L"\\"; | |
136 key_str += UTF8ToWide(chrome::kChromeVersion); | |
137 | |
138 base::win::RegKey regkey; | 141 base::win::RegKey regkey; |
139 if (regkey.Create(HKEY_CURRENT_USER, | 142 if (regkey.Create(HKEY_CURRENT_USER, |
140 key_str.c_str(), | 143 chrome::kBrowserCrashDumpAttemptsRegistryPath, |
141 KEY_ALL_ACCESS) != ERROR_SUCCESS) { | 144 KEY_ALL_ACCESS) != ERROR_SUCCESS) { |
142 return; | 145 return; |
143 } | 146 } |
144 | 147 |
148 // Hold the registry key in a global for update on crash dump. | |
145 g_browser_crash_dump_regkey = regkey.Take(); | 149 g_browser_crash_dump_regkey = regkey.Take(); |
146 | 150 |
147 // We use the current process id and the curren tick count as a (hopefully) | 151 // We use the current process id and the current tick count as a (hopefully) |
148 // unique combination for the crash dump value. There's a small chance that | 152 // unique combination for the crash dump value. There's a small chance that |
149 // across a reboot we might have a crash dump signal written, and the next | 153 // across a reboot we might have a crash dump signal written, and the next |
150 // browser process might have the same process id and tick count, but crash | 154 // browser process might have the same process id and tick count, but crash |
151 // before consuming the signal (overwriting the signal with an identical one). | 155 // before consuming the signal (overwriting the signal with an identical one). |
152 // For now, we're willing to live with that risk. | 156 // For now, we're willing to live with that risk. |
153 int length = swprintf(g_browser_crash_dump_value, | 157 int length = swprintf(g_browser_crash_dump_prefix, |
154 arraysize(g_browser_crash_dump_value), | 158 arraysize(g_browser_crash_dump_prefix), |
155 kBrowserCrashDumpValueFormatStr, | 159 kBrowserCrashDumpPrefixTemplate, |
160 chrome::kChromeVersion, | |
156 ::GetCurrentProcessId(), | 161 ::GetCurrentProcessId(), |
157 ::GetTickCount()); | 162 ::GetTickCount()); |
158 DCHECK_EQ(kBrowserCrashDumpValueLength, length); | 163 DCHECK_GT(length, 0); |
grt (UTC plus 2)
2013/09/06 15:53:23
how about being a little more defensive here:
if
Roger McFarlane (Chromium)
2013/09/10 19:43:44
Done.
| |
164 DCHECK_LT(length, kBrowserCrashDumpPrefixLength); | |
grt (UTC plus 2)
2013/09/06 15:53:23
DCHECK_LE since kBrowserCrashDumpPrefixLength is a
Roger McFarlane (Chromium)
2013/09/10 19:43:44
Done.
| |
159 } | 165 } |
160 | 166 |
161 void SendSmokeSignalForCrashDump() { | 167 void SendSmokeSignalForCrashDump() { |
grt (UTC plus 2)
2013/09/06 15:53:23
Send -> Write or Store since this function doesn't
Roger McFarlane (Chromium)
2013/09/10 19:43:44
RecordCrashDumpAttempt (the smoke signal terminolo
| |
162 if (g_browser_crash_dump_regkey != NULL) { | 168 // If we're not a browser (or the registry is unavailable to us for some |
163 base::win::RegKey regkey(g_browser_crash_dump_regkey); | 169 // reason) then there's nothing to do. |
164 regkey.WriteValue(g_browser_crash_dump_value, 1); | 170 if (g_browser_crash_dump_regkey == NULL) |
165 g_browser_crash_dump_regkey = NULL; | 171 return; |
166 } | 172 |
173 // Generate the final value name we'll use (appends the crash number to the | |
174 // base value name). | |
175 wchar_t value[2 * arraysize(g_browser_crash_dump_prefix)] = { 0 }; | |
grt (UTC plus 2)
2013/09/06 15:53:23
initializer isn't necessary here if you check the
Roger McFarlane (Chromium)
2013/09/10 19:43:44
Don't we always initialize our vars in Chrome?
| |
176 int length = swprintf( | |
177 value, arraysize(value), L"%ls-%x", | |
178 g_browser_crash_dump_prefix, | |
179 base::subtle::NoBarrier_AtomicIncrement(&g_browser_crash_dump_count, 1)); | |
180 | |
grt (UTC plus 2)
2013/09/06 15:53:23
if (length > 0 && length < arraysize(value)) {
Roger McFarlane (Chromium)
2013/09/10 19:43:44
Done.
| |
181 // Write the value to the registry. | |
182 base::win::RegKey regkey(g_browser_crash_dump_regkey); | |
183 regkey.WriteValue(value, 1); | |
grt (UTC plus 2)
2013/09/06 15:53:23
since you don't need data in the value, how about:
Roger McFarlane (Chromium)
2013/09/10 19:43:44
I now need to data in the value (to tell between a
| |
184 | |
185 // Don't let regkey auto-close the key. More crash dumps may follow. | |
186 ignore_result(regkey.Take()); | |
167 } | 187 } |
168 | 188 |
169 // Dumps the current process memory. | 189 // Dumps the current process memory. |
170 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { | 190 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { |
171 if (g_breakpad) { | 191 if (g_breakpad) { |
172 SendSmokeSignalForCrashDump(); | |
173 g_breakpad->WriteMinidump(); | 192 g_breakpad->WriteMinidump(); |
174 } | 193 } |
175 } | 194 } |
176 | 195 |
177 // Used for dumping a process state when there is no crash. | 196 // Used for dumping a process state when there is no crash. |
178 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { | 197 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { |
179 if (g_dumphandler_no_crash) { | 198 if (g_dumphandler_no_crash) { |
180 SendSmokeSignalForCrashDump(); | |
181 g_dumphandler_no_crash->WriteMinidump(); | 199 g_dumphandler_no_crash->WriteMinidump(); |
182 } | 200 } |
183 } | 201 } |
184 | 202 |
185 // We need to prevent ICF from folding DumpForHangDebuggingThread() and | 203 // We need to prevent ICF from folding DumpForHangDebuggingThread() and |
186 // DumpProcessWithoutCrashThread() together, since that makes them | 204 // DumpProcessWithoutCrashThread() together, since that makes them |
187 // indistinguishable in crash dumps. We do this by making the function | 205 // indistinguishable in crash dumps. We do this by making the function |
188 // bodies unique, and prevent optimization from shuffling things around. | 206 // bodies unique, and prevent optimization from shuffling things around. |
189 MSVC_DISABLE_OPTIMIZE() | 207 MSVC_DISABLE_OPTIMIZE() |
190 MSVC_PUSH_DISABLE_WARNING(4748) | 208 MSVC_PUSH_DISABLE_WARNING(4748) |
(...skipping 26 matching lines...) Expand all Loading... | |
217 | 235 |
218 extern "C" HANDLE __declspec(dllexport) __cdecl | 236 extern "C" HANDLE __declspec(dllexport) __cdecl |
219 InjectDumpForHangDebugging(HANDLE process) { | 237 InjectDumpForHangDebugging(HANDLE process) { |
220 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, | 238 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, |
221 0, 0, NULL); | 239 0, 0, NULL); |
222 } | 240 } |
223 | 241 |
224 extern "C" void DumpProcessAbnormalSignature() { | 242 extern "C" void DumpProcessAbnormalSignature() { |
225 if (!g_breakpad) | 243 if (!g_breakpad) |
226 return; | 244 return; |
227 SendSmokeSignalForCrashDump(); | |
228 g_custom_entries->push_back( | 245 g_custom_entries->push_back( |
229 google_breakpad::CustomInfoEntry(L"unusual-crash-signature", L"")); | 246 google_breakpad::CustomInfoEntry(L"unusual-crash-signature", L"")); |
230 g_breakpad->WriteMinidump(); | 247 g_breakpad->WriteMinidump(); |
231 } | 248 } |
232 | 249 |
233 // Reduces the size of the string |str| to a max of 64 chars. Required because | 250 // Reduces the size of the string |str| to a max of 64 chars. Required because |
234 // breakpad's CustomInfoEntry raises an invalid_parameter error if the string | 251 // breakpad's CustomInfoEntry raises an invalid_parameter error if the string |
235 // we want to set is longer. | 252 // we want to set is longer. |
236 std::wstring TrimToBreakpadMax(const std::wstring& str) { | 253 std::wstring TrimToBreakpadMax(const std::wstring& str) { |
237 std::wstring shorter(str); | 254 std::wstring shorter(str); |
(...skipping 356 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
594 } | 611 } |
595 | 612 |
596 // flag to indicate that we are already handling an exception. | 613 // flag to indicate that we are already handling an exception. |
597 volatile LONG handling_exception = 0; | 614 volatile LONG handling_exception = 0; |
598 | 615 |
599 // This callback is used when there is no crash. Note: Unlike the | 616 // This callback is used when there is no crash. Note: Unlike the |
600 // |FilterCallback| below this does not do dupe detection. It is upto the caller | 617 // |FilterCallback| below this does not do dupe detection. It is upto the caller |
601 // to implement it. | 618 // to implement it. |
602 bool FilterCallbackWhenNoCrash( | 619 bool FilterCallbackWhenNoCrash( |
603 void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { | 620 void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { |
621 SendSmokeSignalForCrashDump(); | |
604 return true; | 622 return true; |
605 } | 623 } |
606 | 624 |
607 // This callback is executed when the Chrome process has crashed and *before* | 625 // This callback is executed when the Chrome process has crashed and *before* |
608 // the crash dump is created. To prevent duplicate crash reports we | 626 // the crash dump is created. To prevent duplicate crash reports we |
609 // make every thread calling this method, except the very first one, | 627 // make every thread calling this method, except the very first one, |
610 // go to sleep. | 628 // go to sleep. |
611 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { | 629 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { |
612 // Capture every thread except the first one in the sleep. We don't | 630 // Capture every thread except the first one in the sleep. We don't |
613 // want multiple threads to concurrently report exceptions. | 631 // want multiple threads to concurrently report exceptions. |
614 if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) { | 632 if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) { |
615 ::Sleep(INFINITE); | 633 ::Sleep(INFINITE); |
616 } | 634 } |
635 SendSmokeSignalForCrashDump(); | |
617 return true; | 636 return true; |
618 } | 637 } |
619 | 638 |
620 // Previous unhandled filter. Will be called if not null when we | 639 // Previous unhandled filter. Will be called if not null when we |
621 // intercept a crash. | 640 // intercept a crash. |
622 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL; | 641 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL; |
623 | 642 |
624 // Exception filter used when breakpad is not enabled. We just display | 643 // Exception filter used when breakpad is not enabled. We just display |
625 // the "Do you want to restart" message and then we call the previous filter. | 644 // the "Do you want to restart" message and then we call the previous filter. |
626 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) { | 645 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) { |
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
797 | 816 |
798 // Crashes the process after generating a dump for the provided exception. Note | 817 // Crashes the process after generating a dump for the provided exception. Note |
799 // that the crash reporter should be initialized before calling this function | 818 // that the crash reporter should be initialized before calling this function |
800 // for it to do anything. | 819 // for it to do anything. |
801 // NOTE: This function is used by SyzyASAN to invoke a crash. If you change the | 820 // NOTE: This function is used by SyzyASAN to invoke a crash. If you change the |
802 // the name or signature of this function you will break SyzyASAN instrumented | 821 // the name or signature of this function you will break SyzyASAN instrumented |
803 // releases of Chrome. Please contact syzygy-team@chromium.org before doing so! | 822 // releases of Chrome. Please contact syzygy-team@chromium.org before doing so! |
804 extern "C" int __declspec(dllexport) CrashForException( | 823 extern "C" int __declspec(dllexport) CrashForException( |
805 EXCEPTION_POINTERS* info) { | 824 EXCEPTION_POINTERS* info) { |
806 if (g_breakpad) { | 825 if (g_breakpad) { |
807 SendSmokeSignalForCrashDump(); | |
808 g_breakpad->WriteMinidumpForException(info); | 826 g_breakpad->WriteMinidumpForException(info); |
809 // Patched stub exists based on conditions (See InitCrashReporter). | 827 // Patched stub exists based on conditions (See InitCrashReporter). |
810 // As a side note this function also gets called from | 828 // As a side note this function also gets called from |
811 // WindowProcExceptionFilter. | 829 // WindowProcExceptionFilter. |
812 if (g_real_terminate_process_stub == NULL) { | 830 if (g_real_terminate_process_stub == NULL) { |
813 ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED); | 831 ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED); |
814 } else { | 832 } else { |
815 NtTerminateProcessPtr real_terminate_proc = | 833 NtTerminateProcessPtr real_terminate_proc = |
816 reinterpret_cast<NtTerminateProcessPtr>( | 834 reinterpret_cast<NtTerminateProcessPtr>( |
817 static_cast<char*>(g_real_terminate_process_stub)); | 835 static_cast<char*>(g_real_terminate_process_stub)); |
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1056 previous_filter = SetUnhandledExceptionFilter(filter); | 1074 previous_filter = SetUnhandledExceptionFilter(filter); |
1057 } | 1075 } |
1058 | 1076 |
1059 void StringVectorToCStringVector(const std::vector<std::wstring>& wstrings, | 1077 void StringVectorToCStringVector(const std::vector<std::wstring>& wstrings, |
1060 std::vector<const wchar_t*>* cstrings) { | 1078 std::vector<const wchar_t*>* cstrings) { |
1061 cstrings->clear(); | 1079 cstrings->clear(); |
1062 cstrings->reserve(wstrings.size()); | 1080 cstrings->reserve(wstrings.size()); |
1063 for (size_t i = 0; i < wstrings.size(); ++i) | 1081 for (size_t i = 0; i < wstrings.size(); ++i) |
1064 cstrings->push_back(wstrings[i].c_str()); | 1082 cstrings->push_back(wstrings[i].c_str()); |
1065 } | 1083 } |
OLD | NEW |