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 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
112 DynamicEntriesMap; | 113 DynamicEntriesMap; |
113 DynamicEntriesMap* g_dynamic_entries = NULL; | 114 DynamicEntriesMap* g_dynamic_entries = NULL; |
114 // Allow for 128 entries. POSIX uses 64 entries of 256 bytes, so Windows needs | 115 // 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 | 116 // 256 entries of 64 bytes to match. See CustomInfoEntry::kValueMaxLength in |
116 // Breakpad. | 117 // Breakpad. |
117 const size_t kMaxDynamicEntries = 256; | 118 const size_t kMaxDynamicEntries = 256; |
118 | 119 |
119 // Maximum length for plugin path to include in plugin crash reports. | 120 // Maximum length for plugin path to include in plugin crash reports. |
120 const size_t kMaxPluginPathLength = 256; | 121 const size_t kMaxPluginPathLength = 256; |
121 | 122 |
122 // These values track the browser crash dump registry key and pre-computed | 123 // 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. | 124 // (i.e., "#####.#####.#####.#####-########-########") which easily fits into a |
124 static HKEY g_browser_crash_dump_regkey = NULL; | 125 // 63 character buffer. |
125 static const wchar_t kBrowserCrashDumpValueFormatStr[] = L"%08x-%08x"; | 126 const wchar_t kBrowserCrashDumpPrefixTemplate[] = L"%hs-%08x-%08x"; |
126 static const int kBrowserCrashDumpValueLength = 17; | 127 const int kBrowserCrashDumpPrefixLength = 63; |
127 static wchar_t g_browser_crash_dump_value[kBrowserCrashDumpValueLength+1] = {0}; | 128 wchar_t g_browser_crash_dump_prefix[kBrowserCrashDumpPrefixLength + 1] = {}; |
129 | |
130 // These registry key to which we'll write a value for each crash dump attempt. | |
131 HKEY g_browser_crash_dump_regkey = NULL; | |
132 | |
133 // A atomic counter to make each crash dump value name unique. | |
134 base::subtle::Atomic32 g_browser_crash_dump_count = 0; | |
128 | 135 |
129 void InitBrowserCrashDumpsRegKey() { | 136 void InitBrowserCrashDumpsRegKey() { |
130 DCHECK(g_browser_crash_dump_regkey == NULL); | 137 DCHECK(g_browser_crash_dump_regkey == NULL); |
131 | 138 |
132 base::string16 key_str(chrome::kBrowserCrashDumpAttemptsRegistryPath); | |
133 key_str += L"\\"; | |
134 key_str += UTF8ToWide(chrome::kChromeVersion); | |
135 | |
136 base::win::RegKey regkey; | 139 base::win::RegKey regkey; |
137 if (regkey.Create(HKEY_CURRENT_USER, | 140 if (regkey.Create(HKEY_CURRENT_USER, |
138 key_str.c_str(), | 141 chrome::kBrowserCrashDumpAttemptsRegistryPath, |
139 KEY_ALL_ACCESS) != ERROR_SUCCESS) { | 142 KEY_ALL_ACCESS) != ERROR_SUCCESS) { |
140 return; | 143 return; |
141 } | 144 } |
142 | 145 |
143 g_browser_crash_dump_regkey = regkey.Take(); | 146 // 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 | 147 // 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 | 148 // 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 | 149 // 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). | 150 // before consuming the signal (overwriting the signal with an identical one). |
150 // For now, we're willing to live with that risk. | 151 // For now, we're willing to live with that risk. |
151 int length = swprintf(g_browser_crash_dump_value, | 152 int length = swprintf(g_browser_crash_dump_prefix, |
152 arraysize(g_browser_crash_dump_value), | 153 arraysize(g_browser_crash_dump_prefix), |
153 kBrowserCrashDumpValueFormatStr, | 154 kBrowserCrashDumpPrefixTemplate, |
155 chrome::kChromeVersion, | |
154 ::GetCurrentProcessId(), | 156 ::GetCurrentProcessId(), |
155 ::GetTickCount()); | 157 ::GetTickCount()); |
156 DCHECK_EQ(kBrowserCrashDumpValueLength, length); | 158 DCHECK_GT(length, 0); |
159 DCHECK_LE(length, kBrowserCrashDumpPrefixLength); | |
160 | |
161 // Hold the registry key in a global for update on crash dump. | |
162 if (length > 0 && length < arraysize(g_browser_crash_dump_prefix)) { | |
grt (UTC plus 2)
2013/09/11 03:25:56
nit: remove braces since the conditional and the t
Roger McFarlane (Chromium)
2013/09/11 15:42:52
Done.
| |
163 g_browser_crash_dump_regkey = regkey.Take(); | |
164 } else { | |
165 g_browser_crash_dump_prefix[0] = L'\0'; | |
166 } | |
157 } | 167 } |
158 | 168 |
159 void SendSmokeSignalForCrashDump() { | 169 void RecordCrashDumpAttempt(bool is_real_crash) { |
160 if (g_browser_crash_dump_regkey != NULL) { | 170 // If we're not a browser (or the registry is unavailable to us for some |
171 // reason) then there's nothing to do. | |
172 if (g_browser_crash_dump_regkey == NULL) | |
173 return; | |
174 | |
175 // Generate the final value name we'll use (appends the crash number to the | |
176 // base value name). | |
177 const int kMaxValueSize = 2 * arraysize(g_browser_crash_dump_prefix); | |
grt (UTC plus 2)
2013/09/11 03:25:56
int -> size_t since this is counting things (and a
Roger McFarlane (Chromium)
2013/09/11 15:42:52
Done.
rvargas (doing something else)
2013/09/12 20:15:59
Small clarification about the first part: somethin
grt (UTC plus 2)
2013/09/13 18:39:29
ah, i chose words poorly. thanks for pointing that
rvargas (doing something else)
2013/09/13 19:45:29
yes, that's also my reading of the guide. Once som
| |
178 wchar_t value[kMaxValueSize + 1] = {}; | |
179 int length = swprintf( | |
180 value, arraysize(value), L"%ls-%x", | |
181 g_browser_crash_dump_prefix, | |
182 base::subtle::NoBarrier_AtomicIncrement(&g_browser_crash_dump_count, 1)); | |
183 | |
184 DCHECK_GT(length, 0); | |
185 DCHECK_LE(length, kMaxValueSize); | |
186 | |
187 if (length > 0 && length < arraysize(value)) { | |
188 // Write the value to the registry. | |
161 base::win::RegKey regkey(g_browser_crash_dump_regkey); | 189 base::win::RegKey regkey(g_browser_crash_dump_regkey); |
162 regkey.WriteValue(g_browser_crash_dump_value, 1); | 190 regkey.WriteValue(value, is_real_crash ? 1 : 0); |
163 g_browser_crash_dump_regkey = NULL; | 191 |
192 // Don't let regkey auto-close the key. More crash dumps may follow. | |
193 ignore_result(regkey.Take()); | |
164 } | 194 } |
165 } | 195 } |
166 | 196 |
167 // Dumps the current process memory. | 197 // Dumps the current process memory. |
168 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { | 198 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { |
169 if (g_breakpad) { | 199 if (g_breakpad) { |
170 SendSmokeSignalForCrashDump(); | |
171 g_breakpad->WriteMinidump(); | 200 g_breakpad->WriteMinidump(); |
172 } | 201 } |
173 } | 202 } |
174 | 203 |
175 // Used for dumping a process state when there is no crash. | 204 // Used for dumping a process state when there is no crash. |
176 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { | 205 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { |
177 if (g_dumphandler_no_crash) { | 206 if (g_dumphandler_no_crash) { |
178 SendSmokeSignalForCrashDump(); | |
179 g_dumphandler_no_crash->WriteMinidump(); | 207 g_dumphandler_no_crash->WriteMinidump(); |
180 } | 208 } |
181 } | 209 } |
182 | 210 |
183 // We need to prevent ICF from folding DumpForHangDebuggingThread() and | 211 // We need to prevent ICF from folding DumpForHangDebuggingThread() and |
184 // DumpProcessWithoutCrashThread() together, since that makes them | 212 // DumpProcessWithoutCrashThread() together, since that makes them |
185 // indistinguishable in crash dumps. We do this by making the function | 213 // indistinguishable in crash dumps. We do this by making the function |
186 // bodies unique, and prevent optimization from shuffling things around. | 214 // bodies unique, and prevent optimization from shuffling things around. |
187 MSVC_DISABLE_OPTIMIZE() | 215 MSVC_DISABLE_OPTIMIZE() |
188 MSVC_PUSH_DISABLE_WARNING(4748) | 216 MSVC_PUSH_DISABLE_WARNING(4748) |
(...skipping 26 matching lines...) Expand all Loading... | |
215 | 243 |
216 extern "C" HANDLE __declspec(dllexport) __cdecl | 244 extern "C" HANDLE __declspec(dllexport) __cdecl |
217 InjectDumpForHangDebugging(HANDLE process) { | 245 InjectDumpForHangDebugging(HANDLE process) { |
218 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, | 246 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, |
219 0, 0, NULL); | 247 0, 0, NULL); |
220 } | 248 } |
221 | 249 |
222 extern "C" void DumpProcessAbnormalSignature() { | 250 extern "C" void DumpProcessAbnormalSignature() { |
223 if (!g_breakpad) | 251 if (!g_breakpad) |
224 return; | 252 return; |
225 SendSmokeSignalForCrashDump(); | |
226 g_custom_entries->push_back( | 253 g_custom_entries->push_back( |
227 google_breakpad::CustomInfoEntry(L"unusual-crash-signature", L"")); | 254 google_breakpad::CustomInfoEntry(L"unusual-crash-signature", L"")); |
228 g_breakpad->WriteMinidump(); | 255 g_breakpad->WriteMinidump(); |
229 } | 256 } |
230 | 257 |
231 // Reduces the size of the string |str| to a max of 64 chars. Required because | 258 // 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 | 259 // breakpad's CustomInfoEntry raises an invalid_parameter error if the string |
233 // we want to set is longer. | 260 // we want to set is longer. |
234 std::wstring TrimToBreakpadMax(const std::wstring& str) { | 261 std::wstring TrimToBreakpadMax(const std::wstring& str) { |
235 std::wstring shorter(str); | 262 std::wstring shorter(str); |
(...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
581 } | 608 } |
582 | 609 |
583 // flag to indicate that we are already handling an exception. | 610 // flag to indicate that we are already handling an exception. |
584 volatile LONG handling_exception = 0; | 611 volatile LONG handling_exception = 0; |
585 | 612 |
586 // This callback is used when there is no crash. Note: Unlike the | 613 // 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 | 614 // |FilterCallback| below this does not do dupe detection. It is upto the caller |
588 // to implement it. | 615 // to implement it. |
589 bool FilterCallbackWhenNoCrash( | 616 bool FilterCallbackWhenNoCrash( |
590 void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { | 617 void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { |
618 RecordCrashDumpAttempt(false); | |
591 return true; | 619 return true; |
592 } | 620 } |
593 | 621 |
594 // This callback is executed when the Chrome process has crashed and *before* | 622 // This callback is executed when the Chrome process has crashed and *before* |
595 // the crash dump is created. To prevent duplicate crash reports we | 623 // the crash dump is created. To prevent duplicate crash reports we |
596 // make every thread calling this method, except the very first one, | 624 // make every thread calling this method, except the very first one, |
597 // go to sleep. | 625 // go to sleep. |
598 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { | 626 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { |
599 // Capture every thread except the first one in the sleep. We don't | 627 // Capture every thread except the first one in the sleep. We don't |
600 // want multiple threads to concurrently report exceptions. | 628 // want multiple threads to concurrently report exceptions. |
601 if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) { | 629 if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) { |
602 ::Sleep(INFINITE); | 630 ::Sleep(INFINITE); |
603 } | 631 } |
632 RecordCrashDumpAttempt(true); | |
604 return true; | 633 return true; |
605 } | 634 } |
606 | 635 |
607 // Previous unhandled filter. Will be called if not null when we | 636 // Previous unhandled filter. Will be called if not null when we |
608 // intercept a crash. | 637 // intercept a crash. |
609 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL; | 638 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL; |
610 | 639 |
611 // Exception filter used when breakpad is not enabled. We just display | 640 // 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. | 641 // the "Do you want to restart" message and then we call the previous filter. |
613 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) { | 642 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) { |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
766 | 795 |
767 // Crashes the process after generating a dump for the provided exception. Note | 796 // Crashes the process after generating a dump for the provided exception. Note |
768 // that the crash reporter should be initialized before calling this function | 797 // that the crash reporter should be initialized before calling this function |
769 // for it to do anything. | 798 // for it to do anything. |
770 // NOTE: This function is used by SyzyASAN to invoke a crash. If you change the | 799 // 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 | 800 // 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! | 801 // releases of Chrome. Please contact syzygy-team@chromium.org before doing so! |
773 extern "C" int __declspec(dllexport) CrashForException( | 802 extern "C" int __declspec(dllexport) CrashForException( |
774 EXCEPTION_POINTERS* info) { | 803 EXCEPTION_POINTERS* info) { |
775 if (g_breakpad) { | 804 if (g_breakpad) { |
776 SendSmokeSignalForCrashDump(); | |
777 g_breakpad->WriteMinidumpForException(info); | 805 g_breakpad->WriteMinidumpForException(info); |
778 // Patched stub exists based on conditions (See InitCrashReporter). | 806 // Patched stub exists based on conditions (See InitCrashReporter). |
779 // As a side note this function also gets called from | 807 // As a side note this function also gets called from |
780 // WindowProcExceptionFilter. | 808 // WindowProcExceptionFilter. |
781 if (g_real_terminate_process_stub == NULL) { | 809 if (g_real_terminate_process_stub == NULL) { |
782 ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED); | 810 ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED); |
783 } else { | 811 } else { |
784 NtTerminateProcessPtr real_terminate_proc = | 812 NtTerminateProcessPtr real_terminate_proc = |
785 reinterpret_cast<NtTerminateProcessPtr>( | 813 reinterpret_cast<NtTerminateProcessPtr>( |
786 static_cast<char*>(g_real_terminate_process_stub)); | 814 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); | 1053 previous_filter = SetUnhandledExceptionFilter(filter); |
1026 } | 1054 } |
1027 | 1055 |
1028 void StringVectorToCStringVector(const std::vector<std::wstring>& wstrings, | 1056 void StringVectorToCStringVector(const std::vector<std::wstring>& wstrings, |
1029 std::vector<const wchar_t*>* cstrings) { | 1057 std::vector<const wchar_t*>* cstrings) { |
1030 cstrings->clear(); | 1058 cstrings->clear(); |
1031 cstrings->reserve(wstrings.size()); | 1059 cstrings->reserve(wstrings.size()); |
1032 for (size_t i = 0; i < wstrings.size(); ++i) | 1060 for (size_t i = 0; i < wstrings.size(); ++i) |
1033 cstrings->push_back(wstrings[i].c_str()); | 1061 cstrings->push_back(wstrings[i].c_str()); |
1034 } | 1062 } |
OLD | NEW |