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