Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Side by Side Diff: chrome/app/breakpad_win.cc

Issue 23453032: Chrome.BrowserCrashDumpAttempts needs to account for multiple dumps from the same browser process. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Multi-dump logic was faulty, fixed key proliferation, send smoke signal in filter callback. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/metrics/metrics_service.cc » ('j') | chrome/browser/metrics/metrics_service.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698