Chromium Code Reviews| OLD | NEW |
|---|---|
| 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> |
| 11 #include <winnt.h> | 11 #include <winnt.h> |
| 12 | 12 |
| 13 #include <algorithm> | 13 #include <algorithm> |
| 14 #include <map> | |
| 14 #include <vector> | 15 #include <vector> |
| 15 | 16 |
| 16 #include "base/base_switches.h" | 17 #include "base/base_switches.h" |
| 17 #include "base/basictypes.h" | 18 #include "base/basictypes.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/debug/dump_without_crashing.h" | 21 #include "base/debug/dump_without_crashing.h" |
| 21 #include "base/environment.h" | 22 #include "base/environment.h" |
| 22 #include "base/memory/scoped_ptr.h" | 23 #include "base/memory/scoped_ptr.h" |
| 23 #include "base/strings/string16.h" | 24 #include "base/strings/string16.h" |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 38 #include "sandbox/win/src/sidestep/preamble_patcher.h" | 39 #include "sandbox/win/src/sidestep/preamble_patcher.h" |
| 39 | 40 |
| 40 // userenv.dll is required for GetProfileType(). | 41 // userenv.dll is required for GetProfileType(). |
| 41 #pragma comment(lib, "userenv.lib") | 42 #pragma comment(lib, "userenv.lib") |
| 42 | 43 |
| 43 #pragma intrinsic(_AddressOfReturnAddress) | 44 #pragma intrinsic(_AddressOfReturnAddress) |
| 44 #pragma intrinsic(_ReturnAddress) | 45 #pragma intrinsic(_ReturnAddress) |
| 45 | 46 |
| 46 namespace breakpad { | 47 namespace breakpad { |
| 47 | 48 |
| 48 std::vector<google_breakpad::CustomInfoEntry>* g_custom_entries = NULL; | 49 // Manages the breakpad key/value pair stash, there may only be one instance |
| 50 // of this class per process at one time. | |
| 51 // Exposed for testing only. | |
| 52 class KeyValuePairKeeper { | |
|
Robert Sesek
2014/05/29 20:34:41
Not quite a fan of this name. Perhaps CustomInfoMa
Sigurður Ásgeirsson
2014/05/30 15:14:49
Yeah, me neither :/. You know of course that there
| |
| 53 public: | |
| 54 KeyValuePairKeeper(); | |
| 55 ~KeyValuePairKeeper(); | |
| 56 | |
| 57 // May only be called once. | |
| 58 google_breakpad::CustomClientInfo* | |
| 59 GetCustomInfo(const std::wstring& exe_path, const std::wstring& type); | |
| 60 | |
| 61 void SetCrashKeyValue(const std::wstring& key, const std::wstring& value); | |
| 62 void ClearCrashKeyValue(const std::wstring& key); | |
| 63 | |
| 64 static KeyValuePairKeeper* keeper() { return keeper_; } | |
| 65 | |
| 66 private: | |
| 67 // Sets private | |
|
Robert Sesek
2014/05/29 20:34:41
Incomplete comment?
Sigurður Ásgeirsson
2014/05/30 15:14:49
Done.
| |
| 68 void SetPluginPath(const std::wstring& path); | |
| 69 void SetBreakpadDumpPath(); | |
| 70 | |
| 71 // Must not be resized after GetCustomInfo is invoked. | |
| 72 std::vector<google_breakpad::CustomInfoEntry> custom_entries_; | |
| 73 | |
| 74 typedef std::map<std::wstring, google_breakpad::CustomInfoEntry*> | |
| 75 DynamicEntriesMap; | |
| 76 base::Lock lock_; | |
| 77 // Keeps track of the next index for a new dynamic entry. | |
| 78 size_t dynamic_keys_offset_; // Under lock_. | |
| 79 // Maintains key->entry information for dynamic key/value entries | |
| 80 // in custom_entries_. | |
| 81 DynamicEntriesMap dynamic_entries_; // Under lock_. | |
| 82 | |
| 83 // Stores the sole instance of this class allowed per process. | |
| 84 static KeyValuePairKeeper* keeper_; | |
| 85 }; | |
| 86 | |
| 87 KeyValuePairKeeper* KeyValuePairKeeper::keeper_; | |
| 88 | |
| 89 KeyValuePairKeeper::KeyValuePairKeeper() : dynamic_keys_offset_(0) { | |
| 90 DCHECK_EQ(static_cast<KeyValuePairKeeper*>(NULL), keeper_); | |
| 91 | |
|
Robert Sesek
2014/05/29 20:34:41
nit: remove blank line
Sigurður Ásgeirsson
2014/05/30 15:14:49
Done.
| |
| 92 keeper_ = this; | |
| 93 } | |
| 94 | |
| 95 KeyValuePairKeeper::~KeyValuePairKeeper() { | |
| 96 DCHECK_EQ(this, keeper_); | |
| 97 keeper_ = NULL; | |
| 98 } | |
| 49 | 99 |
| 50 namespace { | 100 namespace { |
| 51 | 101 |
| 52 // Minidump with stacks, PEB, TEB, and unloaded module list. | 102 // Minidump with stacks, PEB, TEB, and unloaded module list. |
| 53 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>( | 103 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>( |
| 54 MiniDumpWithProcessThreadData | // Get PEB and TEB. | 104 MiniDumpWithProcessThreadData | // Get PEB and TEB. |
| 55 MiniDumpWithUnloadedModules); // Get unloaded modules when available. | 105 MiniDumpWithUnloadedModules); // Get unloaded modules when available. |
| 56 | 106 |
| 57 // Minidump with all of the above, plus memory referenced from stack. | 107 // Minidump with all of the above, plus memory referenced from stack. |
| 58 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( | 108 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 79 google_breakpad::ExceptionHandler* g_dumphandler_no_crash = NULL; | 129 google_breakpad::ExceptionHandler* g_dumphandler_no_crash = NULL; |
| 80 | 130 |
| 81 EXCEPTION_POINTERS g_surrogate_exception_pointers = {0}; | 131 EXCEPTION_POINTERS g_surrogate_exception_pointers = {0}; |
| 82 EXCEPTION_RECORD g_surrogate_exception_record = {0}; | 132 EXCEPTION_RECORD g_surrogate_exception_record = {0}; |
| 83 CONTEXT g_surrogate_context = {0}; | 133 CONTEXT g_surrogate_context = {0}; |
| 84 | 134 |
| 85 typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle, | 135 typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle, |
| 86 NTSTATUS ExitStatus); | 136 NTSTATUS ExitStatus); |
| 87 char* g_real_terminate_process_stub = NULL; | 137 char* g_real_terminate_process_stub = NULL; |
| 88 | 138 |
| 89 base::Lock* g_dynamic_entries_lock = NULL; | |
| 90 // Under *g_dynamic_entries_lock. | |
| 91 size_t g_dynamic_keys_offset = 0; | |
| 92 typedef std::map<std::wstring, google_breakpad::CustomInfoEntry*> | |
| 93 DynamicEntriesMap; | |
| 94 // Under *g_dynamic_entries_lock. | |
| 95 DynamicEntriesMap* g_dynamic_entries = NULL; | |
| 96 | 139 |
| 97 // Allow for 256 dynamic entries in addition to the fixed set of entries | 140 // Allow for 256 dynamic entries in addition to the fixed set of entries |
| 98 // set up in this file. | 141 // set up in this file. |
| 99 const size_t kMaxDynamicEntries = 256; | 142 const size_t kMaxDynamicEntries = 256; |
| 100 | 143 |
| 101 // Maximum length for plugin path to include in plugin crash reports. | 144 // Maximum length for plugin path to include in plugin crash reports. |
| 102 const size_t kMaxPluginPathLength = 256; | 145 const size_t kMaxPluginPathLength = 256; |
| 103 | 146 |
| 147 } // namespace | |
| 148 | |
| 104 // Dumps the current process memory. | 149 // Dumps the current process memory. |
| 105 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { | 150 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { |
| 106 if (g_breakpad) { | 151 if (g_breakpad) { |
| 107 g_breakpad->WriteMinidump(); | 152 g_breakpad->WriteMinidump(); |
| 108 } | 153 } |
| 109 } | 154 } |
| 110 | 155 |
| 111 // Used for dumping a process state when there is no crash. | 156 // Used for dumping a process state when there is no crash. |
| 112 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { | 157 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { |
| 113 if (g_dumphandler_no_crash) { | 158 if (g_dumphandler_no_crash) { |
| 114 g_dumphandler_no_crash->WriteMinidump(); | 159 g_dumphandler_no_crash->WriteMinidump(); |
| 115 } | 160 } |
| 116 } | 161 } |
| 117 | 162 |
| 163 namespace { | |
| 164 | |
| 118 // We need to prevent ICF from folding DumpForHangDebuggingThread() and | 165 // We need to prevent ICF from folding DumpForHangDebuggingThread() and |
| 119 // DumpProcessWithoutCrashThread() together, since that makes them | 166 // DumpProcessWithoutCrashThread() together, since that makes them |
| 120 // indistinguishable in crash dumps. We do this by making the function | 167 // indistinguishable in crash dumps. We do this by making the function |
| 121 // bodies unique, and prevent optimization from shuffling things around. | 168 // bodies unique, and prevent optimization from shuffling things around. |
| 122 MSVC_DISABLE_OPTIMIZE() | 169 MSVC_DISABLE_OPTIMIZE() |
| 123 MSVC_PUSH_DISABLE_WARNING(4748) | 170 MSVC_PUSH_DISABLE_WARNING(4748) |
| 124 | 171 |
| 125 DWORD WINAPI DumpProcessWithoutCrashThread(void*) { | 172 DWORD WINAPI DumpProcessWithoutCrashThread(void*) { |
| 126 DumpProcessWithoutCrash(); | 173 DumpProcessWithoutCrash(); |
| 127 return 0; | 174 return 0; |
| 128 } | 175 } |
| 129 | 176 |
| 130 // The following two functions do exactly the same thing as the two above. But | 177 // The following two functions do exactly the same thing as the two above. But |
| 131 // we want the signatures to be different so that we can easily track them in | 178 // we want the signatures to be different so that we can easily track them in |
| 132 // crash reports. | 179 // crash reports. |
| 133 // TODO(yzshen): Remove when enough information is collected and the hang rate | 180 // TODO(yzshen): Remove when enough information is collected and the hang rate |
| 134 // of pepper/renderer processes is reduced. | 181 // of pepper/renderer processes is reduced. |
| 135 DWORD WINAPI DumpForHangDebuggingThread(void*) { | 182 DWORD WINAPI DumpForHangDebuggingThread(void*) { |
| 136 DumpProcessWithoutCrash(); | 183 DumpProcessWithoutCrash(); |
| 137 VLOG(1) << "dumped for hang debugging"; | 184 VLOG(1) << "dumped for hang debugging"; |
| 138 return 0; | 185 return 0; |
| 139 } | 186 } |
| 140 | 187 |
| 141 MSVC_POP_WARNING() | 188 MSVC_POP_WARNING() |
| 142 MSVC_ENABLE_OPTIMIZE() | 189 MSVC_ENABLE_OPTIMIZE() |
| 143 | 190 |
| 191 } // namespace | |
| 192 | |
| 144 // Injects a thread into a remote process to dump state when there is no crash. | 193 // Injects a thread into a remote process to dump state when there is no crash. |
| 145 extern "C" HANDLE __declspec(dllexport) __cdecl | 194 extern "C" HANDLE __declspec(dllexport) __cdecl |
| 146 InjectDumpProcessWithoutCrash(HANDLE process) { | 195 InjectDumpProcessWithoutCrash(HANDLE process) { |
| 147 return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread, | 196 return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread, |
| 148 0, 0, NULL); | 197 0, 0, NULL); |
| 149 } | 198 } |
| 150 | 199 |
| 151 extern "C" HANDLE __declspec(dllexport) __cdecl | 200 extern "C" HANDLE __declspec(dllexport) __cdecl |
| 152 InjectDumpForHangDebugging(HANDLE process) { | 201 InjectDumpForHangDebugging(HANDLE process) { |
| 153 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, | 202 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, |
| 154 0, 0, NULL); | 203 0, 0, NULL); |
| 155 } | 204 } |
| 156 | 205 |
| 157 // Appends the plugin path to |g_custom_entries|. | 206 // Appends the plugin path to |g_custom_entries|. |
| 158 void SetPluginPath(const std::wstring& path) { | 207 void KeyValuePairKeeper::SetPluginPath(const std::wstring& path) { |
| 159 DCHECK(g_custom_entries); | |
| 160 | |
| 161 if (path.size() > kMaxPluginPathLength) { | 208 if (path.size() > kMaxPluginPathLength) { |
| 162 // If the path is too long, truncate from the start rather than the end, | 209 // If the path is too long, truncate from the start rather than the end, |
| 163 // since we want to be able to recover the DLL name. | 210 // since we want to be able to recover the DLL name. |
| 164 SetPluginPath(path.substr(path.size() - kMaxPluginPathLength)); | 211 SetPluginPath(path.substr(path.size() - kMaxPluginPathLength)); |
| 165 return; | 212 return; |
| 166 } | 213 } |
| 167 | 214 |
| 168 // The chunk size without terminator. | 215 // The chunk size without terminator. |
| 169 const size_t kChunkSize = static_cast<size_t>( | 216 const size_t kChunkSize = static_cast<size_t>( |
| 170 google_breakpad::CustomInfoEntry::kValueMaxLength - 1); | 217 google_breakpad::CustomInfoEntry::kValueMaxLength - 1); |
| 171 | 218 |
| 172 int chunk_index = 0; | 219 int chunk_index = 0; |
| 173 size_t chunk_start = 0; // Current position inside |path| | 220 size_t chunk_start = 0; // Current position inside |path| |
| 174 | 221 |
| 175 for (chunk_start = 0; chunk_start < path.size(); chunk_index++) { | 222 for (chunk_start = 0; chunk_start < path.size(); chunk_index++) { |
| 176 size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start); | 223 size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start); |
| 177 | 224 |
| 178 g_custom_entries->push_back(google_breakpad::CustomInfoEntry( | 225 custom_entries_.push_back(google_breakpad::CustomInfoEntry( |
| 179 base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(), | 226 base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(), |
| 180 path.substr(chunk_start, chunk_length).c_str())); | 227 path.substr(chunk_start, chunk_length).c_str())); |
| 181 | 228 |
| 182 chunk_start += chunk_length; | 229 chunk_start += chunk_length; |
| 183 } | 230 } |
| 184 } | 231 } |
| 185 | 232 |
| 186 // Appends the breakpad dump path to |g_custom_entries|. | 233 // Appends the breakpad dump path to |g_custom_entries|. |
| 187 void SetBreakpadDumpPath() { | 234 void KeyValuePairKeeper::SetBreakpadDumpPath() { |
| 188 DCHECK(g_custom_entries); | |
| 189 base::FilePath crash_dumps_dir_path; | 235 base::FilePath crash_dumps_dir_path; |
| 190 if (GetBreakpadClient()->GetAlternativeCrashDumpLocation( | 236 if (GetBreakpadClient()->GetAlternativeCrashDumpLocation( |
| 191 &crash_dumps_dir_path)) { | 237 &crash_dumps_dir_path)) { |
| 192 g_custom_entries->push_back(google_breakpad::CustomInfoEntry( | 238 custom_entries_.push_back(google_breakpad::CustomInfoEntry( |
| 193 L"breakpad-dump-location", crash_dumps_dir_path.value().c_str())); | 239 L"breakpad-dump-location", crash_dumps_dir_path.value().c_str())); |
| 194 } | 240 } |
| 195 } | 241 } |
| 196 | 242 |
| 197 // Returns a string containing a list of all modifiers for the loaded profile. | 243 // Returns a string containing a list of all modifiers for the loaded profile. |
| 198 std::wstring GetProfileType() { | 244 std::wstring GetProfileType() { |
| 199 std::wstring profile_type; | 245 std::wstring profile_type; |
| 200 DWORD profile_bits = 0; | 246 DWORD profile_bits = 0; |
| 201 if (::GetProfileType(&profile_bits)) { | 247 if (::GetProfileType(&profile_bits)) { |
| 202 static const struct { | 248 static const struct { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 218 } | 264 } |
| 219 } else { | 265 } else { |
| 220 DWORD last_error = ::GetLastError(); | 266 DWORD last_error = ::GetLastError(); |
| 221 base::SStringPrintf(&profile_type, L"error %u", last_error); | 267 base::SStringPrintf(&profile_type, L"error %u", last_error); |
| 222 } | 268 } |
| 223 return profile_type; | 269 return profile_type; |
| 224 } | 270 } |
| 225 | 271 |
| 226 // Returns the custom info structure based on the dll in parameter and the | 272 // Returns the custom info structure based on the dll in parameter and the |
| 227 // process type. | 273 // process type. |
| 228 google_breakpad::CustomClientInfo* GetCustomInfo(const std::wstring& exe_path, | 274 google_breakpad::CustomClientInfo* |
| 229 const std::wstring& type) { | 275 KeyValuePairKeeper::GetCustomInfo(const std::wstring& exe_path, |
| 276 const std::wstring& type) { | |
| 230 base::string16 version, product; | 277 base::string16 version, product; |
| 231 base::string16 special_build; | 278 base::string16 special_build; |
| 232 base::string16 channel_name; | 279 base::string16 channel_name; |
| 233 GetBreakpadClient()->GetProductNameAndVersion( | 280 GetBreakpadClient()->GetProductNameAndVersion( |
| 234 base::FilePath(exe_path), | 281 base::FilePath(exe_path), |
| 235 &product, | 282 &product, |
| 236 &version, | 283 &version, |
| 237 &special_build, | 284 &special_build, |
| 238 &channel_name); | 285 &channel_name); |
| 239 | 286 |
| 240 // We only expect this method to be called once per process. | 287 // We only expect this method to be called once per process. |
| 241 DCHECK(!g_custom_entries); | 288 // Common enties |
| 242 g_custom_entries = new std::vector<google_breakpad::CustomInfoEntry>; | 289 custom_entries_.push_back( |
| 243 | |
| 244 // Common g_custom_entries. | |
| 245 g_custom_entries->push_back( | |
| 246 google_breakpad::CustomInfoEntry(L"ver", | 290 google_breakpad::CustomInfoEntry(L"ver", |
| 247 base::UTF16ToWide(version).c_str())); | 291 base::UTF16ToWide(version).c_str())); |
| 248 g_custom_entries->push_back( | 292 custom_entries_.push_back( |
| 249 google_breakpad::CustomInfoEntry(L"prod", | 293 google_breakpad::CustomInfoEntry(L"prod", |
| 250 base::UTF16ToWide(product).c_str())); | 294 base::UTF16ToWide(product).c_str())); |
| 251 g_custom_entries->push_back( | 295 custom_entries_.push_back( |
| 252 google_breakpad::CustomInfoEntry(L"plat", L"Win32")); | 296 google_breakpad::CustomInfoEntry(L"plat", L"Win32")); |
| 253 g_custom_entries->push_back( | 297 custom_entries_.push_back( |
| 254 google_breakpad::CustomInfoEntry(L"ptype", type.c_str())); | 298 google_breakpad::CustomInfoEntry(L"ptype", type.c_str())); |
| 255 g_custom_entries->push_back(google_breakpad::CustomInfoEntry( | 299 custom_entries_.push_back(google_breakpad::CustomInfoEntry( |
| 256 L"pid", base::StringPrintf(L"%d", ::GetCurrentProcessId()).c_str())); | 300 L"pid", base::StringPrintf(L"%d", ::GetCurrentProcessId()).c_str())); |
| 257 g_custom_entries->push_back(google_breakpad::CustomInfoEntry( | 301 custom_entries_.push_back(google_breakpad::CustomInfoEntry( |
| 258 L"channel", base::UTF16ToWide(channel_name).c_str())); | 302 L"channel", base::UTF16ToWide(channel_name).c_str())); |
| 259 g_custom_entries->push_back(google_breakpad::CustomInfoEntry( | 303 custom_entries_.push_back(google_breakpad::CustomInfoEntry( |
| 260 L"profile-type", GetProfileType().c_str())); | 304 L"profile-type", GetProfileType().c_str())); |
| 261 | 305 |
| 262 if (!special_build.empty()) | 306 if (!special_build.empty()) |
| 263 g_custom_entries->push_back(google_breakpad::CustomInfoEntry( | 307 custom_entries_.push_back(google_breakpad::CustomInfoEntry( |
| 264 L"special", base::UTF16ToWide(special_build).c_str())); | 308 L"special", base::UTF16ToWide(special_build).c_str())); |
| 265 | 309 |
| 266 if (type == L"plugin" || type == L"ppapi") { | 310 if (type == L"plugin" || type == L"ppapi") { |
| 267 std::wstring plugin_path = | 311 std::wstring plugin_path = |
| 268 CommandLine::ForCurrentProcess()->GetSwitchValueNative("plugin-path"); | 312 CommandLine::ForCurrentProcess()->GetSwitchValueNative("plugin-path"); |
| 269 if (!plugin_path.empty()) | 313 if (!plugin_path.empty()) |
| 270 SetPluginPath(plugin_path); | 314 SetPluginPath(plugin_path); |
| 271 } | 315 } |
| 272 | 316 |
| 273 // Check whether configuration management controls crash reporting. | 317 // Check whether configuration management controls crash reporting. |
| 274 bool crash_reporting_enabled = true; | 318 bool crash_reporting_enabled = true; |
| 275 bool controlled_by_policy = GetBreakpadClient()->ReportingIsEnforcedByPolicy( | 319 bool controlled_by_policy = GetBreakpadClient()->ReportingIsEnforcedByPolicy( |
| 276 &crash_reporting_enabled); | 320 &crash_reporting_enabled); |
| 277 const CommandLine& command = *CommandLine::ForCurrentProcess(); | 321 const CommandLine& command = *CommandLine::ForCurrentProcess(); |
| 278 bool use_crash_service = | 322 bool use_crash_service = |
| 279 !controlled_by_policy && (command.HasSwitch(switches::kNoErrorDialogs) || | 323 !controlled_by_policy && (command.HasSwitch(switches::kNoErrorDialogs) || |
| 280 GetBreakpadClient()->IsRunningUnattended()); | 324 GetBreakpadClient()->IsRunningUnattended()); |
| 281 if (use_crash_service) | 325 if (use_crash_service) |
| 282 SetBreakpadDumpPath(); | 326 SetBreakpadDumpPath(); |
| 283 | 327 |
| 284 // Create space for dynamic ad-hoc keys. The names and values are set using | 328 // Create space for dynamic ad-hoc keys. The names and values are set using |
| 285 // the API defined in base/debug/crash_logging.h. | 329 // the API defined in base/debug/crash_logging.h. |
| 286 g_dynamic_keys_offset = g_custom_entries->size(); | 330 dynamic_keys_offset_ = custom_entries_.size(); |
| 287 for (size_t i = 0; i < kMaxDynamicEntries; ++i) { | 331 for (size_t i = 0; i < kMaxDynamicEntries; ++i) { |
| 288 // The names will be mutated as they are set. Un-numbered since these are | 332 // The names will be mutated as they are set. Un-numbered since these are |
| 289 // merely placeholders. The name cannot be empty because Breakpad's | 333 // merely placeholders. The name cannot be empty because Breakpad's |
| 290 // HTTPUpload will interpret that as an invalid parameter. | 334 // HTTPUpload will interpret that as an invalid parameter. |
| 291 g_custom_entries->push_back( | 335 custom_entries_.push_back( |
| 292 google_breakpad::CustomInfoEntry(L"unspecified-crash-key", L"")); | 336 google_breakpad::CustomInfoEntry(L"unspecified-crash-key", L"")); |
| 293 } | 337 } |
| 294 | 338 |
| 295 g_dynamic_entries_lock = new base::Lock; | |
| 296 g_dynamic_entries = new DynamicEntriesMap; | |
| 297 | |
| 298 static google_breakpad::CustomClientInfo custom_client_info; | 339 static google_breakpad::CustomClientInfo custom_client_info; |
| 299 custom_client_info.entries = &g_custom_entries->front(); | 340 custom_client_info.entries = &custom_entries_.front(); |
| 300 custom_client_info.count = g_custom_entries->size(); | 341 custom_client_info.count = custom_entries_.size(); |
| 301 | 342 |
| 302 return &custom_client_info; | 343 return &custom_client_info; |
| 303 } | 344 } |
| 304 | 345 |
| 346 namespace { | |
| 347 | |
| 305 // This callback is used when we want to get a dump without crashing the | 348 // This callback is used when we want to get a dump without crashing the |
| 306 // process. | 349 // process. |
| 307 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*, | 350 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*, |
| 308 EXCEPTION_POINTERS* ex_info, | 351 EXCEPTION_POINTERS* ex_info, |
| 309 MDRawAssertionInfo*, bool) { | 352 MDRawAssertionInfo*, bool) { |
| 310 return true; | 353 return true; |
| 311 } | 354 } |
| 312 | 355 |
| 313 // This callback is executed when the browser process has crashed, after | 356 // This callback is executed when the browser process has crashed, after |
| 314 // the crash dump has been created. We need to minimize the amount of work | 357 // the crash dump has been created. We need to minimize the amount of work |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 384 } | 427 } |
| 385 | 428 |
| 386 // Exception filter for the service process used when breakpad is not enabled. | 429 // Exception filter for the service process used when breakpad is not enabled. |
| 387 // We just display the "Do you want to restart" message and then die | 430 // We just display the "Do you want to restart" message and then die |
| 388 // (without calling the previous filter). | 431 // (without calling the previous filter). |
| 389 long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) { | 432 long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) { |
| 390 DumpDoneCallback(NULL, NULL, NULL, info, NULL, false); | 433 DumpDoneCallback(NULL, NULL, NULL, info, NULL, false); |
| 391 return EXCEPTION_EXECUTE_HANDLER; | 434 return EXCEPTION_EXECUTE_HANDLER; |
| 392 } | 435 } |
| 393 | 436 |
| 394 // NOTE: This function is used by SyzyASAN to annotate crash reports. If you | 437 } // namespace |
| 395 // change the name or signature of this function you will break SyzyASAN | |
| 396 // instrumented releases of Chrome. Please contact syzygy-team@chromium.org | |
| 397 // before doing so! | |
| 398 extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl( | |
| 399 const wchar_t* key, const wchar_t* value) { | |
| 400 if (!g_dynamic_entries) | |
| 401 return; | |
| 402 | 438 |
| 439 void KeyValuePairKeeper::SetCrashKeyValue( | |
| 440 const std::wstring& key, const std::wstring& value) { | |
| 403 // CustomInfoEntry limits the length of key and value. If they exceed | 441 // CustomInfoEntry limits the length of key and value. If they exceed |
| 404 // their maximum length the underlying string handling functions raise | 442 // their maximum length the underlying string handling functions raise |
| 405 // an exception and prematurely trigger a crash. Truncate here. | 443 // an exception and prematurely trigger a crash. Truncate here. |
| 406 std::wstring safe_key(std::wstring(key).substr( | 444 std::wstring safe_key(std::wstring(key).substr( |
| 407 0, google_breakpad::CustomInfoEntry::kNameMaxLength - 1)); | 445 0, google_breakpad::CustomInfoEntry::kNameMaxLength - 1)); |
| 408 std::wstring safe_value(std::wstring(value).substr( | 446 std::wstring safe_value(std::wstring(value).substr( |
| 409 0, google_breakpad::CustomInfoEntry::kValueMaxLength - 1)); | 447 0, google_breakpad::CustomInfoEntry::kValueMaxLength - 1)); |
| 410 | 448 |
| 411 // If we already have a value for this key, update it; otherwise, insert | 449 // If we already have a value for this key, update it; otherwise, insert |
| 412 // the new value if we have not exhausted the pre-allocated slots for dynamic | 450 // the new value if we have not exhausted the pre-allocated slots for dynamic |
| 413 // entries. | 451 // entries. |
| 414 DCHECK(g_dynamic_entries_lock); | 452 base::AutoLock lock(lock_); |
| 415 base::AutoLock lock(*g_dynamic_entries_lock); | |
| 416 | 453 |
| 417 DynamicEntriesMap::iterator it = g_dynamic_entries->find(safe_key); | 454 DynamicEntriesMap::iterator it = dynamic_entries_.find(safe_key); |
| 418 google_breakpad::CustomInfoEntry* entry = NULL; | 455 google_breakpad::CustomInfoEntry* entry = NULL; |
| 419 if (it == g_dynamic_entries->end()) { | 456 if (it == dynamic_entries_.end()) { |
| 420 if (g_dynamic_entries->size() >= kMaxDynamicEntries) | 457 if (dynamic_entries_.size() >= kMaxDynamicEntries) |
| 421 return; | 458 return; |
| 422 entry = &(*g_custom_entries)[g_dynamic_keys_offset++]; | 459 entry = &custom_entries_[dynamic_keys_offset_++]; |
| 423 g_dynamic_entries->insert(std::make_pair(safe_key, entry)); | 460 dynamic_entries_.insert(std::make_pair(safe_key, entry)); |
| 424 } else { | 461 } else { |
| 425 entry = it->second; | 462 entry = it->second; |
| 426 } | 463 } |
| 427 | 464 |
| 428 entry->set(safe_key.data(), safe_value.data()); | 465 entry->set(safe_key.data(), safe_value.data()); |
| 429 } | 466 } |
| 430 | 467 |
| 431 extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl( | 468 // NOTE: This function is used by SyzyASAN to annotate crash reports. If you |
| 432 const wchar_t* key) { | 469 // change the name or signature of this function you will break SyzyASAN |
| 433 if (!g_dynamic_entries) | 470 // instrumented releases of Chrome. Please contact syzygy-team@chromium.org |
| 471 // before doing so! | |
| 472 extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl( | |
| 473 const wchar_t* key, const wchar_t* value) { | |
| 474 KeyValuePairKeeper* keeper = KeyValuePairKeeper::keeper(); | |
| 475 if (!keeper) | |
| 434 return; | 476 return; |
| 435 | 477 |
| 436 DCHECK(g_dynamic_entries_lock); | 478 // TODO(siggi): This doesn't look quite right - there's NULL deref potential |
| 437 base::AutoLock lock(*g_dynamic_entries_lock); | 479 // here, and an implicit std::wstring conversion. Fixme. |
| 480 keeper->SetCrashKeyValue(key, value); | |
| 481 } | |
| 482 | |
| 483 void KeyValuePairKeeper::ClearCrashKeyValue(const std::wstring& key) { | |
| 484 base::AutoLock lock(lock_); | |
| 438 | 485 |
| 439 std::wstring key_string(key); | 486 std::wstring key_string(key); |
| 440 DynamicEntriesMap::iterator it = g_dynamic_entries->find(key_string); | 487 DynamicEntriesMap::iterator it = dynamic_entries_.find(key_string); |
| 441 if (it == g_dynamic_entries->end()) | 488 if (it == dynamic_entries_.end()) |
| 442 return; | 489 return; |
| 443 | 490 |
| 444 it->second->set_value(NULL); | 491 it->second->set_value(NULL); |
| 445 } | 492 } |
| 446 | 493 |
| 447 } // namespace | 494 extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl( |
| 495 const wchar_t* key) { | |
| 496 KeyValuePairKeeper* keeper = KeyValuePairKeeper::keeper(); | |
| 497 if (!keeper) | |
| 498 return; | |
| 499 | |
| 500 // TODO(siggi): This doesn't look quite right - there's NULL deref potential | |
| 501 // here, and an implicit std::wstring conversion. Fixme. | |
|
Robert Sesek
2014/05/29 20:34:41
Where's the NULL deref potential?
Sigurður Ásgeirsson
2014/05/30 15:14:49
If there's NULL on the input, std::string will try
| |
| 502 keeper->ClearCrashKeyValue(key); | |
| 503 } | |
| 448 | 504 |
| 449 static bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption, | 505 static bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption, |
| 450 UINT flags, bool* exit_now) { | 506 UINT flags, bool* exit_now) { |
| 451 // We wrap the call to MessageBoxW with a SEH handler because it some | 507 // We wrap the call to MessageBoxW with a SEH handler because it some |
| 452 // machines with CursorXP, PeaDict or with FontExplorer installed it crashes | 508 // machines with CursorXP, PeaDict or with FontExplorer installed it crashes |
| 453 // uncontrollably here. Being this a best effort deal we better go away. | 509 // uncontrollably here. Being this a best effort deal we better go away. |
| 454 __try { | 510 __try { |
| 455 *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags)); | 511 *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags)); |
| 456 } __except(EXCEPTION_EXECUTE_HANDLER) { | 512 } __except(EXCEPTION_EXECUTE_HANDLER) { |
| 457 // Its not safe to continue executing, exit silently here. | 513 // Its not safe to continue executing, exit silently here. |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 648 if (process_type.empty()) | 704 if (process_type.empty()) |
| 649 process_type = L"browser"; | 705 process_type = L"browser"; |
| 650 | 706 |
| 651 wchar_t exe_path[MAX_PATH]; | 707 wchar_t exe_path[MAX_PATH]; |
| 652 exe_path[0] = 0; | 708 exe_path[0] = 0; |
| 653 GetModuleFileNameW(NULL, exe_path, MAX_PATH); | 709 GetModuleFileNameW(NULL, exe_path, MAX_PATH); |
| 654 | 710 |
| 655 bool is_per_user_install = | 711 bool is_per_user_install = |
| 656 GetBreakpadClient()->GetIsPerUserInstall(base::FilePath(exe_path)); | 712 GetBreakpadClient()->GetIsPerUserInstall(base::FilePath(exe_path)); |
| 657 | 713 |
| 714 // This is intentionally leaked. | |
| 715 KeyValuePairKeeper* keeper = new KeyValuePairKeeper(); | |
| 716 | |
| 658 google_breakpad::CustomClientInfo* custom_info = | 717 google_breakpad::CustomClientInfo* custom_info = |
| 659 GetCustomInfo(exe_path, process_type); | 718 keeper->GetCustomInfo(exe_path, process_type); |
| 660 | 719 |
| 661 google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL; | 720 google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL; |
| 662 LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL; | 721 LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL; |
| 663 // We install the post-dump callback only for the browser and service | 722 // We install the post-dump callback only for the browser and service |
| 664 // processes. It spawns a new browser/service process. | 723 // processes. It spawns a new browser/service process. |
| 665 if (process_type == L"browser") { | 724 if (process_type == L"browser") { |
| 666 callback = &DumpDoneCallback; | 725 callback = &DumpDoneCallback; |
| 667 default_filter = &ChromeExceptionFilter; | 726 default_filter = &ChromeExceptionFilter; |
| 668 } else if (process_type == L"service") { | 727 } else if (process_type == L"service") { |
| 669 callback = &DumpDoneCallback; | 728 callback = &DumpDoneCallback; |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 736 if (process_type != L"browser" && | 795 if (process_type != L"browser" && |
| 737 !GetBreakpadClient()->IsRunningUnattended()) { | 796 !GetBreakpadClient()->IsRunningUnattended()) { |
| 738 // Initialize the hook TerminateProcess to catch unexpected exits. | 797 // Initialize the hook TerminateProcess to catch unexpected exits. |
| 739 InitTerminateProcessHooks(); | 798 InitTerminateProcessHooks(); |
| 740 } | 799 } |
| 741 #endif | 800 #endif |
| 742 } | 801 } |
| 743 } | 802 } |
| 744 | 803 |
| 745 } // namespace breakpad | 804 } // namespace breakpad |
| OLD | NEW |