Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 <windows.h> | 7 #include <windows.h> |
| 8 #include <shellapi.h> | 8 #include <shellapi.h> |
| 9 #include <tchar.h> | 9 #include <tchar.h> |
| 10 | 10 |
| 11 #include <algorithm> | 11 #include <algorithm> |
| 12 #include <vector> | 12 #include <vector> |
| 13 | 13 |
| 14 #include "base/base_switches.h" | 14 #include "base/base_switches.h" |
| 15 #include "base/command_line.h" | 15 #include "base/command_line.h" |
| 16 #include "base/environment.h" | 16 #include "base/environment.h" |
| 17 #include "base/file_util.h" | 17 #include "base/file_util.h" |
| 18 #include "base/file_version_info.h" | 18 #include "base/file_version_info.h" |
| 19 #include "base/memory/scoped_ptr.h" | 19 #include "base/memory/scoped_ptr.h" |
| 20 #include "base/string_split.h" | 20 #include "base/string_split.h" |
| 21 #include "base/string_util.h" | 21 #include "base/string_util.h" |
| 22 #include "base/string16.h" | |
| 22 #include "base/stringprintf.h" | 23 #include "base/stringprintf.h" |
| 23 #include "base/utf_string_conversions.h" | 24 #include "base/utf_string_conversions.h" |
| 24 #include "base/win/registry.h" | 25 #include "base/win/registry.h" |
| 25 #include "base/win/win_util.h" | 26 #include "base/win/win_util.h" |
| 26 #include "breakpad/src/client/windows/handler/exception_handler.h" | 27 #include "breakpad/src/client/windows/handler/exception_handler.h" |
| 27 #include "chrome/app/hard_error_handler_win.h" | 28 #include "chrome/app/hard_error_handler_win.h" |
| 28 #include "chrome/common/child_process_logging.h" | 29 #include "chrome/common/child_process_logging.h" |
| 29 #include "chrome/common/chrome_result_codes.h" | 30 #include "chrome/common/chrome_result_codes.h" |
| 30 #include "chrome/common/chrome_switches.h" | 31 #include "chrome/common/chrome_switches.h" |
| 31 #include "chrome/common/env_vars.h" | 32 #include "chrome/common/env_vars.h" |
| 32 #include "chrome/installer/util/google_chrome_sxs_distribution.h" | 33 #include "chrome/installer/util/google_chrome_sxs_distribution.h" |
| 33 #include "chrome/installer/util/google_update_settings.h" | 34 #include "chrome/installer/util/google_update_settings.h" |
| 34 #include "chrome/installer/util/install_util.h" | 35 #include "chrome/installer/util/install_util.h" |
| 35 #include "policy/policy_constants.h" | 36 #include "policy/policy_constants.h" |
| 36 | 37 |
| 38 // NACL_WIN64 build doesn't include field trials. | |
| 39 #if !defined(NACL_WIN64) | |
| 40 #include "base/metrics/field_trial.h" | |
| 41 #endif | |
| 42 | |
| 37 namespace { | 43 namespace { |
| 38 | 44 |
| 39 // Minidump with stacks, PEB, TEB, and unloaded module list. | 45 // Minidump with stacks, PEB, TEB, and unloaded module list. |
| 40 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>( | 46 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>( |
| 41 MiniDumpWithProcessThreadData | // Get PEB and TEB. | 47 MiniDumpWithProcessThreadData | // Get PEB and TEB. |
| 42 MiniDumpWithUnloadedModules); // Get unloaded modules when available. | 48 MiniDumpWithUnloadedModules); // Get unloaded modules when available. |
| 43 | 49 |
| 44 // Minidump with all of the above, plus memory referenced from stack. | 50 // Minidump with all of the above, plus memory referenced from stack. |
| 45 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( | 51 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( |
| 46 MiniDumpWithProcessThreadData | // Get PEB and TEB. | 52 MiniDumpWithProcessThreadData | // Get PEB and TEB. |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 70 // data updated as the state of the browser changes. | 76 // data updated as the state of the browser changes. |
| 71 static std::vector<google_breakpad::CustomInfoEntry>* g_custom_entries = NULL; | 77 static std::vector<google_breakpad::CustomInfoEntry>* g_custom_entries = NULL; |
| 72 static size_t g_url_chunks_offset; | 78 static size_t g_url_chunks_offset; |
| 73 static size_t g_num_of_extensions_offset; | 79 static size_t g_num_of_extensions_offset; |
| 74 static size_t g_extension_ids_offset; | 80 static size_t g_extension_ids_offset; |
| 75 static size_t g_client_id_offset; | 81 static size_t g_client_id_offset; |
| 76 static size_t g_gpu_info_offset; | 82 static size_t g_gpu_info_offset; |
| 77 static size_t g_num_of_views_offset; | 83 static size_t g_num_of_views_offset; |
| 78 static size_t g_num_switches_offset; | 84 static size_t g_num_switches_offset; |
| 79 static size_t g_switches_offset; | 85 static size_t g_switches_offset; |
| 86 static size_t g_num_of_experiments_offset; | |
| 87 static size_t g_experiment_chunks_offset; | |
|
robertshield
2012/03/14 13:51:48
For clarity I'd prefer these to be initialized exp
MAD
2012/03/15 22:37:36
I don't understand... I just did the same thing as
robertshield
2012/03/20 19:24:31
Yes, this wasn't a criticism of your code :-)
| |
| 80 | 88 |
| 81 // Maximum length for plugin path to include in plugin crash reports. | 89 // Maximum length for plugin path to include in plugin crash reports. |
| 82 const size_t kMaxPluginPathLength = 256; | 90 const size_t kMaxPluginPathLength = 256; |
| 83 | 91 |
| 84 // Dumps the current process memory. | 92 // Dumps the current process memory. |
| 85 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { | 93 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { |
| 86 if (g_breakpad) | 94 if (g_breakpad) |
| 87 g_breakpad->WriteMinidump(); | 95 g_breakpad->WriteMinidump(); |
| 88 } | 96 } |
| 89 | 97 |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 161 argv[argv_i].c_str(), | 169 argv[argv_i].c_str(), |
| 162 google_breakpad::CustomInfoEntry::kValueMaxLength); | 170 google_breakpad::CustomInfoEntry::kValueMaxLength); |
| 163 num_added++; | 171 num_added++; |
| 164 } | 172 } |
| 165 | 173 |
| 166 // Make note of the total number of switches. This is useful in case we have | 174 // Make note of the total number of switches. This is useful in case we have |
| 167 // truncated at kMaxSwitches, to see how many were unaccounted for. | 175 // truncated at kMaxSwitches, to see how many were unaccounted for. |
| 168 SetIntegerValue(g_num_switches_offset, static_cast<int>(argv.size()) - 1); | 176 SetIntegerValue(g_num_switches_offset, static_cast<int>(argv.size()) - 1); |
| 169 } | 177 } |
| 170 | 178 |
| 179 #if !defined(NACL_WIN64) | |
|
robertshield
2012/03/14 13:51:48
Would you consider moving this code to a new file
MAD
2012/03/15 22:37:36
It's doable, I guess I could call the function imp
robertshield
2012/03/20 19:24:31
Yes, that's what I meant. It would be a first step
| |
| 180 void UpdateExperiments() { | |
| 181 std::vector<base::FieldTrial::NameGroupId> name_group_ids; | |
| 182 base::FieldTrialList::GetFieldTrialNameGroupIds(&name_group_ids); | |
| 183 | |
| 184 std::vector<string16> experiment_strings(name_group_ids.size()); | |
| 185 for (size_t i = 0; i < name_group_ids.size(); ++i) { | |
| 186 experiment_strings[i] = base::StringPrintf( | |
| 187 L"%x-%x", name_group_ids[i].name, name_group_ids[i].group); | |
| 188 } | |
| 189 | |
| 190 size_t num_chunks = 0; | |
| 191 size_t current_experiment = 0; | |
| 192 string16 current_chunk; | |
| 193 // Preallocate the string... | |
| 194 current_chunk.resize(google_breakpad::CustomInfoEntry::kValueMaxLength); | |
| 195 current_chunk.erase(); // But start empty... This won't force a realloc. | |
| 196 while (current_experiment < experiment_strings.size() && | |
| 197 num_chunks < kMaxReportedExperimentChunks) { | |
| 198 // Check if we have enough room to add another experiment to the current | |
| 199 // chunk string. If not, we commit the current chunk string and start over. | |
| 200 if (current_chunk.size() + experiment_strings[current_experiment].size() > | |
| 201 google_breakpad::CustomInfoEntry::kValueMaxLength) { | |
| 202 base::wcslcpy( | |
| 203 (*g_custom_entries)[g_experiment_chunks_offset + num_chunks].value, | |
|
robertshield
2012/03/14 13:51:48
Not 100% sure, but I believe this will do the wron
MAD
2012/03/15 22:37:36
You're right, I'll add a DCHECK...
robertshield
2012/03/20 19:24:31
Sounds good. I suggest also handling this case in
| |
| 204 current_chunk.c_str(), | |
| 205 current_chunk.size() + 1); // This must include the NULL termination. | |
| 206 ++num_chunks; | |
| 207 current_chunk = experiment_strings[current_experiment]; | |
| 208 } else { | |
| 209 if (!current_chunk.empty()) | |
| 210 current_chunk += L","; | |
| 211 current_chunk += experiment_strings[current_experiment]; | |
| 212 } | |
| 213 ++current_experiment; | |
| 214 } | |
| 215 | |
| 216 // Commit the last chunk that didn't get big enough yet. | |
| 217 if (!current_chunk.empty() && num_chunks < kMaxReportedExperimentChunks) { | |
| 218 base::wcslcpy( | |
| 219 (*g_custom_entries)[g_experiment_chunks_offset + num_chunks].value, | |
| 220 current_chunk.c_str(), | |
| 221 current_chunk.size() + 1); // This must include the NULL termination. | |
| 222 } | |
| 223 | |
| 224 // Make note of the total number of experiments, | |
| 225 // even if it's > kMaxReportedExperimentChunks. This is useful when | |
| 226 // correlating stability with the number of experiments running | |
| 227 // simultaneously. | |
| 228 SetIntegerValue(g_num_of_experiments_offset, | |
| 229 static_cast<int>(name_group_ids.size())); | |
| 230 } | |
| 231 | |
| 232 extern "C" void __declspec(dllexport) __cdecl InitExperimentList() { | |
| 233 UpdateExperiments(); | |
| 234 } | |
| 235 | |
| 236 extern "C" void __declspec(dllexport) __cdecl AddFieldTrialGroup( | |
| 237 const std::string& field_trial_name, const std::string& group_name) { | |
| 238 // The trial is already in the list, we only use this as a notification. | |
| 239 // TODO(mad): Find a way to just add the new info instead of starting over. | |
|
robertshield
2012/03/14 13:51:48
please clarify that "the new info" is the data sto
MAD
2012/03/15 22:37:36
Done.
robertshield
2012/03/20 19:24:31
Where?
| |
| 240 UpdateExperiments(); | |
| 241 } | |
| 242 #endif // NACL_WIN64 | |
| 243 | |
| 171 // Appends the plugin path to |g_custom_entries|. | 244 // Appends the plugin path to |g_custom_entries|. |
| 172 void SetPluginPath(const std::wstring& path) { | 245 void SetPluginPath(const std::wstring& path) { |
| 173 DCHECK(g_custom_entries); | 246 DCHECK(g_custom_entries); |
| 174 | 247 |
| 175 if (path.size() > kMaxPluginPathLength) { | 248 if (path.size() > kMaxPluginPathLength) { |
| 176 // If the path is too long, truncate from the start rather than the end, | 249 // If the path is too long, truncate from the start rather than the end, |
| 177 // since we want to be able to recover the DLL name. | 250 // since we want to be able to recover the DLL name. |
| 178 SetPluginPath(path.substr(path.size() - kMaxPluginPathLength)); | 251 SetPluginPath(path.substr(path.size() - kMaxPluginPathLength)); |
| 179 return; | 252 return; |
| 180 } | 253 } |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 314 std::wstring plugin_path = | 387 std::wstring plugin_path = |
| 315 CommandLine::ForCurrentProcess()->GetSwitchValueNative("plugin-path"); | 388 CommandLine::ForCurrentProcess()->GetSwitchValueNative("plugin-path"); |
| 316 if (!plugin_path.empty()) | 389 if (!plugin_path.empty()) |
| 317 SetPluginPath(plugin_path); | 390 SetPluginPath(plugin_path); |
| 318 } | 391 } |
| 319 } else { | 392 } else { |
| 320 g_custom_entries->push_back( | 393 g_custom_entries->push_back( |
| 321 google_breakpad::CustomInfoEntry(L"num-views", L"N/A")); | 394 google_breakpad::CustomInfoEntry(L"num-views", L"N/A")); |
| 322 } | 395 } |
| 323 | 396 |
| 397 g_num_of_experiments_offset = g_custom_entries->size(); | |
| 398 g_custom_entries->push_back( | |
| 399 google_breakpad::CustomInfoEntry(L"num-experiments", L"N/A")); | |
| 400 | |
| 401 g_experiment_chunks_offset = g_custom_entries->size(); | |
| 402 for (int i = 0; i < kMaxReportedExperimentChunks; ++i) { | |
| 403 g_custom_entries->push_back(google_breakpad::CustomInfoEntry( | |
| 404 base::StringPrintf(L"experiment-chunk-%i", i + 1).c_str(), L"")); | |
| 405 } | |
| 406 | |
| 324 static google_breakpad::CustomClientInfo custom_client_info; | 407 static google_breakpad::CustomClientInfo custom_client_info; |
| 325 custom_client_info.entries = &g_custom_entries->front(); | 408 custom_client_info.entries = &g_custom_entries->front(); |
| 326 custom_client_info.count = g_custom_entries->size(); | 409 custom_client_info.count = g_custom_entries->size(); |
| 327 | 410 |
| 328 return &custom_client_info; | 411 return &custom_client_info; |
| 329 } | 412 } |
| 330 | 413 |
| 331 // This callback is used when we want to get a dump without crashing the | 414 // This callback is used when we want to get a dump without crashing the |
| 332 // process. | 415 // process. |
| 333 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*, | 416 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*, |
| (...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 504 google_breakpad::CustomInfoEntry::kValueMaxLength); | 587 google_breakpad::CustomInfoEntry::kValueMaxLength); |
| 505 } | 588 } |
| 506 | 589 |
| 507 extern "C" void __declspec(dllexport) __cdecl SetNumberOfViews( | 590 extern "C" void __declspec(dllexport) __cdecl SetNumberOfViews( |
| 508 int number_of_views) { | 591 int number_of_views) { |
| 509 SetIntegerValue(g_num_of_views_offset, number_of_views); | 592 SetIntegerValue(g_num_of_views_offset, number_of_views); |
| 510 } | 593 } |
| 511 | 594 |
| 512 } // namespace | 595 } // namespace |
| 513 | 596 |
| 597 namespace testing { | |
| 598 | |
| 599 // Access to namespace protected functions for test purposes. | |
| 600 google_breakpad::CustomClientInfo* TestGetCustomInfo() { | |
| 601 return GetCustomInfo(L"", L"", L""); | |
| 602 } | |
| 603 | |
| 604 #if !defined(NACL_WIN64) | |
| 605 void TestUpdateExperiments() { | |
| 606 UpdateExperiments(); | |
| 607 } | |
| 608 #endif // NACL_WIN64 | |
| 609 | |
| 610 } // namespace testing | |
| 611 | |
| 514 bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption, | 612 bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption, |
| 515 UINT flags, bool* exit_now) { | 613 UINT flags, bool* exit_now) { |
| 516 // We wrap the call to MessageBoxW with a SEH handler because it some | 614 // We wrap the call to MessageBoxW with a SEH handler because it some |
| 517 // machines with CursorXP, PeaDict or with FontExplorer installed it crashes | 615 // machines with CursorXP, PeaDict or with FontExplorer installed it crashes |
| 518 // uncontrollably here. Being this a best effort deal we better go away. | 616 // uncontrollably here. Being this a best effort deal we better go away. |
| 519 __try { | 617 __try { |
| 520 *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags)); | 618 *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags)); |
| 521 } __except(EXCEPTION_EXECUTE_HANDLER) { | 619 } __except(EXCEPTION_EXECUTE_HANDLER) { |
| 522 // Its not safe to continue executing, exit silently here. | 620 // Its not safe to continue executing, exit silently here. |
| 523 ::ExitProcess(chrome::RESULT_CODE_RESPAWN_FAILED); | 621 ::ExitProcess(chrome::RESULT_CODE_RESPAWN_FAILED); |
| (...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 753 // Tells breakpad to handle breakpoint and single step exceptions. | 851 // Tells breakpad to handle breakpoint and single step exceptions. |
| 754 // This might break JIT debuggers, but at least it will always | 852 // This might break JIT debuggers, but at least it will always |
| 755 // generate a crashdump for these exceptions. | 853 // generate a crashdump for these exceptions. |
| 756 g_breakpad->set_handle_debug_exceptions(true); | 854 g_breakpad->set_handle_debug_exceptions(true); |
| 757 } | 855 } |
| 758 } | 856 } |
| 759 | 857 |
| 760 void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) { | 858 void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) { |
| 761 previous_filter = SetUnhandledExceptionFilter(filter); | 859 previous_filter = SetUnhandledExceptionFilter(filter); |
| 762 } | 860 } |
| OLD | NEW |