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

Side by Side Diff: components/breakpad/app/breakpad_win.cc

Issue 327853002: Move the CrashKeysWin class to a pair of files as first step. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address Robert's comments. Created 6 years, 6 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 | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 "components/breakpad/app/breakpad_win.h" 5 #include "components/breakpad/app/breakpad_win.h"
6 6
7 #include <windows.h> 7 #include <windows.h>
8 #include <shellapi.h> 8 #include <shellapi.h>
9 #include <tchar.h> 9 #include <tchar.h>
10 #include <userenv.h> 10 #include <userenv.h>
(...skipping 15 matching lines...) Expand all
26 #include "base/strings/string_util.h" 26 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h" 27 #include "base/strings/stringprintf.h"
28 #include "base/strings/utf_string_conversions.h" 28 #include "base/strings/utf_string_conversions.h"
29 #include "base/synchronization/lock.h" 29 #include "base/synchronization/lock.h"
30 #include "base/win/metro.h" 30 #include "base/win/metro.h"
31 #include "base/win/pe_image.h" 31 #include "base/win/pe_image.h"
32 #include "base/win/registry.h" 32 #include "base/win/registry.h"
33 #include "base/win/win_util.h" 33 #include "base/win/win_util.h"
34 #include "breakpad/src/client/windows/handler/exception_handler.h" 34 #include "breakpad/src/client/windows/handler/exception_handler.h"
35 #include "components/breakpad/app/breakpad_client.h" 35 #include "components/breakpad/app/breakpad_client.h"
36 #include "components/breakpad/app/crash_keys_win.h"
36 #include "components/breakpad/app/hard_error_handler_win.h" 37 #include "components/breakpad/app/hard_error_handler_win.h"
37 #include "content/public/common/result_codes.h" 38 #include "content/public/common/result_codes.h"
38 #include "sandbox/win/src/nt_internals.h" 39 #include "sandbox/win/src/nt_internals.h"
39 #include "sandbox/win/src/sidestep/preamble_patcher.h" 40 #include "sandbox/win/src/sidestep/preamble_patcher.h"
40 41
41 // userenv.dll is required for GetProfileType(). 42 // userenv.dll is required for GetProfileType().
42 #pragma comment(lib, "userenv.lib") 43 #pragma comment(lib, "userenv.lib")
43 44
44 #pragma intrinsic(_AddressOfReturnAddress) 45 #pragma intrinsic(_AddressOfReturnAddress)
45 #pragma intrinsic(_ReturnAddress) 46 #pragma intrinsic(_ReturnAddress)
46 47
47 namespace breakpad { 48 namespace breakpad {
48 49
49 // Manages the breakpad key/value pair stash, there may only be one instance
50 // of this class per process at one time.
51 class CrashKeysWin {
52 public:
53 CrashKeysWin();
54 ~CrashKeysWin();
55
56 // May only be called once.
57 google_breakpad::CustomClientInfo*
58 GetCustomInfo(const std::wstring& exe_path, const std::wstring& type);
59
60 void SetCrashKeyValue(const std::wstring& key, const std::wstring& value);
61 void ClearCrashKeyValue(const std::wstring& key);
62
63 static CrashKeysWin* keeper() { return keeper_; }
64
65 private:
66 // One-time initialization of private key/value pairs.
67 void SetPluginPath(const std::wstring& path);
68 void SetBreakpadDumpPath();
69
70 // Must not be resized after GetCustomInfo is invoked.
71 std::vector<google_breakpad::CustomInfoEntry> custom_entries_;
72
73 typedef std::map<std::wstring, google_breakpad::CustomInfoEntry*>
74 DynamicEntriesMap;
75 base::Lock lock_;
76 // Keeps track of the next index for a new dynamic entry.
77 size_t dynamic_keys_offset_; // Under lock_.
78 // Maintains key->entry information for dynamic key/value entries
79 // in custom_entries_.
80 DynamicEntriesMap dynamic_entries_; // Under lock_.
81
82 // Stores the sole instance of this class allowed per process.
83 static CrashKeysWin* keeper_;
84 };
85
86 CrashKeysWin* CrashKeysWin::keeper_;
87
88 CrashKeysWin::CrashKeysWin() : dynamic_keys_offset_(0) {
89 DCHECK_EQ(static_cast<CrashKeysWin*>(NULL), keeper_);
90 keeper_ = this;
91 }
92
93 CrashKeysWin::~CrashKeysWin() {
94 DCHECK_EQ(this, keeper_);
95 keeper_ = NULL;
96 }
97
98 namespace { 50 namespace {
99 51
100 // Minidump with stacks, PEB, TEB, and unloaded module list. 52 // Minidump with stacks, PEB, TEB, and unloaded module list.
101 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>( 53 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
102 MiniDumpWithProcessThreadData | // Get PEB and TEB. 54 MiniDumpWithProcessThreadData | // Get PEB and TEB.
103 MiniDumpWithUnloadedModules); // Get unloaded modules when available. 55 MiniDumpWithUnloadedModules); // Get unloaded modules when available.
104 56
105 // Minidump with all of the above, plus memory referenced from stack. 57 // Minidump with all of the above, plus memory referenced from stack.
106 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( 58 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
107 MiniDumpWithProcessThreadData | // Get PEB and TEB. 59 MiniDumpWithProcessThreadData | // Get PEB and TEB.
(...skipping 22 matching lines...) Expand all
130 EXCEPTION_RECORD g_surrogate_exception_record = {0}; 82 EXCEPTION_RECORD g_surrogate_exception_record = {0};
131 CONTEXT g_surrogate_context = {0}; 83 CONTEXT g_surrogate_context = {0};
132 84
133 typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle, 85 typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle,
134 NTSTATUS ExitStatus); 86 NTSTATUS ExitStatus);
135 char* g_real_terminate_process_stub = NULL; 87 char* g_real_terminate_process_stub = NULL;
136 88
137 89
138 // Allow for 256 dynamic entries in addition to the fixed set of entries 90 // Allow for 256 dynamic entries in addition to the fixed set of entries
139 // set up in this file. 91 // set up in this file.
140 const size_t kMaxDynamicEntries = 256; 92 const size_t kMaxDynamicEntries = 256;
erikwright (departed) 2014/06/11 13:27:47 Are these two constants still needed here? If so,
Sigurður Ásgeirsson 2014/06/11 13:55:26 Thanks, removed.
141 93
142 // Maximum length for plugin path to include in plugin crash reports. 94 // Maximum length for plugin path to include in plugin crash reports.
143 const size_t kMaxPluginPathLength = 256; 95 const size_t kMaxPluginPathLength = 256;
144 96
145 } // namespace 97 } // namespace
146 98
147 // Dumps the current process memory. 99 // Dumps the current process memory.
148 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { 100 extern "C" void __declspec(dllexport) __cdecl DumpProcess() {
149 if (g_breakpad) { 101 if (g_breakpad) {
150 g_breakpad->WriteMinidump(); 102 g_breakpad->WriteMinidump();
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
194 return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread, 146 return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread,
195 0, 0, NULL); 147 0, 0, NULL);
196 } 148 }
197 149
198 extern "C" HANDLE __declspec(dllexport) __cdecl 150 extern "C" HANDLE __declspec(dllexport) __cdecl
199 InjectDumpForHangDebugging(HANDLE process) { 151 InjectDumpForHangDebugging(HANDLE process) {
200 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, 152 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread,
201 0, 0, NULL); 153 0, 0, NULL);
202 } 154 }
203 155
204 // Appends the plugin path to |g_custom_entries|.
205 void CrashKeysWin::SetPluginPath(const std::wstring& path) {
206 if (path.size() > kMaxPluginPathLength) {
207 // If the path is too long, truncate from the start rather than the end,
208 // since we want to be able to recover the DLL name.
209 SetPluginPath(path.substr(path.size() - kMaxPluginPathLength));
210 return;
211 }
212
213 // The chunk size without terminator.
214 const size_t kChunkSize = static_cast<size_t>(
215 google_breakpad::CustomInfoEntry::kValueMaxLength - 1);
216
217 int chunk_index = 0;
218 size_t chunk_start = 0; // Current position inside |path|
219
220 for (chunk_start = 0; chunk_start < path.size(); chunk_index++) {
221 size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start);
222
223 custom_entries_.push_back(google_breakpad::CustomInfoEntry(
224 base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(),
225 path.substr(chunk_start, chunk_length).c_str()));
226
227 chunk_start += chunk_length;
228 }
229 }
230
231 // Appends the breakpad dump path to |g_custom_entries|.
232 void CrashKeysWin::SetBreakpadDumpPath() {
233 base::FilePath crash_dumps_dir_path;
234 if (GetBreakpadClient()->GetAlternativeCrashDumpLocation(
235 &crash_dumps_dir_path)) {
236 custom_entries_.push_back(google_breakpad::CustomInfoEntry(
237 L"breakpad-dump-location", crash_dumps_dir_path.value().c_str()));
238 }
239 }
240
241 // Returns a string containing a list of all modifiers for the loaded profile. 156 // Returns a string containing a list of all modifiers for the loaded profile.
242 std::wstring GetProfileType() { 157 std::wstring GetProfileType() {
243 std::wstring profile_type; 158 std::wstring profile_type;
244 DWORD profile_bits = 0; 159 DWORD profile_bits = 0;
245 if (::GetProfileType(&profile_bits)) { 160 if (::GetProfileType(&profile_bits)) {
246 static const struct { 161 static const struct {
247 DWORD bit; 162 DWORD bit;
248 const wchar_t* name; 163 const wchar_t* name;
249 } kBitNames[] = { 164 } kBitNames[] = {
250 { PT_MANDATORY, L"mandatory" }, 165 { PT_MANDATORY, L"mandatory" },
251 { PT_ROAMING, L"roaming" }, 166 { PT_ROAMING, L"roaming" },
252 { PT_TEMPORARY, L"temporary" }, 167 { PT_TEMPORARY, L"temporary" },
253 }; 168 };
254 for (size_t i = 0; i < arraysize(kBitNames); ++i) { 169 for (size_t i = 0; i < arraysize(kBitNames); ++i) {
255 const DWORD this_bit = kBitNames[i].bit; 170 const DWORD this_bit = kBitNames[i].bit;
256 if ((profile_bits & this_bit) != 0) { 171 if ((profile_bits & this_bit) != 0) {
257 profile_type.append(kBitNames[i].name); 172 profile_type.append(kBitNames[i].name);
258 profile_bits &= ~this_bit; 173 profile_bits &= ~this_bit;
259 if (profile_bits != 0) 174 if (profile_bits != 0)
260 profile_type.append(L", "); 175 profile_type.append(L", ");
261 } 176 }
262 } 177 }
263 } else { 178 } else {
264 DWORD last_error = ::GetLastError(); 179 DWORD last_error = ::GetLastError();
265 base::SStringPrintf(&profile_type, L"error %u", last_error); 180 base::SStringPrintf(&profile_type, L"error %u", last_error);
266 } 181 }
267 return profile_type; 182 return profile_type;
268 } 183 }
269 184
270 // Returns the custom info structure based on the dll in parameter and the
271 // process type.
272 google_breakpad::CustomClientInfo*
273 CrashKeysWin::GetCustomInfo(const std::wstring& exe_path,
274 const std::wstring& type) {
275 base::string16 version, product;
276 base::string16 special_build;
277 base::string16 channel_name;
278 GetBreakpadClient()->GetProductNameAndVersion(
279 base::FilePath(exe_path),
280 &product,
281 &version,
282 &special_build,
283 &channel_name);
284
285 // We only expect this method to be called once per process.
286 // Common enties
287 custom_entries_.push_back(
288 google_breakpad::CustomInfoEntry(L"ver", version.c_str()));
289 custom_entries_.push_back(
290 google_breakpad::CustomInfoEntry(L"prod", product.c_str()));
291 custom_entries_.push_back(
292 google_breakpad::CustomInfoEntry(L"plat", L"Win32"));
293 custom_entries_.push_back(
294 google_breakpad::CustomInfoEntry(L"ptype", type.c_str()));
295 custom_entries_.push_back(google_breakpad::CustomInfoEntry(
296 L"pid", base::StringPrintf(L"%d", ::GetCurrentProcessId()).c_str()));
297 custom_entries_.push_back(google_breakpad::CustomInfoEntry(
298 L"channel", channel_name.c_str()));
299 custom_entries_.push_back(google_breakpad::CustomInfoEntry(
300 L"profile-type", GetProfileType().c_str()));
301
302 if (!special_build.empty()) {
303 custom_entries_.push_back(google_breakpad::CustomInfoEntry(
304 L"special", special_build.c_str()));
305 }
306
307 if (type == L"plugin" || type == L"ppapi") {
308 std::wstring plugin_path =
309 CommandLine::ForCurrentProcess()->GetSwitchValueNative("plugin-path");
310 if (!plugin_path.empty())
311 SetPluginPath(plugin_path);
312 }
313
314 // Check whether configuration management controls crash reporting.
315 bool crash_reporting_enabled = true;
316 bool controlled_by_policy = GetBreakpadClient()->ReportingIsEnforcedByPolicy(
317 &crash_reporting_enabled);
318 const CommandLine& command = *CommandLine::ForCurrentProcess();
319 bool use_crash_service =
320 !controlled_by_policy && (command.HasSwitch(switches::kNoErrorDialogs) ||
321 GetBreakpadClient()->IsRunningUnattended());
322 if (use_crash_service)
323 SetBreakpadDumpPath();
324
325 // Create space for dynamic ad-hoc keys. The names and values are set using
326 // the API defined in base/debug/crash_logging.h.
327 dynamic_keys_offset_ = custom_entries_.size();
328 for (size_t i = 0; i < kMaxDynamicEntries; ++i) {
329 // The names will be mutated as they are set. Un-numbered since these are
330 // merely placeholders. The name cannot be empty because Breakpad's
331 // HTTPUpload will interpret that as an invalid parameter.
332 custom_entries_.push_back(
333 google_breakpad::CustomInfoEntry(L"unspecified-crash-key", L""));
334 }
335
336 static google_breakpad::CustomClientInfo custom_client_info;
337 custom_client_info.entries = &custom_entries_.front();
338 custom_client_info.count = custom_entries_.size();
339
340 return &custom_client_info;
341 }
342
343 namespace { 185 namespace {
344 186
345 // This callback is used when we want to get a dump without crashing the 187 // This callback is used when we want to get a dump without crashing the
346 // process. 188 // process.
347 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*, 189 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*,
348 EXCEPTION_POINTERS* ex_info, 190 EXCEPTION_POINTERS* ex_info,
349 MDRawAssertionInfo*, bool) { 191 MDRawAssertionInfo*, bool) {
350 return true; 192 return true;
351 } 193 }
352 194
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
426 // Exception filter for the service process used when breakpad is not enabled. 268 // Exception filter for the service process used when breakpad is not enabled.
427 // We just display the "Do you want to restart" message and then die 269 // We just display the "Do you want to restart" message and then die
428 // (without calling the previous filter). 270 // (without calling the previous filter).
429 long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) { 271 long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) {
430 DumpDoneCallback(NULL, NULL, NULL, info, NULL, false); 272 DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
431 return EXCEPTION_EXECUTE_HANDLER; 273 return EXCEPTION_EXECUTE_HANDLER;
432 } 274 }
433 275
434 } // namespace 276 } // namespace
435 277
436 void CrashKeysWin::SetCrashKeyValue(
437 const std::wstring& key, const std::wstring& value) {
438 // CustomInfoEntry limits the length of key and value. If they exceed
439 // their maximum length the underlying string handling functions raise
440 // an exception and prematurely trigger a crash. Truncate here.
441 std::wstring safe_key(std::wstring(key).substr(
442 0, google_breakpad::CustomInfoEntry::kNameMaxLength - 1));
443 std::wstring safe_value(std::wstring(value).substr(
444 0, google_breakpad::CustomInfoEntry::kValueMaxLength - 1));
445
446 // If we already have a value for this key, update it; otherwise, insert
447 // the new value if we have not exhausted the pre-allocated slots for dynamic
448 // entries.
449 base::AutoLock lock(lock_);
450
451 DynamicEntriesMap::iterator it = dynamic_entries_.find(safe_key);
452 google_breakpad::CustomInfoEntry* entry = NULL;
453 if (it == dynamic_entries_.end()) {
454 if (dynamic_entries_.size() >= kMaxDynamicEntries)
455 return;
456 entry = &custom_entries_[dynamic_keys_offset_++];
457 dynamic_entries_.insert(std::make_pair(safe_key, entry));
458 } else {
459 entry = it->second;
460 }
461
462 entry->set(safe_key.data(), safe_value.data());
463 }
464
465 // NOTE: This function is used by SyzyASAN to annotate crash reports. If you 278 // NOTE: This function is used by SyzyASAN to annotate crash reports. If you
466 // change the name or signature of this function you will break SyzyASAN 279 // change the name or signature of this function you will break SyzyASAN
467 // instrumented releases of Chrome. Please contact syzygy-team@chromium.org 280 // instrumented releases of Chrome. Please contact syzygy-team@chromium.org
468 // before doing so! 281 // before doing so!
469 extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl( 282 extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl(
470 const wchar_t* key, const wchar_t* value) { 283 const wchar_t* key, const wchar_t* value) {
471 CrashKeysWin* keeper = CrashKeysWin::keeper(); 284 CrashKeysWin* keeper = CrashKeysWin::keeper();
472 if (!keeper) 285 if (!keeper)
473 return; 286 return;
474 287
475 // TODO(siggi): This doesn't look quite right - there's NULL deref potential 288 // TODO(siggi): This doesn't look quite right - there's NULL deref potential
476 // here, and an implicit std::wstring conversion. Fixme. 289 // here, and an implicit std::wstring conversion. Fixme.
477 keeper->SetCrashKeyValue(key, value); 290 keeper->SetCrashKeyValue(key, value);
478 } 291 }
479 292
480 void CrashKeysWin::ClearCrashKeyValue(const std::wstring& key) {
481 base::AutoLock lock(lock_);
482
483 std::wstring key_string(key);
484 DynamicEntriesMap::iterator it = dynamic_entries_.find(key_string);
485 if (it == dynamic_entries_.end())
486 return;
487
488 it->second->set_value(NULL);
489 }
490
491 extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl( 293 extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl(
492 const wchar_t* key) { 294 const wchar_t* key) {
493 CrashKeysWin* keeper = CrashKeysWin::keeper(); 295 CrashKeysWin* keeper = CrashKeysWin::keeper();
494 if (!keeper) 296 if (!keeper)
495 return; 297 return;
496 298
497 // TODO(siggi): This doesn't look quite right - there's NULL deref potential 299 // TODO(siggi): This doesn't look quite right - there's NULL deref potential
498 // here, and an implicit std::wstring conversion. Fixme. 300 // here, and an implicit std::wstring conversion. Fixme.
499 keeper->ClearCrashKeyValue(key); 301 keeper->ClearCrashKeyValue(key);
500 } 302 }
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
702 exe_path[0] = 0; 504 exe_path[0] = 0;
703 GetModuleFileNameW(NULL, exe_path, MAX_PATH); 505 GetModuleFileNameW(NULL, exe_path, MAX_PATH);
704 506
705 bool is_per_user_install = 507 bool is_per_user_install =
706 GetBreakpadClient()->GetIsPerUserInstall(base::FilePath(exe_path)); 508 GetBreakpadClient()->GetIsPerUserInstall(base::FilePath(exe_path));
707 509
708 // This is intentionally leaked. 510 // This is intentionally leaked.
709 CrashKeysWin* keeper = new CrashKeysWin(); 511 CrashKeysWin* keeper = new CrashKeysWin();
710 512
711 google_breakpad::CustomClientInfo* custom_info = 513 google_breakpad::CustomClientInfo* custom_info =
712 keeper->GetCustomInfo(exe_path, process_type); 514 keeper->GetCustomInfo(exe_path, process_type,
515 GetProfileType(), CommandLine::ForCurrentProcess(),
516 GetBreakpadClient());
713 517
714 google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL; 518 google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL;
715 LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL; 519 LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL;
716 // We install the post-dump callback only for the browser and service 520 // We install the post-dump callback only for the browser and service
717 // processes. It spawns a new browser/service process. 521 // processes. It spawns a new browser/service process.
718 if (process_type == L"browser") { 522 if (process_type == L"browser") {
719 callback = &DumpDoneCallback; 523 callback = &DumpDoneCallback;
720 default_filter = &ChromeExceptionFilter; 524 default_filter = &ChromeExceptionFilter;
721 } else if (process_type == L"service") { 525 } else if (process_type == L"service") {
722 callback = &DumpDoneCallback; 526 callback = &DumpDoneCallback;
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
789 if (process_type != L"browser" && 593 if (process_type != L"browser" &&
790 !GetBreakpadClient()->IsRunningUnattended()) { 594 !GetBreakpadClient()->IsRunningUnattended()) {
791 // Initialize the hook TerminateProcess to catch unexpected exits. 595 // Initialize the hook TerminateProcess to catch unexpected exits.
792 InitTerminateProcessHooks(); 596 InitTerminateProcessHooks();
793 } 597 }
794 #endif 598 #endif
795 } 599 }
796 } 600 }
797 601
798 } // namespace breakpad 602 } // namespace breakpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698