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

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: Count dumps with/without crashes separately. 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
« no previous file with comments | « no previous file | chrome/browser/metrics/metrics_service.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/metrics/metrics_service.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698