From 8ba0b88ca93a3ec2fc75e5fb3afdc49775313ad3 Mon Sep 17 00:00:00 2001 From: sdefresne Date: Fri, 18 Sep 2015 03:33:13 -0700 Subject: Turn components/crash into a layered component. The crash component cannot be used on iOS (as it depends on //content). Turn it into a layered component so that it is possible to put shared code related to crash there (components/crash_keys and objc_zombie.{h,mm}). BUG=522955 TBR=sky@chromium.org TBR=jschuh@chromium.org NOPRESUBMIT=true Review URL: https://codereview.chromium.org/1315303004 Cr-Commit-Position: refs/heads/master@{#349643} --- BUILD.gn | 2 +- PRESUBMIT_test.py | 2 +- WATCHLISTS | 2 +- .../crash_reporter/aw_microdump_crash_reporter.cc | 4 +- chrome/BUILD.gn | 2 +- chrome/app/chrome_crash_reporter_client.h | 2 +- chrome/app/chrome_main_delegate.cc | 6 +- chrome/app/client_util.cc | 4 +- chrome/app/kasko_client.cc | 2 +- chrome/breakpad.isolate | 2 +- chrome/browser/BUILD.gn | 4 +- chrome/browser/chrome_browser_main_android.cc | 4 +- chrome/browser/chrome_browser_main_linux.cc | 2 +- chrome/browser/chrome_browser_main_mac.mm | 2 +- chrome/browser/chrome_content_browser_client.cc | 6 +- chrome/browser/chromeos/login/wizard_controller.cc | 2 +- chrome/browser/crash_upload_list_mac.cc | 2 +- .../browser/google/google_update_settings_posix.cc | 2 +- chrome/browser/ui/views/first_run_dialog.cc | 2 +- chrome/nacl/nacl_exe_win_64.cc | 2 +- chrome/tools/crash_service/main.cc | 2 +- chromecast/app/BUILD.gn | 6 +- .../android/cast_crash_reporter_client_android.h | 2 +- chromecast/app/android/crash_handler.cc | 4 +- chromecast/app/cast_main_delegate.cc | 2 +- chromecast/app/linux/cast_crash_reporter_client.cc | 2 +- chromecast/app/linux/cast_crash_reporter_client.h | 2 +- chromecast/browser/BUILD.gn | 4 +- chromecast/browser/cast_browser_main_parts.cc | 2 +- chromecast/browser/cast_browser_process.cc | 2 +- chromecast/browser/cast_content_browser_client.cc | 6 +- components/BUILD.gn | 8 +- components/components.gyp | 2 +- components/components_tests.gyp | 4 +- components/crash.gypi | 576 +++---- components/crash/README | 2 + components/crash/app/BUILD.gn | 79 - components/crash/app/DEPS | 8 - components/crash/app/breakpad_linux.cc | 1760 -------------------- components/crash/app/breakpad_linux.h | 32 - components/crash/app/breakpad_linux_impl.h | 65 - components/crash/app/breakpad_mac.h | 27 - components/crash/app/breakpad_mac.mm | 286 ---- components/crash/app/breakpad_mac_stubs.mm | 24 - components/crash/app/breakpad_win.cc | 745 --------- components/crash/app/breakpad_win.h | 28 - components/crash/app/crash_keys_win.cc | 192 --- components/crash/app/crash_keys_win.h | 82 - components/crash/app/crash_keys_win_unittest.cc | 142 -- components/crash/app/crash_reporter_client.cc | 148 -- components/crash/app/crash_reporter_client.h | 158 -- components/crash/app/crashpad_mac.h | 54 - components/crash/app/crashpad_mac.mm | 251 --- components/crash/app/hard_error_handler_win.cc | 112 -- components/crash/app/hard_error_handler_win.h | 34 - components/crash/browser/BUILD.gn | 38 - components/crash/browser/DEPS | 4 - .../crash/browser/crash_dump_manager_android.cc | 174 -- .../crash/browser/crash_dump_manager_android.h | 85 - .../crash/browser/crash_handler_host_linux.cc | 437 ----- .../crash/browser/crash_handler_host_linux.h | 93 -- components/crash/content/app/BUILD.gn | 79 + components/crash/content/app/DEPS | 8 + components/crash/content/app/breakpad_linux.cc | 1760 ++++++++++++++++++++ components/crash/content/app/breakpad_linux.h | 32 + components/crash/content/app/breakpad_linux_impl.h | 65 + components/crash/content/app/breakpad_mac.h | 27 + components/crash/content/app/breakpad_mac.mm | 286 ++++ components/crash/content/app/breakpad_mac_stubs.mm | 24 + components/crash/content/app/breakpad_win.cc | 745 +++++++++ components/crash/content/app/breakpad_win.h | 28 + components/crash/content/app/crash_keys_win.cc | 192 +++ components/crash/content/app/crash_keys_win.h | 87 + .../crash/content/app/crash_keys_win_unittest.cc | 142 ++ .../crash/content/app/crash_reporter_client.cc | 148 ++ .../crash/content/app/crash_reporter_client.h | 158 ++ components/crash/content/app/crashpad_mac.h | 54 + components/crash/content/app/crashpad_mac.mm | 251 +++ .../crash/content/app/hard_error_handler_win.cc | 112 ++ .../crash/content/app/hard_error_handler_win.h | 34 + components/crash/content/browser/BUILD.gn | 38 + components/crash/content/browser/DEPS | 4 + .../content/browser/crash_dump_manager_android.cc | 174 ++ .../content/browser/crash_dump_manager_android.h | 85 + .../content/browser/crash_handler_host_linux.cc | 437 +++++ .../content/browser/crash_handler_host_linux.h | 93 ++ components/crash/content/tools/BUILD.gn | 18 + components/crash/content/tools/crash_service.cc | 487 ++++++ components/crash/content/tools/crash_service.h | 125 ++ components/crash/content/tools/dmp2minidump.py | 51 + .../content/tools/generate_breakpad_symbols.py | 262 +++ components/crash/tools/BUILD.gn | 18 - components/crash/tools/crash_service.cc | 487 ------ components/crash/tools/crash_service.h | 125 -- components/crash/tools/dmp2minidump.py | 51 - .../crash/tools/generate_breakpad_symbols.py | 262 --- content/shell/BUILD.gn | 6 +- content/shell/android/BUILD.gn | 2 +- content/shell/app/shell_crash_reporter_client.h | 2 +- content/shell/app/shell_main_delegate.cc | 6 +- .../layout_test/layout_test_browser_main_parts.cc | 2 +- content/shell/browser/shell_browser_main_parts.cc | 2 +- .../shell/browser/shell_content_browser_client.cc | 6 +- content/shell/tools/content_shell_crash_service.cc | 2 +- remoting/base/breakpad_win.cc | 2 +- tools/msan/blacklist.txt | 2 +- 106 files changed, 6366 insertions(+), 6355 deletions(-) create mode 100644 components/crash/README delete mode 100644 components/crash/app/BUILD.gn delete mode 100644 components/crash/app/DEPS delete mode 100644 components/crash/app/breakpad_linux.cc delete mode 100644 components/crash/app/breakpad_linux.h delete mode 100644 components/crash/app/breakpad_linux_impl.h delete mode 100644 components/crash/app/breakpad_mac.h delete mode 100644 components/crash/app/breakpad_mac.mm delete mode 100644 components/crash/app/breakpad_mac_stubs.mm delete mode 100644 components/crash/app/breakpad_win.cc delete mode 100644 components/crash/app/breakpad_win.h delete mode 100644 components/crash/app/crash_keys_win.cc delete mode 100644 components/crash/app/crash_keys_win.h delete mode 100644 components/crash/app/crash_keys_win_unittest.cc delete mode 100644 components/crash/app/crash_reporter_client.cc delete mode 100644 components/crash/app/crash_reporter_client.h delete mode 100644 components/crash/app/crashpad_mac.h delete mode 100644 components/crash/app/crashpad_mac.mm delete mode 100644 components/crash/app/hard_error_handler_win.cc delete mode 100644 components/crash/app/hard_error_handler_win.h delete mode 100644 components/crash/browser/BUILD.gn delete mode 100644 components/crash/browser/DEPS delete mode 100644 components/crash/browser/crash_dump_manager_android.cc delete mode 100644 components/crash/browser/crash_dump_manager_android.h delete mode 100644 components/crash/browser/crash_handler_host_linux.cc delete mode 100644 components/crash/browser/crash_handler_host_linux.h create mode 100644 components/crash/content/app/BUILD.gn create mode 100644 components/crash/content/app/DEPS create mode 100644 components/crash/content/app/breakpad_linux.cc create mode 100644 components/crash/content/app/breakpad_linux.h create mode 100644 components/crash/content/app/breakpad_linux_impl.h create mode 100644 components/crash/content/app/breakpad_mac.h create mode 100644 components/crash/content/app/breakpad_mac.mm create mode 100644 components/crash/content/app/breakpad_mac_stubs.mm create mode 100644 components/crash/content/app/breakpad_win.cc create mode 100644 components/crash/content/app/breakpad_win.h create mode 100644 components/crash/content/app/crash_keys_win.cc create mode 100644 components/crash/content/app/crash_keys_win.h create mode 100644 components/crash/content/app/crash_keys_win_unittest.cc create mode 100644 components/crash/content/app/crash_reporter_client.cc create mode 100644 components/crash/content/app/crash_reporter_client.h create mode 100644 components/crash/content/app/crashpad_mac.h create mode 100644 components/crash/content/app/crashpad_mac.mm create mode 100644 components/crash/content/app/hard_error_handler_win.cc create mode 100644 components/crash/content/app/hard_error_handler_win.h create mode 100644 components/crash/content/browser/BUILD.gn create mode 100644 components/crash/content/browser/DEPS create mode 100644 components/crash/content/browser/crash_dump_manager_android.cc create mode 100644 components/crash/content/browser/crash_dump_manager_android.h create mode 100644 components/crash/content/browser/crash_handler_host_linux.cc create mode 100644 components/crash/content/browser/crash_handler_host_linux.h create mode 100644 components/crash/content/tools/BUILD.gn create mode 100644 components/crash/content/tools/crash_service.cc create mode 100644 components/crash/content/tools/crash_service.h create mode 100755 components/crash/content/tools/dmp2minidump.py create mode 100755 components/crash/content/tools/generate_breakpad_symbols.py delete mode 100644 components/crash/tools/BUILD.gn delete mode 100644 components/crash/tools/crash_service.cc delete mode 100644 components/crash/tools/crash_service.h delete mode 100755 components/crash/tools/dmp2minidump.py delete mode 100755 components/crash/tools/generate_breakpad_symbols.py diff --git a/BUILD.gn b/BUILD.gn index 8ca8bb3..cc27100 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -571,7 +571,7 @@ group("both_gn_and_gyp") { "//chrome/installer/setup:setup_unittests", "//chrome_elf:chrome_elf_unittests", "//chrome_elf:dll_hash_main", - "//components/crash/tools:crash_service", + "//components/crash/content/tools:crash_service", "//components/wifi:wifi_test", "//net:quic_client", "//net:quic_server", diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py index f57159f..a864f1f 100755 --- a/PRESUBMIT_test.py +++ b/PRESUBMIT_test.py @@ -482,7 +482,7 @@ class CheckAddedDepsHaveTetsApprovalsTest(unittest.TestCase): 'chrome/plugin/chrome_content_plugin_client.h', 'chrome/utility/chrome_content_utility_client.h', 'chromeos/chromeos_paths.h', - 'components/crash/DEPS', + 'components/crash/content/DEPS', 'components/nacl/common/DEPS', 'content/public/browser/render_process_host.h', 'policy/DEPS', diff --git a/WATCHLISTS b/WATCHLISTS index a4f9999..ffdc523 100644 --- a/WATCHLISTS +++ b/WATCHLISTS @@ -142,7 +142,7 @@ '|components/bookmarks' }, 'breakpad_app': { - 'filepath': 'components/crash/app/', + 'filepath': 'components/crash/content/app/', }, 'browser': { 'filepath': 'chrome/browser/', diff --git a/android_webview/crash_reporter/aw_microdump_crash_reporter.cc b/android_webview/crash_reporter/aw_microdump_crash_reporter.cc index f59c2f3..a95aaf9 100644 --- a/android_webview/crash_reporter/aw_microdump_crash_reporter.cc +++ b/android_webview/crash_reporter/aw_microdump_crash_reporter.cc @@ -9,8 +9,8 @@ #include "base/lazy_instance.h" #include "base/scoped_native_library.h" #include "build/build_config.h" -#include "components/crash/app/breakpad_linux.h" -#include "components/crash/app/crash_reporter_client.h" +#include "components/crash/content/app/breakpad_linux.h" +#include "components/crash/content/app/crash_reporter_client.h" namespace android_webview { namespace crash_reporter { diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn index edff1e0..9c7e07c 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn @@ -106,7 +106,7 @@ if (!is_android) { "//chrome/installer/util", "//chrome_elf", "//components/browser_watcher:browser_watcher_client", - "//components/crash/app", + "//components/crash/content/app", "//components/crash_keys", "//content:startup_helper_win", "//crypto", diff --git a/chrome/app/chrome_crash_reporter_client.h b/chrome/app/chrome_crash_reporter_client.h index 217f154..26a6d2b 100644 --- a/chrome/app/chrome_crash_reporter_client.h +++ b/chrome/app/chrome_crash_reporter_client.h @@ -8,7 +8,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" -#include "components/crash/app/crash_reporter_client.h" +#include "components/crash/content/app/crash_reporter_client.h" namespace browser_watcher { class CrashReportingMetrics; diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc index e7f4d44..c16628b 100644 --- a/chrome/app/chrome_main_delegate.cc +++ b/chrome/app/chrome_main_delegate.cc @@ -64,7 +64,7 @@ #include "chrome/browser/mac/relauncher.h" #include "chrome/common/mac/cfbundle_blocker.h" #include "chrome/common/mac/objc_zombie.h" -#include "components/crash/app/crashpad_mac.h" +#include "components/crash/content/app/crashpad_mac.h" #include "ui/base/l10n/l10n_util_mac.h" #endif @@ -72,7 +72,7 @@ #include #include #include "chrome/app/chrome_crash_reporter_client.h" -#include "components/crash/app/crash_reporter_client.h" +#include "components/crash/content/app/crash_reporter_client.h" #endif #if !defined(DISABLE_NACL) && defined(OS_LINUX) @@ -103,7 +103,7 @@ #endif #if defined(OS_POSIX) && !defined(OS_MACOSX) -#include "components/crash/app/breakpad_linux.h" +#include "components/crash/content/app/breakpad_linux.h" #endif #if defined(OS_LINUX) diff --git a/chrome/app/client_util.cc b/chrome/app/client_util.cc index 963b9db..4a156a7 100644 --- a/chrome/app/client_util.cc +++ b/chrome/app/client_util.cc @@ -38,8 +38,8 @@ #include "chrome/installer/util/google_update_settings.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/util_constants.h" -#include "components/crash/app/breakpad_win.h" -#include "components/crash/app/crash_reporter_client.h" +#include "components/crash/content/app/breakpad_win.h" +#include "components/crash/content/app/crash_reporter_client.h" #include "components/metrics/client_info.h" #include "content/public/app/startup_helper_win.h" #include "sandbox/win/src/sandbox.h" diff --git a/chrome/app/kasko_client.cc b/chrome/app/kasko_client.cc index bbc6cbb..f9699e9 100644 --- a/chrome/app/kasko_client.cc +++ b/chrome/app/kasko_client.cc @@ -17,7 +17,7 @@ #include "chrome/app/chrome_watcher_client_win.h" #include "chrome/chrome_watcher/chrome_watcher_main_api.h" #include "chrome/common/chrome_constants.h" -#include "components/crash/app/crash_keys_win.h" +#include "components/crash/content/app/crash_keys_win.h" #include "syzygy/kasko/api/client.h" namespace { diff --git a/chrome/breakpad.isolate b/chrome/breakpad.isolate index f15377d..4aa76be 100644 --- a/chrome/breakpad.isolate +++ b/chrome/breakpad.isolate @@ -4,7 +4,7 @@ { 'variables': { 'files': [ - '../components/crash/tools/generate_breakpad_symbols.py', + '../components/crash/content/tools/generate_breakpad_symbols.py', ], }, 'conditions': [ diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index cb5812c..2c5489ea 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -586,8 +586,8 @@ source_set("browser") { "//chrome/app/chrome_crash_reporter_client.h", ] deps += [ - "//components/crash/app", - "//components/crash/browser", + "//components/crash/content/app", + "//components/crash/content/browser", ] } if (use_nss_certs) { diff --git a/chrome/browser/chrome_browser_main_android.cc b/chrome/browser/chrome_browser_main_android.cc index 659ea40..c8dcda4 100644 --- a/chrome/browser/chrome_browser_main_android.cc +++ b/chrome/browser/chrome_browser_main_android.cc @@ -15,8 +15,8 @@ #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" -#include "components/crash/app/breakpad_linux.h" -#include "components/crash/browser/crash_dump_manager_android.h" +#include "components/crash/content/app/breakpad_linux.h" +#include "components/crash/content/browser/crash_dump_manager_android.h" #include "components/enhanced_bookmarks/persistent_image_store.h" #include "components/signin/core/browser/signin_manager.h" #include "content/public/browser/android/compositor.h" diff --git a/chrome/browser/chrome_browser_main_linux.cc b/chrome/browser/chrome_browser_main_linux.cc index 4f4e4ab..24b37c7 100644 --- a/chrome/browser/chrome_browser_main_linux.cc +++ b/chrome/browser/chrome_browser_main_linux.cc @@ -8,7 +8,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/grit/chromium_strings.h" -#include "components/crash/app/breakpad_linux.h" +#include "components/crash/content/app/breakpad_linux.h" #include "components/metrics/metrics_service.h" #include "media/audio/audio_manager.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chrome/browser/chrome_browser_main_mac.mm b/chrome/browser/chrome_browser_main_mac.mm index edffdad..5423f6d 100644 --- a/chrome/browser/chrome_browser_main_mac.mm +++ b/chrome/browser/chrome_browser_main_mac.mm @@ -24,7 +24,7 @@ #include "chrome/browser/ui/app_list/app_list_service.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" -#include "components/crash/app/crashpad_mac.h" +#include "components/crash/content/app/crashpad_mac.h" #include "components/metrics/metrics_service.h" #include "content/public/common/main_function_params.h" #include "content/public/common/result_codes.h" diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 3aa5fc7..b71448f 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -172,7 +172,7 @@ #include "chrome/browser/android/webapps/single_tab_mode_tab_helper.h" #include "chrome/browser/chrome_browser_main_android.h" #include "chrome/common/descriptors_android.h" -#include "components/crash/browser/crash_dump_manager_android.h" +#include "components/crash/content/browser/crash_dump_manager_android.h" #include "components/service_tab_launcher/browser/android/service_tab_launcher.h" #include "ui/base/resource/resource_bundle_android.h" #elif defined(OS_POSIX) @@ -181,8 +181,8 @@ #if defined(OS_POSIX) && !defined(OS_MACOSX) #include "base/debug/leak_annotations.h" -#include "components/crash/app/breakpad_linux.h" -#include "components/crash/browser/crash_handler_host_linux.h" +#include "components/crash/content/app/breakpad_linux.h" +#include "components/crash/content/browser/crash_handler_host_linux.h" #endif #if defined(OS_ANDROID) diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc index 2677dc9..c39b345 100644 --- a/chrome/browser/chromeos/login/wizard_controller.cc +++ b/chrome/browser/chromeos/login/wizard_controller.cc @@ -75,7 +75,7 @@ #include "chromeos/settings/cros_settings_provider.h" #include "chromeos/settings/timezone_settings.h" #include "chromeos/timezone/timezone_provider.h" -#include "components/crash/app/breakpad_linux.h" +#include "components/crash/content/app/breakpad_linux.h" #include "components/pairing/bluetooth_controller_pairing_controller.h" #include "components/pairing/bluetooth_host_pairing_controller.h" #include "components/pairing/shark_connection_listener.h" diff --git a/chrome/browser/crash_upload_list_mac.cc b/chrome/browser/crash_upload_list_mac.cc index 541faac..55cc043 100644 --- a/chrome/browser/crash_upload_list_mac.cc +++ b/chrome/browser/crash_upload_list_mac.cc @@ -6,7 +6,7 @@ #include "base/threading/sequenced_worker_pool.h" #include "base/time/time.h" -#include "components/crash/app/crashpad_mac.h" +#include "components/crash/content/app/crashpad_mac.h" CrashUploadListMac::CrashUploadListMac( Delegate* delegate, diff --git a/chrome/browser/google/google_update_settings_posix.cc b/chrome/browser/google/google_update_settings_posix.cc index 31e7301..8cfadc8 100644 --- a/chrome/browser/google/google_update_settings_posix.cc +++ b/chrome/browser/google/google_update_settings_posix.cc @@ -13,7 +13,7 @@ #include "chrome/common/chrome_paths.h" #if defined(OS_MACOSX) -#include "components/crash/app/crashpad_mac.h" +#include "components/crash/content/app/crashpad_mac.h" #endif namespace { diff --git a/chrome/browser/ui/views/first_run_dialog.cc b/chrome/browser/ui/views/first_run_dialog.cc index 0dbc4e9..2dc7e25 100644 --- a/chrome/browser/ui/views/first_run_dialog.cc +++ b/chrome/browser/ui/views/first_run_dialog.cc @@ -15,7 +15,7 @@ #include "chrome/grit/generated_resources.h" #include "chrome/grit/locale_settings.h" #include "chrome/installer/util/google_update_settings.h" -#include "components/crash/app/breakpad_linux.h" +#include "components/crash/content/app/breakpad_linux.h" #include "grit/components_strings.h" #include "ui/aura/env.h" #include "ui/aura/window.h" diff --git a/chrome/nacl/nacl_exe_win_64.cc b/chrome/nacl/nacl_exe_win_64.cc index d8fd8ad..5727ebb 100644 --- a/chrome/nacl/nacl_exe_win_64.cc +++ b/chrome/nacl/nacl_exe_win_64.cc @@ -6,7 +6,7 @@ #include "base/command_line.h" #include "base/lazy_instance.h" #include "chrome/app/chrome_crash_reporter_client.h" -#include "components/crash/app/breakpad_win.h" +#include "components/crash/content/app/breakpad_win.h" #include "components/nacl/loader/nacl_helper_win_64.h" #include "content/public/common/content_switches.h" diff --git a/chrome/tools/crash_service/main.cc b/chrome/tools/crash_service/main.cc index 94844ab..4b175dd 100644 --- a/chrome/tools/crash_service/main.cc +++ b/chrome/tools/crash_service/main.cc @@ -13,7 +13,7 @@ #include "base/path_service.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" -#include "components/crash/tools/crash_service.h" +#include "components/crash/content/tools/crash_service.h" namespace { diff --git a/chromecast/app/BUILD.gn b/chromecast/app/BUILD.gn index 7b0732d..3f27c1d 100644 --- a/chromecast/app/BUILD.gn +++ b/chromecast/app/BUILD.gn @@ -18,7 +18,7 @@ source_set("app") { "//chromecast/browser", "//chromecast/common", "//chromecast/renderer", - "//components/crash/app:lib", + "//components/crash/content/app:lib", "//content/public/app:both", "//content/public/browser", "//content/public/common", @@ -40,8 +40,8 @@ source_set("cast_crash_client") { "//base", "//chromecast/base", "//chromecast/crash", - "//components/crash/app", - "//components/crash/app:lib", + "//components/crash/content/app", + "//components/crash/content/app:lib", "//content/public/common", ] } diff --git a/chromecast/app/android/cast_crash_reporter_client_android.h b/chromecast/app/android/cast_crash_reporter_client_android.h index a87d167..9761683 100644 --- a/chromecast/app/android/cast_crash_reporter_client_android.h +++ b/chromecast/app/android/cast_crash_reporter_client_android.h @@ -6,7 +6,7 @@ #define CHROMECAST_APP_ANDROID_CAST_CRASH_REPORTER_CLIENT_ANDROID_H_ #include "base/compiler_specific.h" -#include "components/crash/app/crash_reporter_client.h" +#include "components/crash/content/app/crash_reporter_client.h" namespace chromecast { diff --git a/chromecast/app/android/crash_handler.cc b/chromecast/app/android/crash_handler.cc index 8a2dc7a..b4223f8 100644 --- a/chromecast/app/android/crash_handler.cc +++ b/chromecast/app/android/crash_handler.cc @@ -17,8 +17,8 @@ #include "breakpad/src/client/linux/handler/minidump_descriptor.h" #include "chromecast/app/android/cast_crash_reporter_client_android.h" #include "chromecast/base/version.h" -#include "components/crash/app/breakpad_linux.h" -#include "components/crash/app/crash_reporter_client.h" +#include "components/crash/content/app/breakpad_linux.h" +#include "components/crash/content/app/crash_reporter_client.h" #include "content/public/common/content_switches.h" #include "jni/CastCrashHandler_jni.h" diff --git a/chromecast/app/cast_main_delegate.cc b/chromecast/app/cast_main_delegate.cc index 768a0bc..698d14c 100644 --- a/chromecast/app/cast_main_delegate.cc +++ b/chromecast/app/cast_main_delegate.cc @@ -22,7 +22,7 @@ #include "chromecast/common/cast_resource_delegate.h" #include "chromecast/common/global_descriptors.h" #include "chromecast/renderer/cast_content_renderer_client.h" -#include "components/crash/app/crash_reporter_client.h" +#include "components/crash/content/app/crash_reporter_client.h" #include "content/public/browser/browser_main_runner.h" #include "content/public/common/content_switches.h" #include "ui/base/resource/resource_bundle.h" diff --git a/chromecast/app/linux/cast_crash_reporter_client.cc b/chromecast/app/linux/cast_crash_reporter_client.cc index 0bf2877..e90d4f1 100644 --- a/chromecast/app/linux/cast_crash_reporter_client.cc +++ b/chromecast/app/linux/cast_crash_reporter_client.cc @@ -7,7 +7,7 @@ #include "base/time/time.h" #include "chromecast/base/error_codes.h" #include "chromecast/crash/linux/crash_util.h" -#include "components/crash/app/breakpad_linux.h" +#include "components/crash/content/app/breakpad_linux.h" #include "content/public/common/content_switches.h" namespace chromecast { diff --git a/chromecast/app/linux/cast_crash_reporter_client.h b/chromecast/app/linux/cast_crash_reporter_client.h index 07712af..ef9f900 100644 --- a/chromecast/app/linux/cast_crash_reporter_client.h +++ b/chromecast/app/linux/cast_crash_reporter_client.h @@ -8,7 +8,7 @@ #include #include "base/macros.h" -#include "components/crash/app/crash_reporter_client.h" +#include "components/crash/content/app/crash_reporter_client.h" namespace chromecast { diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn index f787f03..4f6cc5a 100644 --- a/chromecast/browser/BUILD.gn +++ b/chromecast/browser/BUILD.gn @@ -74,8 +74,8 @@ source_set("browser") { "//chromecast/media", "//chromecast/net", "//chromecast/service", - "//components/crash/app", - "//components/crash/browser", + "//components/crash/content/app", + "//components/crash/content/browser", "//components/devtools_http_handler:devtools_http_handler", "//components/metrics", "//components/metrics:gpu", diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc index f363ed7..d15952f 100644 --- a/chromecast/browser/cast_browser_main_parts.cc +++ b/chromecast/browser/cast_browser_main_parts.cc @@ -51,7 +51,7 @@ #if defined(OS_ANDROID) #include "chromecast/app/android/crash_handler.h" #include "chromecast/browser/media/cast_media_client_android.h" -#include "components/crash/browser/crash_dump_manager_android.h" +#include "components/crash/content/browser/crash_dump_manager_android.h" #include "media/base/android/media_client_android.h" #include "net/android/network_change_notifier_factory_android.h" #else diff --git a/chromecast/browser/cast_browser_process.cc b/chromecast/browser/cast_browser_process.cc index 713fa74..077ee1d 100644 --- a/chromecast/browser/cast_browser_process.cc +++ b/chromecast/browser/cast_browser_process.cc @@ -15,7 +15,7 @@ #include "chromecast/service/cast_service.h" #if defined(OS_ANDROID) -#include "components/crash/browser/crash_dump_manager_android.h" +#include "components/crash/content/browser/crash_dump_manager_android.h" #endif // defined(OS_ANDROID) #if defined(USE_AURA) diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc index e693075..c73fa19 100644 --- a/chromecast/browser/cast_content_browser_client.cc +++ b/chromecast/browser/cast_content_browser_client.cc @@ -29,8 +29,8 @@ #include "chromecast/media/base/media_message_loop.h" #include "chromecast/public/cast_media_shlib.h" #include "chromecast/public/media/media_pipeline_backend.h" -#include "components/crash/app/breakpad_linux.h" -#include "components/crash/browser/crash_handler_host_linux.h" +#include "components/crash/content/app/breakpad_linux.h" +#include "components/crash/content/browser/crash_handler_host_linux.h" #include "components/network_hints/browser/network_hints_message_filter.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/certificate_request_result_type.h" @@ -48,7 +48,7 @@ #include "ui/gl/gl_switches.h" #if defined(OS_ANDROID) -#include "components/crash/browser/crash_dump_manager_android.h" +#include "components/crash/content/browser/crash_dump_manager_android.h" #include "components/external_video_surface/browser/android/external_video_surface_container_impl.h" #else #include "chromecast/browser/media/cast_browser_cdm_factory.h" diff --git a/components/BUILD.gn b/components/BUILD.gn index 0e86dbe..11b45ac 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn @@ -35,8 +35,8 @@ group("all_components") { "//components/compression", "//components/content_settings/core/browser", "//components/content_settings/core/common", - "//components/crash/app", - "//components/crash/browser", + "//components/crash/content/app", + "//components/crash/content/browser", "//components/crx_file", "//components/data_reduction_proxy/core/browser", "//components/data_reduction_proxy/core/common", @@ -190,8 +190,8 @@ group("all_components") { "//components/autofill/content/browser", # Blocked on content/blink. "//components/autofill/content/common", # Blocked on content. "//components/autofill/content/renderer", # Blocked on content/blink. - "//components/crash/app", # Should work, needs checking. - "//components/crash/browser", # Should work, needs checking. + "//components/crash/content/app", # Should work, needs checking. + "//components/crash/content/browser", # Should work, needs checking. "//components/captive_portal", # Should work, needs checking. "//components/cloud_devices/common", # Should work, needs checking. "//components/cdm/browser", # Blocked on content. diff --git a/components/components.gyp b/components/components.gyp index 24069c0..a4daf8c 100644 --- a/components/components.gyp +++ b/components/components.gyp @@ -21,6 +21,7 @@ 'component_updater.gypi', 'compression.gypi', 'content_settings.gypi', + 'crash.gypi', 'crash_keys.gypi', 'cronet.gypi', 'crx_file.gypi', @@ -105,7 +106,6 @@ 'browsing_data.gypi', 'cdm.gypi', 'certificate_transparency.gypi', - 'crash.gypi', 'devtools_discovery.gypi', 'devtools_http_handler.gypi', 'drive.gypi', diff --git a/components/components_tests.gyp b/components/components_tests.gyp index eb592eb..fc5ae36 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -119,7 +119,7 @@ 'content_settings/core/common/content_settings_pattern_unittest.cc', ], 'crash_unittest_sources': [ - 'crash/app/crash_keys_win_unittest.cc', + 'crash/content/app/crash_keys_win_unittest.cc', ], 'crx_file_unittest_sources': [ 'crx_file/id_util_unittest.cc', @@ -236,8 +236,8 @@ 'feedback/feedback_uploader_unittest.cc', ], 'gcm_driver_unittest_sources': [ - 'gcm_driver/gcm_account_tracker_unittest.cc', 'gcm_driver/gcm_account_mapper_unittest.cc', + 'gcm_driver/gcm_account_tracker_unittest.cc', 'gcm_driver/gcm_channel_status_request_unittest.cc', 'gcm_driver/gcm_client_impl_unittest.cc', 'gcm_driver/gcm_delayed_task_controller_unittest.cc', diff --git a/components/crash.gypi b/components/crash.gypi index 6ba2402..05b3bea 100644 --- a/components/crash.gypi +++ b/components/crash.gypi @@ -3,326 +3,330 @@ # found in the LICENSE file. { - 'targets': [ - { - 'target_name': 'crash_component_lib', - 'type': 'static_library', - 'sources': [ - 'crash/app/crash_keys_win.cc', - 'crash/app/crash_keys_win.h', - 'crash/app/crash_reporter_client.cc', - 'crash/app/crash_reporter_client.h', - ], - 'include_dirs': [ - '..', - '../breakpad/src', - ], - }, - { - # TODO(mark): https://crbug.com/466890: merge this target with - # crash_component. - # - # This is a temporary base target that is depended on by both - # crash_component and crash_component_breakpad_mac_to_be_deleted. It - # provides everything common to both of those targets. For a short period, - # there are two Mac crash component implementations. The new one uses a - # Crashpad implementation and is used by Chrome. The old one uses a - # Breakpad implementation and is used by content_shell. Consumers should - # depend on the desired target. All three targets behave identically on - # non-Mac. When content_shell and any other consumers are migrated to the - # Crashpad implementation on Mac, crash_component will merge back into - # this target, crash_component_non_mac, which will be renamed - # crash_component. crash_component_breakpad_mac_to_be_deleted will be - # deleted. - # - # While this situation exists: - # - # Do not depend on this target directly! Depend on - # crash_component_breakpad_mac_to_be_deleted for old Breakpad behavior on - # all platforms, or preferably, depend on crash_component to get Breakpad - # everywhere except for Mac, where you will get Crashpad. - 'target_name': 'crash_component_non_mac', - 'variables': { - 'conditions': [ - ['OS == "ios" or OS == "mac"', { - # On IOS there are no files compiled into the library, and we - # can't have libraries with zero objects. - # For now, the same applies to Mac OS X, until this target merges - # with crash_component. - 'crash_component_target_type%': 'none', - }, { - 'crash_component_target_type%': 'static_library', - }], - ], - }, - 'type': '<(crash_component_target_type)', - 'sources': [ - 'crash/app/breakpad_linux.cc', - 'crash/app/breakpad_linux.h', - 'crash/app/breakpad_linux_impl.h', - 'crash/app/breakpad_win.cc', - 'crash/app/breakpad_win.h', - 'crash/app/hard_error_handler_win.cc', - 'crash/app/hard_error_handler_win.h', - ], - 'dependencies': [ - 'crash_component_lib', - '../base/base.gyp:base', - ], - 'defines': ['CRASH_IMPLEMENTATION'], - 'conditions': [ - ['OS=="win"', { - 'dependencies': [ - '../breakpad/breakpad.gyp:breakpad_handler', - '../breakpad/breakpad.gyp:breakpad_sender', - '../sandbox/sandbox.gyp:sandbox', - ], - }], - ['os_posix == 1 and OS != "mac" and OS != "ios"', { - 'dependencies': [ - '../breakpad/breakpad.gyp:breakpad_client', - ], - 'include_dirs': [ - '../breakpad/src', - ], - }], - ], - 'target_conditions': [ - # Need 'target_conditions' to override default filename_rules to include - # the files on Android. - ['OS=="android"', { - 'sources/': [ - ['include', '^crash/app/breakpad_linux\\.cc$'], - ], - }], - ], - }, - { - # Note: if you depend on this target, you need to either link in - # content.gyp:content_common, or add - # content/public/common/content_switches.cc to your sources. - # - # GN version: //components/crash/app - - # TODO(mark): https://crbug.com/466890: merge this target with - # crash_component_non_mac. - # - # Most of this target is actually in its dependency, - # crash_component_non_mac. See the comment in that target for an - # explanation for the split. The split is temporary and the two targets - # will be unified again soon. - 'target_name': 'crash_component', - 'variables': { - 'conditions': [ - ['OS != "mac" ', { - # There are no source files on any platform but Mac OS X. - 'crash_component_target_type%': 'none', - }, { - 'crash_component_target_type%': 'static_library', - }], - ], - }, - 'type': '<(crash_component_target_type)', - 'sources': [ - 'crash/app/crashpad_mac.h', - 'crash/app/crashpad_mac.mm', - ], - 'dependencies': [ - 'crash_component_non_mac', - 'crash_component_lib', - '../base/base.gyp:base', - ], - 'defines': ['CRASH_IMPLEMENTATION'], - 'conditions': [ - ['OS=="mac"', { - 'dependencies': [ - '../third_party/crashpad/crashpad/client/client.gyp:crashpad_client', - ], - }], - ], - }, - { - # TODO(mark): https://crbug.com/466890: remove this target. - # - # This is a temporary target provided for Mac Breakpad users that have not - # yet migrated to Crashpad (namely content_shell). This target will be - # removed shortly and all consumers will be expected to use Crashpad as - # the Mac crash-reporting client. See the comment in the - # crash_component_non_mac target for more details. - 'target_name': 'crash_component_breakpad_mac_to_be_deleted', - 'variables': { - 'conditions': [ - ['OS != "mac" ', { - # There are no source files on any platform but Mac OS X. - 'crash_component_target_type%': 'none', - }, { - 'crash_component_target_type%': 'static_library', - }], - ], - }, - 'type': '<(crash_component_target_type)', - 'sources': [ - 'crash/app/breakpad_mac.h', - 'crash/app/breakpad_mac.mm', - ], - 'dependencies': [ - 'crash_component_non_mac', - 'crash_component_lib', - ], - 'defines': ['CRASH_IMPLEMENTATION'], - 'conditions': [ - ['OS=="mac"', { - 'dependencies': [ - '../breakpad/breakpad.gyp:breakpad', + 'conditions': [ + ['OS!="ios"', { + 'targets': [ + { + 'target_name': 'crash_component_lib', + 'type': 'static_library', + 'sources': [ + 'crash/content/app/crash_keys_win.cc', + 'crash/content/app/crash_keys_win.h', + 'crash/content/app/crash_reporter_client.cc', + 'crash/content/app/crash_reporter_client.h', ], 'include_dirs': [ '..', + '../breakpad/src', ], - }], - ], - }, - { - # GN version: //components/crash/app:test_support - 'target_name': 'crash_test_support', - 'type': 'none', - 'dependencies': [ - 'crash_component_lib', - ], - 'direct_dependent_settings': { - 'include_dirs' : [ - '../breakpad/src', - ], - } - }, - ], - 'conditions': [ - ['OS=="win"', { - 'targets': [ + }, { - # GN version: //components/crash/tools:crash_service - 'target_name': 'breakpad_crash_service', - 'type': 'static_library', + # TODO(mark): https://crbug.com/466890: merge this target with + # crash_component. + # + # This is a temporary base target that is depended on by both + # crash_component and crash_component_breakpad_mac_to_be_deleted. It + # provides everything common to both of those targets. For a short period, + # there are two Mac crash component implementations. The new one uses a + # Crashpad implementation and is used by Chrome. The old one uses a + # Breakpad implementation and is used by content_shell. Consumers should + # depend on the desired target. All three targets behave identically on + # non-Mac. When content_shell and any other consumers are migrated to the + # Crashpad implementation on Mac, crash_component will merge back into + # this target, crash_component_non_mac, which will be renamed + # crash_component. crash_component_breakpad_mac_to_be_deleted will be + # deleted. + # + # While this situation exists: + # + # Do not depend on this target directly! Depend on + # crash_component_breakpad_mac_to_be_deleted for old Breakpad behavior on + # all platforms, or preferably, depend on crash_component to get Breakpad + # everywhere except for Mac, where you will get Crashpad. + 'target_name': 'crash_component_non_mac', + 'variables': { + 'conditions': [ + ['OS == "ios" or OS == "mac"', { + # On IOS there are no files compiled into the library, and we + # can't have libraries with zero objects. + # For now, the same applies to Mac OS X, until this target merges + # with crash_component. + 'crash_component_target_type%': 'none', + }, { + 'crash_component_target_type%': 'static_library', + }], + ], + }, + 'type': '<(crash_component_target_type)', + 'sources': [ + 'crash/content/app/breakpad_linux.cc', + 'crash/content/app/breakpad_linux.h', + 'crash/content/app/breakpad_linux_impl.h', + 'crash/content/app/breakpad_win.cc', + 'crash/content/app/breakpad_win.h', + 'crash/content/app/hard_error_handler_win.cc', + 'crash/content/app/hard_error_handler_win.h', + ], 'dependencies': [ + 'crash_component_lib', '../base/base.gyp:base', - '../breakpad/breakpad.gyp:breakpad_handler', - '../breakpad/breakpad.gyp:breakpad_sender', ], - 'sources': [ - 'crash/tools/crash_service.cc', - 'crash/tools/crash_service.h', + 'defines': ['CRASH_IMPLEMENTATION'], + 'conditions': [ + ['OS=="win"', { + 'dependencies': [ + '../breakpad/breakpad.gyp:breakpad_handler', + '../breakpad/breakpad.gyp:breakpad_sender', + '../sandbox/sandbox.gyp:sandbox', + ], + }], + ['os_posix == 1 and OS != "mac" and OS != "ios"', { + 'dependencies': [ + '../breakpad/breakpad.gyp:breakpad_client', + ], + 'include_dirs': [ + '../breakpad/src', + ], + }], + ], + 'target_conditions': [ + # Need 'target_conditions' to override default filename_rules to include + # the files on Android. + ['OS=="android"', { + 'sources/': [ + ['include', '^crash/content/app/breakpad_linux\\.cc$'], + ], + }], ], }, - ], - }], - ['OS=="win" and target_arch=="ia32"', { - 'targets': [ { # Note: if you depend on this target, you need to either link in # content.gyp:content_common, or add # content/public/common/content_switches.cc to your sources. - 'target_name': 'breakpad_win64', - 'type': 'static_library', + # + # GN version: //components/crash/content/app + + # TODO(mark): https://crbug.com/466890: merge this target with + # crash_component_non_mac. + # + # Most of this target is actually in its dependency, + # crash_component_non_mac. See the comment in that target for an + # explanation for the split. The split is temporary and the two targets + # will be unified again soon. + 'target_name': 'crash_component', + 'variables': { + 'conditions': [ + ['OS != "mac" ', { + # There are no source files on any platform but Mac OS X. + 'crash_component_target_type%': 'none', + }, { + 'crash_component_target_type%': 'static_library', + }], + ], + }, + 'type': '<(crash_component_target_type)', 'sources': [ - 'crash/app/breakpad_linux.cc', - 'crash/app/breakpad_linux.h', - 'crash/app/breakpad_linux_impl.h', - 'crash/app/breakpad_mac.h', - 'crash/app/breakpad_mac.mm', - 'crash/app/breakpad_win.cc', - 'crash/app/breakpad_win.h', - # TODO(siggi): test the x64 version too. - 'crash/app/crash_keys_win.cc', - 'crash/app/crash_keys_win.h', - 'crash/app/crash_reporter_client.cc', - 'crash/app/crash_reporter_client.h', - 'crash/app/hard_error_handler_win.cc', - 'crash/app/hard_error_handler_win.h', - ], - 'defines': [ - 'COMPILE_CONTENT_STATICALLY', - 'CRASH_IMPLEMENTATION', + 'crash/content/app/crashpad_mac.h', + 'crash/content/app/crashpad_mac.mm', ], 'dependencies': [ - '../base/base.gyp:base_win64', - '../breakpad/breakpad.gyp:breakpad_handler_win64', - '../breakpad/breakpad.gyp:breakpad_sender_win64', - '../sandbox/sandbox.gyp:sandbox_win64', + 'crash_component_non_mac', + 'crash_component_lib', + '../base/base.gyp:base', + ], + 'defines': ['CRASH_IMPLEMENTATION'], + 'conditions': [ + ['OS=="mac"', { + 'dependencies': [ + '../third_party/crashpad/crashpad/client/client.gyp:crashpad_client', + ], + }], ], - 'configurations': { - 'Common_Base': { - 'msvs_target_platform': 'x64', - }, - }, }, { - 'target_name': 'breakpad_crash_service_win64', - 'type': 'static_library', + # TODO(mark): https://crbug.com/466890: remove this target. + # + # This is a temporary target provided for Mac Breakpad users that have not + # yet migrated to Crashpad (namely content_shell). This target will be + # removed shortly and all consumers will be expected to use Crashpad as + # the Mac crash-reporting client. See the comment in the + # crash_component_non_mac target for more details. + 'target_name': 'crash_component_breakpad_mac_to_be_deleted', + 'variables': { + 'conditions': [ + ['OS != "mac" ', { + # There are no source files on any platform but Mac OS X. + 'crash_component_target_type%': 'none', + }, { + 'crash_component_target_type%': 'static_library', + }], + ], + }, + 'type': '<(crash_component_target_type)', + 'sources': [ + 'crash/content/app/breakpad_mac.h', + 'crash/content/app/breakpad_mac.mm', + ], 'dependencies': [ - '../base/base.gyp:base_win64', - '../breakpad/breakpad.gyp:breakpad_handler_win64', - '../breakpad/breakpad.gyp:breakpad_sender_win64', + 'crash_component_non_mac', + 'crash_component_lib', ], - 'sources': [ - 'crash/tools/crash_service.cc', - 'crash/tools/crash_service.h', + 'defines': ['CRASH_IMPLEMENTATION'], + 'conditions': [ + ['OS=="mac"', { + 'dependencies': [ + '../breakpad/breakpad.gyp:breakpad', + ], + 'include_dirs': [ + '..', + ], + }], ], - 'configurations': { - 'Common_Base': { - 'msvs_target_platform': 'x64', - }, - }, }, - ], - }], - ['OS=="mac"', { - 'targets': [ { - 'target_name': 'breakpad_stubs', - 'type': 'static_library', + # GN version: //components/crash/content/app:test_support + 'target_name': 'crash_test_support', + 'type': 'none', 'dependencies': [ - '../base/base.gyp:base', - ], - 'sources': [ - 'crash/app/breakpad_mac.h', - 'crash/app/breakpad_mac_stubs.mm', - 'crash/app/crash_reporter_client.cc', - 'crash/app/crash_reporter_client.h', + 'crash_component_lib', ], + 'direct_dependent_settings': { + 'include_dirs' : [ + '../breakpad/src', + ], + } }, ], - }], - ['os_posix == 1 and OS != "mac" and OS != "ios"', { - 'targets': [ - { - # GN version: //components/crash/browser - 'target_name': 'breakpad_host', - 'type': 'static_library', - 'dependencies': [ - 'crash_component', - '../base/base.gyp:base', - '../breakpad/breakpad.gyp:breakpad_client', - '../content/content.gyp:content_browser', - '../content/content.gyp:content_common', + 'conditions': [ + ['OS=="win"', { + 'targets': [ + { + # GN version: //components/crash/content/tools:crash_service + 'target_name': 'breakpad_crash_service', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base', + '../breakpad/breakpad.gyp:breakpad_handler', + '../breakpad/breakpad.gyp:breakpad_sender', + ], + 'sources': [ + 'crash/content/tools/crash_service.cc', + 'crash/content/tools/crash_service.h', + ], + }, ], - 'sources': [ - 'crash/browser/crash_dump_manager_android.cc', - 'crash/browser/crash_dump_manager_android.h', - 'crash/browser/crash_handler_host_linux.cc', - 'crash/browser/crash_handler_host_linux.h', + }], + ['OS=="win" and target_arch=="ia32"', { + 'targets': [ + { + # Note: if you depend on this target, you need to either link in + # content.gyp:content_common, or add + # content/public/common/content_switches.cc to your sources. + 'target_name': 'breakpad_win64', + 'type': 'static_library', + 'sources': [ + 'crash/content/app/breakpad_linux.cc', + 'crash/content/app/breakpad_linux.h', + 'crash/content/app/breakpad_linux_impl.h', + 'crash/content/app/breakpad_mac.h', + 'crash/content/app/breakpad_mac.mm', + 'crash/content/app/breakpad_win.cc', + 'crash/content/app/breakpad_win.h', + # TODO(siggi): test the x64 version too. + 'crash/content/app/crash_keys_win.cc', + 'crash/content/app/crash_keys_win.h', + 'crash/content/app/crash_reporter_client.cc', + 'crash/content/app/crash_reporter_client.h', + 'crash/content/app/hard_error_handler_win.cc', + 'crash/content/app/hard_error_handler_win.h', + ], + 'defines': [ + 'COMPILE_CONTENT_STATICALLY', + 'CRASH_IMPLEMENTATION', + ], + 'dependencies': [ + '../base/base.gyp:base_win64', + '../breakpad/breakpad.gyp:breakpad_handler_win64', + '../breakpad/breakpad.gyp:breakpad_sender_win64', + '../sandbox/sandbox.gyp:sandbox_win64', + ], + 'configurations': { + 'Common_Base': { + 'msvs_target_platform': 'x64', + }, + }, + }, + { + 'target_name': 'breakpad_crash_service_win64', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base_win64', + '../breakpad/breakpad.gyp:breakpad_handler_win64', + '../breakpad/breakpad.gyp:breakpad_sender_win64', + ], + 'sources': [ + 'crash/content/tools/crash_service.cc', + 'crash/content/tools/crash_service.h', + ], + 'configurations': { + 'Common_Base': { + 'msvs_target_platform': 'x64', + }, + }, + }, ], - 'include_dirs': [ - '../breakpad/src', + }], + ['OS=="mac"', { + 'targets': [ + { + 'target_name': 'breakpad_stubs', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base', + ], + 'sources': [ + 'crash/content/app/breakpad_mac.h', + 'crash/content/app/breakpad_mac_stubs.mm', + 'crash/content/app/crash_reporter_client.cc', + 'crash/content/app/crash_reporter_client.h', + ], + }, ], - 'target_conditions': [ - # Need 'target_conditions' to override default filename_rules to include - # the files on Android. - ['OS=="android"', { - 'sources/': [ - ['include', '^crash/browser/crash_handler_host_linux\\.cc$'], + }], + ['os_posix == 1 and OS != "mac"', { + 'targets': [ + { + # GN version: //components/crash/content/browser + 'target_name': 'breakpad_host', + 'type': 'static_library', + 'dependencies': [ + 'crash_component', + '../base/base.gyp:base', + '../breakpad/breakpad.gyp:breakpad_client', + '../content/content.gyp:content_browser', + '../content/content.gyp:content_common', ], - }], + 'sources': [ + 'crash/content/browser/crash_dump_manager_android.cc', + 'crash/content/browser/crash_dump_manager_android.h', + 'crash/content/browser/crash_handler_host_linux.cc', + 'crash/content/browser/crash_handler_host_linux.h', + ], + 'include_dirs': [ + '../breakpad/src', + ], + 'target_conditions': [ + # Need 'target_conditions' to override default filename_rules to include + # the files on Android. + ['OS=="android"', { + 'sources/': [ + ['include', '^crash/content/browser/crash_handler_host_linux\\.cc$'], + ], + }], + ], + }, ], - }, + }], ], }], ], diff --git a/components/crash/README b/components/crash/README new file mode 100644 index 0000000..9f11c87 --- /dev/null +++ b/components/crash/README @@ -0,0 +1,2 @@ +Crash is a layered component [1] to enable it to be shared cleanly on iOS. +[1]: (https://sites.google.com/a/chromium.org/dev/developers/design-documents/layered-components-design) diff --git a/components/crash/app/BUILD.gn b/components/crash/app/BUILD.gn deleted file mode 100644 index ccaf7b6..0000000 --- a/components/crash/app/BUILD.gn +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -if (is_android) { - import("//build/config/android/config.gni") -} - -source_set("lib") { - sources = [ - "crash_keys_win.cc", - "crash_keys_win.h", - "crash_reporter_client.cc", - "crash_reporter_client.h", - ] - - include_dirs = [ "../../../breakpad/src" ] -} - -# Note: if you depend on this target, you need to either link in -# content.gyp:content_common, or add content/public/common/content_switches.cc -# to your sources. -# -# GYP version: components/crash.gypi:crash_component -source_set("app") { - sources = [ - "breakpad_linux_impl.h", - "breakpad_mac.h", - "breakpad_mac.mm", - "breakpad_win.cc", - "breakpad_win.h", - "hard_error_handler_win.cc", - "hard_error_handler_win.h", - ] - - if (is_android) { - libs = [ "log" ] - } - - if (is_android || is_linux) { - # Want these files on both Linux and Android. - set_sources_assignment_filter([]) - sources += [ - "breakpad_linux.cc", - "breakpad_linux.h", - ] - } - - defines = [ "CRASH_IMPLEMENTATION" ] - - deps = [ - "//base", - "//base:base_static", - ":lib", - ] - - if (is_android) { - defines += [ "CHROME_BUILD_ID=" + android_chrome_build_id ] - } - - if (is_mac) { - deps += [ "//breakpad" ] - } else if (is_win) { - deps += [ - "//sandbox", - "//breakpad:breakpad_handler", - - #'../breakpad/breakpad.gyp:breakpad_sender', TODO(GYP) - ] - } else if (is_posix && !is_ios) { - deps += [ "//breakpad:client" ] - } -} - -source_set("test_support") { - deps = [ - ":lib", - ] -} diff --git a/components/crash/app/DEPS b/components/crash/app/DEPS deleted file mode 100644 index dc5e932..0000000 --- a/components/crash/app/DEPS +++ /dev/null @@ -1,8 +0,0 @@ -include_rules = [ - "+sandbox", - - "+content/public/common/content_descriptors.h", - "+content/public/common/result_codes.h", - "+third_party/crashpad", - "+third_party/lss/linux_syscall_support.h", -] diff --git a/components/crash/app/breakpad_linux.cc b/components/crash/app/breakpad_linux.cc deleted file mode 100644 index 579d769..0000000 --- a/components/crash/app/breakpad_linux.cc +++ /dev/null @@ -1,1760 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// For linux_syscall_support.h. This makes it safe to call embedded system -// calls when in seccomp mode. - -#include "components/crash/app/breakpad_linux.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/debug/crash_logging.h" -#include "base/debug/dump_without_crashing.h" -#include "base/files/file_path.h" -#include "base/linux_util.h" -#include "base/path_service.h" -#include "base/posix/eintr_wrapper.h" -#include "base/posix/global_descriptors.h" -#include "base/process/memory.h" -#include "base/strings/string_util.h" -#include "breakpad/src/client/linux/crash_generation/crash_generation_client.h" -#include "breakpad/src/client/linux/handler/exception_handler.h" -#include "breakpad/src/client/linux/minidump_writer/directory_reader.h" -#include "breakpad/src/common/linux/linux_libc_support.h" -#include "breakpad/src/common/memory.h" -#include "build/build_config.h" -#include "components/crash/app/breakpad_linux_impl.h" -#include "components/crash/app/crash_reporter_client.h" -#include "content/public/common/content_descriptors.h" - -#if defined(OS_ANDROID) -#include -#include - -#include "base/android/build_info.h" -#include "base/android/path_utils.h" -#include "base/debug/leak_annotations.h" -#endif -#include "third_party/lss/linux_syscall_support.h" - -#if defined(ADDRESS_SANITIZER) -#include // for getcontext(). -#endif - -#if defined(OS_ANDROID) -#define STAT_STRUCT struct stat -#define FSTAT_FUNC fstat -#else -#define STAT_STRUCT struct kernel_stat -#define FSTAT_FUNC sys_fstat -#endif - -// Some versions of gcc are prone to warn about unused return values. In cases -// where we either a) know the call cannot fail, or b) there is nothing we -// can do when a call fails, we mark the return code as ignored. This avoids -// spurious compiler warnings. -#define IGNORE_RET(x) do { if (x); } while (0) - -using crash_reporter::GetCrashReporterClient; -using google_breakpad::ExceptionHandler; -using google_breakpad::MinidumpDescriptor; - -namespace breakpad { - -namespace { - -#if !defined(OS_CHROMEOS) -const char kUploadURL[] = "https://clients2.google.com/cr/report"; -#endif - -bool g_is_crash_reporter_enabled = false; -uint64_t g_process_start_time = 0; -pid_t g_pid = 0; -char* g_crash_log_path = nullptr; -ExceptionHandler* g_breakpad = nullptr; - -#if defined(ADDRESS_SANITIZER) -const char* g_asan_report_str = nullptr; -#endif -#if defined(OS_ANDROID) -char* g_process_type = nullptr; -ExceptionHandler* g_microdump = nullptr; -const char* g_microdump_build_fingerprint = nullptr; -const char* g_microdump_product_info = nullptr; -#endif - -CrashKeyStorage* g_crash_keys = nullptr; - -// Writes the value |v| as 16 hex characters to the memory pointed at by -// |output|. -void write_uint64_hex(char* output, uint64_t v) { - static const char hextable[] = "0123456789abcdef"; - - for (int i = 15; i >= 0; --i) { - output[i] = hextable[v & 15]; - v >>= 4; - } -} - -// The following helper functions are for calculating uptime. - -// Converts a struct timeval to milliseconds. -uint64_t timeval_to_ms(struct timeval *tv) { - uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t. - ret *= 1000; - ret += tv->tv_usec / 1000; - return ret; -} - -// Converts a struct timeval to milliseconds. -uint64_t kernel_timeval_to_ms(struct kernel_timeval *tv) { - uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t. - ret *= 1000; - ret += tv->tv_usec / 1000; - return ret; -} - -// String buffer size to use to convert a uint64_t to string. -const size_t kUint64StringSize = 21; - -void SetProcessStartTime() { - // Set the base process start time value. - struct timeval tv; - if (!gettimeofday(&tv, nullptr)) - g_process_start_time = timeval_to_ms(&tv); - else - g_process_start_time = 0; -} - -// uint64_t version of my_int_len() from -// breakpad/src/common/linux/linux_libc_support.h. Return the length of the -// given, non-negative integer when expressed in base 10. -unsigned my_uint64_len(uint64_t i) { - if (!i) - return 1; - - unsigned len = 0; - while (i) { - len++; - i /= 10; - } - - return len; -} - -// uint64_t version of my_uitos() from -// breakpad/src/common/linux/linux_libc_support.h. Convert a non-negative -// integer to a string (not null-terminated). -void my_uint64tos(char* output, uint64_t i, unsigned i_len) { - for (unsigned index = i_len; index; --index, i /= 10) - output[index - 1] = '0' + (i % 10); -} - -#if !defined(OS_CHROMEOS) -bool my_isxdigit(char c) { - return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); -} -#endif - -size_t LengthWithoutTrailingSpaces(const char* str, size_t len) { - while (len > 0 && str[len - 1] == ' ') { - len--; - } - return len; -} - -void SetClientIdFromCommandLine(const base::CommandLine& command_line) { - // Get the guid from the command line switch. - std::string switch_value = - command_line.GetSwitchValueASCII(switches::kEnableCrashReporter); - GetCrashReporterClient()->SetCrashReporterClientIdFromGUID(switch_value); -} - -// MIME substrings. -#if defined(OS_CHROMEOS) -const char g_sep[] = ":"; -#endif -const char g_rn[] = "\r\n"; -const char g_form_data_msg[] = "Content-Disposition: form-data; name=\""; -const char g_quote_msg[] = "\""; -const char g_dashdash_msg[] = "--"; -const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\""; -#if defined(ADDRESS_SANITIZER) -const char g_log_msg[] = "upload_file_log\"; filename=\"log\""; -#endif -const char g_content_type_msg[] = "Content-Type: application/octet-stream"; - -// MimeWriter manages an iovec for writing MIMEs to a file. -class MimeWriter { - public: - static const int kIovCapacity = 30; - static const size_t kMaxCrashChunkSize = 64; - - MimeWriter(int fd, const char* const mime_boundary); - ~MimeWriter(); - - // Append boundary. - virtual void AddBoundary(); - - // Append end of file boundary. - virtual void AddEnd(); - - // Append key/value pair with specified sizes. - virtual void AddPairData(const char* msg_type, - size_t msg_type_size, - const char* msg_data, - size_t msg_data_size); - - // Append key/value pair. - void AddPairString(const char* msg_type, - const char* msg_data) { - AddPairData(msg_type, my_strlen(msg_type), msg_data, my_strlen(msg_data)); - } - - // Append key/value pair, splitting value into chunks no larger than - // |chunk_size|. |chunk_size| cannot be greater than |kMaxCrashChunkSize|. - // The msg_type string will have a counter suffix to distinguish each chunk. - virtual void AddPairDataInChunks(const char* msg_type, - size_t msg_type_size, - const char* msg_data, - size_t msg_data_size, - size_t chunk_size, - bool strip_trailing_spaces); - - // Add binary file contents to be uploaded with the specified filename. - virtual void AddFileContents(const char* filename_msg, - uint8_t* file_data, - size_t file_size); - - // Flush any pending iovecs to the output file. - void Flush() { - IGNORE_RET(sys_writev(fd_, iov_, iov_index_)); - iov_index_ = 0; - } - - protected: - void AddItem(const void* base, size_t size); - // Minor performance trade-off for easier-to-maintain code. - void AddString(const char* str) { - AddItem(str, my_strlen(str)); - } - void AddItemWithoutTrailingSpaces(const void* base, size_t size); - - struct kernel_iovec iov_[kIovCapacity]; - int iov_index_; - - // Output file descriptor. - int fd_; - - const char* const mime_boundary_; - - private: - DISALLOW_COPY_AND_ASSIGN(MimeWriter); -}; - -MimeWriter::MimeWriter(int fd, const char* const mime_boundary) - : iov_index_(0), - fd_(fd), - mime_boundary_(mime_boundary) { -} - -MimeWriter::~MimeWriter() { -} - -void MimeWriter::AddBoundary() { - AddString(mime_boundary_); - AddString(g_rn); -} - -void MimeWriter::AddEnd() { - AddString(mime_boundary_); - AddString(g_dashdash_msg); - AddString(g_rn); -} - -void MimeWriter::AddPairData(const char* msg_type, - size_t msg_type_size, - const char* msg_data, - size_t msg_data_size) { - AddString(g_form_data_msg); - AddItem(msg_type, msg_type_size); - AddString(g_quote_msg); - AddString(g_rn); - AddString(g_rn); - AddItem(msg_data, msg_data_size); - AddString(g_rn); -} - -void MimeWriter::AddPairDataInChunks(const char* msg_type, - size_t msg_type_size, - const char* msg_data, - size_t msg_data_size, - size_t chunk_size, - bool strip_trailing_spaces) { - if (chunk_size > kMaxCrashChunkSize) - return; - - unsigned i = 0; - size_t done = 0, msg_length = msg_data_size; - - while (msg_length) { - char num[kUint64StringSize]; - const unsigned num_len = my_uint_len(++i); - my_uitos(num, i, num_len); - - size_t chunk_len = std::min(chunk_size, msg_length); - - AddString(g_form_data_msg); - AddItem(msg_type, msg_type_size); - AddItem(num, num_len); - AddString(g_quote_msg); - AddString(g_rn); - AddString(g_rn); - if (strip_trailing_spaces) { - AddItemWithoutTrailingSpaces(msg_data + done, chunk_len); - } else { - AddItem(msg_data + done, chunk_len); - } - AddString(g_rn); - AddBoundary(); - Flush(); - - done += chunk_len; - msg_length -= chunk_len; - } -} - -void MimeWriter::AddFileContents(const char* filename_msg, uint8_t* file_data, - size_t file_size) { - AddString(g_form_data_msg); - AddString(filename_msg); - AddString(g_rn); - AddString(g_content_type_msg); - AddString(g_rn); - AddString(g_rn); - AddItem(file_data, file_size); - AddString(g_rn); -} - -void MimeWriter::AddItem(const void* base, size_t size) { - // Check if the iovec is full and needs to be flushed to output file. - if (iov_index_ == kIovCapacity) { - Flush(); - } - iov_[iov_index_].iov_base = const_cast(base); - iov_[iov_index_].iov_len = size; - ++iov_index_; -} - -void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) { - AddItem(base, LengthWithoutTrailingSpaces(static_cast(base), - size)); -} - -#if defined(OS_CHROMEOS) -// This subclass is used on Chromium OS to report crashes in a format easy for -// the central crash reporting facility to understand. -// Format is :: -class CrashReporterWriter : public MimeWriter { - public: - explicit CrashReporterWriter(int fd); - - void AddBoundary() override; - - void AddEnd() override; - - void AddPairData(const char* msg_type, - size_t msg_type_size, - const char* msg_data, - size_t msg_data_size) override; - - void AddPairDataInChunks(const char* msg_type, - size_t msg_type_size, - const char* msg_data, - size_t msg_data_size, - size_t chunk_size, - bool strip_trailing_spaces) override; - - void AddFileContents(const char* filename_msg, - uint8_t* file_data, - size_t file_size) override; - - private: - DISALLOW_COPY_AND_ASSIGN(CrashReporterWriter); -}; - - -CrashReporterWriter::CrashReporterWriter(int fd) : MimeWriter(fd, "") {} - -// No-ops. -void CrashReporterWriter::AddBoundary() {} -void CrashReporterWriter::AddEnd() {} - -void CrashReporterWriter::AddPairData(const char* msg_type, - size_t msg_type_size, - const char* msg_data, - size_t msg_data_size) { - char data[kUint64StringSize]; - const unsigned data_len = my_uint_len(msg_data_size); - my_uitos(data, msg_data_size, data_len); - - AddItem(msg_type, msg_type_size); - AddString(g_sep); - AddItem(data, data_len); - AddString(g_sep); - AddItem(msg_data, msg_data_size); - Flush(); -} - -void CrashReporterWriter::AddPairDataInChunks(const char* msg_type, - size_t msg_type_size, - const char* msg_data, - size_t msg_data_size, - size_t chunk_size, - bool strip_trailing_spaces) { - if (chunk_size > kMaxCrashChunkSize) - return; - - unsigned i = 0; - size_t done = 0; - size_t msg_length = msg_data_size; - - while (msg_length) { - char num[kUint64StringSize]; - const unsigned num_len = my_uint_len(++i); - my_uitos(num, i, num_len); - - size_t chunk_len = std::min(chunk_size, msg_length); - - size_t write_len = chunk_len; - if (strip_trailing_spaces) { - // Take care of this here because we need to know the exact length of - // what is going to be written. - write_len = LengthWithoutTrailingSpaces(msg_data + done, write_len); - } - - char data[kUint64StringSize]; - const unsigned data_len = my_uint_len(write_len); - my_uitos(data, write_len, data_len); - - AddItem(msg_type, msg_type_size); - AddItem(num, num_len); - AddString(g_sep); - AddItem(data, data_len); - AddString(g_sep); - AddItem(msg_data + done, write_len); - Flush(); - - done += chunk_len; - msg_length -= chunk_len; - } -} - -void CrashReporterWriter::AddFileContents(const char* filename_msg, - uint8_t* file_data, - size_t file_size) { - char data[kUint64StringSize]; - const unsigned data_len = my_uint_len(file_size); - my_uitos(data, file_size, data_len); - - AddString(filename_msg); - AddString(g_sep); - AddItem(data, data_len); - AddString(g_sep); - AddItem(file_data, file_size); - Flush(); -} -#endif // defined(OS_CHROMEOS) - -void DumpProcess() { - if (g_breakpad) - g_breakpad->WriteMinidump(); - -#if defined(OS_ANDROID) - // If microdumps are enabled write also a microdump on the system log. - if (g_microdump) - g_microdump->WriteMinidump(); -#endif -} - -#if defined(OS_ANDROID) -const char kGoogleBreakpad[] = "google-breakpad"; -#endif - -size_t WriteLog(const char* buf, size_t nbytes) { -#if defined(OS_ANDROID) - return __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, buf); -#else - return sys_write(2, buf, nbytes); -#endif -} - -size_t WriteNewline() { - return WriteLog("\n", 1); -} - -#if defined(OS_ANDROID) -void AndroidLogWriteHorizontalRule() { - __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, - "### ### ### ### ### ### ### ### ### ### ### ### ###"); -} - -// Android's native crash handler outputs a diagnostic tombstone to the device -// log. By returning false from the HandlerCallbacks, breakpad will reinstall -// the previous (i.e. native) signal handlers before returning from its own -// handler. A Chrome build fingerprint is written to the log, so that the -// specific build of Chrome and the location of the archived Chrome symbols can -// be determined directly from it. -bool FinalizeCrashDoneAndroid(bool is_browser_process) { - base::android::BuildInfo* android_build_info = - base::android::BuildInfo::GetInstance(); - - AndroidLogWriteHorizontalRule(); - __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, - "Chrome build fingerprint:"); - __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, - android_build_info->package_version_name()); - __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, - android_build_info->package_version_code()); - __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, - CHROME_BUILD_ID); - AndroidLogWriteHorizontalRule(); - - if (!is_browser_process && - android_build_info->sdk_int() >= 18 && - my_strcmp(android_build_info->build_type(), "eng") != 0 && - my_strcmp(android_build_info->build_type(), "userdebug") != 0) { - // On JB MR2 and later, the system crash handler displays a dialog. For - // renderer crashes, this is a bad user experience and so this is disabled - // for user builds of Android. - // TODO(cjhopman): There should be some way to recover the crash stack from - // non-uploading user clients. See http://crbug.com/273706. - __android_log_write(ANDROID_LOG_WARN, - kGoogleBreakpad, - "Tombstones are disabled on JB MR2+ user builds."); - AndroidLogWriteHorizontalRule(); - return true; - } - return false; -} -#endif - -bool CrashDone(const MinidumpDescriptor& minidump, - const bool upload, - const bool succeeded) { - // WARNING: this code runs in a compromised context. It may not call into - // libc nor allocate memory normally. - if (!succeeded) { - const char msg[] = "Failed to generate minidump."; - WriteLog(msg, sizeof(msg) - 1); - return false; - } - - DCHECK(!minidump.IsFD()); - - BreakpadInfo info = {0}; - info.filename = minidump.path(); - info.fd = minidump.fd(); -#if defined(ADDRESS_SANITIZER) - google_breakpad::PageAllocator allocator; - const size_t log_path_len = my_strlen(minidump.path()); - char* log_path = reinterpret_cast(allocator.Alloc(log_path_len + 1)); - my_memcpy(log_path, minidump.path(), log_path_len); - my_memcpy(log_path + log_path_len - 4, ".log", 4); - log_path[log_path_len] = '\0'; - info.log_filename = log_path; -#endif - info.process_type = "browser"; - info.process_type_length = 7; - info.distro = base::g_linux_distro; - info.distro_length = my_strlen(base::g_linux_distro); - info.upload = upload; - info.process_start_time = g_process_start_time; - info.oom_size = base::g_oom_size; - info.pid = g_pid; - info.crash_keys = g_crash_keys; - HandleCrashDump(info); -#if defined(OS_ANDROID) - return FinalizeCrashDoneAndroid(true /* is_browser_process */); -#else - return true; -#endif -} - -// Wrapper function, do not add more code here. -bool CrashDoneNoUpload(const MinidumpDescriptor& minidump, - void* context, - bool succeeded) { - return CrashDone(minidump, false, succeeded); -} - -#if !defined(OS_ANDROID) -// Wrapper function, do not add more code here. -bool CrashDoneUpload(const MinidumpDescriptor& minidump, - void* context, - bool succeeded) { - return CrashDone(minidump, true, succeeded); -} -#endif - -#if defined(ADDRESS_SANITIZER) -extern "C" -void __asan_set_error_report_callback(void (*cb)(const char*)); - -extern "C" -void AsanLinuxBreakpadCallback(const char* report) { - g_asan_report_str = report; - // Send minidump here. - g_breakpad->SimulateSignalDelivery(SIGKILL); -} -#endif - -void EnableCrashDumping(bool unattended) { - g_is_crash_reporter_enabled = true; - - base::FilePath tmp_path("/tmp"); - PathService::Get(base::DIR_TEMP, &tmp_path); - - base::FilePath dumps_path(tmp_path); - if (GetCrashReporterClient()->GetCrashDumpLocation(&dumps_path)) { - base::FilePath logfile = - dumps_path.Append(GetCrashReporterClient()->GetReporterLogFilename()); - std::string logfile_str = logfile.value(); - const size_t crash_log_path_len = logfile_str.size() + 1; - g_crash_log_path = new char[crash_log_path_len]; - strncpy(g_crash_log_path, logfile_str.c_str(), crash_log_path_len); - } - DCHECK(!g_breakpad); - MinidumpDescriptor minidump_descriptor(dumps_path.value()); - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kFullMemoryCrashReport)) { - minidump_descriptor.set_size_limit(-1); // unlimited. - } else { - minidump_descriptor.set_size_limit(kMaxMinidumpFileSize); - } -#if defined(OS_ANDROID) - unattended = true; // Android never uploads directly. -#endif - if (unattended) { - g_breakpad = new ExceptionHandler( - minidump_descriptor, - nullptr, - CrashDoneNoUpload, - nullptr, - true, // Install handlers. - -1); // Server file descriptor. -1 for in-process. - return; - } - -#if !defined(OS_ANDROID) - // Attended mode - g_breakpad = new ExceptionHandler( - minidump_descriptor, - nullptr, - CrashDoneUpload, - nullptr, - true, // Install handlers. - -1); // Server file descriptor. -1 for in-process. -#endif -} - -#if defined(OS_ANDROID) -bool MicrodumpCrashDone(const MinidumpDescriptor& minidump, - void* context, - bool succeeded) { - // WARNING: this code runs in a compromised context. It may not call into - // libc nor allocate memory normally. - if (!succeeded) { - static const char msg[] = "Microdump crash handler failed.\n"; - WriteLog(msg, sizeof(msg) - 1); - return false; - } - - const bool is_browser_process = (context != nullptr); - return FinalizeCrashDoneAndroid(is_browser_process); - } - -// The microdump handler does NOT upload anything. It just dumps out on the -// system console (logcat) a restricted and serialized variant of a minidump. -// See crbug.com/410294 for more details. -void InitMicrodumpCrashHandlerIfNecessary(const std::string& process_type) { - if (!GetCrashReporterClient()->ShouldEnableBreakpadMicrodumps()) - return; - - VLOG(1) << "Enabling microdumps crash handler (process_type:" - << process_type << ")"; - - // The exception handler runs in a compromised context and cannot use c_str() - // as that would require the heap. Therefore, we have to guarantee that the - // build fingerprint and product info pointers are always valid. - const char* product_name = nullptr; - const char* product_version = nullptr; - GetCrashReporterClient()->GetProductNameAndVersion(&product_name, - &product_version); - - MinidumpDescriptor descriptor(MinidumpDescriptor::kMicrodumpOnConsole); - - if (product_name && product_version) { - g_microdump_product_info = strdup( - (product_name + std::string(":") + product_version).c_str()); - ANNOTATE_LEAKING_OBJECT_PTR(g_microdump_product_info); - descriptor.SetMicrodumpProductInfo(g_microdump_product_info); - } - - const char* android_build_fp = - base::android::BuildInfo::GetInstance()->android_build_fp(); - if (android_build_fp) { - g_microdump_build_fingerprint = strdup(android_build_fp); - ANNOTATE_LEAKING_OBJECT_PTR(g_microdump_build_fingerprint); - descriptor.SetMicrodumpBuildFingerprint(g_microdump_build_fingerprint); - } - - DCHECK(!g_microdump); - bool is_browser_process = process_type.empty() || process_type == "webview"; - g_microdump = new ExceptionHandler( - descriptor, - nullptr, - MicrodumpCrashDone, - reinterpret_cast(is_browser_process), - true, // Install handlers. - -1); // Server file descriptor. -1 for in-process. - return; -} - -bool CrashDoneInProcessNoUpload( - const google_breakpad::MinidumpDescriptor& descriptor, - void* context, - const bool succeeded) { - // WARNING: this code runs in a compromised context. It may not call into - // libc nor allocate memory normally. - if (!succeeded) { - static const char msg[] = "Crash dump generation failed.\n"; - WriteLog(msg, sizeof(msg) - 1); - return false; - } - - // Start constructing the message to send to the browser. - BreakpadInfo info = {0}; - info.filename = nullptr; - info.fd = descriptor.fd(); - info.process_type = g_process_type; - info.process_type_length = my_strlen(g_process_type); - info.distro = nullptr; - info.distro_length = 0; - info.upload = false; - info.process_start_time = g_process_start_time; - info.pid = g_pid; - info.crash_keys = g_crash_keys; - HandleCrashDump(info); - return FinalizeCrashDoneAndroid(false /* is_browser_process */); -} - -void EnableNonBrowserCrashDumping(const std::string& process_type, - int minidump_fd) { - // This will guarantee that the BuildInfo has been initialized and subsequent - // calls will not require memory allocation. - base::android::BuildInfo::GetInstance(); - SetClientIdFromCommandLine(*base::CommandLine::ForCurrentProcess()); - - // On Android, the current sandboxing uses process isolation, in which the - // child process runs with a different UID. That breaks the normal crash - // reporting where the browser process generates the minidump by inspecting - // the child process. This is because the browser process now does not have - // the permission to access the states of the child process (as it has a - // different UID). - // TODO(jcivelli): http://b/issue?id=6776356 we should use a watchdog - // process forked from the renderer process that generates the minidump. - if (minidump_fd == -1) { - LOG(ERROR) << "Minidump file descriptor not found, crash reporting will " - " not work."; - return; - } - SetProcessStartTime(); - g_pid = getpid(); - - g_is_crash_reporter_enabled = true; - // Save the process type (it is leaked). - const size_t process_type_len = process_type.size() + 1; - g_process_type = new char[process_type_len]; - strncpy(g_process_type, process_type.c_str(), process_type_len); - new google_breakpad::ExceptionHandler(MinidumpDescriptor(minidump_fd), - nullptr, CrashDoneInProcessNoUpload, nullptr, true, -1); -} -#else -// Non-Browser = Extension, Gpu, Plugins, Ppapi and Renderer -class NonBrowserCrashHandler : public google_breakpad::CrashGenerationClient { - public: - NonBrowserCrashHandler() - : server_fd_(base::GlobalDescriptors::GetInstance()->Get( - kCrashDumpSignal)) { - } - - ~NonBrowserCrashHandler() override {} - - bool RequestDump(const void* crash_context, - size_t crash_context_size) override { - int fds[2] = { -1, -1 }; - if (sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { - static const char msg[] = "Failed to create socket for crash dumping.\n"; - WriteLog(msg, sizeof(msg) - 1); - return false; - } - - // Start constructing the message to send to the browser. - char b; // Dummy variable for sys_read below. - const char* b_addr = &b; // Get the address of |b| so we can create the - // expected /proc/[pid]/syscall content in the - // browser to convert namespace tids. - - // The length of the control message: - static const unsigned kControlMsgSize = sizeof(int); - static const unsigned kControlMsgSpaceSize = CMSG_SPACE(kControlMsgSize); - static const unsigned kControlMsgLenSize = CMSG_LEN(kControlMsgSize); - - struct kernel_msghdr msg; - my_memset(&msg, 0, sizeof(struct kernel_msghdr)); - struct kernel_iovec iov[kCrashIovSize]; - iov[0].iov_base = const_cast(crash_context); - iov[0].iov_len = crash_context_size; - iov[1].iov_base = &b_addr; - iov[1].iov_len = sizeof(b_addr); - iov[2].iov_base = &fds[0]; - iov[2].iov_len = sizeof(fds[0]); - iov[3].iov_base = &g_process_start_time; - iov[3].iov_len = sizeof(g_process_start_time); - iov[4].iov_base = &base::g_oom_size; - iov[4].iov_len = sizeof(base::g_oom_size); - google_breakpad::SerializedNonAllocatingMap* serialized_map; - iov[5].iov_len = g_crash_keys->Serialize( - const_cast( - &serialized_map)); - iov[5].iov_base = serialized_map; -#if !defined(ADDRESS_SANITIZER) - static_assert(5 == kCrashIovSize - 1, "kCrashIovSize should equal 6"); -#else - iov[6].iov_base = const_cast(g_asan_report_str); - iov[6].iov_len = kMaxAsanReportSize + 1; - static_assert(6 == kCrashIovSize - 1, "kCrashIovSize should equal 7"); -#endif - - msg.msg_iov = iov; - msg.msg_iovlen = kCrashIovSize; - char cmsg[kControlMsgSpaceSize]; - my_memset(cmsg, 0, kControlMsgSpaceSize); - msg.msg_control = cmsg; - msg.msg_controllen = sizeof(cmsg); - - struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); - hdr->cmsg_level = SOL_SOCKET; - hdr->cmsg_type = SCM_RIGHTS; - hdr->cmsg_len = kControlMsgLenSize; - ((int*)CMSG_DATA(hdr))[0] = fds[1]; - - if (HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)) < 0) { - static const char errmsg[] = "Failed to tell parent about crash.\n"; - WriteLog(errmsg, sizeof(errmsg) - 1); - IGNORE_RET(sys_close(fds[0])); - IGNORE_RET(sys_close(fds[1])); - return false; - } - IGNORE_RET(sys_close(fds[1])); - - if (HANDLE_EINTR(sys_read(fds[0], &b, 1)) != 1) { - static const char errmsg[] = "Parent failed to complete crash dump.\n"; - WriteLog(errmsg, sizeof(errmsg) - 1); - } - IGNORE_RET(sys_close(fds[0])); - - return true; - } - - private: - // The pipe FD to the browser process, which will handle the crash dumping. - const int server_fd_; - - DISALLOW_COPY_AND_ASSIGN(NonBrowserCrashHandler); -}; - -void EnableNonBrowserCrashDumping() { - g_is_crash_reporter_enabled = true; - // We deliberately leak this object. - DCHECK(!g_breakpad); - - g_breakpad = new ExceptionHandler( - MinidumpDescriptor("/tmp"), // Unused but needed or Breakpad will assert. - nullptr, - nullptr, - nullptr, - true, - -1); - g_breakpad->set_crash_generation_client(new NonBrowserCrashHandler()); -} -#endif // defined(OS_ANDROID) - -void SetCrashKeyValue(const base::StringPiece& key, - const base::StringPiece& value) { - g_crash_keys->SetKeyValue(key.data(), value.data()); -} - -void ClearCrashKey(const base::StringPiece& key) { - g_crash_keys->RemoveKey(key.data()); -} - -// GetCrashReporterClient() cannot call any Set methods until after -// InitCrashKeys(). -void InitCrashKeys() { - g_crash_keys = new CrashKeyStorage; - GetCrashReporterClient()->RegisterCrashKeys(); - base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValue, &ClearCrashKey); -} - -// Miscellaneous initialization functions to call after Breakpad has been -// enabled. -void PostEnableBreakpadInitialization() { - SetProcessStartTime(); - g_pid = getpid(); - - base::debug::SetDumpWithoutCrashingFunction(&DumpProcess); -#if defined(ADDRESS_SANITIZER) - // Register the callback for AddressSanitizer error reporting. - __asan_set_error_report_callback(AsanLinuxBreakpadCallback); -#endif -} - -} // namespace - -void LoadDataFromFD(google_breakpad::PageAllocator& allocator, - int fd, bool close_fd, uint8_t** file_data, size_t* size) { - STAT_STRUCT st; - if (FSTAT_FUNC(fd, &st) != 0) { - static const char msg[] = "Cannot upload crash dump: stat failed\n"; - WriteLog(msg, sizeof(msg) - 1); - if (close_fd) - IGNORE_RET(sys_close(fd)); - return; - } - - *file_data = reinterpret_cast(allocator.Alloc(st.st_size)); - if (!(*file_data)) { - static const char msg[] = "Cannot upload crash dump: cannot alloc\n"; - WriteLog(msg, sizeof(msg) - 1); - if (close_fd) - IGNORE_RET(sys_close(fd)); - return; - } - my_memset(*file_data, 0xf, st.st_size); - - *size = st.st_size; - int byte_read = sys_read(fd, *file_data, *size); - if (byte_read == -1) { - static const char msg[] = "Cannot upload crash dump: read failed\n"; - WriteLog(msg, sizeof(msg) - 1); - if (close_fd) - IGNORE_RET(sys_close(fd)); - return; - } - - if (close_fd) - IGNORE_RET(sys_close(fd)); -} - -void LoadDataFromFile(google_breakpad::PageAllocator& allocator, - const char* filename, - int* fd, uint8_t** file_data, size_t* size) { - // WARNING: this code runs in a compromised context. It may not call into - // libc nor allocate memory normally. - *fd = sys_open(filename, O_RDONLY, 0); - *size = 0; - - if (*fd < 0) { - static const char msg[] = "Cannot upload crash dump: failed to open\n"; - WriteLog(msg, sizeof(msg) - 1); - return; - } - - LoadDataFromFD(allocator, *fd, true, file_data, size); -} - -// Spawn the appropriate upload process for the current OS: -// - generic Linux invokes wget. -// - ChromeOS invokes crash_reporter. -// |dumpfile| is the path to the dump data file. -// |mime_boundary| is only used on Linux. -// |exe_buf| is only used on CrOS and is the crashing process' name. -void ExecUploadProcessOrTerminate(const BreakpadInfo& info, - const char* dumpfile, - const char* mime_boundary, - const char* exe_buf, - google_breakpad::PageAllocator* allocator) { -#if defined(OS_CHROMEOS) - // CrOS uses crash_reporter instead of wget to report crashes, - // it needs to know where the crash dump lives and the pid and uid of the - // crashing process. - static const char kCrashReporterBinary[] = "/sbin/crash_reporter"; - - char pid_buf[kUint64StringSize]; - uint64_t pid_str_length = my_uint64_len(info.pid); - my_uint64tos(pid_buf, info.pid, pid_str_length); - pid_buf[pid_str_length] = '\0'; - - char uid_buf[kUint64StringSize]; - uid_t uid = geteuid(); - uint64_t uid_str_length = my_uint64_len(uid); - my_uint64tos(uid_buf, uid, uid_str_length); - uid_buf[uid_str_length] = '\0'; - - const char kChromeFlag[] = "--chrome="; - size_t buf_len = my_strlen(dumpfile) + sizeof(kChromeFlag); - char* chrome_flag = reinterpret_cast(allocator->Alloc(buf_len)); - chrome_flag[0] = '\0'; - my_strlcat(chrome_flag, kChromeFlag, buf_len); - my_strlcat(chrome_flag, dumpfile, buf_len); - - const char kPidFlag[] = "--pid="; - buf_len = my_strlen(pid_buf) + sizeof(kPidFlag); - char* pid_flag = reinterpret_cast(allocator->Alloc(buf_len)); - pid_flag[0] = '\0'; - my_strlcat(pid_flag, kPidFlag, buf_len); - my_strlcat(pid_flag, pid_buf, buf_len); - - const char kUidFlag[] = "--uid="; - buf_len = my_strlen(uid_buf) + sizeof(kUidFlag); - char* uid_flag = reinterpret_cast(allocator->Alloc(buf_len)); - uid_flag[0] = '\0'; - my_strlcat(uid_flag, kUidFlag, buf_len); - my_strlcat(uid_flag, uid_buf, buf_len); - - const char kExeBuf[] = "--exe="; - buf_len = my_strlen(exe_buf) + sizeof(kExeBuf); - char* exe_flag = reinterpret_cast(allocator->Alloc(buf_len)); - exe_flag[0] = '\0'; - my_strlcat(exe_flag, kExeBuf, buf_len); - my_strlcat(exe_flag, exe_buf, buf_len); - - const char* args[] = { - kCrashReporterBinary, - chrome_flag, - pid_flag, - uid_flag, - exe_flag, - nullptr, - }; - static const char msg[] = "Cannot upload crash dump: cannot exec " - "/sbin/crash_reporter\n"; -#else - // Compress |dumpfile| with gzip. - const pid_t gzip_child = sys_fork(); - if (gzip_child < 0) { - static const char msg[] = "sys_fork() for gzip process failed.\n"; - WriteLog(msg, sizeof(msg) - 1); - sys__exit(1); - } - if (!gzip_child) { - // gzip process. - const char* args[] = { - "/bin/gzip", - "-f", // Do not prompt to verify before overwriting. - dumpfile, - nullptr, - }; - execve(args[0], const_cast(args), environ); - static const char msg[] = "Cannot exec gzip.\n"; - WriteLog(msg, sizeof(msg) - 1); - sys__exit(1); - } - // Wait for gzip process. - int status = 0; - if (sys_waitpid(gzip_child, &status, 0) != gzip_child || - !WIFEXITED(status) || WEXITSTATUS(status) != 0) { - static const char msg[] = "sys_waitpid() for gzip process failed.\n"; - WriteLog(msg, sizeof(msg) - 1); - sys_kill(gzip_child, SIGKILL); - sys__exit(1); - } - - static const char kGzipExtension[] = ".gz"; - const size_t gzip_file_size = my_strlen(dumpfile) + sizeof(kGzipExtension); - char* const gzip_file = reinterpret_cast(allocator->Alloc( - gzip_file_size)); - my_strlcpy(gzip_file, dumpfile, gzip_file_size); - my_strlcat(gzip_file, kGzipExtension, gzip_file_size); - - // Rename |gzip_file| to |dumpfile| (the original file was deleted by gzip). - if (rename(gzip_file, dumpfile)) { - static const char msg[] = "Failed to rename gzipped file.\n"; - WriteLog(msg, sizeof(msg) - 1); - sys__exit(1); - } - - // The --header argument to wget looks like: - // --header=Content-Encoding: gzip - // --header=Content-Type: multipart/form-data; boundary=XYZ - // where the boundary has two fewer leading '-' chars - static const char header_content_encoding[] = - "--header=Content-Encoding: gzip"; - static const char header_msg[] = - "--header=Content-Type: multipart/form-data; boundary="; - const size_t header_content_type_size = - sizeof(header_msg) - 1 + my_strlen(mime_boundary) - 2 + 1; - char* const header_content_type = reinterpret_cast(allocator->Alloc( - header_content_type_size)); - my_strlcpy(header_content_type, header_msg, header_content_type_size); - my_strlcat(header_content_type, mime_boundary + 2, header_content_type_size); - - // The --post-file argument to wget looks like: - // --post-file=/tmp/... - static const char post_file_msg[] = "--post-file="; - const size_t post_file_size = - sizeof(post_file_msg) - 1 + my_strlen(dumpfile) + 1; - char* const post_file = reinterpret_cast(allocator->Alloc( - post_file_size)); - my_strlcpy(post_file, post_file_msg, post_file_size); - my_strlcat(post_file, dumpfile, post_file_size); - - static const char kWgetBinary[] = "/usr/bin/wget"; - const char* args[] = { - kWgetBinary, - header_content_encoding, - header_content_type, - post_file, - kUploadURL, - "--timeout=10", // Set a timeout so we don't hang forever. - "--tries=1", // Don't retry if the upload fails. - "-O", // output reply to fd 3 - "/dev/fd/3", - nullptr, - }; - static const char msg[] = "Cannot upload crash dump: cannot exec " - "/usr/bin/wget\n"; -#endif - execve(args[0], const_cast(args), environ); - WriteLog(msg, sizeof(msg) - 1); - sys__exit(1); -} - -// Runs in the helper process to wait for the upload process running -// ExecUploadProcessOrTerminate() to finish. Returns the number of bytes written -// to |fd| and save the written contents to |buf|. -// |buf| needs to be big enough to hold |bytes_to_read| + 1 characters. -size_t WaitForCrashReportUploadProcess(int fd, size_t bytes_to_read, - char* buf) { - size_t bytes_read = 0; - - // Upload should finish in about 10 seconds. Add a few more 500 ms - // internals to account for process startup time. - for (size_t wait_count = 0; wait_count < 24; ++wait_count) { - struct kernel_pollfd poll_fd; - poll_fd.fd = fd; - poll_fd.events = POLLIN | POLLPRI | POLLERR; - int ret = sys_poll(&poll_fd, 1, 500); - if (ret < 0) { - // Error - break; - } else if (ret > 0) { - // There is data to read. - ssize_t len = HANDLE_EINTR( - sys_read(fd, buf + bytes_read, bytes_to_read - bytes_read)); - if (len < 0) - break; - bytes_read += len; - if (bytes_read == bytes_to_read) - break; - } - // |ret| == 0 -> timed out, continue waiting. - // or |bytes_read| < |bytes_to_read| still, keep reading. - } - buf[bytes_to_read] = 0; // Always NUL terminate the buffer. - return bytes_read; -} - -// |buf| should be |expected_len| + 1 characters in size and nullptr terminated. -bool IsValidCrashReportId(const char* buf, size_t bytes_read, - size_t expected_len) { - if (bytes_read != expected_len) - return false; -#if defined(OS_CHROMEOS) - return my_strcmp(buf, "_sys_cr_finished") == 0; -#else - for (size_t i = 0; i < bytes_read; ++i) { - if (!my_isxdigit(buf[i])) - return false; - } - return true; -#endif -} - -// |buf| should be |expected_len| + 1 characters in size and nullptr terminated. -void HandleCrashReportId(const char* buf, size_t bytes_read, - size_t expected_len) { - WriteNewline(); - if (!IsValidCrashReportId(buf, bytes_read, expected_len)) { -#if defined(OS_CHROMEOS) - static const char msg[] = - "System crash-reporter failed to process crash report."; -#else - static const char msg[] = "Failed to get crash dump id."; -#endif - WriteLog(msg, sizeof(msg) - 1); - WriteNewline(); - - static const char id_msg[] = "Report Id: "; - WriteLog(id_msg, sizeof(id_msg) - 1); - WriteLog(buf, bytes_read); - WriteNewline(); - return; - } - -#if defined(OS_CHROMEOS) - static const char msg[] = "Crash dump received by crash_reporter\n"; - WriteLog(msg, sizeof(msg) - 1); -#else - // Write crash dump id to stderr. - static const char msg[] = "Crash dump id: "; - WriteLog(msg, sizeof(msg) - 1); - WriteLog(buf, my_strlen(buf)); - WriteNewline(); - - // Write crash dump id to crash log as: seconds_since_epoch,crash_id - struct kernel_timeval tv; - if (g_crash_log_path && !sys_gettimeofday(&tv, nullptr)) { - uint64_t time = kernel_timeval_to_ms(&tv) / 1000; - char time_str[kUint64StringSize]; - const unsigned time_len = my_uint64_len(time); - my_uint64tos(time_str, time, time_len); - - const int kLogOpenFlags = O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC; - int log_fd = sys_open(g_crash_log_path, kLogOpenFlags, 0600); - if (log_fd > 0) { - sys_write(log_fd, time_str, time_len); - sys_write(log_fd, ",", 1); - sys_write(log_fd, buf, my_strlen(buf)); - sys_write(log_fd, "\n", 1); - IGNORE_RET(sys_close(log_fd)); - } - } -#endif -} - -#if defined(OS_CHROMEOS) -const char* GetCrashingProcessName(const BreakpadInfo& info, - google_breakpad::PageAllocator* allocator) { - // Symlink to process binary is at /proc/###/exe. - char linkpath[kUint64StringSize + sizeof("/proc/") + sizeof("/exe")] = - "/proc/"; - uint64_t pid_value_len = my_uint64_len(info.pid); - my_uint64tos(linkpath + sizeof("/proc/") - 1, info.pid, pid_value_len); - linkpath[sizeof("/proc/") - 1 + pid_value_len] = '\0'; - my_strlcat(linkpath, "/exe", sizeof(linkpath)); - - const int kMaxSize = 4096; - char* link = reinterpret_cast(allocator->Alloc(kMaxSize)); - if (link) { - ssize_t size = readlink(linkpath, link, kMaxSize); - if (size < kMaxSize && size > 0) { - // readlink(2) doesn't add a terminating NUL, so do it now. - link[size] = '\0'; - - const char* name = my_strrchr(link, '/'); - if (name) - return name + 1; - return link; - } - } - // Either way too long, or a read error. - return "chrome-crash-unknown-process"; -} -#endif - -void HandleCrashDump(const BreakpadInfo& info) { - int dumpfd; - bool keep_fd = false; - size_t dump_size; - uint8_t* dump_data; - google_breakpad::PageAllocator allocator; - const char* exe_buf = nullptr; - - if (GetCrashReporterClient()->HandleCrashDump(info.filename)) { - return; - } - -#if defined(OS_CHROMEOS) - // Grab the crashing process' name now, when it should still be available. - // If we try to do this later in our grandchild the crashing process has - // already terminated. - exe_buf = GetCrashingProcessName(info, &allocator); -#endif - - if (info.fd != -1) { - // Dump is provided with an open FD. - keep_fd = true; - dumpfd = info.fd; - - // The FD is pointing to the end of the file. - // Rewind, we'll read the data next. - if (lseek(dumpfd, 0, SEEK_SET) == -1) { - static const char msg[] = "Cannot upload crash dump: failed to " - "reposition minidump FD\n"; - WriteLog(msg, sizeof(msg) - 1); - IGNORE_RET(sys_close(dumpfd)); - return; - } - LoadDataFromFD(allocator, info.fd, false, &dump_data, &dump_size); - } else { - // Dump is provided with a path. - keep_fd = false; - LoadDataFromFile(allocator, info.filename, &dumpfd, &dump_data, &dump_size); - } - - // TODO(jcivelli): make log work when using FDs. -#if defined(ADDRESS_SANITIZER) - int logfd; - size_t log_size; - uint8_t* log_data; - // Load the AddressSanitizer log into log_data. - LoadDataFromFile(allocator, info.log_filename, &logfd, &log_data, &log_size); -#endif - - // We need to build a MIME block for uploading to the server. Since we are - // going to fork and run wget, it needs to be written to a temp file. - const int ufd = sys_open("/dev/urandom", O_RDONLY, 0); - if (ufd < 0) { - static const char msg[] = "Cannot upload crash dump because /dev/urandom" - " is missing\n"; - WriteLog(msg, sizeof(msg) - 1); - return; - } - - static const char temp_file_template[] = - "/tmp/chromium-upload-XXXXXXXXXXXXXXXX"; - char temp_file[sizeof(temp_file_template)]; - int temp_file_fd = -1; - if (keep_fd) { - temp_file_fd = dumpfd; - // Rewind the destination, we are going to overwrite it. - if (lseek(dumpfd, 0, SEEK_SET) == -1) { - static const char msg[] = "Cannot upload crash dump: failed to " - "reposition minidump FD (2)\n"; - WriteLog(msg, sizeof(msg) - 1); - IGNORE_RET(sys_close(dumpfd)); - return; - } - } else { - if (info.upload) { - my_memcpy(temp_file, temp_file_template, sizeof(temp_file_template)); - - for (unsigned i = 0; i < 10; ++i) { - uint64_t t; - sys_read(ufd, &t, sizeof(t)); - write_uint64_hex(temp_file + sizeof(temp_file) - (16 + 1), t); - - temp_file_fd = sys_open(temp_file, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (temp_file_fd >= 0) - break; - } - - if (temp_file_fd < 0) { - static const char msg[] = "Failed to create temporary file in /tmp: " - "cannot upload crash dump\n"; - WriteLog(msg, sizeof(msg) - 1); - IGNORE_RET(sys_close(ufd)); - return; - } - } else { - temp_file_fd = sys_open(info.filename, O_WRONLY, 0600); - if (temp_file_fd < 0) { - static const char msg[] = "Failed to save crash dump: failed to open\n"; - WriteLog(msg, sizeof(msg) - 1); - IGNORE_RET(sys_close(ufd)); - return; - } - } - } - - // The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL. - char mime_boundary[28 + 16 + 1]; - my_memset(mime_boundary, '-', 28); - uint64_t boundary_rand; - sys_read(ufd, &boundary_rand, sizeof(boundary_rand)); - write_uint64_hex(mime_boundary + 28, boundary_rand); - mime_boundary[28 + 16] = 0; - IGNORE_RET(sys_close(ufd)); - - // The MIME block looks like this: - // BOUNDARY \r\n - // Content-Disposition: form-data; name="prod" \r\n \r\n - // Chrome_Linux \r\n - // BOUNDARY \r\n - // Content-Disposition: form-data; name="ver" \r\n \r\n - // 1.2.3.4 \r\n - // BOUNDARY \r\n - // - // zero or one: - // Content-Disposition: form-data; name="ptime" \r\n \r\n - // abcdef \r\n - // BOUNDARY \r\n - // - // zero or one: - // Content-Disposition: form-data; name="ptype" \r\n \r\n - // abcdef \r\n - // BOUNDARY \r\n - // - // zero or one: - // Content-Disposition: form-data; name="lsb-release" \r\n \r\n - // abcdef \r\n - // BOUNDARY \r\n - // - // zero or one: - // Content-Disposition: form-data; name="oom-size" \r\n \r\n - // 1234567890 \r\n - // BOUNDARY \r\n - // - // zero or more (up to CrashKeyStorage::num_entries = 64): - // Content-Disposition: form-data; name=crash-key-name \r\n - // crash-key-value \r\n - // BOUNDARY \r\n - // - // Content-Disposition: form-data; name="dump"; filename="dump" \r\n - // Content-Type: application/octet-stream \r\n \r\n - // - // \r\n BOUNDARY -- \r\n - -#if defined(OS_CHROMEOS) - CrashReporterWriter writer(temp_file_fd); -#else - MimeWriter writer(temp_file_fd, mime_boundary); -#endif - { - const char* product_name = ""; - const char* version = ""; - - GetCrashReporterClient()->GetProductNameAndVersion(&product_name, &version); - - writer.AddBoundary(); - writer.AddPairString("prod", product_name); - writer.AddBoundary(); - writer.AddPairString("ver", version); - writer.AddBoundary(); - if (info.pid > 0) { - char pid_value_buf[kUint64StringSize]; - uint64_t pid_value_len = my_uint64_len(info.pid); - my_uint64tos(pid_value_buf, info.pid, pid_value_len); - static const char pid_key_name[] = "pid"; - writer.AddPairData(pid_key_name, sizeof(pid_key_name) - 1, - pid_value_buf, pid_value_len); - writer.AddBoundary(); - } -#if defined(OS_ANDROID) - // Addtional MIME blocks are added for logging on Android devices. - static const char android_build_id[] = "android_build_id"; - static const char android_build_fp[] = "android_build_fp"; - static const char device[] = "device"; - static const char model[] = "model"; - static const char brand[] = "brand"; - static const char exception_info[] = "exception_info"; - - base::android::BuildInfo* android_build_info = - base::android::BuildInfo::GetInstance(); - writer.AddPairString( - android_build_id, android_build_info->android_build_id()); - writer.AddBoundary(); - writer.AddPairString( - android_build_fp, android_build_info->android_build_fp()); - writer.AddBoundary(); - writer.AddPairString(device, android_build_info->device()); - writer.AddBoundary(); - writer.AddPairString(model, android_build_info->model()); - writer.AddBoundary(); - writer.AddPairString(brand, android_build_info->brand()); - writer.AddBoundary(); - if (android_build_info->java_exception_info() != nullptr) { - writer.AddPairString(exception_info, - android_build_info->java_exception_info()); - writer.AddBoundary(); - } -#endif - writer.Flush(); - } - - if (info.process_start_time > 0) { - struct kernel_timeval tv; - if (!sys_gettimeofday(&tv, nullptr)) { - uint64_t time = kernel_timeval_to_ms(&tv); - if (time > info.process_start_time) { - time -= info.process_start_time; - char time_str[kUint64StringSize]; - const unsigned time_len = my_uint64_len(time); - my_uint64tos(time_str, time, time_len); - - static const char process_time_msg[] = "ptime"; - writer.AddPairData(process_time_msg, sizeof(process_time_msg) - 1, - time_str, time_len); - writer.AddBoundary(); - writer.Flush(); - } - } - } - - if (info.process_type_length) { - writer.AddPairString("ptype", info.process_type); - writer.AddBoundary(); - writer.Flush(); - } - - if (info.distro_length) { - static const char distro_msg[] = "lsb-release"; - writer.AddPairString(distro_msg, info.distro); - writer.AddBoundary(); - writer.Flush(); - } - - if (info.oom_size) { - char oom_size_str[kUint64StringSize]; - const unsigned oom_size_len = my_uint64_len(info.oom_size); - my_uint64tos(oom_size_str, info.oom_size, oom_size_len); - static const char oom_size_msg[] = "oom-size"; - writer.AddPairData(oom_size_msg, sizeof(oom_size_msg) - 1, - oom_size_str, oom_size_len); - writer.AddBoundary(); - writer.Flush(); - } - - if (info.crash_keys) { - CrashKeyStorage::Iterator crash_key_iterator(*info.crash_keys); - const CrashKeyStorage::Entry* entry; - while ((entry = crash_key_iterator.Next())) { - writer.AddPairString(entry->key, entry->value); - writer.AddBoundary(); - writer.Flush(); - } - } - - writer.AddFileContents(g_dump_msg, dump_data, dump_size); -#if defined(ADDRESS_SANITIZER) - // Append a multipart boundary and the contents of the AddressSanitizer log. - writer.AddBoundary(); - writer.AddFileContents(g_log_msg, log_data, log_size); -#endif - writer.AddEnd(); - writer.Flush(); - - IGNORE_RET(sys_close(temp_file_fd)); - -#if defined(OS_ANDROID) - if (info.filename) { - size_t filename_length = my_strlen(info.filename); - - // If this was a file, we need to copy it to the right place and use the - // right file name so it gets uploaded by the browser. - const char msg[] = "Output crash dump file:"; - WriteLog(msg, sizeof(msg) - 1); - WriteLog(info.filename, filename_length); - - char pid_buf[kUint64StringSize]; - size_t pid_str_length = my_uint64_len(info.pid); - my_uint64tos(pid_buf, info.pid, pid_str_length); - pid_buf[pid_str_length] = 0; // my_uint64tos() doesn't null-terminate. - - size_t done_filename_len = filename_length + pid_str_length + 1; - char* done_filename = reinterpret_cast( - allocator.Alloc(done_filename_len)); - // Rename the file such that the pid is the suffix in order signal to other - // processes that the minidump is complete. The advantage of using the pid - // as the suffix is that it is trivial to associate the minidump with the - // crashed process. - my_strlcpy(done_filename, info.filename, done_filename_len); - my_strlcat(done_filename, pid_buf, done_filename_len); - // Rename the minidump file to signal that it is complete. - if (rename(info.filename, done_filename)) { - const char failed_msg[] = "Failed to rename:"; - WriteLog(failed_msg, sizeof(failed_msg) - 1); - WriteLog(info.filename, filename_length); - const char to_msg[] = "to"; - WriteLog(to_msg, sizeof(to_msg) - 1); - WriteLog(done_filename, done_filename_len - 1); - } - } -#endif - - if (!info.upload) - return; - - const pid_t child = sys_fork(); - if (!child) { - // Spawned helper process. - // - // This code is called both when a browser is crashing (in which case, - // nothing really matters any more) and when a renderer/plugin crashes, in - // which case we need to continue. - // - // Since we are a multithreaded app, if we were just to fork(), we might - // grab file descriptors which have just been created in another thread and - // hold them open for too long. - // - // Thus, we have to loop and try and close everything. - const int fd = sys_open("/proc/self/fd", O_DIRECTORY | O_RDONLY, 0); - if (fd < 0) { - for (unsigned i = 3; i < 8192; ++i) - IGNORE_RET(sys_close(i)); - } else { - google_breakpad::DirectoryReader reader(fd); - const char* name; - while (reader.GetNextEntry(&name)) { - int i; - if (my_strtoui(&i, name) && i > 2 && i != fd) - IGNORE_RET(sys_close(i)); - reader.PopEntry(); - } - - IGNORE_RET(sys_close(fd)); - } - - IGNORE_RET(sys_setsid()); - - // Leave one end of a pipe in the upload process and watch for it getting - // closed by the upload process exiting. - int fds[2]; - if (sys_pipe(fds) >= 0) { - const pid_t upload_child = sys_fork(); - if (!upload_child) { - // Upload process. - IGNORE_RET(sys_close(fds[0])); - IGNORE_RET(sys_dup2(fds[1], 3)); - ExecUploadProcessOrTerminate(info, temp_file, mime_boundary, exe_buf, - &allocator); - } - - // Helper process. - if (upload_child > 0) { - IGNORE_RET(sys_close(fds[1])); - - const size_t kCrashIdLength = 16; - char id_buf[kCrashIdLength + 1]; - size_t bytes_read = - WaitForCrashReportUploadProcess(fds[0], kCrashIdLength, id_buf); - HandleCrashReportId(id_buf, bytes_read, kCrashIdLength); - - if (sys_waitpid(upload_child, nullptr, WNOHANG) == 0) { - // Upload process is still around, kill it. - sys_kill(upload_child, SIGKILL); - } - } - } - - // Helper process. - IGNORE_RET(sys_unlink(info.filename)); -#if defined(ADDRESS_SANITIZER) - IGNORE_RET(sys_unlink(info.log_filename)); -#endif - IGNORE_RET(sys_unlink(temp_file)); - sys__exit(0); - } - - // Main browser process. - if (child <= 0) - return; - (void) HANDLE_EINTR(sys_waitpid(child, nullptr, 0)); -} - -void InitCrashReporter(const std::string& process_type) { -#if defined(OS_ANDROID) - // This will guarantee that the BuildInfo has been initialized and subsequent - // calls will not require memory allocation. - base::android::BuildInfo::GetInstance(); - - // Handler registration is LIFO. Install the microdump handler first, such - // that if conventional minidump crash reporting is enabled below, it takes - // precedence (i.e. its handler is run first) over the microdump handler. - InitMicrodumpCrashHandlerIfNecessary(process_type); -#endif - // Determine the process type and take appropriate action. - const base::CommandLine& parsed_command_line = - *base::CommandLine::ForCurrentProcess(); - if (parsed_command_line.HasSwitch(switches::kDisableBreakpad)) - return; - - if (process_type.empty()) { - bool enable_breakpad = GetCrashReporterClient()->GetCollectStatsConsent() || - GetCrashReporterClient()->IsRunningUnattended(); - enable_breakpad &= - !parsed_command_line.HasSwitch(switches::kDisableBreakpad); - if (!enable_breakpad) { - enable_breakpad = parsed_command_line.HasSwitch( - switches::kEnableCrashReporterForTesting); - } - if (!enable_breakpad) { - VLOG(1) << "Breakpad disabled"; - return; - } - - InitCrashKeys(); - EnableCrashDumping(GetCrashReporterClient()->IsRunningUnattended()); - } else if (GetCrashReporterClient()->EnableBreakpadForProcess(process_type)) { -#if defined(OS_ANDROID) - NOTREACHED() << "Breakpad initialized with InitCrashReporter() instead of " - "InitNonBrowserCrashReporter in " << process_type << " process."; - return; -#else - // We might be chrooted in a zygote or renderer process so we cannot call - // GetCollectStatsConsent because that needs access the the user's home - // dir. Instead, we set a command line flag for these processes. - // Even though plugins are not chrooted, we share the same code path for - // simplicity. - if (!parsed_command_line.HasSwitch(switches::kEnableCrashReporter)) - return; - InitCrashKeys(); - SetClientIdFromCommandLine(parsed_command_line); - EnableNonBrowserCrashDumping(); - VLOG(1) << "Non Browser crash dumping enabled for: " << process_type; -#endif // #if defined(OS_ANDROID) - } - - PostEnableBreakpadInitialization(); -} - -#if defined(OS_ANDROID) -void InitNonBrowserCrashReporterForAndroid(const std::string& process_type) { - const base::CommandLine* command_line = - base::CommandLine::ForCurrentProcess(); - - // Handler registration is LIFO. Install the microdump handler first, such - // that if conventional minidump crash reporting is enabled below, it takes - // precedence (i.e. its handler is run first) over the microdump handler. - InitMicrodumpCrashHandlerIfNecessary(process_type); - - if (command_line->HasSwitch(switches::kEnableCrashReporter)) { - // On Android we need to provide a FD to the file where the minidump is - // generated as the renderer and browser run with different UIDs - // (preventing the browser from inspecting the renderer process). - int minidump_fd = base::GlobalDescriptors::GetInstance()->MaybeGet( - GetCrashReporterClient()->GetAndroidMinidumpDescriptor()); - if (minidump_fd < 0) { - NOTREACHED() << "Could not find minidump FD, crash reporting disabled."; - } else { - InitCrashKeys(); - EnableNonBrowserCrashDumping(process_type, minidump_fd); - } - } -} -#endif // OS_ANDROID - -bool IsCrashReporterEnabled() { - return g_is_crash_reporter_enabled; -} - -} // namespace breakpad diff --git a/components/crash/app/breakpad_linux.h b/components/crash/app/breakpad_linux.h deleted file mode 100644 index 83a3068..0000000 --- a/components/crash/app/breakpad_linux.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Public interface for enabling Breakpad on Linux systems. - -#ifndef COMPONENTS_CRASH_APP_BREAKPAD_LINUX_H_ -#define COMPONENTS_CRASH_APP_BREAKPAD_LINUX_H_ - -#include - -#include "build/build_config.h" - -namespace breakpad { - -// Turns on the crash reporter in any process. -extern void InitCrashReporter(const std::string& process_type); - -// Enables the crash reporter in child processes. -#if defined(OS_ANDROID) -extern void InitNonBrowserCrashReporterForAndroid( - const std::string& process_type); -#endif - -// Checks if crash reporting is enabled. Note that this is not the same as -// being opted into metrics reporting (and crash reporting), which controls -// whether InitCrashReporter() is called. -bool IsCrashReporterEnabled(); - -} // namespace breakpad - -#endif // COMPONENTS_CRASH_APP_BREAKPAD_LINUX_H_ diff --git a/components/crash/app/breakpad_linux_impl.h b/components/crash/app/breakpad_linux_impl.h deleted file mode 100644 index b306e52..0000000 --- a/components/crash/app/breakpad_linux_impl.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Internal header file for the Linux breakpad implementation. This file is -// shared between crash_handler_host_linux.cc and breakpad_linux.cc. It should -// only be used in files compiled with linux_breakpad=1. - -#ifndef COMPONENTS_CRASH_APP_BREAKPAD_LINUX_IMPL_H_ -#define COMPONENTS_CRASH_APP_BREAKPAD_LINUX_IMPL_H_ - -#include - -#include "base/basictypes.h" -#include "breakpad/src/common/simple_string_dictionary.h" -#include "components/crash/app/breakpad_linux.h" - -namespace breakpad { - -typedef google_breakpad::NonAllocatingMap<256, 256, 64> CrashKeyStorage; - -#if defined(ADDRESS_SANITIZER) -static const size_t kMaxAsanReportSize = 1 << 16; -#endif -// Define a preferred limit on minidump sizes, because Crash Server currently -// throws away any larger than 1.2MB (1.2 * 1024 * 1024). A value of -1 means -// no limit. -static const off_t kMaxMinidumpFileSize = 1258291; - -// The size of the iovec used to transfer crash data from a child back to the -// browser. -#if !defined(ADDRESS_SANITIZER) -const size_t kCrashIovSize = 6; -#else -// Additional field to pass the AddressSanitizer log to the crash handler. -const size_t kCrashIovSize = 7; -#endif - -// BreakpadInfo describes a crash report. -// The minidump information can either be contained in a file descriptor (fd) or -// in a file (whose path is in filename). -struct BreakpadInfo { - int fd; // File descriptor to the Breakpad dump data. - const char* filename; // Path to the Breakpad dump data. -#if defined(ADDRESS_SANITIZER) - const char* log_filename; // Path to the ASan log file. - const char* asan_report_str; // ASan report. - unsigned asan_report_length; // Length of |asan_report_length|. -#endif - const char* process_type; // Process type, e.g. "renderer". - unsigned process_type_length; // Length of |process_type|. - const char* distro; // Linux distro string. - unsigned distro_length; // Length of |distro|. - bool upload; // Whether to upload or save crash dump. - uint64_t process_start_time; // Uptime of the crashing process. - size_t oom_size; // Amount of memory requested if OOM. - uint64_t pid; // PID where applicable. - CrashKeyStorage* crash_keys; -}; - -extern void HandleCrashDump(const BreakpadInfo& info); - -} // namespace breakpad - -#endif // COMPONENTS_CRASH_APP_BREAKPAD_LINUX_IMPL_H_ diff --git a/components/crash/app/breakpad_mac.h b/components/crash/app/breakpad_mac.h deleted file mode 100644 index 73f576a..0000000 --- a/components/crash/app/breakpad_mac.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRASH_APP_BREAKPAD_MAC_H_ -#define COMPONENTS_CRASH_APP_BREAKPAD_MAC_H_ - -#include - -// This header defines the entry points for Breakpad integration. - -namespace breakpad { - -// Initializes Breakpad. -void InitCrashReporter(const std::string& process_type); - -// Give Breakpad a chance to store information about the current process. -// Extra information requires a parsed command line, so call this after -// CommandLine::Init has been called. -void InitCrashProcessInfo(const std::string& process_type_switch); - -// Is Breakpad enabled? -bool IsCrashReporterEnabled(); - -} // namespace breakpad - -#endif // COMPONENTS_CRASH_APP_BREAKPAD_MAC_H_ diff --git a/components/crash/app/breakpad_mac.mm b/components/crash/app/breakpad_mac.mm deleted file mode 100644 index f435483..0000000 --- a/components/crash/app/breakpad_mac.mm +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "components/crash/app/breakpad_mac.h" - -#include -#import - -#include "base/auto_reset.h" -#include "base/base_switches.h" -#import "base/basictypes.h" -#include "base/command_line.h" -#include "base/debug/crash_logging.h" -#include "base/debug/dump_without_crashing.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#import "base/logging.h" -#include "base/mac/bundle_locations.h" -#include "base/mac/foundation_util.h" -#include "base/mac/mac_util.h" -#include "base/mac/scoped_cftyperef.h" -#import "base/mac/scoped_nsautorelease_pool.h" -#include "base/strings/sys_string_conversions.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread_restrictions.h" -#import "breakpad/src/client/mac/Framework/Breakpad.h" -#include "components/crash/app/crash_reporter_client.h" - -using crash_reporter::GetCrashReporterClient; - -namespace breakpad { - -namespace { - -BreakpadRef gBreakpadRef = NULL; - -void SetCrashKeyValue(NSString* key, NSString* value) { - // Comment repeated from header to prevent confusion: - // IMPORTANT: On OS X, the key/value pairs are sent to the crash server - // out of bounds and not recorded on disk in the minidump, this means - // that if you look at the minidump file locally you won't see them! - if (gBreakpadRef == NULL) { - return; - } - - BreakpadAddUploadParameter(gBreakpadRef, key, value); -} - -void ClearCrashKeyValue(NSString* key) { - if (gBreakpadRef == NULL) { - return; - } - - BreakpadRemoveUploadParameter(gBreakpadRef, key); -} - -void SetCrashKeyValueImpl(const base::StringPiece& key, - const base::StringPiece& value) { - @autoreleasepool { - SetCrashKeyValue(base::SysUTF8ToNSString(key.as_string()), - base::SysUTF8ToNSString(value.as_string())); - } -} - -void ClearCrashKeyValueImpl(const base::StringPiece& key) { - @autoreleasepool { - ClearCrashKeyValue(base::SysUTF8ToNSString(key.as_string())); - } -} - -bool FatalMessageHandler(int severity, const char* file, int line, - size_t message_start, const std::string& str) { - // Do not handle non-FATAL. - if (severity != logging::LOG_FATAL) - return false; - - // In case of OOM condition, this code could be reentered when - // constructing and storing the key. Using a static is not - // thread-safe, but if multiple threads are in the process of a - // fatal crash at the same time, this should work. - static bool guarded = false; - if (guarded) - return false; - - base::AutoReset guard(&guarded, true); - - // Only log last path component. This matches logging.cc. - if (file) { - const char* slash = strrchr(file, '/'); - if (slash) - file = slash + 1; - } - - NSString* fatal_key = @"LOG_FATAL"; - NSString* fatal_value = - [NSString stringWithFormat:@"%s:%d: %s", - file, line, str.c_str() + message_start]; - SetCrashKeyValue(fatal_key, fatal_value); - - // Rather than including the code to force the crash here, allow the - // caller to do it. - return false; -} - -// BreakpadGenerateAndSendReport() does not report the current -// thread. This class can be used to spin up a thread to run it. -class DumpHelper : public base::PlatformThread::Delegate { - public: - static void DumpWithoutCrashing() { - DumpHelper dumper; - base::PlatformThreadHandle handle; - if (base::PlatformThread::Create(0, &dumper, &handle)) { - // The entire point of this is to block so that the correct - // stack is logged. - base::ThreadRestrictions::ScopedAllowIO allow_io; - base::PlatformThread::Join(handle); - } - } - - private: - DumpHelper() {} - - void ThreadMain() override { - base::PlatformThread::SetName("CrDumpHelper"); - BreakpadGenerateAndSendReport(gBreakpadRef); - } - - DISALLOW_COPY_AND_ASSIGN(DumpHelper); -}; - -void SIGABRTHandler(int signal) { - // The OSX abort() (link below) masks all signals for the process, - // and all except SIGABRT for the thread. SIGABRT will be masked - // when the SIGABRT is sent, which means at this point only SIGKILL - // and SIGSTOP can be delivered. Unmask others so that the code - // below crashes as desired. - // - // http://www.opensource.apple.com/source/Libc/Libc-825.26/stdlib/FreeBSD/abort.c - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, signal); - pthread_sigmask(SIG_SETMASK, &mask, NULL); - - // Most interesting operations are not safe in a signal handler, just crash. - char* volatile death_ptr = NULL; - *death_ptr = '!'; -} - -} // namespace - -bool IsCrashReporterEnabled() { - return gBreakpadRef != NULL; -} - -// Only called for a branded build of Chrome.app. -void InitCrashReporter(const std::string& process_type) { - DCHECK(!gBreakpadRef); - base::mac::ScopedNSAutoreleasePool autorelease_pool; - - // Check whether crash reporting should be enabled. If enterprise - // configuration management controls crash reporting, it takes precedence. - // Otherwise, check whether the user has consented to stats and crash - // reporting. The browser process can make this determination directly. - // Helper processes may not have access to the disk or to the same data as - // the browser process, so the browser passes the decision to them on the - // command line. - NSBundle* main_bundle = base::mac::FrameworkBundle(); - bool is_browser = !base::mac::IsBackgroundOnlyProcess(); - bool enable_breakpad = false; - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - - if (is_browser) { - // Since the configuration management infrastructure is possibly not - // initialized when this code runs, read the policy preference directly. - if (!GetCrashReporterClient()->ReportingIsEnforcedByPolicy( - &enable_breakpad)) { - // Controlled by the user. The crash reporter may be enabled by - // preference or through an environment variable, but the kDisableBreakpad - // switch overrides both. - enable_breakpad = GetCrashReporterClient()->GetCollectStatsConsent() || - GetCrashReporterClient()->IsRunningUnattended(); - enable_breakpad &= !command_line->HasSwitch(switches::kDisableBreakpad); - } - } else { - // This is a helper process, check the command line switch. - enable_breakpad = command_line->HasSwitch(switches::kEnableCrashReporter); - } - - if (!enable_breakpad) { - VLOG_IF(1, is_browser) << "Breakpad disabled"; - return; - } - - // Tell Breakpad where crash_inspector and crash_report_sender are. - NSString* resource_path = [main_bundle resourcePath]; - NSString *inspector_location = - [resource_path stringByAppendingPathComponent:@"crash_inspector"]; - NSString *reporter_bundle_location = - [resource_path stringByAppendingPathComponent:@"crash_report_sender.app"]; - NSString *reporter_location = - [[NSBundle bundleWithPath:reporter_bundle_location] executablePath]; - - if (!inspector_location || !reporter_location) { - VLOG_IF(1, is_browser && base::mac::AmIBundled()) << "Breakpad disabled"; - return; - } - - NSDictionary* info_dictionary = [main_bundle infoDictionary]; - NSMutableDictionary *breakpad_config = - [[info_dictionary mutableCopy] autorelease]; - [breakpad_config setObject:inspector_location - forKey:@BREAKPAD_INSPECTOR_LOCATION]; - [breakpad_config setObject:reporter_location - forKey:@BREAKPAD_REPORTER_EXE_LOCATION]; - - // In the main application (the browser process), crashes can be passed to - // the system's Crash Reporter. This allows the system to notify the user - // when the application crashes, and provide the user with the option to - // restart it. - if (is_browser) - [breakpad_config setObject:@"NO" forKey:@BREAKPAD_SEND_AND_EXIT]; - - base::FilePath dir_crash_dumps; - GetCrashReporterClient()->GetCrashDumpLocation(&dir_crash_dumps); - [breakpad_config setObject:base::SysUTF8ToNSString(dir_crash_dumps.value()) - forKey:@BREAKPAD_DUMP_DIRECTORY]; - - // Temporarily run Breakpad in-process on 10.10 and later because APIs that - // it depends on got broken (http://crbug.com/386208). - // This can catch crashes in the browser process only. - if (is_browser && base::mac::IsOSYosemiteOrLater()) { - [breakpad_config setObject:[NSNumber numberWithBool:YES] - forKey:@BREAKPAD_IN_PROCESS]; - } - - // Initialize Breakpad. - gBreakpadRef = BreakpadCreate(breakpad_config); - if (!gBreakpadRef) { - LOG_IF(ERROR, base::mac::AmIBundled()) << "Breakpad initialization failed"; - return; - } - - // Initialize the scoped crash key system. - base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueImpl, - &ClearCrashKeyValueImpl); - GetCrashReporterClient()->RegisterCrashKeys(); - - // Set Breakpad metadata values. These values are added to Info.plist during - // the branded Google Chrome.app build. - SetCrashKeyValue(@"ver", [info_dictionary objectForKey:@BREAKPAD_VERSION]); - SetCrashKeyValue(@"prod", [info_dictionary objectForKey:@BREAKPAD_PRODUCT]); - SetCrashKeyValue(@"plat", @"OS X"); - - logging::SetLogMessageHandler(&FatalMessageHandler); - base::debug::SetDumpWithoutCrashingFunction(&DumpHelper::DumpWithoutCrashing); - - // abort() sends SIGABRT, which breakpad does not intercept. - // Register a signal handler to crash in a way breakpad will - // intercept. - struct sigaction sigact; - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = SIGABRTHandler; - CHECK(0 == sigaction(SIGABRT, &sigact, NULL)); -} - -void InitCrashProcessInfo(const std::string& process_type_switch) { - if (gBreakpadRef == NULL) { - return; - } - - // Determine the process type. - NSString* process_type = @"browser"; - if (!process_type_switch.empty()) { - process_type = base::SysUTF8ToNSString(process_type_switch); - } - - // Store process type in crash dump. - SetCrashKeyValue(@"ptype", process_type); - - NSString* pid_value = - [NSString stringWithFormat:@"%d", static_cast(getpid())]; - SetCrashKeyValue(@"pid", pid_value); -} - -} // namespace breakpad diff --git a/components/crash/app/breakpad_mac_stubs.mm b/components/crash/app/breakpad_mac_stubs.mm deleted file mode 100644 index e2b325c..0000000 --- a/components/crash/app/breakpad_mac_stubs.mm +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "components/crash/app/breakpad_mac.h" - -#import - -// Stubbed out versions of breakpad integration functions so we can compile -// without linking in Breakpad. - -namespace breakpad { - -bool IsCrashReporterEnabled() { - return false; -} - -void InitCrashProcessInfo(const std::string& process_type_switch) { -} - -void InitCrashReporter(const std::string& process_type) { -} - -} // namespace breakpad diff --git a/components/crash/app/breakpad_win.cc b/components/crash/app/breakpad_win.cc deleted file mode 100644 index 05a29ae8..0000000 --- a/components/crash/app/breakpad_win.cc +++ /dev/null @@ -1,745 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/crash/app/breakpad_win.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "base/base_switches.h" -#include "base/basictypes.h" -#include "base/command_line.h" -#include "base/debug/crash_logging.h" -#include "base/debug/dump_without_crashing.h" -#include "base/environment.h" -#include "base/memory/scoped_ptr.h" -#include "base/numerics/safe_conversions.h" -#include "base/strings/string16.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/synchronization/lock.h" -#include "base/win/metro.h" -#include "base/win/pe_image.h" -#include "base/win/registry.h" -#include "base/win/win_util.h" -#include "breakpad/src/client/windows/handler/exception_handler.h" -#include "components/crash/app/crash_keys_win.h" -#include "components/crash/app/crash_reporter_client.h" -#include "components/crash/app/hard_error_handler_win.h" -#include "content/public/common/result_codes.h" -#include "sandbox/win/src/nt_internals.h" -#include "sandbox/win/src/sidestep/preamble_patcher.h" - -// userenv.dll is required for GetProfileType(). -#pragma comment(lib, "userenv.lib") - -#pragma intrinsic(_AddressOfReturnAddress) -#pragma intrinsic(_ReturnAddress) - -#ifdef _WIN64 -// See http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx -typedef struct _UNWIND_INFO { - unsigned char Version : 3; - unsigned char Flags : 5; - unsigned char SizeOfProlog; - unsigned char CountOfCodes; - unsigned char FrameRegister : 4; - unsigned char FrameOffset : 4; - ULONG ExceptionHandler; -} UNWIND_INFO, *PUNWIND_INFO; -#endif - -namespace breakpad { - -using crash_reporter::GetCrashReporterClient; - -namespace { - -// Minidump with stacks, PEB, TEB, and unloaded module list. -const MINIDUMP_TYPE kSmallDumpType = static_cast( - MiniDumpWithProcessThreadData | // Get PEB and TEB. - MiniDumpWithUnloadedModules); // Get unloaded modules when available. - -// Minidump with all of the above, plus memory referenced from stack. -const MINIDUMP_TYPE kLargerDumpType = static_cast( - MiniDumpWithProcessThreadData | // Get PEB and TEB. - MiniDumpWithUnloadedModules | // Get unloaded modules when available. - MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. - -// Large dump with all process memory. -const MINIDUMP_TYPE kFullDumpType = static_cast( - MiniDumpWithFullMemory | // Full memory from process. - MiniDumpWithProcessThreadData | // Get PEB and TEB. - MiniDumpWithHandleData | // Get all handle information. - MiniDumpWithUnloadedModules); // Get unloaded modules when available. - -const char kPipeNameVar[] = "CHROME_BREAKPAD_PIPE_NAME"; - -const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; -const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; - -// This is the well known SID for the system principal. -const wchar_t kSystemPrincipalSid[] =L"S-1-5-18"; - -google_breakpad::ExceptionHandler* g_breakpad = NULL; -google_breakpad::ExceptionHandler* g_dumphandler_no_crash = NULL; - -#if !defined(_WIN64) -EXCEPTION_POINTERS g_surrogate_exception_pointers = {0}; -EXCEPTION_RECORD g_surrogate_exception_record = {0}; -CONTEXT g_surrogate_context = {0}; -#endif // !defined(_WIN64) - -typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle, - NTSTATUS ExitStatus); -char* g_real_terminate_process_stub = NULL; - -} // namespace - -// Dumps the current process memory. -extern "C" void __declspec(dllexport) __cdecl DumpProcess() { - if (g_breakpad) { - g_breakpad->WriteMinidump(); - } -} - -// Used for dumping a process state when there is no crash. -extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { - if (g_dumphandler_no_crash) { - g_dumphandler_no_crash->WriteMinidump(); - } -} - -namespace { - -// We need to prevent ICF from folding DumpForHangDebuggingThread() and -// DumpProcessWithoutCrashThread() together, since that makes them -// indistinguishable in crash dumps. We do this by making the function -// bodies unique, and prevent optimization from shuffling things around. -MSVC_DISABLE_OPTIMIZE() -MSVC_PUSH_DISABLE_WARNING(4748) - -DWORD WINAPI DumpProcessWithoutCrashThread(void*) { - DumpProcessWithoutCrash(); - return 0; -} - -// The following two functions do exactly the same thing as the two above. But -// we want the signatures to be different so that we can easily track them in -// crash reports. -// TODO(yzshen): Remove when enough information is collected and the hang rate -// of pepper/renderer processes is reduced. -DWORD WINAPI DumpForHangDebuggingThread(void*) { - DumpProcessWithoutCrash(); - VLOG(1) << "dumped for hang debugging"; - return 0; -} - -MSVC_POP_WARNING() -MSVC_ENABLE_OPTIMIZE() - -} // namespace - -// Injects a thread into a remote process to dump state when there is no crash. -extern "C" HANDLE __declspec(dllexport) __cdecl -InjectDumpProcessWithoutCrash(HANDLE process) { - return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread, - 0, 0, NULL); -} - -extern "C" HANDLE __declspec(dllexport) __cdecl -InjectDumpForHangDebugging(HANDLE process) { - return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, - 0, 0, NULL); -} - -// Returns a string containing a list of all modifiers for the loaded profile. -std::wstring GetProfileType() { - std::wstring profile_type; - DWORD profile_bits = 0; - if (::GetProfileType(&profile_bits)) { - static const struct { - DWORD bit; - const wchar_t* name; - } kBitNames[] = { - { PT_MANDATORY, L"mandatory" }, - { PT_ROAMING, L"roaming" }, - { PT_TEMPORARY, L"temporary" }, - }; - for (size_t i = 0; i < arraysize(kBitNames); ++i) { - const DWORD this_bit = kBitNames[i].bit; - if ((profile_bits & this_bit) != 0) { - profile_type.append(kBitNames[i].name); - profile_bits &= ~this_bit; - if (profile_bits != 0) - profile_type.append(L", "); - } - } - } else { - DWORD last_error = ::GetLastError(); - base::SStringPrintf(&profile_type, L"error %u", last_error); - } - return profile_type; -} - -namespace { - -// This callback is used when we want to get a dump without crashing the -// process. -bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*, - EXCEPTION_POINTERS* ex_info, - MDRawAssertionInfo*, bool succeeded) { - GetCrashReporterClient()->RecordCrashDumpAttemptResult( - false /* is_real_crash */, succeeded); - return true; -} - -// This callback is executed when the browser process has crashed, after -// the crash dump has been created. We need to minimize the amount of work -// done here since we have potentially corrupted process. Our job is to -// spawn another instance of chrome which will show a 'chrome has crashed' -// dialog. This code needs to live in the exe and thus has no access to -// facilities such as the i18n helpers. -bool DumpDoneCallback(const wchar_t*, const wchar_t*, void*, - EXCEPTION_POINTERS* ex_info, - MDRawAssertionInfo*, bool succeeded) { - GetCrashReporterClient()->RecordCrashDumpAttemptResult( - true /* is_real_crash */, succeeded); - // Check if the exception is one of the kind which would not be solved - // by simply restarting chrome. In this case we show a message box with - // and exit silently. Remember that chrome is in a crashed state so we - // can't show our own UI from this process. - if (HardErrorHandler(ex_info)) - return true; - - if (!GetCrashReporterClient()->AboutToRestart()) - return true; - - // Now we just start chrome browser with the same command line. - STARTUPINFOW si = {sizeof(si)}; - PROCESS_INFORMATION pi; - if (::CreateProcessW(NULL, ::GetCommandLineW(), NULL, NULL, FALSE, - CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi)) { - ::CloseHandle(pi.hProcess); - ::CloseHandle(pi.hThread); - } - // After this return we will be terminated. The actual return value is - // not used at all. - return true; -} - -// flag to indicate that we are already handling an exception. -volatile LONG handling_exception = 0; - -// This callback is used when there is no crash. Note: Unlike the -// |FilterCallback| below this does not do dupe detection. It is upto the caller -// to implement it. -bool FilterCallbackWhenNoCrash( - void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { - GetCrashReporterClient()->RecordCrashDumpAttempt(false); - return true; -} - -// This callback is executed when the Chrome process has crashed and *before* -// the crash dump is created. To prevent duplicate crash reports we -// make every thread calling this method, except the very first one, -// go to sleep. -bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { - // Capture every thread except the first one in the sleep. We don't - // want multiple threads to concurrently report exceptions. - if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) { - ::Sleep(INFINITE); - } - GetCrashReporterClient()->RecordCrashDumpAttempt(true); - return true; -} - -// Previous unhandled filter. Will be called if not null when we -// intercept a crash. -LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL; - -// Exception filter used when breakpad is not enabled. We just display -// the "Do you want to restart" message and then we call the previous filter. -long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) { - DumpDoneCallback(NULL, NULL, NULL, info, NULL, false); - - if (previous_filter) - return previous_filter(info); - - return EXCEPTION_EXECUTE_HANDLER; -} - -// Exception filter for the service process used when breakpad is not enabled. -// We just display the "Do you want to restart" message and then die -// (without calling the previous filter). -long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) { - DumpDoneCallback(NULL, NULL, NULL, info, NULL, false); - return EXCEPTION_EXECUTE_HANDLER; -} - -#if !defined(COMPONENT_BUILD) -// Installed via base::debug::SetCrashKeyReportingFunctions. -void SetCrashKeyValueForBaseDebug(const base::StringPiece& key, - const base::StringPiece& value) { - DCHECK(CrashKeysWin::keeper()); - CrashKeysWin::keeper()->SetCrashKeyValue(base::UTF8ToUTF16(key), - base::UTF8ToUTF16(value)); -} - -// Installed via base::debug::SetCrashKeyReportingFunctions. -void ClearCrashKeyForBaseDebug(const base::StringPiece& key) { - DCHECK(CrashKeysWin::keeper()); - CrashKeysWin::keeper()->ClearCrashKeyValue(base::UTF8ToUTF16(key)); -} -#endif // !defined(COMPONENT_BUILD) - -} // namespace - -// NOTE: This function is used by SyzyASAN to annotate crash reports. If you -// change the name or signature of this function you will break SyzyASAN -// instrumented releases of Chrome. Please contact syzygy-team@chromium.org -// before doing so! -extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl( - const wchar_t* key, const wchar_t* value) { - CrashKeysWin* keeper = CrashKeysWin::keeper(); - if (!keeper) - return; - - // TODO(siggi): This doesn't look quite right - there's NULL deref potential - // here, and an implicit std::wstring conversion. Fixme. - keeper->SetCrashKeyValue(key, value); -} - -extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl( - const wchar_t* key) { - CrashKeysWin* keeper = CrashKeysWin::keeper(); - if (!keeper) - return; - - // TODO(siggi): This doesn't look quite right - there's NULL deref potential - // here, and an implicit std::wstring conversion. Fixme. - keeper->ClearCrashKeyValue(key); -} - -static bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption, - UINT flags, bool* exit_now) { - // We wrap the call to MessageBoxW with a SEH handler because it some - // machines with CursorXP, PeaDict or with FontExplorer installed it crashes - // uncontrollably here. Being this a best effort deal we better go away. - __try { - *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags)); - } __except(EXCEPTION_EXECUTE_HANDLER) { - // Its not safe to continue executing, exit silently here. - ::TerminateProcess(::GetCurrentProcess(), - GetCrashReporterClient()->GetResultCodeRespawnFailed()); - } - - return true; -} - -// This function is executed by the child process that DumpDoneCallback() -// spawned and basically just shows the 'chrome has crashed' dialog if -// the CHROME_CRASHED environment variable is present. -bool ShowRestartDialogIfCrashed(bool* exit_now) { - // If we are being launched in metro mode don't try to show the dialog. - if (base::win::IsMetroProcess()) - return false; - - base::string16 message; - base::string16 title; - bool is_rtl_locale; - if (!GetCrashReporterClient()->ShouldShowRestartDialog( - &title, &message, &is_rtl_locale)) { - return false; - } - - // If the UI layout is right-to-left, we need to pass the appropriate MB_XXX - // flags so that an RTL message box is displayed. - UINT flags = MB_OKCANCEL | MB_ICONWARNING; - if (is_rtl_locale) - flags |= MB_RIGHT | MB_RTLREADING; - - return WrapMessageBoxWithSEH(message.c_str(), title.c_str(), flags, exit_now); -} - -extern "C" void __declspec(dllexport) TerminateProcessWithoutDump() { - // Patched stub exists based on conditions (See InitCrashReporter). - // As a side note this function also gets called from - // WindowProcExceptionFilter. - if (g_real_terminate_process_stub == NULL) { - ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED); - } else { - NtTerminateProcessPtr real_terminate_proc = - reinterpret_cast( - static_cast(g_real_terminate_process_stub)); - real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED); - } -} - -// Crashes the process after generating a dump for the provided exception. Note -// that the crash reporter should be initialized before calling this function -// for it to do anything. -// NOTE: This function is used by SyzyASAN to invoke a crash. If you change the -// the name or signature of this function you will break SyzyASAN instrumented -// releases of Chrome. Please contact syzygy-team@chromium.org before doing so! -extern "C" int __declspec(dllexport) CrashForException( - EXCEPTION_POINTERS* info) { - if (g_breakpad) { - g_breakpad->WriteMinidumpForException(info); - TerminateProcessWithoutDump(); - } - return EXCEPTION_CONTINUE_SEARCH; -} - -#ifndef _WIN64 -static NTSTATUS WINAPI HookNtTerminateProcess(HANDLE ProcessHandle, - NTSTATUS ExitStatus) { - if (g_breakpad && - (ProcessHandle == ::GetCurrentProcess() || ProcessHandle == NULL)) { - NT_TIB* tib = reinterpret_cast(NtCurrentTeb()); - void* address_on_stack = _AddressOfReturnAddress(); - if (address_on_stack < tib->StackLimit || - address_on_stack > tib->StackBase) { - g_surrogate_exception_record.ExceptionAddress = _ReturnAddress(); - g_surrogate_exception_record.ExceptionCode = DBG_TERMINATE_PROCESS; - g_surrogate_exception_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE; - CrashForException(&g_surrogate_exception_pointers); - } - } - - NtTerminateProcessPtr real_proc = - reinterpret_cast( - static_cast(g_real_terminate_process_stub)); - return real_proc(ProcessHandle, ExitStatus); -} - -static void InitTerminateProcessHooks() { - NtTerminateProcessPtr terminate_process_func_address = - reinterpret_cast(::GetProcAddress( - ::GetModuleHandle(L"ntdll.dll"), "NtTerminateProcess")); - if (terminate_process_func_address == NULL) - return; - - DWORD old_protect = 0; - if (!::VirtualProtect(terminate_process_func_address, 5, - PAGE_EXECUTE_READWRITE, &old_protect)) - return; - - g_real_terminate_process_stub = reinterpret_cast(VirtualAllocEx( - ::GetCurrentProcess(), NULL, sidestep::kMaxPreambleStubSize, - MEM_COMMIT, PAGE_EXECUTE_READWRITE)); - if (g_real_terminate_process_stub == NULL) - return; - - g_surrogate_exception_pointers.ContextRecord = &g_surrogate_context; - g_surrogate_exception_pointers.ExceptionRecord = - &g_surrogate_exception_record; - - sidestep::SideStepError patch_result = - sidestep::PreamblePatcher::Patch( - terminate_process_func_address, HookNtTerminateProcess, - g_real_terminate_process_stub, sidestep::kMaxPreambleStubSize); - if (patch_result != sidestep::SIDESTEP_SUCCESS) { - CHECK(::VirtualFreeEx(::GetCurrentProcess(), g_real_terminate_process_stub, - 0, MEM_RELEASE)); - CHECK(::VirtualProtect(terminate_process_func_address, 5, old_protect, - &old_protect)); - return; - } - - DWORD dummy = 0; - CHECK(::VirtualProtect(terminate_process_func_address, - 5, - old_protect, - &dummy)); - CHECK(::VirtualProtect(g_real_terminate_process_stub, - sidestep::kMaxPreambleStubSize, - old_protect, - &old_protect)); -} -#endif - -static void InitPipeNameEnvVar(bool is_per_user_install) { - scoped_ptr env(base::Environment::Create()); - if (env->HasVar(kPipeNameVar)) { - // The Breakpad pipe name is already configured: nothing to do. - return; - } - - // Check whether configuration management controls crash reporting. - bool crash_reporting_enabled = true; - bool controlled_by_policy = - GetCrashReporterClient()->ReportingIsEnforcedByPolicy( - &crash_reporting_enabled); - - const base::CommandLine& command = *base::CommandLine::ForCurrentProcess(); - bool use_crash_service = !controlled_by_policy && - (command.HasSwitch(switches::kNoErrorDialogs) || - GetCrashReporterClient()->IsRunningUnattended()); - - std::wstring pipe_name; - if (use_crash_service) { - // Crash reporting is done by crash_service.exe. - pipe_name = kChromePipeName; - } else { - // We want to use the Google Update crash reporting. We need to check if the - // user allows it first (in case the administrator didn't already decide - // via policy). - if (!controlled_by_policy) - crash_reporting_enabled = - GetCrashReporterClient()->GetCollectStatsConsent(); - - if (!crash_reporting_enabled) { - // Crash reporting is disabled, don't set the environment variable. - return; - } - - // Build the pipe name. It can be either: - // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18" - // Per-user install: "NamedPipe\GoogleCrashServices\" - std::wstring user_sid; - if (is_per_user_install) { - if (!base::win::GetUserSidString(&user_sid)) { - return; - } - } else { - user_sid = kSystemPrincipalSid; - } - - pipe_name = kGoogleUpdatePipeName; - pipe_name += user_sid; - } - env->SetVar(kPipeNameVar, base::UTF16ToASCII(pipe_name)); -} - -void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) { - previous_filter = SetUnhandledExceptionFilter(filter); -} - -void InitCrashReporter(const std::string& process_type_switch) { - const base::CommandLine& command = *base::CommandLine::ForCurrentProcess(); - if (command.HasSwitch(switches::kDisableBreakpad)) - return; - - // Disable the message box for assertions. - _CrtSetReportMode(_CRT_ASSERT, 0); - - base::string16 process_type = base::ASCIIToUTF16(process_type_switch); - if (process_type.empty()) - process_type = L"browser"; - - wchar_t exe_path[MAX_PATH]; - exe_path[0] = 0; - GetModuleFileNameW(NULL, exe_path, MAX_PATH); - - bool is_per_user_install = - GetCrashReporterClient()->GetIsPerUserInstall(base::FilePath(exe_path)); - - // This is intentionally leaked. - CrashKeysWin* keeper = new CrashKeysWin(); - - google_breakpad::CustomClientInfo* custom_info = - keeper->GetCustomInfo(exe_path, process_type, GetProfileType(), - base::CommandLine::ForCurrentProcess(), - GetCrashReporterClient()); - -#if !defined(COMPONENT_BUILD) - // chrome/common/child_process_logging_win.cc registers crash keys for - // chrome.dll. In a component build, that is sufficient as chrome.dll and - // chrome.exe share a copy of base (in base.dll). - // In a static build, the EXE must separately initialize the crash keys - // configuration as it has its own statically linked copy of base. - base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueForBaseDebug, - &ClearCrashKeyForBaseDebug); - GetCrashReporterClient()->RegisterCrashKeys(); -#endif - - google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL; - LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL; - // We install the post-dump callback only for the browser and service - // processes. It spawns a new browser/service process. - if (process_type == L"browser") { - callback = &DumpDoneCallback; - default_filter = &ChromeExceptionFilter; - } else if (process_type == L"service") { - callback = &DumpDoneCallback; - default_filter = &ServiceExceptionFilter; - } - - if (process_type == L"browser") { - InitPipeNameEnvVar(is_per_user_install); - GetCrashReporterClient()->InitBrowserCrashDumpsRegKey(); - } - - scoped_ptr env(base::Environment::Create()); - std::string pipe_name_ascii; - if (!env->GetVar(kPipeNameVar, &pipe_name_ascii)) { - // Breakpad is not enabled. Configuration is managed or the user - // did not allow Google Update to send crashes. We need to use - // our default crash handler instead, but only for the - // browser/service processes. - if (default_filter) - InitDefaultCrashCallback(default_filter); - return; - } - base::string16 pipe_name = base::ASCIIToUTF16(pipe_name_ascii); - -#ifdef _WIN64 - // The protocol for connecting to the out-of-process Breakpad crash - // reporter is different for x86-32 and x86-64: the message sizes - // are different because the message struct contains a pointer. As - // a result, there are two different named pipes to connect to. The - // 64-bit one is distinguished with an "-x64" suffix. - pipe_name += L"-x64"; -#endif - - // Get the alternate dump directory. We use the temp path. - wchar_t temp_dir[MAX_PATH] = {0}; - ::GetTempPathW(MAX_PATH, temp_dir); - - MINIDUMP_TYPE dump_type = kSmallDumpType; - // Capture full memory if explicitly instructed to. - if (command.HasSwitch(switches::kFullMemoryCrashReport)) - dump_type = kFullDumpType; - else if (GetCrashReporterClient()->GetShouldDumpLargerDumps( - is_per_user_install)) - dump_type = kLargerDumpType; - - g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, &FilterCallback, - callback, NULL, - google_breakpad::ExceptionHandler::HANDLER_ALL, - dump_type, pipe_name.c_str(), custom_info); - - // Now initialize the non crash dump handler. - g_dumphandler_no_crash = new google_breakpad::ExceptionHandler(temp_dir, - &FilterCallbackWhenNoCrash, - &DumpDoneCallbackWhenNoCrash, - NULL, - // Set the handler to none so this handler would not be added to - // |handler_stack_| in |ExceptionHandler| which is a list of exception - // handlers. - google_breakpad::ExceptionHandler::HANDLER_NONE, - dump_type, pipe_name.c_str(), custom_info); - - // Set the DumpWithoutCrashingFunction for this instance of base.lib. Other - // executable images linked with base should set this again for - // DumpWithoutCrashing to function correctly. - // See chrome_main.cc for example. - base::debug::SetDumpWithoutCrashingFunction(&DumpProcessWithoutCrash); - - if (g_breakpad->IsOutOfProcess()) { - // Tells breakpad to handle breakpoint and single step exceptions. - // This might break JIT debuggers, but at least it will always - // generate a crashdump for these exceptions. - g_breakpad->set_handle_debug_exceptions(true); - -#ifndef _WIN64 - if (process_type != L"browser" && - !GetCrashReporterClient()->IsRunningUnattended()) { - // Initialize the hook TerminateProcess to catch unexpected exits. - InitTerminateProcessHooks(); - } -#endif - } -} - -void ConsumeInvalidHandleExceptions() { - if (g_breakpad) { - g_breakpad->set_consume_invalid_handle_exceptions(true); - } - if (g_dumphandler_no_crash) { - g_dumphandler_no_crash->set_consume_invalid_handle_exceptions(true); - } -} - -// If the user has disabled crash reporting uploads and restarted Chrome, the -// restarted instance will still contain the pipe environment variable, which -// will allow the restarted process to still upload crash reports. This function -// clears the environment variable, so that the restarted Chrome, which inherits -// its environment from the current Chrome, will no longer contain the variable. -extern "C" void __declspec(dllexport) __cdecl - ClearBreakpadPipeEnvironmentVariable() { - scoped_ptr env(base::Environment::Create()); - env->UnSetVar(kPipeNameVar); -} - -#ifdef _WIN64 -int CrashForExceptionInNonABICompliantCodeRange( - PEXCEPTION_RECORD ExceptionRecord, - ULONG64 EstablisherFrame, - PCONTEXT ContextRecord, - PDISPATCHER_CONTEXT DispatcherContext) { - EXCEPTION_POINTERS info = { ExceptionRecord, ContextRecord }; - return CrashForException(&info); -} - -struct ExceptionHandlerRecord { - RUNTIME_FUNCTION runtime_function; - UNWIND_INFO unwind_info; - unsigned char thunk[12]; -}; - -extern "C" void __declspec(dllexport) __cdecl -RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) { - ExceptionHandlerRecord* record = - reinterpret_cast(start); - - // We assume that the first page of the code range is executable and - // committed and reserved for breakpad. What could possibly go wrong? - - // All addresses are 32bit relative offsets to start. - record->runtime_function.BeginAddress = 0; - record->runtime_function.EndAddress = - base::checked_cast(size_in_bytes); - record->runtime_function.UnwindData = - offsetof(ExceptionHandlerRecord, unwind_info); - - // Create unwind info that only specifies an exception handler. - record->unwind_info.Version = 1; - record->unwind_info.Flags = UNW_FLAG_EHANDLER; - record->unwind_info.SizeOfProlog = 0; - record->unwind_info.CountOfCodes = 0; - record->unwind_info.FrameRegister = 0; - record->unwind_info.FrameOffset = 0; - record->unwind_info.ExceptionHandler = - offsetof(ExceptionHandlerRecord, thunk); - - // Hardcoded thunk. - // mov imm64, rax - record->thunk[0] = 0x48; - record->thunk[1] = 0xb8; - void* handler = &CrashForExceptionInNonABICompliantCodeRange; - memcpy(&record->thunk[2], &handler, 8); - - // jmp rax - record->thunk[10] = 0xff; - record->thunk[11] = 0xe0; - - // Protect reserved page against modifications. - DWORD old_protect; - CHECK(VirtualProtect( - start, sizeof(ExceptionHandlerRecord), PAGE_EXECUTE_READ, &old_protect)); - CHECK(RtlAddFunctionTable( - &record->runtime_function, 1, reinterpret_cast(start))); -} - -extern "C" void __declspec(dllexport) __cdecl -UnregisterNonABICompliantCodeRange(void* start) { - ExceptionHandlerRecord* record = - reinterpret_cast(start); - - CHECK(RtlDeleteFunctionTable(&record->runtime_function)); -} -#endif - -} // namespace breakpad diff --git a/components/crash/app/breakpad_win.h b/components/crash/app/breakpad_win.h deleted file mode 100644 index c574dcfe..0000000 --- a/components/crash/app/breakpad_win.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRASH_APP_BREAKPAD_WIN_H_ -#define COMPONENTS_CRASH_APP_BREAKPAD_WIN_H_ - -#include -#include -#include - -namespace breakpad { - -void InitCrashReporter(const std::string& process_type_switch); - -// If chrome has been restarted because it crashed, this function will display -// a dialog asking for permission to continue execution or to exit now. -bool ShowRestartDialogIfCrashed(bool* exit_now); - -// Tells Breakpad that our process is shutting down and to consume -// EXCEPTION_INVALID_HANDLE exceptions which occur if bad handle detection is -// enabled and the sandbox handle closer has previously closed handles owned by -// Windows DLLs. -void ConsumeInvalidHandleExceptions(); - -} // namespace breakpad - -#endif // COMPONENTS_CRASH_APP_BREAKPAD_WIN_H_ diff --git a/components/crash/app/crash_keys_win.cc b/components/crash/app/crash_keys_win.cc deleted file mode 100644 index eda4340..0000000 --- a/components/crash/app/crash_keys_win.cc +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/crash/app/crash_keys_win.h" - -#include - -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" -#include "components/crash/app/crash_reporter_client.h" - -namespace breakpad { - -using crash_reporter::CrashReporterClient; - -namespace { - -const size_t kMaxPluginPathLength = 256; -const size_t kMaxDynamicEntries = 256; - -} // namespace - -CrashKeysWin* CrashKeysWin::keeper_; - -CrashKeysWin::CrashKeysWin() : dynamic_keys_offset_(0) { - DCHECK_EQ(static_cast(NULL), keeper_); - keeper_ = this; -} - -CrashKeysWin::~CrashKeysWin() { - DCHECK_EQ(this, keeper_); - keeper_ = NULL; -} - -// Appends the plugin path to |g_custom_entries|. -void CrashKeysWin::SetPluginPath(const std::wstring& path) { - if (path.size() > kMaxPluginPathLength) { - // If the path is too long, truncate from the start rather than the end, - // since we want to be able to recover the DLL name. - SetPluginPath(path.substr(path.size() - kMaxPluginPathLength)); - return; - } - - // The chunk size without terminator. - const size_t kChunkSize = static_cast( - google_breakpad::CustomInfoEntry::kValueMaxLength - 1); - - int chunk_index = 0; - size_t chunk_start = 0; // Current position inside |path| - - for (chunk_start = 0; chunk_start < path.size(); chunk_index++) { - size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start); - - custom_entries_.push_back(google_breakpad::CustomInfoEntry( - base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(), - path.substr(chunk_start, chunk_length).c_str())); - - chunk_start += chunk_length; - } -} - -// Appends the breakpad dump path to |g_custom_entries|. -void CrashKeysWin::SetBreakpadDumpPath(CrashReporterClient* crash_client) { - base::FilePath crash_dumps_dir_path; - if (crash_client->GetAlternativeCrashDumpLocation(&crash_dumps_dir_path)) { - custom_entries_.push_back(google_breakpad::CustomInfoEntry( - L"breakpad-dump-location", crash_dumps_dir_path.value().c_str())); - } -} - -// Returns the custom info structure based on the dll in parameter and the -// process type. -google_breakpad::CustomClientInfo* -CrashKeysWin::GetCustomInfo(const std::wstring& exe_path, - const std::wstring& type, - const std::wstring& profile_type, - base::CommandLine* cmd_line, - CrashReporterClient* crash_client) { - base::string16 version, product; - base::string16 special_build; - base::string16 channel_name; - - crash_client->GetProductNameAndVersion( - base::FilePath(exe_path), - &product, - &version, - &special_build, - &channel_name); - - // We only expect this method to be called once per process. - // Common enties - custom_entries_.push_back( - google_breakpad::CustomInfoEntry(L"ver", version.c_str())); - custom_entries_.push_back( - google_breakpad::CustomInfoEntry(L"prod", product.c_str())); - custom_entries_.push_back( - google_breakpad::CustomInfoEntry(L"plat", L"Win32")); - custom_entries_.push_back( - google_breakpad::CustomInfoEntry(L"ptype", type.c_str())); - custom_entries_.push_back( - google_breakpad::CustomInfoEntry( - L"pid", base::IntToString16(::GetCurrentProcessId()).c_str())); - custom_entries_.push_back( - google_breakpad::CustomInfoEntry(L"channel", channel_name.c_str())); - custom_entries_.push_back( - google_breakpad::CustomInfoEntry(L"profile-type", profile_type.c_str())); - - if (!special_build.empty()) { - custom_entries_.push_back( - google_breakpad::CustomInfoEntry(L"special", special_build.c_str())); - } - - if (type == L"plugin" || type == L"ppapi") { - std::wstring plugin_path = cmd_line->GetSwitchValueNative("plugin-path"); - if (!plugin_path.empty()) - SetPluginPath(plugin_path); - } - - // Check whether configuration management controls crash reporting. - bool crash_reporting_enabled = true; - bool controlled_by_policy = crash_client->ReportingIsEnforcedByPolicy( - &crash_reporting_enabled); - bool use_crash_service = !controlled_by_policy && - (cmd_line->HasSwitch(switches::kNoErrorDialogs) || - crash_client->IsRunningUnattended()); - if (use_crash_service) - SetBreakpadDumpPath(crash_client); - - // Create space for dynamic ad-hoc keys. The names and values are set using - // the API defined in base/debug/crash_logging.h. - dynamic_keys_offset_ = custom_entries_.size(); - for (size_t i = 0; i < kMaxDynamicEntries; ++i) { - // The names will be mutated as they are set. Un-numbered since these are - // merely placeholders. The name cannot be empty because Breakpad's - // HTTPUpload will interpret that as an invalid parameter. - custom_entries_.push_back( - google_breakpad::CustomInfoEntry(L"unspecified-crash-key", L"")); - } - - static google_breakpad::CustomClientInfo custom_client_info; - custom_client_info.entries = &custom_entries_.front(); - custom_client_info.count = custom_entries_.size(); - - return &custom_client_info; -} - -void CrashKeysWin::SetCrashKeyValue( - const std::wstring& key, const std::wstring& value) { - // CustomInfoEntry limits the length of key and value. If they exceed - // their maximum length the underlying string handling functions raise - // an exception and prematurely trigger a crash. Truncate here. - std::wstring safe_key(std::wstring(key).substr( - 0, google_breakpad::CustomInfoEntry::kNameMaxLength - 1)); - std::wstring safe_value(std::wstring(value).substr( - 0, google_breakpad::CustomInfoEntry::kValueMaxLength - 1)); - - // If we already have a value for this key, update it; otherwise, insert - // the new value if we have not exhausted the pre-allocated slots for dynamic - // entries. - base::AutoLock lock(lock_); - - DynamicEntriesMap::iterator it = dynamic_entries_.find(safe_key); - google_breakpad::CustomInfoEntry* entry = NULL; - if (it == dynamic_entries_.end()) { - if (dynamic_entries_.size() >= kMaxDynamicEntries) - return; - entry = &custom_entries_[dynamic_keys_offset_++]; - dynamic_entries_.insert(std::make_pair(safe_key, entry)); - } else { - entry = it->second; - } - - entry->set(safe_key.data(), safe_value.data()); -} - -void CrashKeysWin::ClearCrashKeyValue(const std::wstring& key) { - base::AutoLock lock(lock_); - - std::wstring key_string(key); - DynamicEntriesMap::iterator it = dynamic_entries_.find(key_string); - if (it == dynamic_entries_.end()) - return; - - it->second->set_value(NULL); -} - -} // namespace breakpad diff --git a/components/crash/app/crash_keys_win.h b/components/crash/app/crash_keys_win.h deleted file mode 100644 index d61ea2c..0000000 --- a/components/crash/app/crash_keys_win.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include - -#include "base/macros.h" -#include "base/synchronization/lock.h" -#include "breakpad/src/client/windows/common/ipc_protocol.h" -#include "breakpad/src/client/windows/handler/exception_handler.h" - - -namespace base { -class CommandLine; -} // namespace base - -namespace crash_reporter { -class CrashReporterClient; -} - -namespace breakpad { - -// Manages the breakpad key/value pair stash, there may only be one instance -// of this class per process at one time. -class CrashKeysWin { - public: - CrashKeysWin(); - ~CrashKeysWin(); - - // May only be called once. - // |exe_path| is the path to the executable running, which may be used - // to figure out whether this is a user or system install. - // |type| is the process type, or mode this process is running in e.g. - // something like "browser" or "renderer". - // |profile_type| is a string describing the kind of the user's Windows - // profile, e.g. "mandatory", or "roaming" or similar. - // |cmd_line| is the current process' command line consulted for explicit - // crash reporting flags. - // |crash_client| is consulted for crash reporting settings. - google_breakpad::CustomClientInfo* GetCustomInfo( - const std::wstring& exe_path, - const std::wstring& type, - const std::wstring& profile_type, - base::CommandLine* cmd_line, - crash_reporter::CrashReporterClient* crash_client); - - void SetCrashKeyValue(const std::wstring& key, const std::wstring& value); - void ClearCrashKeyValue(const std::wstring& key); - - const std::vector& custom_info_entries() - const { - return custom_entries_; - } - - static CrashKeysWin* keeper() { return keeper_; } - - private: - // One-time initialization of private key/value pairs. - void SetPluginPath(const std::wstring& path); - void SetBreakpadDumpPath(crash_reporter::CrashReporterClient* crash_client); - - // Must not be resized after GetCustomInfo is invoked. - std::vector custom_entries_; - - typedef std::map - DynamicEntriesMap; - base::Lock lock_; - // Keeps track of the next index for a new dynamic entry. - size_t dynamic_keys_offset_; // Under lock_. - // Maintains key->entry information for dynamic key/value entries - // in custom_entries_. - DynamicEntriesMap dynamic_entries_; // Under lock_. - - // Stores the sole instance of this class allowed per process. - static CrashKeysWin* keeper_; - - DISALLOW_COPY_AND_ASSIGN(CrashKeysWin); -}; - -} // namespace breakpad diff --git a/components/crash/app/crash_keys_win_unittest.cc b/components/crash/app/crash_keys_win_unittest.cc deleted file mode 100644 index 133746f..0000000 --- a/components/crash/app/crash_keys_win_unittest.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/crash/app/crash_keys_win.h" - -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/strings/stringprintf.h" -#include "components/crash/app/crash_reporter_client.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace breakpad { - -using testing::_; -using testing::DoAll; -using testing::Return; -using testing::SetArgPointee; - -class MockCrashReporterClient : public crash_reporter::CrashReporterClient { - public: - MOCK_METHOD1(GetAlternativeCrashDumpLocation, - bool(base::FilePath* crash_dir)); - MOCK_METHOD5(GetProductNameAndVersion, void(const base::FilePath& exe_path, - base::string16* product_name, - base::string16* version, - base::string16* special_build, - base::string16* channel_name)); - MOCK_METHOD3(ShouldShowRestartDialog, bool(base::string16* title, - base::string16* message, - bool* is_rtl_locale)); - MOCK_METHOD0(AboutToRestart, bool()); - MOCK_METHOD1(GetDeferredUploadsSupported, bool(bool is_per_user_install)); - MOCK_METHOD1(GetIsPerUserInstall, bool(const base::FilePath& exe_path)); - MOCK_METHOD1(GetShouldDumpLargerDumps, bool(bool is_per_user_install)); - MOCK_METHOD0(GetResultCodeRespawnFailed, int()); - MOCK_METHOD0(InitBrowserCrashDumpsRegKey, void()); - MOCK_METHOD1(RecordCrashDumpAttempt, void(bool is_real_crash)); - - MOCK_METHOD2(GetProductNameAndVersion, void(std::string* product_name, - std::string* version)); - MOCK_METHOD0(GetReporterLogFilename, base::FilePath()); - MOCK_METHOD1(GetCrashDumpLocation, bool(base::FilePath* crash_dir)); - MOCK_METHOD0(RegisterCrashKeys, size_t()); - MOCK_METHOD0(IsRunningUnattended, bool()); - MOCK_METHOD0(GetCollectStatsConsent, bool()); - MOCK_METHOD1(ReportingIsEnforcedByPolicy, bool(bool* breakpad_enabled)); - MOCK_METHOD0(GetAndroidMinidumpDescriptor, int()); - MOCK_METHOD1(EnableBreakpadForProcess, bool(const std::string& process_type)); -}; - -class CrashKeysWinTest : public testing::Test { - public: - - size_t CountKeyValueOccurences( - const google_breakpad::CustomClientInfo* client_info, - const wchar_t* key, const wchar_t* value); - - protected: - testing::StrictMock crash_client_; -}; - -size_t CrashKeysWinTest::CountKeyValueOccurences( - const google_breakpad::CustomClientInfo* client_info, - const wchar_t* key, const wchar_t* value) { - size_t occurrences = 0; - for (size_t i = 0; i < client_info->count; ++i) { - if (wcscmp(client_info->entries[i].name, key) == 0 && - wcscmp(client_info->entries[i].value, value) == 0) { - ++occurrences; - } - } - - return occurrences; -} - -TEST_F(CrashKeysWinTest, RecordsSelf) { - ASSERT_EQ(static_cast(NULL), CrashKeysWin::keeper()); - - { - CrashKeysWin crash_keys; - - ASSERT_EQ(&crash_keys, CrashKeysWin::keeper()); - } - - ASSERT_EQ(static_cast(NULL), CrashKeysWin::keeper()); -} - -// Tests the crash keys set up for the most common official build consumer -// scenario. No policy controls, not running unattended and no explicit -// switches. -TEST_F(CrashKeysWinTest, OfficialLikeKeys) { - CrashKeysWin crash_keys; - - const base::FilePath kExePath(L"C:\\temp\\exe_path.exe"); - // The exe path ought to get passed through to the breakpad client. - EXPECT_CALL(crash_client_, GetProductNameAndVersion(kExePath, _, _, _, _)) - .WillRepeatedly(DoAll( - SetArgPointee<1>(L"SomeProdName"), - SetArgPointee<2>(L"1.2.3.4"), - SetArgPointee<3>(L""), - SetArgPointee<4>(L"-devm"))); - - EXPECT_CALL(crash_client_, GetAlternativeCrashDumpLocation(_)) - .WillRepeatedly(DoAll( - SetArgPointee<0>(base::FilePath(L"C:\\temp")), - Return(false))); - - EXPECT_CALL(crash_client_, ReportingIsEnforcedByPolicy(_)) - .WillRepeatedly(Return(false)); - - EXPECT_CALL(crash_client_, IsRunningUnattended()) - .WillRepeatedly(Return(false)); - - // Provide an empty command line. - base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM); - google_breakpad::CustomClientInfo* info = - crash_keys.GetCustomInfo(kExePath.value(), - L"made_up_type", - L"temporary", - &cmd_line, - &crash_client_); - - ASSERT_TRUE(info != NULL); - ASSERT_TRUE(info->entries != NULL); - - // We expect 7 fixed keys and a "freeboard" of 256 keys for dynamic entries. - EXPECT_EQ(256U + 7U, info->count); - - EXPECT_EQ(1, CountKeyValueOccurences(info, L"ver", L"1.2.3.4")); - EXPECT_EQ(1, CountKeyValueOccurences(info, L"prod", L"SomeProdName")); - EXPECT_EQ(1, CountKeyValueOccurences(info, L"plat", L"Win32")); - EXPECT_EQ(1, CountKeyValueOccurences(info, L"ptype", L"made_up_type")); - std::wstring pid_str(base::StringPrintf(L"%d", ::GetCurrentProcessId())); - EXPECT_EQ(1, CountKeyValueOccurences(info, L"pid", pid_str.c_str())); - EXPECT_EQ(1, CountKeyValueOccurences(info, L"channel", L"-devm")); - EXPECT_EQ(1, CountKeyValueOccurences(info, L"profile-type", L"temporary")); - EXPECT_EQ(256, CountKeyValueOccurences(info, L"unspecified-crash-key", L"")); -} - -} // namespace breakpad diff --git a/components/crash/app/crash_reporter_client.cc b/components/crash/app/crash_reporter_client.cc deleted file mode 100644 index 18cc67e..0000000 --- a/components/crash/app/crash_reporter_client.cc +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/crash/app/crash_reporter_client.h" - -#include "base/files/file_path.h" -#include "base/logging.h" - -namespace crash_reporter { - -namespace { - -CrashReporterClient* g_client = NULL; - -} // namespace - -void SetCrashReporterClient(CrashReporterClient* client) { - g_client = client; -} - -CrashReporterClient* GetCrashReporterClient() { - DCHECK(g_client); - return g_client; -} - -CrashReporterClient::CrashReporterClient() {} -CrashReporterClient::~CrashReporterClient() {} - -#if !defined(OS_MACOSX) -void CrashReporterClient::SetCrashReporterClientIdFromGUID( - const std::string& client_guid) { -} -#endif - -#if defined(OS_WIN) -bool CrashReporterClient::GetAlternativeCrashDumpLocation( - base::FilePath* crash_dir) { - return false; -} - -void CrashReporterClient::GetProductNameAndVersion( - const base::FilePath& exe_path, - base::string16* product_name, - base::string16* version, - base::string16* special_build, - base::string16* channel_name) { -} - -bool CrashReporterClient::ShouldShowRestartDialog(base::string16* title, - base::string16* message, - bool* is_rtl_locale) { - return false; -} - -bool CrashReporterClient::AboutToRestart() { - return false; -} - -bool CrashReporterClient::GetDeferredUploadsSupported(bool is_per_usr_install) { - return false; -} - -bool CrashReporterClient::GetIsPerUserInstall(const base::FilePath& exe_path) { - return true; -} - -bool CrashReporterClient::GetShouldDumpLargerDumps(bool is_per_user_install) { - return false; -} - -int CrashReporterClient::GetResultCodeRespawnFailed() { - return 0; -} - -void CrashReporterClient::InitBrowserCrashDumpsRegKey() { -} - -void CrashReporterClient::RecordCrashDumpAttempt(bool is_real_crash) { -} - -void CrashReporterClient::RecordCrashDumpAttemptResult(bool is_real_crash, - bool succeeded) { -} -#endif - -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) -void CrashReporterClient::GetProductNameAndVersion(const char** product_name, - const char** version) { -} - -base::FilePath CrashReporterClient::GetReporterLogFilename() { - return base::FilePath(); -} - -bool CrashReporterClient::HandleCrashDump(const char* crashdump_filename) { - return false; -} -#endif - -bool CrashReporterClient::GetCrashDumpLocation(base::FilePath* crash_dir) { - return false; -} - -size_t CrashReporterClient::RegisterCrashKeys() { - return 0; -} - -bool CrashReporterClient::IsRunningUnattended() { - return true; -} - -bool CrashReporterClient::GetCollectStatsConsent() { - return false; -} - -#if defined(OS_WIN) || defined(OS_MACOSX) -bool CrashReporterClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) { - return false; -} -#endif - -#if defined(OS_ANDROID) -int CrashReporterClient::GetAndroidMinidumpDescriptor() { - return 0; -} - -bool CrashReporterClient::ShouldEnableBreakpadMicrodumps() { -// Always enable microdumps on Android when stripping unwind tables. Rationale: -// when unwind tables are stripped out (to save binary size) the stack traces -// produced locally in the case of a crash / CHECK are meaningless. In order to -// provide meaningful development diagnostics (and keep the binary size savings) -// on Android we attach a secondary crash handler which serializes a reduced -// form of logcat on the console. -#if defined(NO_UNWIND_TABLES) - return true; -#else - return false; -#endif -} -#endif - -bool CrashReporterClient::EnableBreakpadForProcess( - const std::string& process_type) { - return false; -} - -} // namespace crash_reporter diff --git a/components/crash/app/crash_reporter_client.h b/components/crash/app/crash_reporter_client.h deleted file mode 100644 index a3a003c4..0000000 --- a/components/crash/app/crash_reporter_client.h +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRASH_APP_CRASH_REPORTER_CLIENT_H_ -#define COMPONENTS_CRASH_APP_CRASH_REPORTER_CLIENT_H_ - -#include - -#include "base/strings/string16.h" -#include "build/build_config.h" - -namespace base { -class FilePath; -} - -#if defined(OS_MACOSX) -// We don't want to directly include -// breakpad/src/client/mac/Framework/Breakpad.h here, so we repeat the -// definition of BreakpadRef. -// -// On Mac, when compiling without breakpad support, a stub implementation is -// compiled in. Not having any includes of the breakpad library allows for -// reusing this header for the stub. -typedef void* BreakpadRef; -#endif - -namespace crash_reporter { - -class CrashReporterClient; - -// Setter and getter for the client. The client should be set early, before any -// crash reporter code is called, and should stay alive throughout the entire -// runtime. -void SetCrashReporterClient(CrashReporterClient* client); - -#if defined(CRASH_IMPLEMENTATION) -// The components's embedder API should only be used by the component. -CrashReporterClient* GetCrashReporterClient(); -#endif - -// Interface that the embedder implements. -class CrashReporterClient { - public: - CrashReporterClient(); - virtual ~CrashReporterClient(); - -#if !defined(OS_MACOSX) - // Sets the crash reporting client ID, a unique identifier for the client - // that is sending crash reports. After it is set, it should not be changed. - // |client_guid| may either be a full GUID or a GUID that was already stripped - // from its dashes. - // - // On Mac OS X, this is the responsibility of Crashpad, and can not be set - // directly by the client. - virtual void SetCrashReporterClientIdFromGUID(const std::string& client_guid); -#endif - -#if defined(OS_WIN) - // Returns true if an alternative location to store the minidump files was - // specified. Returns true if |crash_dir| was set. - virtual bool GetAlternativeCrashDumpLocation(base::FilePath* crash_dir); - - // Returns a textual description of the product type and version to include - // in the crash report. - virtual void GetProductNameAndVersion(const base::FilePath& exe_path, - base::string16* product_name, - base::string16* version, - base::string16* special_build, - base::string16* channel_name); - - // Returns true if a restart dialog should be displayed. In that case, - // |message| and |title| are set to a message to display in a dialog box with - // the given title before restarting, and |is_rtl_locale| indicates whether - // to display the text as RTL. - virtual bool ShouldShowRestartDialog(base::string16* title, - base::string16* message, - bool* is_rtl_locale); - - // Returns true if it is ok to restart the application. Invoked right before - // restarting after a crash. - virtual bool AboutToRestart(); - - // Returns true if the crash report uploader supports deferred uploads. - virtual bool GetDeferredUploadsSupported(bool is_per_user_install); - - // Returns true if the running binary is a per-user installation. - virtual bool GetIsPerUserInstall(const base::FilePath& exe_path); - - // Returns true if larger crash dumps should be dumped. - virtual bool GetShouldDumpLargerDumps(bool is_per_user_install); - - // Returns the result code to return when breakpad failed to respawn a - // crashed process. - virtual int GetResultCodeRespawnFailed(); - - // Invoked when initializing the crash reporter in the browser process. - virtual void InitBrowserCrashDumpsRegKey(); - - // Invoked before attempting to write a minidump. - virtual void RecordCrashDumpAttempt(bool is_real_crash); - - // Invoked with the results of a minidump attempt. - virtual void RecordCrashDumpAttemptResult(bool is_real_crash, bool succeeded); -#endif - -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) - // Returns a textual description of the product type and version to include - // in the crash report. Neither out parameter should be set to NULL. - virtual void GetProductNameAndVersion(const char** product_name, - const char** version); - - virtual base::FilePath GetReporterLogFilename(); - - // Custom crash minidump handler after the minidump is generated. - // Returns true if the minidump is handled (client); otherwise, return false - // to fallback to default handler. - // WARNING: this handler runs in a compromised context. It may not call into - // libc nor allocate memory normally. - virtual bool HandleCrashDump(const char* crashdump_filename); -#endif - - // The location where minidump files should be written. Returns true if - // |crash_dir| was set. - virtual bool GetCrashDumpLocation(base::FilePath* crash_dir); - - // Register all of the potential crash keys that can be sent to the crash - // reporting server. Returns the size of the union of all keys. - virtual size_t RegisterCrashKeys(); - - // Returns true if running in unattended mode (for automated testing). - virtual bool IsRunningUnattended(); - - // Returns true if the user has given consent to collect stats. - virtual bool GetCollectStatsConsent(); - -#if defined(OS_WIN) || defined(OS_MACOSX) - // Returns true if crash reporting is enforced via management policies. In - // that case, |breakpad_enabled| is set to the value enforced by policies. - virtual bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled); -#endif - -#if defined(OS_ANDROID) - // Returns the descriptor key of the android minidump global descriptor. - virtual int GetAndroidMinidumpDescriptor(); - - // Returns true if breakpad microdumps should be enabled. This orthogonal to - // the standard minidump uploader (which depends on the user consent). - virtual bool ShouldEnableBreakpadMicrodumps(); -#endif - - // Returns true if breakpad should run in the given process type. - virtual bool EnableBreakpadForProcess(const std::string& process_type); -}; - -} // namespace crash_reporter - -#endif // COMPONENTS_CRASH_APP_CRASH_REPORTER_CLIENT_H_ diff --git a/components/crash/app/crashpad_mac.h b/components/crash/app/crashpad_mac.h deleted file mode 100644 index 895cb2b..0000000 --- a/components/crash/app/crashpad_mac.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRASH_APP_CRASHPAD_MAC_H_ -#define COMPONENTS_CRASH_APP_CRASHPAD_MAC_H_ - -#include - -#include -#include - -namespace crash_reporter { - -// Initializes Crashpad in a way that is appropriate for process_type. If -// process_type is empty, initializes Crashpad for the browser process, which -// starts crashpad_handler and sets it as the exception handler. Other process -// types inherit this exception handler from the browser, but still need to -// perform additional initialization. -void InitializeCrashpad(const std::string& process_type); - -// Enables or disables crash report upload. This is a property of the Crashpad -// database. In a newly-created database, uploads will be disabled. This -// function only has an effect when called in the browser process. Its effect is -// immediate and applies to all other process types, including processes that -// are already running. -void SetUploadsEnabled(bool enabled); - -// Determines whether uploads are enabled or disabled. This information is only -// available in the browser process. -bool GetUploadsEnabled(); - -struct UploadedReport { - std::string local_id; - std::string remote_id; - time_t creation_time; -}; - -// Obtains a list of reports uploaded to the collection server. This function -// only operates when called in the browser process. All reports in the Crashpad -// database that have been successfully uploaded will be included in this list. -// The list will be sorted in descending order by report creation time (newest -// reports first). -// -// TODO(mark): The about:crashes UI expects to show only uploaded reports. If it -// is ever enhanced to work well with un-uploaded reports, those should be -// returned as well. Un-uploaded reports may have a pending upload, may have -// experienced upload failure, or may have been collected while uploads were -// disabled. -void GetUploadedReports(std::vector* uploaded_reports); - -} // namespace crash_reporter - -#endif // COMPONENTS_CRASH_APP_CRASHPAD_MAC_H_ diff --git a/components/crash/app/crashpad_mac.mm b/components/crash/app/crashpad_mac.mm deleted file mode 100644 index cab4133..0000000 --- a/components/crash/app/crashpad_mac.mm +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/crash/app/crashpad_mac.h" - -#include -#include - -#include -#include -#include - -#include "base/auto_reset.h" -#include "base/debug/crash_logging.h" -#include "base/debug/dump_without_crashing.h" -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/mac/bundle_locations.h" -#include "base/mac/foundation_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" -#include "base/strings/stringprintf.h" -#include "base/strings/sys_string_conversions.h" -#include "components/crash/app/crash_reporter_client.h" -#include "third_party/crashpad/crashpad/client/crash_report_database.h" -#include "third_party/crashpad/crashpad/client/crashpad_client.h" -#include "third_party/crashpad/crashpad/client/crashpad_info.h" -#include "third_party/crashpad/crashpad/client/settings.h" -#include "third_party/crashpad/crashpad/client/simple_string_dictionary.h" -#include "third_party/crashpad/crashpad/client/simulate_crash.h" - -namespace crash_reporter { - -namespace { - -crashpad::SimpleStringDictionary* g_simple_string_dictionary; -crashpad::CrashReportDatabase* g_database; - -void SetCrashKeyValue(const base::StringPiece& key, - const base::StringPiece& value) { - g_simple_string_dictionary->SetKeyValue(key.data(), value.data()); -} - -void ClearCrashKey(const base::StringPiece& key) { - g_simple_string_dictionary->RemoveKey(key.data()); -} - -bool LogMessageHandler(int severity, - const char* file, - int line, - size_t message_start, - const std::string& string) { - // Only handle FATAL. - if (severity != logging::LOG_FATAL) { - return false; - } - - // In case of an out-of-memory condition, this code could be reentered when - // constructing and storing the key. Using a static is not thread-safe, but if - // multiple threads are in the process of a fatal crash at the same time, this - // should work. - static bool guarded = false; - if (guarded) { - return false; - } - base::AutoReset guard(&guarded, true); - - // Only log last path component. This matches logging.cc. - if (file) { - const char* slash = strrchr(file, '/'); - if (slash) { - file = slash + 1; - } - } - - std::string message = base::StringPrintf("%s:%d: %s", file, line, - string.c_str() + message_start); - SetCrashKeyValue("LOG_FATAL", message); - - // Rather than including the code to force the crash here, allow the caller to - // do it. - return false; -} - -void DumpWithoutCrashing() { - CRASHPAD_SIMULATE_CRASH(); -} - -} // namespace - -void InitializeCrashpad(const std::string& process_type) { - static bool initialized = false; - DCHECK(!initialized); - initialized = true; - - const bool browser_process = process_type.empty(); - CrashReporterClient* crash_reporter_client = GetCrashReporterClient(); - - base::FilePath database_path; // Only valid in the browser process. - - if (browser_process) { - @autoreleasepool { - base::FilePath framework_bundle_path = base::mac::FrameworkBundlePath(); - base::FilePath handler_path = - framework_bundle_path.Append("Helpers").Append("crashpad_handler"); - - // Is there a way to recover if this fails? - crash_reporter_client->GetCrashDumpLocation(&database_path); - - // TODO(mark): Reading the Breakpad keys is temporary and transitional. At - // the very least, they should be renamed to Crashpad. For the time being, - // this isn't the worst thing: Crashpad is still uploading to a - // Breakpad-type server, after all. - NSBundle* framework_bundle = base::mac::FrameworkBundle(); - NSString* product = base::mac::ObjCCast( - [framework_bundle objectForInfoDictionaryKey:@"BreakpadProduct"]); - NSString* version = base::mac::ObjCCast( - [framework_bundle objectForInfoDictionaryKey:@"BreakpadVersion"]); - NSString* url_ns = base::mac::ObjCCast( - [framework_bundle objectForInfoDictionaryKey:@"BreakpadURL"]); - - std::string url = base::SysNSStringToUTF8(url_ns); - - std::map process_annotations; - process_annotations["prod"] = base::SysNSStringToUTF8(product); - process_annotations["ver"] = base::SysNSStringToUTF8(version); - process_annotations["plat"] = std::string("OS X"); - - crashpad::CrashpadClient crashpad_client; - if (crashpad_client.StartHandler(handler_path, database_path, url, - process_annotations, - std::vector())) { - crashpad_client.UseHandler(); - } - } // @autoreleasepool - } - - crashpad::CrashpadInfo* crashpad_info = - crashpad::CrashpadInfo::GetCrashpadInfo(); - -#if defined(NDEBUG) - const bool is_debug_build = false; -#else - const bool is_debug_build = true; -#endif - - // Disable forwarding to the system's crash reporter in processes other than - // the browser process. For the browser, the system's crash reporter presents - // the crash UI to the user, so it's desirable there. Additionally, having - // crash reports appear in ~/Library/Logs/DiagnosticReports provides a - // fallback. Forwarding is turned off for debug-mode builds even for the - // browser process, because the system's crash reporter can take a very long - // time to chew on symbols. - if (!browser_process || is_debug_build) { - crashpad_info->set_system_crash_reporter_forwarding( - crashpad::TriState::kDisabled); - } - - g_simple_string_dictionary = new crashpad::SimpleStringDictionary(); - crashpad_info->set_simple_annotations(g_simple_string_dictionary); - - base::debug::SetCrashKeyReportingFunctions(SetCrashKeyValue, ClearCrashKey); - crash_reporter_client->RegisterCrashKeys(); - - SetCrashKeyValue("ptype", browser_process ? base::StringPiece("browser") - : base::StringPiece(process_type)); - SetCrashKeyValue("pid", base::IntToString(getpid())); - - logging::SetLogMessageHandler(LogMessageHandler); - - // If clients called CRASHPAD_SIMULATE_CRASH() instead of - // base::debug::DumpWithoutCrashing(), these dumps would appear as crashes in - // the correct function, at the correct file and line. This would be - // preferable to having all occurrences show up in DumpWithoutCrashing() at - // the same file and line. - base::debug::SetDumpWithoutCrashingFunction(DumpWithoutCrashing); - - if (browser_process) { - g_database = - crashpad::CrashReportDatabase::Initialize(database_path).release(); - - bool enable_uploads = false; - if (!crash_reporter_client->ReportingIsEnforcedByPolicy(&enable_uploads)) { - enable_uploads = crash_reporter_client->GetCollectStatsConsent() && - !crash_reporter_client->IsRunningUnattended(); - // Breakpad provided a --disable-breakpad switch to disable crash dumping - // (not just uploading) here. Crashpad doesn't need it: dumping is enabled - // unconditionally and uploading is gated on consent, which tests/bots - // shouldn't have. As a precaution, we also force disable uploading on - // bots even if consent is present. - } - - SetUploadsEnabled(enable_uploads); - } -} - -void SetUploadsEnabled(bool enable_uploads) { - if (g_database) { - crashpad::Settings* settings = g_database->GetSettings(); - settings->SetUploadsEnabled(enable_uploads); - } -} - -bool GetUploadsEnabled() { - if (g_database) { - crashpad::Settings* settings = g_database->GetSettings(); - bool enable_uploads; - if (settings->GetUploadsEnabled(&enable_uploads)) { - return enable_uploads; - } - } - - return false; -} - -void GetUploadedReports(std::vector* uploaded_reports) { - uploaded_reports->clear(); - - if (!g_database) { - return; - } - - std::vector completed_reports; - crashpad::CrashReportDatabase::OperationStatus status = - g_database->GetCompletedReports(&completed_reports); - if (status != crashpad::CrashReportDatabase::kNoError) { - return; - } - - for (const crashpad::CrashReportDatabase::Report& completed_report : - completed_reports) { - if (completed_report.uploaded) { - UploadedReport uploaded_report; - uploaded_report.local_id = completed_report.uuid.ToString(); - uploaded_report.remote_id = completed_report.id; - uploaded_report.creation_time = completed_report.creation_time; - - uploaded_reports->push_back(uploaded_report); - } - } - - struct { - bool operator()(const UploadedReport& a, const UploadedReport& b) { - return a.creation_time >= b.creation_time; - } - } sort_by_time; - std::sort(uploaded_reports->begin(), uploaded_reports->end(), sort_by_time); -} - -} // namespace crash_reporter diff --git a/components/crash/app/hard_error_handler_win.cc b/components/crash/app/hard_error_handler_win.cc deleted file mode 100644 index ee88522..0000000 --- a/components/crash/app/hard_error_handler_win.cc +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/crash/app/hard_error_handler_win.h" - -#include -#include - -#include "base/basictypes.h" -#include "base/strings/string_util.h" -#include "components/crash/app/crash_reporter_client.h" - -namespace breakpad { - -using crash_reporter::GetCrashReporterClient; - -namespace { -const DWORD kExceptionModuleNotFound = VcppException(ERROR_SEVERITY_ERROR, - ERROR_MOD_NOT_FOUND); -const DWORD kExceptionEntryPtNotFound = VcppException(ERROR_SEVERITY_ERROR, - ERROR_PROC_NOT_FOUND); -// This is defined in but we can't include this file here. -const DWORD FACILITY_GRAPHICS_KERNEL = 0x1E; -const DWORD NT_STATUS_ENTRYPOINT_NOT_FOUND = 0xC0000139; -const DWORD NT_STATUS_DLL_NOT_FOUND = 0xC0000135; - -// We assume that exception codes are NT_STATUS codes. -DWORD FacilityFromException(DWORD exception_code) { - return (exception_code >> 16) & 0x0FFF; -} - -// This is not a generic function. It only works with some |nt_status| values. -// Check the strings here http://msdn.microsoft.com/en-us/library/cc704588.aspx -// before attempting to use this function. -void RaiseHardErrorMsg(long nt_status, const std::string& p1, - const std::string& p2) { - // If headless just exit silently. - if (GetCrashReporterClient()->IsRunningUnattended()) - return; - - HMODULE ntdll = ::GetModuleHandleA("NTDLL.DLL"); - wchar_t* msg_template = NULL; - size_t count = ::FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_HMODULE, - ntdll, - nt_status, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&msg_template), - 0, - NULL); - - if (!count) - return; - count += p1.size() + p2.size() + 1; - base::string16 message; - ::wsprintf(base::WriteInto(&message, count), msg_template, - p1.c_str(), p2.c_str()); - // The MB_SERVICE_NOTIFICATION causes this message to be displayed by - // csrss. This means that we are not creating windows or pumping WM messages - // in this process. - ::MessageBox(NULL, message.c_str(), - L"chrome.exe", - MB_OK | MB_SERVICE_NOTIFICATION); - ::LocalFree(msg_template); -} - -void ModuleNotFoundHardError(const EXCEPTION_RECORD* ex_record) { - DelayLoadInfo* dli = reinterpret_cast( - ex_record->ExceptionInformation[0]); - if (!dli->szDll) - return; - RaiseHardErrorMsg(NT_STATUS_DLL_NOT_FOUND, dli->szDll, std::string()); -} - -void EntryPointNotFoundHardError(const EXCEPTION_RECORD* ex_record) { - DelayLoadInfo* dli = reinterpret_cast( - ex_record->ExceptionInformation[0]); - if (!dli->dlp.fImportByName) - return; - if (!dli->dlp.szProcName) - return; - if (!dli->szDll) - return; - RaiseHardErrorMsg(NT_STATUS_ENTRYPOINT_NOT_FOUND, - dli->dlp.szProcName, dli->szDll); -} - -} // namespace - -bool HardErrorHandler(EXCEPTION_POINTERS* ex_info) { - if (!ex_info) - return false; - if (!ex_info->ExceptionRecord) - return false; - - long exception = ex_info->ExceptionRecord->ExceptionCode; - if (exception == kExceptionModuleNotFound) { - ModuleNotFoundHardError(ex_info->ExceptionRecord); - return true; - } else if (exception == kExceptionEntryPtNotFound) { - EntryPointNotFoundHardError(ex_info->ExceptionRecord); - return true; - } else if (FacilityFromException(exception) == FACILITY_GRAPHICS_KERNEL) { - RaiseHardErrorMsg(exception, std::string(), std::string()); - return true; - } - return false; -} - -} // namespace breakpad diff --git a/components/crash/app/hard_error_handler_win.h b/components/crash/app/hard_error_handler_win.h deleted file mode 100644 index 7855616..0000000 --- a/components/crash/app/hard_error_handler_win.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRASH_APP_HARD_ERROR_HANDLER_WIN_H_ -#define COMPONENTS_CRASH_APP_HARD_ERROR_HANDLER_WIN_H_ - -#include - -namespace breakpad { - -// This function is in charge of displaying a dialog box that informs the -// user of a fatal condition in chrome. It is meant to be called from -// breakpad's unhandled exception handler after the crash dump has been -// created. The return value will be true if we are to retry launching -// chrome (and show the 'chrome has crashed' dialog) or to silently exit. -// -// This function only handles a few known exceptions, currently: -// - Failure to load a delayload dll. -// - Failure to bind to a delayloaded import. -// - Fatal Graphics card failure (aura build only). -// -// If any of these conditions are encountered, a message box shown by -// the operating system CSRSS process via NtRaiseHardError is invoked. -// The wording and localization is up to the operating system. -// -// Do not call this function for memory related errors like heap corruption -// or stack exahustion. This function assumes that memory allocations are -// possible. -bool HardErrorHandler(EXCEPTION_POINTERS* ex_info); - -} // namespace breakpad - -#endif // COMPONENTS_CRASH_APP_HARD_ERROR_HANDLER_WIN_H_ diff --git a/components/crash/browser/BUILD.gn b/components/crash/browser/BUILD.gn deleted file mode 100644 index 05e0c05..0000000 --- a/components/crash/browser/BUILD.gn +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -if (is_android) { - import("//build/config/android/config.gni") -} - -source_set("browser") { - sources = [ - "crash_dump_manager_android.cc", - "crash_dump_manager_android.h", - ] - - if (is_linux || is_android) { - set_sources_assignment_filter([]) - - # Want this file on both Linux and Android. - sources += [ - "crash_handler_host_linux.cc", - "crash_handler_host_linux.h", - ] - } - - deps = [ - "//base", - "//components/crash/app", - "//content/public/browser", - "//content/public/common", - ] - - # This is not in the GYP build but this target includes breakpad client - # headers, so add the dependency here. - if (is_posix && !is_ios) { - configs += [ "//breakpad:client_config" ] - public_configs = [ "//breakpad:client_config" ] - } -} diff --git a/components/crash/browser/DEPS b/components/crash/browser/DEPS deleted file mode 100644 index c24130e..0000000 --- a/components/crash/browser/DEPS +++ /dev/null @@ -1,4 +0,0 @@ -include_rules = [ - "+content/public/browser", - "+content/public/common", -] diff --git a/components/crash/browser/crash_dump_manager_android.cc b/components/crash/browser/crash_dump_manager_android.cc deleted file mode 100644 index 05860a7..0000000 --- a/components/crash/browser/crash_dump_manager_android.cc +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/crash/browser/crash_dump_manager_android.h" - -#include "base/bind.h" -#include "base/files/file_util.h" -#include "base/format_macros.h" -#include "base/logging.h" -#include "base/posix/global_descriptors.h" -#include "base/process/process.h" -#include "base/rand_util.h" -#include "base/stl_util.h" -#include "base/strings/stringprintf.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/child_process_data.h" -#include "content/public/browser/file_descriptor_info.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/notification_types.h" -#include "content/public/browser/render_process_host.h" - -using content::BrowserThread; - -namespace breakpad { - -// static -CrashDumpManager* CrashDumpManager::instance_ = NULL; - -// static -CrashDumpManager* CrashDumpManager::GetInstance() { - CHECK(instance_); - return instance_; -} - -CrashDumpManager::CrashDumpManager(const base::FilePath& crash_dump_dir) - : crash_dump_dir_(crash_dump_dir) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(!instance_); - - instance_ = this; - - notification_registrar_.Add(this, - content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, - content::NotificationService::AllSources()); - notification_registrar_.Add(this, - content::NOTIFICATION_RENDERER_PROCESS_CLOSED, - content::NotificationService::AllSources()); - - BrowserChildProcessObserver::Add(this); -} - -CrashDumpManager::~CrashDumpManager() { - instance_ = NULL; - - BrowserChildProcessObserver::Remove(this); -} - -base::File CrashDumpManager::CreateMinidumpFile(int child_process_id) { - DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); - base::FilePath minidump_path; - if (!base::CreateTemporaryFile(&minidump_path)) - return base::File(); - - // We need read permission as the minidump is generated in several phases - // and needs to be read at some point. - int flags = base::File::FLAG_OPEN | base::File::FLAG_READ | - base::File::FLAG_WRITE; - base::File minidump_file(minidump_path, flags); - if (!minidump_file.IsValid()) { - LOG(ERROR) << "Failed to create temporary file, crash won't be reported."; - return base::File(); - } - - { - base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_); - DCHECK(!ContainsKey(child_process_id_to_minidump_path_, child_process_id)); - child_process_id_to_minidump_path_[child_process_id] = minidump_path; - } - return minidump_file.Pass(); -} - -// static -void CrashDumpManager::ProcessMinidump(const base::FilePath& minidump_path, - base::ProcessHandle pid) { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - CHECK(instance_); - int64 file_size = 0; - int r = base::GetFileSize(minidump_path, &file_size); - DCHECK(r) << "Failed to retrieve size for minidump " - << minidump_path.value(); - - if (file_size == 0) { - // Empty minidump, this process did not crash. Just remove the file. - r = base::DeleteFile(minidump_path, false); - DCHECK(r) << "Failed to delete temporary minidump file " - << minidump_path.value(); - return; - } - - // We are dealing with a valid minidump. Copy it to the crash report - // directory from where Java code will upload it later on. - if (instance_->crash_dump_dir_.empty()) { - NOTREACHED() << "Failed to retrieve the crash dump directory."; - return; - } - const uint64 rand = base::RandUint64(); - const std::string filename = - base::StringPrintf("chromium-renderer-minidump-%016" PRIx64 ".dmp%d", - rand, pid); - base::FilePath dest_path = instance_->crash_dump_dir_.Append(filename); - r = base::Move(minidump_path, dest_path); - if (!r) { - LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value() - << " to " << dest_path.value(); - base::DeleteFile(minidump_path, false); - return; - } - VLOG(1) << "Crash minidump successfully generated: " << - instance_->crash_dump_dir_.Append(filename).value(); -} - -void CrashDumpManager::BrowserChildProcessHostDisconnected( - const content::ChildProcessData& data) { - OnChildExit(data.id, data.handle); -} - -void CrashDumpManager::BrowserChildProcessCrashed( - const content::ChildProcessData& data, - int exit_code) { - OnChildExit(data.id, data.handle); -} - -void CrashDumpManager::Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - switch (type) { - case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: - // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer - // process is cleanly shutdown. However, we need to fallthrough so that - // we close the minidump_fd we kept open. - case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { - content::RenderProcessHost* rph = - content::Source(source).ptr(); - OnChildExit(rph->GetID(), rph->GetHandle()); - break; - } - default: - NOTREACHED(); - return; - } -} - -void CrashDumpManager::OnChildExit(int child_process_id, - base::ProcessHandle pid) { - base::FilePath minidump_path; - { - base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_); - ChildProcessIDToMinidumpPath::iterator iter = - child_process_id_to_minidump_path_.find(child_process_id); - if (iter == child_process_id_to_minidump_path_.end()) { - // We might get a NOTIFICATION_RENDERER_PROCESS_TERMINATED and a - // NOTIFICATION_RENDERER_PROCESS_CLOSED. - return; - } - minidump_path = iter->second; - child_process_id_to_minidump_path_.erase(iter); - } - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&CrashDumpManager::ProcessMinidump, minidump_path, pid)); -} - -} // namespace breakpad diff --git a/components/crash/browser/crash_dump_manager_android.h b/components/crash/browser/crash_dump_manager_android.h deleted file mode 100644 index 18d2859..0000000 --- a/components/crash/browser/crash_dump_manager_android.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRASH_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_ -#define COMPONENTS_CRASH_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_ - -#include - -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/process/process.h" -#include "base/synchronization/lock.h" -#include "content/public/browser/browser_child_process_observer.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" - -namespace content { -class RenderProcessHost; -} - -namespace breakpad { - -// This class manages the crash minidumps. -// On Android, because of process isolation, each renderer process runs with a -// different UID. As a result, we cannot generate the minidumps in the browser -// (as the browser process does not have access to some system files for the -// crashed process). So the minidump is generated in the renderer process. -// Since the isolated process cannot open files, we provide it on creation with -// a file descriptor where to write the minidump in the event of a crash. -// This class creates these file descriptors and associates them with render -// processes and take the appropriate action when the render process terminates. -class CrashDumpManager : public content::BrowserChildProcessObserver, - public content::NotificationObserver { - public: - // The embedder should create a single instance of the CrashDumpManager. - static CrashDumpManager* GetInstance(); - - // Should be created on the UI thread. - explicit CrashDumpManager(const base::FilePath& crash_dump_dir); - - ~CrashDumpManager() override; - - // Returns a file that should be used to generate a minidump for the process - // |child_process_id|. - base::File CreateMinidumpFile(int child_process_id); - - private: - typedef std::map ChildProcessIDToMinidumpPath; - - static void ProcessMinidump(const base::FilePath& minidump_path, - base::ProcessHandle pid); - - // content::BrowserChildProcessObserver implementation: - void BrowserChildProcessHostDisconnected( - const content::ChildProcessData& data) override; - void BrowserChildProcessCrashed( - const content::ChildProcessData& data, - int exit_code) override; - - // NotificationObserver implementation: - void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) override; - - // Called on child process exit (including crash). - void OnChildExit(int child_process_id, base::ProcessHandle pid); - - content::NotificationRegistrar notification_registrar_; - - // This map should only be accessed with its lock aquired as it is accessed - // from the PROCESS_LAUNCHER and UI threads. - base::Lock child_process_id_to_minidump_path_lock_; - ChildProcessIDToMinidumpPath child_process_id_to_minidump_path_; - - base::FilePath crash_dump_dir_; - - static CrashDumpManager* instance_; - - DISALLOW_COPY_AND_ASSIGN(CrashDumpManager); -}; - -} // namespace breakpad - -#endif // COMPONENTS_CRASH_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_ diff --git a/components/crash/browser/crash_handler_host_linux.cc b/components/crash/browser/crash_handler_host_linux.cc deleted file mode 100644 index 0f68216..0000000 --- a/components/crash/browser/crash_handler_host_linux.cc +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/crash/browser/crash_handler_host_linux.h" - -#include -#include -#include -#include -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/format_macros.h" -#include "base/linux_util.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/posix/eintr_wrapper.h" -#include "base/rand_util.h" -#include "base/single_thread_task_runner.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/threading/thread.h" -#include "breakpad/src/client/linux/handler/exception_handler.h" -#include "breakpad/src/client/linux/minidump_writer/linux_dumper.h" -#include "breakpad/src/client/linux/minidump_writer/minidump_writer.h" -#include "components/crash/app/breakpad_linux_impl.h" -#include "content/public/browser/browser_thread.h" - -#if defined(OS_ANDROID) && !defined(__LP64__) -#include - -#define SYS_read __NR_read -#endif - -using content::BrowserThread; -using google_breakpad::ExceptionHandler; - -namespace breakpad { - -namespace { - -const size_t kNumFDs = 1; -// The length of the control message: -const size_t kControlMsgSize = - CMSG_SPACE(kNumFDs * sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); -// The length of the regular payload: -const size_t kCrashContextSize = sizeof(ExceptionHandler::CrashContext); - -// Handles the crash dump and frees the allocated BreakpadInfo struct. -void CrashDumpTask(CrashHandlerHostLinux* handler, - scoped_ptr info) { - if (handler->IsShuttingDown() && info->upload) { - base::DeleteFile(base::FilePath(info->filename), false); -#if defined(ADDRESS_SANITIZER) - base::DeleteFile(base::FilePath(info->log_filename), false); -#endif - return; - } - - HandleCrashDump(*info); - delete[] info->filename; -#if defined(ADDRESS_SANITIZER) - delete[] info->log_filename; - delete[] info->asan_report_str; -#endif - delete[] info->process_type; - delete[] info->distro; - delete info->crash_keys; -} - -} // namespace - -// Since instances of CrashHandlerHostLinux are leaked, they are only destroyed -// at the end of the processes lifetime, which is greater in span than the -// lifetime of the IO message loop. Thus, all calls to base::Bind() use -// non-refcounted pointers. - -CrashHandlerHostLinux::CrashHandlerHostLinux(const std::string& process_type, - const base::FilePath& dumps_path, - bool upload) - : process_type_(process_type), - dumps_path_(dumps_path), - upload_(upload), - shutting_down_(false), - worker_pool_token_(BrowserThread::GetBlockingPool()->GetSequenceToken()) { - int fds[2]; - // We use SOCK_SEQPACKET rather than SOCK_DGRAM to prevent the process from - // sending datagrams to other sockets on the system. The sandbox may prevent - // the process from calling socket() to create new sockets, but it'll still - // inherit some sockets. With PF_UNIX+SOCK_DGRAM, it can call sendmsg to send - // a datagram to any (abstract) socket on the same system. With - // SOCK_SEQPACKET, this is prevented. - CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); - static const int on = 1; - - // Enable passcred on the server end of the socket - CHECK_EQ(0, setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))); - - process_socket_ = fds[0]; - browser_socket_ = fds[1]; - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&CrashHandlerHostLinux::Init, base::Unretained(this))); -} - -CrashHandlerHostLinux::~CrashHandlerHostLinux() { - close(process_socket_); - close(browser_socket_); -} - -void CrashHandlerHostLinux::StartUploaderThread() { - uploader_thread_.reset( - new base::Thread(process_type_ + "_crash_uploader")); - uploader_thread_->Start(); -} - -void CrashHandlerHostLinux::Init() { - base::MessageLoopForIO* ml = base::MessageLoopForIO::current(); - CHECK(ml->WatchFileDescriptor( - browser_socket_, true /* persistent */, - base::MessageLoopForIO::WATCH_READ, - &file_descriptor_watcher_, this)); - ml->AddDestructionObserver(this); -} - -void CrashHandlerHostLinux::OnFileCanWriteWithoutBlocking(int fd) { - NOTREACHED(); -} - -void CrashHandlerHostLinux::OnFileCanReadWithoutBlocking(int fd) { - DCHECK_EQ(browser_socket_, fd); - - // A process has crashed and has signaled us by writing a datagram - // to the death signal socket. The datagram contains the crash context needed - // for writing the minidump as well as a file descriptor and a credentials - // block so that they can't lie about their pid. - // - // The message sender is in components/crash/app/breakpad_linux.cc. - - struct msghdr msg = {0}; - struct iovec iov[kCrashIovSize]; - - scoped_ptr crash_context(new char[kCrashContextSize]); -#if defined(ADDRESS_SANITIZER) - scoped_ptr asan_report(new char[kMaxAsanReportSize + 1]); -#endif - - scoped_ptr crash_keys(new CrashKeyStorage); - google_breakpad::SerializedNonAllocatingMap* serialized_crash_keys; - size_t crash_keys_size = crash_keys->Serialize( - const_cast( - &serialized_crash_keys)); - - char* tid_buf_addr = NULL; - int tid_fd = -1; - uint64_t uptime; - size_t oom_size; - char control[kControlMsgSize]; - const ssize_t expected_msg_size = - kCrashContextSize + - sizeof(tid_buf_addr) + sizeof(tid_fd) + - sizeof(uptime) + -#if defined(ADDRESS_SANITIZER) - kMaxAsanReportSize + 1 + -#endif - sizeof(oom_size) + - crash_keys_size; - iov[0].iov_base = crash_context.get(); - iov[0].iov_len = kCrashContextSize; - iov[1].iov_base = &tid_buf_addr; - iov[1].iov_len = sizeof(tid_buf_addr); - iov[2].iov_base = &tid_fd; - iov[2].iov_len = sizeof(tid_fd); - iov[3].iov_base = &uptime; - iov[3].iov_len = sizeof(uptime); - iov[4].iov_base = &oom_size; - iov[4].iov_len = sizeof(oom_size); - iov[5].iov_base = serialized_crash_keys; - iov[5].iov_len = crash_keys_size; -#if !defined(ADDRESS_SANITIZER) - static_assert(5 == kCrashIovSize - 1, "kCrashIovSize should equal 6"); -#else - iov[6].iov_base = asan_report.get(); - iov[6].iov_len = kMaxAsanReportSize + 1; - static_assert(6 == kCrashIovSize - 1, "kCrashIovSize should equal 7"); -#endif - msg.msg_iov = iov; - msg.msg_iovlen = kCrashIovSize; - msg.msg_control = control; - msg.msg_controllen = kControlMsgSize; - - const ssize_t msg_size = HANDLE_EINTR(recvmsg(browser_socket_, &msg, 0)); - if (msg_size < 0) { - LOG(ERROR) << "Error reading from death signal socket. Crash dumping" - << " is disabled." - << " msg_size:" << msg_size - << " errno:" << errno; - file_descriptor_watcher_.StopWatchingFileDescriptor(); - return; - } - const bool bad_message = (msg_size != expected_msg_size || - msg.msg_controllen != kControlMsgSize || - msg.msg_flags & ~MSG_TRUNC); - base::ScopedFD signal_fd; - pid_t crashing_pid = -1; - if (msg.msg_controllen > 0) { - // Walk the control payload and extract the file descriptor and - // validated pid. - for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; - hdr = CMSG_NXTHDR(&msg, hdr)) { - if (hdr->cmsg_level != SOL_SOCKET) - continue; - if (hdr->cmsg_type == SCM_RIGHTS) { - const size_t len = hdr->cmsg_len - - (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); - DCHECK_EQ(0U, len % sizeof(int)); - const size_t num_fds = len / sizeof(int); - if (num_fds != kNumFDs) { - // A nasty process could try and send us too many descriptors and - // force a leak. - LOG(ERROR) << "Death signal contained wrong number of descriptors;" - << " num_fds:" << num_fds; - for (size_t i = 0; i < num_fds; ++i) - close(reinterpret_cast(CMSG_DATA(hdr))[i]); - return; - } - DCHECK(!signal_fd.is_valid()); - int fd = reinterpret_cast(CMSG_DATA(hdr))[0]; - DCHECK_GE(fd, 0); // The kernel should never send a negative fd. - signal_fd.reset(fd); - } else if (hdr->cmsg_type == SCM_CREDENTIALS) { - DCHECK_EQ(-1, crashing_pid); - const struct ucred *cred = - reinterpret_cast(CMSG_DATA(hdr)); - crashing_pid = cred->pid; - } - } - } - - if (bad_message) { - LOG(ERROR) << "Received death signal message with the wrong size;" - << " msg.msg_controllen:" << msg.msg_controllen - << " msg.msg_flags:" << msg.msg_flags - << " kCrashContextSize:" << kCrashContextSize - << " kControlMsgSize:" << kControlMsgSize; - return; - } - if (crashing_pid == -1 || !signal_fd.is_valid()) { - LOG(ERROR) << "Death signal message didn't contain all expected control" - << " messages"; - return; - } - - // The crashing TID set inside the compromised context via - // sys_gettid() in ExceptionHandler::HandleSignal might be wrong (if - // the kernel supports PID namespacing) and may need to be - // translated. - // - // We expect the crashing thread to be in sys_read(), waiting for us to - // write to |signal_fd|. Most newer kernels where we have the different pid - // namespaces also have /proc/[pid]/syscall, so we can look through - // |actual_crashing_pid|'s thread group and find the thread that's in the - // read syscall with the right arguments. - - std::string expected_syscall_data; - // /proc/[pid]/syscall is formatted as follows: - // syscall_number arg1 ... arg6 sp pc - // but we just check syscall_number through arg3. - base::StringAppendF(&expected_syscall_data, "%d 0x%x %p 0x1 ", - SYS_read, tid_fd, tid_buf_addr); - bool syscall_supported = false; - pid_t crashing_tid = - base::FindThreadIDWithSyscall(crashing_pid, - expected_syscall_data, - &syscall_supported); - if (crashing_tid == -1) { - // We didn't find the thread we want. Maybe it didn't reach - // sys_read() yet or the thread went away. We'll just take a - // guess here and assume the crashing thread is the thread group - // leader. If procfs syscall is not supported by the kernel, then - // we assume the kernel also does not support TID namespacing and - // trust the TID passed by the crashing process. - LOG(WARNING) << "Could not translate tid - assuming crashing thread is " - "thread group leader; syscall_supported=" << syscall_supported; - crashing_tid = crashing_pid; - } - - ExceptionHandler::CrashContext* bad_context = - reinterpret_cast(crash_context.get()); - bad_context->tid = crashing_tid; - - scoped_ptr info(new BreakpadInfo); - - info->fd = -1; - info->process_type_length = process_type_.length(); - // Freed in CrashDumpTask(). - char* process_type_str = new char[info->process_type_length + 1]; - process_type_.copy(process_type_str, info->process_type_length); - process_type_str[info->process_type_length] = '\0'; - info->process_type = process_type_str; - - // Memory released from scoped_ptrs below are also freed in CrashDumpTask(). - info->crash_keys = crash_keys.release(); -#if defined(ADDRESS_SANITIZER) - asan_report[kMaxAsanReportSize] = '\0'; - info->asan_report_str = asan_report.release(); - info->asan_report_length = strlen(info->asan_report_str); -#endif - - info->process_start_time = uptime; - info->oom_size = oom_size; -#if defined(OS_ANDROID) - // Nothing gets uploaded in android. - info->upload = false; -#else - info->upload = upload_; -#endif - - - BrowserThread::GetBlockingPool()->PostSequencedWorkerTask( - worker_pool_token_, - FROM_HERE, - base::Bind(&CrashHandlerHostLinux::WriteDumpFile, - base::Unretained(this), - base::Passed(&info), - base::Passed(&crash_context), - crashing_pid, - signal_fd.release())); -} - -void CrashHandlerHostLinux::WriteDumpFile(scoped_ptr info, - scoped_ptr crash_context, - pid_t crashing_pid, - int signal_fd) { - DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread( - worker_pool_token_)); - - // Set |info->distro| here because base::GetLinuxDistro() needs to run on a - // blocking thread. - std::string distro = base::GetLinuxDistro(); - info->distro_length = distro.length(); - // Freed in CrashDumpTask(). - char* distro_str = new char[info->distro_length + 1]; - distro.copy(distro_str, info->distro_length); - distro_str[info->distro_length] = '\0'; - info->distro = distro_str; - - base::FilePath dumps_path("/tmp"); - PathService::Get(base::DIR_TEMP, &dumps_path); - if (!info->upload) - dumps_path = dumps_path_; - const std::string minidump_filename = - base::StringPrintf("%s/chromium-%s-minidump-%016" PRIx64 ".dmp", - dumps_path.value().c_str(), - process_type_.c_str(), - base::RandUint64()); - - if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), - kMaxMinidumpFileSize, - crashing_pid, - crash_context.get(), - kCrashContextSize, - google_breakpad::MappingList(), - google_breakpad::AppMemoryList())) { - LOG(ERROR) << "Failed to write crash dump for pid " << crashing_pid; - } -#if defined(ADDRESS_SANITIZER) - // Create a temporary file holding the AddressSanitizer report. - const base::FilePath log_path = - base::FilePath(minidump_filename).ReplaceExtension("log"); - base::WriteFile(log_path, info->asan_report_str, info->asan_report_length); -#endif - - // Freed in CrashDumpTask(). - char* minidump_filename_str = new char[minidump_filename.length() + 1]; - minidump_filename.copy(minidump_filename_str, minidump_filename.length()); - minidump_filename_str[minidump_filename.length()] = '\0'; - info->filename = minidump_filename_str; -#if defined(ADDRESS_SANITIZER) - // Freed in CrashDumpTask(). - char* minidump_log_filename_str = new char[minidump_filename.length() + 1]; - minidump_filename.copy(minidump_log_filename_str, minidump_filename.length()); - memcpy(minidump_log_filename_str + minidump_filename.length() - 3, "log", 3); - minidump_log_filename_str[minidump_filename.length()] = '\0'; - info->log_filename = minidump_log_filename_str; -#endif - info->pid = crashing_pid; - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&CrashHandlerHostLinux::QueueCrashDumpTask, - base::Unretained(this), - base::Passed(&info), - signal_fd)); -} - -void CrashHandlerHostLinux::QueueCrashDumpTask(scoped_ptr info, - int signal_fd) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - // Send the done signal to the process: it can exit now. - struct msghdr msg = {0}; - struct iovec done_iov; - done_iov.iov_base = const_cast("\x42"); - done_iov.iov_len = 1; - msg.msg_iov = &done_iov; - msg.msg_iovlen = 1; - - HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); - close(signal_fd); - - uploader_thread_->task_runner()->PostTask( - FROM_HERE, - base::Bind(&CrashDumpTask, base::Unretained(this), base::Passed(&info))); -} - -void CrashHandlerHostLinux::WillDestroyCurrentMessageLoop() { - file_descriptor_watcher_.StopWatchingFileDescriptor(); - - // If we are quitting and there are crash dumps in the queue, turn them into - // no-ops. - shutting_down_ = true; - uploader_thread_->Stop(); -} - -bool CrashHandlerHostLinux::IsShuttingDown() const { - return shutting_down_; -} - -} // namespace breakpad diff --git a/components/crash/browser/crash_handler_host_linux.h b/components/crash/browser/crash_handler_host_linux.h deleted file mode 100644 index 876a0a5..0000000 --- a/components/crash/browser/crash_handler_host_linux.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRASH_BROWSER_CRASH_HANDLER_HOST_LINUX_H_ -#define COMPONENTS_CRASH_BROWSER_CRASH_HANDLER_HOST_LINUX_H_ - -#include - -#include - -#include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/threading/sequenced_worker_pool.h" - -namespace base { -class Thread; -} - -namespace breakpad { - -struct BreakpadInfo; - -// This is the host for processes which run breakpad inside the sandbox on -// Linux or Android. We perform the crash dump from the browser because it -// allows us to be outside the sandbox. -// -// Processes signal that they need to be dumped by sending a datagram over a -// UNIX domain socket. All processes of the same type share the client end of -// this socket which is installed in their descriptor table before exec. -class CrashHandlerHostLinux : public base::MessageLoopForIO::Watcher, - public base::MessageLoop::DestructionObserver { - public: - CrashHandlerHostLinux(const std::string& process_type, - const base::FilePath& dumps_path, - bool upload); - ~CrashHandlerHostLinux() override; - - // Starts the uploader thread. Must be called immediately after creating the - // class. - void StartUploaderThread(); - - // Get the file descriptor which processes should be given in order to signal - // crashes to the browser. - int GetDeathSignalSocket() const { - return process_socket_; - } - - // MessagePumbLibevent::Watcher impl: - void OnFileCanWriteWithoutBlocking(int fd) override; - void OnFileCanReadWithoutBlocking(int fd) override; - - // MessageLoop::DestructionObserver impl: - void WillDestroyCurrentMessageLoop() override; - - // Whether we are shutting down or not. - bool IsShuttingDown() const; - - private: - void Init(); - - // Do work in a sequenced worker pool for OnFileCanReadWithoutBlocking(). - void WriteDumpFile(scoped_ptr info, - scoped_ptr crash_context, - pid_t crashing_pid, - int signal_fd); - - // Continue OnFileCanReadWithoutBlocking()'s work on the IO thread. - void QueueCrashDumpTask(scoped_ptr info, int signal_fd); - - std::string process_type_; - base::FilePath dumps_path_; - bool upload_; - - int process_socket_; - int browser_socket_; - - base::MessageLoopForIO::FileDescriptorWatcher file_descriptor_watcher_; - scoped_ptr uploader_thread_; - bool shutting_down_; - - // Unique sequence token so that writing crash dump won't be blocked - // by other tasks. - base::SequencedWorkerPool::SequenceToken worker_pool_token_; - - DISALLOW_COPY_AND_ASSIGN(CrashHandlerHostLinux); -}; - -} // namespace breakpad - -#endif // COMPONENTS_CRASH_BROWSER_CRASH_HANDLER_HOST_LINUX_H_ diff --git a/components/crash/content/app/BUILD.gn b/components/crash/content/app/BUILD.gn new file mode 100644 index 0000000..e0985c5 --- /dev/null +++ b/components/crash/content/app/BUILD.gn @@ -0,0 +1,79 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if (is_android) { + import("//build/config/android/config.gni") +} + +source_set("lib") { + sources = [ + "crash_keys_win.cc", + "crash_keys_win.h", + "crash_reporter_client.cc", + "crash_reporter_client.h", + ] + + include_dirs = [ "../../../../breakpad/src" ] +} + +# Note: if you depend on this target, you need to either link in +# content.gyp:content_common, or add content/public/common/content_switches.cc +# to your sources. +# +# GYP version: components/crash.gypi:crash_component +source_set("app") { + sources = [ + "breakpad_linux_impl.h", + "breakpad_mac.h", + "breakpad_mac.mm", + "breakpad_win.cc", + "breakpad_win.h", + "hard_error_handler_win.cc", + "hard_error_handler_win.h", + ] + + if (is_android) { + libs = [ "log" ] + } + + if (is_android || is_linux) { + # Want these files on both Linux and Android. + set_sources_assignment_filter([]) + sources += [ + "breakpad_linux.cc", + "breakpad_linux.h", + ] + } + + defines = [ "CRASH_IMPLEMENTATION" ] + + deps = [ + "//base", + "//base:base_static", + ":lib", + ] + + if (is_android) { + defines += [ "CHROME_BUILD_ID=" + android_chrome_build_id ] + } + + if (is_mac) { + deps += [ "//breakpad" ] + } else if (is_win) { + deps += [ + "//sandbox", + "//breakpad:breakpad_handler", + + #'../breakpad/breakpad.gyp:breakpad_sender', TODO(GYP) + ] + } else if (is_posix && !is_ios) { + deps += [ "//breakpad:client" ] + } +} + +source_set("test_support") { + deps = [ + ":lib", + ] +} diff --git a/components/crash/content/app/DEPS b/components/crash/content/app/DEPS new file mode 100644 index 0000000..dc5e932 --- /dev/null +++ b/components/crash/content/app/DEPS @@ -0,0 +1,8 @@ +include_rules = [ + "+sandbox", + + "+content/public/common/content_descriptors.h", + "+content/public/common/result_codes.h", + "+third_party/crashpad", + "+third_party/lss/linux_syscall_support.h", +] diff --git a/components/crash/content/app/breakpad_linux.cc b/components/crash/content/app/breakpad_linux.cc new file mode 100644 index 0000000..c08a7f9 --- /dev/null +++ b/components/crash/content/app/breakpad_linux.cc @@ -0,0 +1,1760 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// For linux_syscall_support.h. This makes it safe to call embedded system +// calls when in seccomp mode. + +#include "components/crash/content/app/breakpad_linux.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/debug/crash_logging.h" +#include "base/debug/dump_without_crashing.h" +#include "base/files/file_path.h" +#include "base/linux_util.h" +#include "base/path_service.h" +#include "base/posix/eintr_wrapper.h" +#include "base/posix/global_descriptors.h" +#include "base/process/memory.h" +#include "base/strings/string_util.h" +#include "breakpad/src/client/linux/crash_generation/crash_generation_client.h" +#include "breakpad/src/client/linux/handler/exception_handler.h" +#include "breakpad/src/client/linux/minidump_writer/directory_reader.h" +#include "breakpad/src/common/linux/linux_libc_support.h" +#include "breakpad/src/common/memory.h" +#include "build/build_config.h" +#include "components/crash/content/app/breakpad_linux_impl.h" +#include "components/crash/content/app/crash_reporter_client.h" +#include "content/public/common/content_descriptors.h" + +#if defined(OS_ANDROID) +#include +#include + +#include "base/android/build_info.h" +#include "base/android/path_utils.h" +#include "base/debug/leak_annotations.h" +#endif +#include "third_party/lss/linux_syscall_support.h" + +#if defined(ADDRESS_SANITIZER) +#include // for getcontext(). +#endif + +#if defined(OS_ANDROID) +#define STAT_STRUCT struct stat +#define FSTAT_FUNC fstat +#else +#define STAT_STRUCT struct kernel_stat +#define FSTAT_FUNC sys_fstat +#endif + +// Some versions of gcc are prone to warn about unused return values. In cases +// where we either a) know the call cannot fail, or b) there is nothing we +// can do when a call fails, we mark the return code as ignored. This avoids +// spurious compiler warnings. +#define IGNORE_RET(x) do { if (x); } while (0) + +using crash_reporter::GetCrashReporterClient; +using google_breakpad::ExceptionHandler; +using google_breakpad::MinidumpDescriptor; + +namespace breakpad { + +namespace { + +#if !defined(OS_CHROMEOS) +const char kUploadURL[] = "https://clients2.google.com/cr/report"; +#endif + +bool g_is_crash_reporter_enabled = false; +uint64_t g_process_start_time = 0; +pid_t g_pid = 0; +char* g_crash_log_path = nullptr; +ExceptionHandler* g_breakpad = nullptr; + +#if defined(ADDRESS_SANITIZER) +const char* g_asan_report_str = nullptr; +#endif +#if defined(OS_ANDROID) +char* g_process_type = nullptr; +ExceptionHandler* g_microdump = nullptr; +const char* g_microdump_build_fingerprint = nullptr; +const char* g_microdump_product_info = nullptr; +#endif + +CrashKeyStorage* g_crash_keys = nullptr; + +// Writes the value |v| as 16 hex characters to the memory pointed at by +// |output|. +void write_uint64_hex(char* output, uint64_t v) { + static const char hextable[] = "0123456789abcdef"; + + for (int i = 15; i >= 0; --i) { + output[i] = hextable[v & 15]; + v >>= 4; + } +} + +// The following helper functions are for calculating uptime. + +// Converts a struct timeval to milliseconds. +uint64_t timeval_to_ms(struct timeval *tv) { + uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t. + ret *= 1000; + ret += tv->tv_usec / 1000; + return ret; +} + +// Converts a struct timeval to milliseconds. +uint64_t kernel_timeval_to_ms(struct kernel_timeval *tv) { + uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t. + ret *= 1000; + ret += tv->tv_usec / 1000; + return ret; +} + +// String buffer size to use to convert a uint64_t to string. +const size_t kUint64StringSize = 21; + +void SetProcessStartTime() { + // Set the base process start time value. + struct timeval tv; + if (!gettimeofday(&tv, nullptr)) + g_process_start_time = timeval_to_ms(&tv); + else + g_process_start_time = 0; +} + +// uint64_t version of my_int_len() from +// breakpad/src/common/linux/linux_libc_support.h. Return the length of the +// given, non-negative integer when expressed in base 10. +unsigned my_uint64_len(uint64_t i) { + if (!i) + return 1; + + unsigned len = 0; + while (i) { + len++; + i /= 10; + } + + return len; +} + +// uint64_t version of my_uitos() from +// breakpad/src/common/linux/linux_libc_support.h. Convert a non-negative +// integer to a string (not null-terminated). +void my_uint64tos(char* output, uint64_t i, unsigned i_len) { + for (unsigned index = i_len; index; --index, i /= 10) + output[index - 1] = '0' + (i % 10); +} + +#if !defined(OS_CHROMEOS) +bool my_isxdigit(char c) { + return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); +} +#endif + +size_t LengthWithoutTrailingSpaces(const char* str, size_t len) { + while (len > 0 && str[len - 1] == ' ') { + len--; + } + return len; +} + +void SetClientIdFromCommandLine(const base::CommandLine& command_line) { + // Get the guid from the command line switch. + std::string switch_value = + command_line.GetSwitchValueASCII(switches::kEnableCrashReporter); + GetCrashReporterClient()->SetCrashReporterClientIdFromGUID(switch_value); +} + +// MIME substrings. +#if defined(OS_CHROMEOS) +const char g_sep[] = ":"; +#endif +const char g_rn[] = "\r\n"; +const char g_form_data_msg[] = "Content-Disposition: form-data; name=\""; +const char g_quote_msg[] = "\""; +const char g_dashdash_msg[] = "--"; +const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\""; +#if defined(ADDRESS_SANITIZER) +const char g_log_msg[] = "upload_file_log\"; filename=\"log\""; +#endif +const char g_content_type_msg[] = "Content-Type: application/octet-stream"; + +// MimeWriter manages an iovec for writing MIMEs to a file. +class MimeWriter { + public: + static const int kIovCapacity = 30; + static const size_t kMaxCrashChunkSize = 64; + + MimeWriter(int fd, const char* const mime_boundary); + ~MimeWriter(); + + // Append boundary. + virtual void AddBoundary(); + + // Append end of file boundary. + virtual void AddEnd(); + + // Append key/value pair with specified sizes. + virtual void AddPairData(const char* msg_type, + size_t msg_type_size, + const char* msg_data, + size_t msg_data_size); + + // Append key/value pair. + void AddPairString(const char* msg_type, + const char* msg_data) { + AddPairData(msg_type, my_strlen(msg_type), msg_data, my_strlen(msg_data)); + } + + // Append key/value pair, splitting value into chunks no larger than + // |chunk_size|. |chunk_size| cannot be greater than |kMaxCrashChunkSize|. + // The msg_type string will have a counter suffix to distinguish each chunk. + virtual void AddPairDataInChunks(const char* msg_type, + size_t msg_type_size, + const char* msg_data, + size_t msg_data_size, + size_t chunk_size, + bool strip_trailing_spaces); + + // Add binary file contents to be uploaded with the specified filename. + virtual void AddFileContents(const char* filename_msg, + uint8_t* file_data, + size_t file_size); + + // Flush any pending iovecs to the output file. + void Flush() { + IGNORE_RET(sys_writev(fd_, iov_, iov_index_)); + iov_index_ = 0; + } + + protected: + void AddItem(const void* base, size_t size); + // Minor performance trade-off for easier-to-maintain code. + void AddString(const char* str) { + AddItem(str, my_strlen(str)); + } + void AddItemWithoutTrailingSpaces(const void* base, size_t size); + + struct kernel_iovec iov_[kIovCapacity]; + int iov_index_; + + // Output file descriptor. + int fd_; + + const char* const mime_boundary_; + + private: + DISALLOW_COPY_AND_ASSIGN(MimeWriter); +}; + +MimeWriter::MimeWriter(int fd, const char* const mime_boundary) + : iov_index_(0), + fd_(fd), + mime_boundary_(mime_boundary) { +} + +MimeWriter::~MimeWriter() { +} + +void MimeWriter::AddBoundary() { + AddString(mime_boundary_); + AddString(g_rn); +} + +void MimeWriter::AddEnd() { + AddString(mime_boundary_); + AddString(g_dashdash_msg); + AddString(g_rn); +} + +void MimeWriter::AddPairData(const char* msg_type, + size_t msg_type_size, + const char* msg_data, + size_t msg_data_size) { + AddString(g_form_data_msg); + AddItem(msg_type, msg_type_size); + AddString(g_quote_msg); + AddString(g_rn); + AddString(g_rn); + AddItem(msg_data, msg_data_size); + AddString(g_rn); +} + +void MimeWriter::AddPairDataInChunks(const char* msg_type, + size_t msg_type_size, + const char* msg_data, + size_t msg_data_size, + size_t chunk_size, + bool strip_trailing_spaces) { + if (chunk_size > kMaxCrashChunkSize) + return; + + unsigned i = 0; + size_t done = 0, msg_length = msg_data_size; + + while (msg_length) { + char num[kUint64StringSize]; + const unsigned num_len = my_uint_len(++i); + my_uitos(num, i, num_len); + + size_t chunk_len = std::min(chunk_size, msg_length); + + AddString(g_form_data_msg); + AddItem(msg_type, msg_type_size); + AddItem(num, num_len); + AddString(g_quote_msg); + AddString(g_rn); + AddString(g_rn); + if (strip_trailing_spaces) { + AddItemWithoutTrailingSpaces(msg_data + done, chunk_len); + } else { + AddItem(msg_data + done, chunk_len); + } + AddString(g_rn); + AddBoundary(); + Flush(); + + done += chunk_len; + msg_length -= chunk_len; + } +} + +void MimeWriter::AddFileContents(const char* filename_msg, uint8_t* file_data, + size_t file_size) { + AddString(g_form_data_msg); + AddString(filename_msg); + AddString(g_rn); + AddString(g_content_type_msg); + AddString(g_rn); + AddString(g_rn); + AddItem(file_data, file_size); + AddString(g_rn); +} + +void MimeWriter::AddItem(const void* base, size_t size) { + // Check if the iovec is full and needs to be flushed to output file. + if (iov_index_ == kIovCapacity) { + Flush(); + } + iov_[iov_index_].iov_base = const_cast(base); + iov_[iov_index_].iov_len = size; + ++iov_index_; +} + +void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) { + AddItem(base, LengthWithoutTrailingSpaces(static_cast(base), + size)); +} + +#if defined(OS_CHROMEOS) +// This subclass is used on Chromium OS to report crashes in a format easy for +// the central crash reporting facility to understand. +// Format is :: +class CrashReporterWriter : public MimeWriter { + public: + explicit CrashReporterWriter(int fd); + + void AddBoundary() override; + + void AddEnd() override; + + void AddPairData(const char* msg_type, + size_t msg_type_size, + const char* msg_data, + size_t msg_data_size) override; + + void AddPairDataInChunks(const char* msg_type, + size_t msg_type_size, + const char* msg_data, + size_t msg_data_size, + size_t chunk_size, + bool strip_trailing_spaces) override; + + void AddFileContents(const char* filename_msg, + uint8_t* file_data, + size_t file_size) override; + + private: + DISALLOW_COPY_AND_ASSIGN(CrashReporterWriter); +}; + + +CrashReporterWriter::CrashReporterWriter(int fd) : MimeWriter(fd, "") {} + +// No-ops. +void CrashReporterWriter::AddBoundary() {} +void CrashReporterWriter::AddEnd() {} + +void CrashReporterWriter::AddPairData(const char* msg_type, + size_t msg_type_size, + const char* msg_data, + size_t msg_data_size) { + char data[kUint64StringSize]; + const unsigned data_len = my_uint_len(msg_data_size); + my_uitos(data, msg_data_size, data_len); + + AddItem(msg_type, msg_type_size); + AddString(g_sep); + AddItem(data, data_len); + AddString(g_sep); + AddItem(msg_data, msg_data_size); + Flush(); +} + +void CrashReporterWriter::AddPairDataInChunks(const char* msg_type, + size_t msg_type_size, + const char* msg_data, + size_t msg_data_size, + size_t chunk_size, + bool strip_trailing_spaces) { + if (chunk_size > kMaxCrashChunkSize) + return; + + unsigned i = 0; + size_t done = 0; + size_t msg_length = msg_data_size; + + while (msg_length) { + char num[kUint64StringSize]; + const unsigned num_len = my_uint_len(++i); + my_uitos(num, i, num_len); + + size_t chunk_len = std::min(chunk_size, msg_length); + + size_t write_len = chunk_len; + if (strip_trailing_spaces) { + // Take care of this here because we need to know the exact length of + // what is going to be written. + write_len = LengthWithoutTrailingSpaces(msg_data + done, write_len); + } + + char data[kUint64StringSize]; + const unsigned data_len = my_uint_len(write_len); + my_uitos(data, write_len, data_len); + + AddItem(msg_type, msg_type_size); + AddItem(num, num_len); + AddString(g_sep); + AddItem(data, data_len); + AddString(g_sep); + AddItem(msg_data + done, write_len); + Flush(); + + done += chunk_len; + msg_length -= chunk_len; + } +} + +void CrashReporterWriter::AddFileContents(const char* filename_msg, + uint8_t* file_data, + size_t file_size) { + char data[kUint64StringSize]; + const unsigned data_len = my_uint_len(file_size); + my_uitos(data, file_size, data_len); + + AddString(filename_msg); + AddString(g_sep); + AddItem(data, data_len); + AddString(g_sep); + AddItem(file_data, file_size); + Flush(); +} +#endif // defined(OS_CHROMEOS) + +void DumpProcess() { + if (g_breakpad) + g_breakpad->WriteMinidump(); + +#if defined(OS_ANDROID) + // If microdumps are enabled write also a microdump on the system log. + if (g_microdump) + g_microdump->WriteMinidump(); +#endif +} + +#if defined(OS_ANDROID) +const char kGoogleBreakpad[] = "google-breakpad"; +#endif + +size_t WriteLog(const char* buf, size_t nbytes) { +#if defined(OS_ANDROID) + return __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, buf); +#else + return sys_write(2, buf, nbytes); +#endif +} + +size_t WriteNewline() { + return WriteLog("\n", 1); +} + +#if defined(OS_ANDROID) +void AndroidLogWriteHorizontalRule() { + __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, + "### ### ### ### ### ### ### ### ### ### ### ### ###"); +} + +// Android's native crash handler outputs a diagnostic tombstone to the device +// log. By returning false from the HandlerCallbacks, breakpad will reinstall +// the previous (i.e. native) signal handlers before returning from its own +// handler. A Chrome build fingerprint is written to the log, so that the +// specific build of Chrome and the location of the archived Chrome symbols can +// be determined directly from it. +bool FinalizeCrashDoneAndroid(bool is_browser_process) { + base::android::BuildInfo* android_build_info = + base::android::BuildInfo::GetInstance(); + + AndroidLogWriteHorizontalRule(); + __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, + "Chrome build fingerprint:"); + __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, + android_build_info->package_version_name()); + __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, + android_build_info->package_version_code()); + __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, + CHROME_BUILD_ID); + AndroidLogWriteHorizontalRule(); + + if (!is_browser_process && + android_build_info->sdk_int() >= 18 && + my_strcmp(android_build_info->build_type(), "eng") != 0 && + my_strcmp(android_build_info->build_type(), "userdebug") != 0) { + // On JB MR2 and later, the system crash handler displays a dialog. For + // renderer crashes, this is a bad user experience and so this is disabled + // for user builds of Android. + // TODO(cjhopman): There should be some way to recover the crash stack from + // non-uploading user clients. See http://crbug.com/273706. + __android_log_write(ANDROID_LOG_WARN, + kGoogleBreakpad, + "Tombstones are disabled on JB MR2+ user builds."); + AndroidLogWriteHorizontalRule(); + return true; + } + return false; +} +#endif + +bool CrashDone(const MinidumpDescriptor& minidump, + const bool upload, + const bool succeeded) { + // WARNING: this code runs in a compromised context. It may not call into + // libc nor allocate memory normally. + if (!succeeded) { + const char msg[] = "Failed to generate minidump."; + WriteLog(msg, sizeof(msg) - 1); + return false; + } + + DCHECK(!minidump.IsFD()); + + BreakpadInfo info = {0}; + info.filename = minidump.path(); + info.fd = minidump.fd(); +#if defined(ADDRESS_SANITIZER) + google_breakpad::PageAllocator allocator; + const size_t log_path_len = my_strlen(minidump.path()); + char* log_path = reinterpret_cast(allocator.Alloc(log_path_len + 1)); + my_memcpy(log_path, minidump.path(), log_path_len); + my_memcpy(log_path + log_path_len - 4, ".log", 4); + log_path[log_path_len] = '\0'; + info.log_filename = log_path; +#endif + info.process_type = "browser"; + info.process_type_length = 7; + info.distro = base::g_linux_distro; + info.distro_length = my_strlen(base::g_linux_distro); + info.upload = upload; + info.process_start_time = g_process_start_time; + info.oom_size = base::g_oom_size; + info.pid = g_pid; + info.crash_keys = g_crash_keys; + HandleCrashDump(info); +#if defined(OS_ANDROID) + return FinalizeCrashDoneAndroid(true /* is_browser_process */); +#else + return true; +#endif +} + +// Wrapper function, do not add more code here. +bool CrashDoneNoUpload(const MinidumpDescriptor& minidump, + void* context, + bool succeeded) { + return CrashDone(minidump, false, succeeded); +} + +#if !defined(OS_ANDROID) +// Wrapper function, do not add more code here. +bool CrashDoneUpload(const MinidumpDescriptor& minidump, + void* context, + bool succeeded) { + return CrashDone(minidump, true, succeeded); +} +#endif + +#if defined(ADDRESS_SANITIZER) +extern "C" +void __asan_set_error_report_callback(void (*cb)(const char*)); + +extern "C" +void AsanLinuxBreakpadCallback(const char* report) { + g_asan_report_str = report; + // Send minidump here. + g_breakpad->SimulateSignalDelivery(SIGKILL); +} +#endif + +void EnableCrashDumping(bool unattended) { + g_is_crash_reporter_enabled = true; + + base::FilePath tmp_path("/tmp"); + PathService::Get(base::DIR_TEMP, &tmp_path); + + base::FilePath dumps_path(tmp_path); + if (GetCrashReporterClient()->GetCrashDumpLocation(&dumps_path)) { + base::FilePath logfile = + dumps_path.Append(GetCrashReporterClient()->GetReporterLogFilename()); + std::string logfile_str = logfile.value(); + const size_t crash_log_path_len = logfile_str.size() + 1; + g_crash_log_path = new char[crash_log_path_len]; + strncpy(g_crash_log_path, logfile_str.c_str(), crash_log_path_len); + } + DCHECK(!g_breakpad); + MinidumpDescriptor minidump_descriptor(dumps_path.value()); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kFullMemoryCrashReport)) { + minidump_descriptor.set_size_limit(-1); // unlimited. + } else { + minidump_descriptor.set_size_limit(kMaxMinidumpFileSize); + } +#if defined(OS_ANDROID) + unattended = true; // Android never uploads directly. +#endif + if (unattended) { + g_breakpad = new ExceptionHandler( + minidump_descriptor, + nullptr, + CrashDoneNoUpload, + nullptr, + true, // Install handlers. + -1); // Server file descriptor. -1 for in-process. + return; + } + +#if !defined(OS_ANDROID) + // Attended mode + g_breakpad = new ExceptionHandler( + minidump_descriptor, + nullptr, + CrashDoneUpload, + nullptr, + true, // Install handlers. + -1); // Server file descriptor. -1 for in-process. +#endif +} + +#if defined(OS_ANDROID) +bool MicrodumpCrashDone(const MinidumpDescriptor& minidump, + void* context, + bool succeeded) { + // WARNING: this code runs in a compromised context. It may not call into + // libc nor allocate memory normally. + if (!succeeded) { + static const char msg[] = "Microdump crash handler failed.\n"; + WriteLog(msg, sizeof(msg) - 1); + return false; + } + + const bool is_browser_process = (context != nullptr); + return FinalizeCrashDoneAndroid(is_browser_process); + } + +// The microdump handler does NOT upload anything. It just dumps out on the +// system console (logcat) a restricted and serialized variant of a minidump. +// See crbug.com/410294 for more details. +void InitMicrodumpCrashHandlerIfNecessary(const std::string& process_type) { + if (!GetCrashReporterClient()->ShouldEnableBreakpadMicrodumps()) + return; + + VLOG(1) << "Enabling microdumps crash handler (process_type:" + << process_type << ")"; + + // The exception handler runs in a compromised context and cannot use c_str() + // as that would require the heap. Therefore, we have to guarantee that the + // build fingerprint and product info pointers are always valid. + const char* product_name = nullptr; + const char* product_version = nullptr; + GetCrashReporterClient()->GetProductNameAndVersion(&product_name, + &product_version); + + MinidumpDescriptor descriptor(MinidumpDescriptor::kMicrodumpOnConsole); + + if (product_name && product_version) { + g_microdump_product_info = strdup( + (product_name + std::string(":") + product_version).c_str()); + ANNOTATE_LEAKING_OBJECT_PTR(g_microdump_product_info); + descriptor.SetMicrodumpProductInfo(g_microdump_product_info); + } + + const char* android_build_fp = + base::android::BuildInfo::GetInstance()->android_build_fp(); + if (android_build_fp) { + g_microdump_build_fingerprint = strdup(android_build_fp); + ANNOTATE_LEAKING_OBJECT_PTR(g_microdump_build_fingerprint); + descriptor.SetMicrodumpBuildFingerprint(g_microdump_build_fingerprint); + } + + DCHECK(!g_microdump); + bool is_browser_process = process_type.empty() || process_type == "webview"; + g_microdump = new ExceptionHandler( + descriptor, + nullptr, + MicrodumpCrashDone, + reinterpret_cast(is_browser_process), + true, // Install handlers. + -1); // Server file descriptor. -1 for in-process. + return; +} + +bool CrashDoneInProcessNoUpload( + const google_breakpad::MinidumpDescriptor& descriptor, + void* context, + const bool succeeded) { + // WARNING: this code runs in a compromised context. It may not call into + // libc nor allocate memory normally. + if (!succeeded) { + static const char msg[] = "Crash dump generation failed.\n"; + WriteLog(msg, sizeof(msg) - 1); + return false; + } + + // Start constructing the message to send to the browser. + BreakpadInfo info = {0}; + info.filename = nullptr; + info.fd = descriptor.fd(); + info.process_type = g_process_type; + info.process_type_length = my_strlen(g_process_type); + info.distro = nullptr; + info.distro_length = 0; + info.upload = false; + info.process_start_time = g_process_start_time; + info.pid = g_pid; + info.crash_keys = g_crash_keys; + HandleCrashDump(info); + return FinalizeCrashDoneAndroid(false /* is_browser_process */); +} + +void EnableNonBrowserCrashDumping(const std::string& process_type, + int minidump_fd) { + // This will guarantee that the BuildInfo has been initialized and subsequent + // calls will not require memory allocation. + base::android::BuildInfo::GetInstance(); + SetClientIdFromCommandLine(*base::CommandLine::ForCurrentProcess()); + + // On Android, the current sandboxing uses process isolation, in which the + // child process runs with a different UID. That breaks the normal crash + // reporting where the browser process generates the minidump by inspecting + // the child process. This is because the browser process now does not have + // the permission to access the states of the child process (as it has a + // different UID). + // TODO(jcivelli): http://b/issue?id=6776356 we should use a watchdog + // process forked from the renderer process that generates the minidump. + if (minidump_fd == -1) { + LOG(ERROR) << "Minidump file descriptor not found, crash reporting will " + " not work."; + return; + } + SetProcessStartTime(); + g_pid = getpid(); + + g_is_crash_reporter_enabled = true; + // Save the process type (it is leaked). + const size_t process_type_len = process_type.size() + 1; + g_process_type = new char[process_type_len]; + strncpy(g_process_type, process_type.c_str(), process_type_len); + new google_breakpad::ExceptionHandler(MinidumpDescriptor(minidump_fd), + nullptr, CrashDoneInProcessNoUpload, nullptr, true, -1); +} +#else +// Non-Browser = Extension, Gpu, Plugins, Ppapi and Renderer +class NonBrowserCrashHandler : public google_breakpad::CrashGenerationClient { + public: + NonBrowserCrashHandler() + : server_fd_(base::GlobalDescriptors::GetInstance()->Get( + kCrashDumpSignal)) { + } + + ~NonBrowserCrashHandler() override {} + + bool RequestDump(const void* crash_context, + size_t crash_context_size) override { + int fds[2] = { -1, -1 }; + if (sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + static const char msg[] = "Failed to create socket for crash dumping.\n"; + WriteLog(msg, sizeof(msg) - 1); + return false; + } + + // Start constructing the message to send to the browser. + char b; // Dummy variable for sys_read below. + const char* b_addr = &b; // Get the address of |b| so we can create the + // expected /proc/[pid]/syscall content in the + // browser to convert namespace tids. + + // The length of the control message: + static const unsigned kControlMsgSize = sizeof(int); + static const unsigned kControlMsgSpaceSize = CMSG_SPACE(kControlMsgSize); + static const unsigned kControlMsgLenSize = CMSG_LEN(kControlMsgSize); + + struct kernel_msghdr msg; + my_memset(&msg, 0, sizeof(struct kernel_msghdr)); + struct kernel_iovec iov[kCrashIovSize]; + iov[0].iov_base = const_cast(crash_context); + iov[0].iov_len = crash_context_size; + iov[1].iov_base = &b_addr; + iov[1].iov_len = sizeof(b_addr); + iov[2].iov_base = &fds[0]; + iov[2].iov_len = sizeof(fds[0]); + iov[3].iov_base = &g_process_start_time; + iov[3].iov_len = sizeof(g_process_start_time); + iov[4].iov_base = &base::g_oom_size; + iov[4].iov_len = sizeof(base::g_oom_size); + google_breakpad::SerializedNonAllocatingMap* serialized_map; + iov[5].iov_len = g_crash_keys->Serialize( + const_cast( + &serialized_map)); + iov[5].iov_base = serialized_map; +#if !defined(ADDRESS_SANITIZER) + static_assert(5 == kCrashIovSize - 1, "kCrashIovSize should equal 6"); +#else + iov[6].iov_base = const_cast(g_asan_report_str); + iov[6].iov_len = kMaxAsanReportSize + 1; + static_assert(6 == kCrashIovSize - 1, "kCrashIovSize should equal 7"); +#endif + + msg.msg_iov = iov; + msg.msg_iovlen = kCrashIovSize; + char cmsg[kControlMsgSpaceSize]; + my_memset(cmsg, 0, kControlMsgSpaceSize); + msg.msg_control = cmsg; + msg.msg_controllen = sizeof(cmsg); + + struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_RIGHTS; + hdr->cmsg_len = kControlMsgLenSize; + ((int*)CMSG_DATA(hdr))[0] = fds[1]; + + if (HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)) < 0) { + static const char errmsg[] = "Failed to tell parent about crash.\n"; + WriteLog(errmsg, sizeof(errmsg) - 1); + IGNORE_RET(sys_close(fds[0])); + IGNORE_RET(sys_close(fds[1])); + return false; + } + IGNORE_RET(sys_close(fds[1])); + + if (HANDLE_EINTR(sys_read(fds[0], &b, 1)) != 1) { + static const char errmsg[] = "Parent failed to complete crash dump.\n"; + WriteLog(errmsg, sizeof(errmsg) - 1); + } + IGNORE_RET(sys_close(fds[0])); + + return true; + } + + private: + // The pipe FD to the browser process, which will handle the crash dumping. + const int server_fd_; + + DISALLOW_COPY_AND_ASSIGN(NonBrowserCrashHandler); +}; + +void EnableNonBrowserCrashDumping() { + g_is_crash_reporter_enabled = true; + // We deliberately leak this object. + DCHECK(!g_breakpad); + + g_breakpad = new ExceptionHandler( + MinidumpDescriptor("/tmp"), // Unused but needed or Breakpad will assert. + nullptr, + nullptr, + nullptr, + true, + -1); + g_breakpad->set_crash_generation_client(new NonBrowserCrashHandler()); +} +#endif // defined(OS_ANDROID) + +void SetCrashKeyValue(const base::StringPiece& key, + const base::StringPiece& value) { + g_crash_keys->SetKeyValue(key.data(), value.data()); +} + +void ClearCrashKey(const base::StringPiece& key) { + g_crash_keys->RemoveKey(key.data()); +} + +// GetCrashReporterClient() cannot call any Set methods until after +// InitCrashKeys(). +void InitCrashKeys() { + g_crash_keys = new CrashKeyStorage; + GetCrashReporterClient()->RegisterCrashKeys(); + base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValue, &ClearCrashKey); +} + +// Miscellaneous initialization functions to call after Breakpad has been +// enabled. +void PostEnableBreakpadInitialization() { + SetProcessStartTime(); + g_pid = getpid(); + + base::debug::SetDumpWithoutCrashingFunction(&DumpProcess); +#if defined(ADDRESS_SANITIZER) + // Register the callback for AddressSanitizer error reporting. + __asan_set_error_report_callback(AsanLinuxBreakpadCallback); +#endif +} + +} // namespace + +void LoadDataFromFD(google_breakpad::PageAllocator& allocator, + int fd, bool close_fd, uint8_t** file_data, size_t* size) { + STAT_STRUCT st; + if (FSTAT_FUNC(fd, &st) != 0) { + static const char msg[] = "Cannot upload crash dump: stat failed\n"; + WriteLog(msg, sizeof(msg) - 1); + if (close_fd) + IGNORE_RET(sys_close(fd)); + return; + } + + *file_data = reinterpret_cast(allocator.Alloc(st.st_size)); + if (!(*file_data)) { + static const char msg[] = "Cannot upload crash dump: cannot alloc\n"; + WriteLog(msg, sizeof(msg) - 1); + if (close_fd) + IGNORE_RET(sys_close(fd)); + return; + } + my_memset(*file_data, 0xf, st.st_size); + + *size = st.st_size; + int byte_read = sys_read(fd, *file_data, *size); + if (byte_read == -1) { + static const char msg[] = "Cannot upload crash dump: read failed\n"; + WriteLog(msg, sizeof(msg) - 1); + if (close_fd) + IGNORE_RET(sys_close(fd)); + return; + } + + if (close_fd) + IGNORE_RET(sys_close(fd)); +} + +void LoadDataFromFile(google_breakpad::PageAllocator& allocator, + const char* filename, + int* fd, uint8_t** file_data, size_t* size) { + // WARNING: this code runs in a compromised context. It may not call into + // libc nor allocate memory normally. + *fd = sys_open(filename, O_RDONLY, 0); + *size = 0; + + if (*fd < 0) { + static const char msg[] = "Cannot upload crash dump: failed to open\n"; + WriteLog(msg, sizeof(msg) - 1); + return; + } + + LoadDataFromFD(allocator, *fd, true, file_data, size); +} + +// Spawn the appropriate upload process for the current OS: +// - generic Linux invokes wget. +// - ChromeOS invokes crash_reporter. +// |dumpfile| is the path to the dump data file. +// |mime_boundary| is only used on Linux. +// |exe_buf| is only used on CrOS and is the crashing process' name. +void ExecUploadProcessOrTerminate(const BreakpadInfo& info, + const char* dumpfile, + const char* mime_boundary, + const char* exe_buf, + google_breakpad::PageAllocator* allocator) { +#if defined(OS_CHROMEOS) + // CrOS uses crash_reporter instead of wget to report crashes, + // it needs to know where the crash dump lives and the pid and uid of the + // crashing process. + static const char kCrashReporterBinary[] = "/sbin/crash_reporter"; + + char pid_buf[kUint64StringSize]; + uint64_t pid_str_length = my_uint64_len(info.pid); + my_uint64tos(pid_buf, info.pid, pid_str_length); + pid_buf[pid_str_length] = '\0'; + + char uid_buf[kUint64StringSize]; + uid_t uid = geteuid(); + uint64_t uid_str_length = my_uint64_len(uid); + my_uint64tos(uid_buf, uid, uid_str_length); + uid_buf[uid_str_length] = '\0'; + + const char kChromeFlag[] = "--chrome="; + size_t buf_len = my_strlen(dumpfile) + sizeof(kChromeFlag); + char* chrome_flag = reinterpret_cast(allocator->Alloc(buf_len)); + chrome_flag[0] = '\0'; + my_strlcat(chrome_flag, kChromeFlag, buf_len); + my_strlcat(chrome_flag, dumpfile, buf_len); + + const char kPidFlag[] = "--pid="; + buf_len = my_strlen(pid_buf) + sizeof(kPidFlag); + char* pid_flag = reinterpret_cast(allocator->Alloc(buf_len)); + pid_flag[0] = '\0'; + my_strlcat(pid_flag, kPidFlag, buf_len); + my_strlcat(pid_flag, pid_buf, buf_len); + + const char kUidFlag[] = "--uid="; + buf_len = my_strlen(uid_buf) + sizeof(kUidFlag); + char* uid_flag = reinterpret_cast(allocator->Alloc(buf_len)); + uid_flag[0] = '\0'; + my_strlcat(uid_flag, kUidFlag, buf_len); + my_strlcat(uid_flag, uid_buf, buf_len); + + const char kExeBuf[] = "--exe="; + buf_len = my_strlen(exe_buf) + sizeof(kExeBuf); + char* exe_flag = reinterpret_cast(allocator->Alloc(buf_len)); + exe_flag[0] = '\0'; + my_strlcat(exe_flag, kExeBuf, buf_len); + my_strlcat(exe_flag, exe_buf, buf_len); + + const char* args[] = { + kCrashReporterBinary, + chrome_flag, + pid_flag, + uid_flag, + exe_flag, + nullptr, + }; + static const char msg[] = "Cannot upload crash dump: cannot exec " + "/sbin/crash_reporter\n"; +#else + // Compress |dumpfile| with gzip. + const pid_t gzip_child = sys_fork(); + if (gzip_child < 0) { + static const char msg[] = "sys_fork() for gzip process failed.\n"; + WriteLog(msg, sizeof(msg) - 1); + sys__exit(1); + } + if (!gzip_child) { + // gzip process. + const char* args[] = { + "/bin/gzip", + "-f", // Do not prompt to verify before overwriting. + dumpfile, + nullptr, + }; + execve(args[0], const_cast(args), environ); + static const char msg[] = "Cannot exec gzip.\n"; + WriteLog(msg, sizeof(msg) - 1); + sys__exit(1); + } + // Wait for gzip process. + int status = 0; + if (sys_waitpid(gzip_child, &status, 0) != gzip_child || + !WIFEXITED(status) || WEXITSTATUS(status) != 0) { + static const char msg[] = "sys_waitpid() for gzip process failed.\n"; + WriteLog(msg, sizeof(msg) - 1); + sys_kill(gzip_child, SIGKILL); + sys__exit(1); + } + + static const char kGzipExtension[] = ".gz"; + const size_t gzip_file_size = my_strlen(dumpfile) + sizeof(kGzipExtension); + char* const gzip_file = reinterpret_cast(allocator->Alloc( + gzip_file_size)); + my_strlcpy(gzip_file, dumpfile, gzip_file_size); + my_strlcat(gzip_file, kGzipExtension, gzip_file_size); + + // Rename |gzip_file| to |dumpfile| (the original file was deleted by gzip). + if (rename(gzip_file, dumpfile)) { + static const char msg[] = "Failed to rename gzipped file.\n"; + WriteLog(msg, sizeof(msg) - 1); + sys__exit(1); + } + + // The --header argument to wget looks like: + // --header=Content-Encoding: gzip + // --header=Content-Type: multipart/form-data; boundary=XYZ + // where the boundary has two fewer leading '-' chars + static const char header_content_encoding[] = + "--header=Content-Encoding: gzip"; + static const char header_msg[] = + "--header=Content-Type: multipart/form-data; boundary="; + const size_t header_content_type_size = + sizeof(header_msg) - 1 + my_strlen(mime_boundary) - 2 + 1; + char* const header_content_type = reinterpret_cast(allocator->Alloc( + header_content_type_size)); + my_strlcpy(header_content_type, header_msg, header_content_type_size); + my_strlcat(header_content_type, mime_boundary + 2, header_content_type_size); + + // The --post-file argument to wget looks like: + // --post-file=/tmp/... + static const char post_file_msg[] = "--post-file="; + const size_t post_file_size = + sizeof(post_file_msg) - 1 + my_strlen(dumpfile) + 1; + char* const post_file = reinterpret_cast(allocator->Alloc( + post_file_size)); + my_strlcpy(post_file, post_file_msg, post_file_size); + my_strlcat(post_file, dumpfile, post_file_size); + + static const char kWgetBinary[] = "/usr/bin/wget"; + const char* args[] = { + kWgetBinary, + header_content_encoding, + header_content_type, + post_file, + kUploadURL, + "--timeout=10", // Set a timeout so we don't hang forever. + "--tries=1", // Don't retry if the upload fails. + "-O", // output reply to fd 3 + "/dev/fd/3", + nullptr, + }; + static const char msg[] = "Cannot upload crash dump: cannot exec " + "/usr/bin/wget\n"; +#endif + execve(args[0], const_cast(args), environ); + WriteLog(msg, sizeof(msg) - 1); + sys__exit(1); +} + +// Runs in the helper process to wait for the upload process running +// ExecUploadProcessOrTerminate() to finish. Returns the number of bytes written +// to |fd| and save the written contents to |buf|. +// |buf| needs to be big enough to hold |bytes_to_read| + 1 characters. +size_t WaitForCrashReportUploadProcess(int fd, size_t bytes_to_read, + char* buf) { + size_t bytes_read = 0; + + // Upload should finish in about 10 seconds. Add a few more 500 ms + // internals to account for process startup time. + for (size_t wait_count = 0; wait_count < 24; ++wait_count) { + struct kernel_pollfd poll_fd; + poll_fd.fd = fd; + poll_fd.events = POLLIN | POLLPRI | POLLERR; + int ret = sys_poll(&poll_fd, 1, 500); + if (ret < 0) { + // Error + break; + } else if (ret > 0) { + // There is data to read. + ssize_t len = HANDLE_EINTR( + sys_read(fd, buf + bytes_read, bytes_to_read - bytes_read)); + if (len < 0) + break; + bytes_read += len; + if (bytes_read == bytes_to_read) + break; + } + // |ret| == 0 -> timed out, continue waiting. + // or |bytes_read| < |bytes_to_read| still, keep reading. + } + buf[bytes_to_read] = 0; // Always NUL terminate the buffer. + return bytes_read; +} + +// |buf| should be |expected_len| + 1 characters in size and nullptr terminated. +bool IsValidCrashReportId(const char* buf, size_t bytes_read, + size_t expected_len) { + if (bytes_read != expected_len) + return false; +#if defined(OS_CHROMEOS) + return my_strcmp(buf, "_sys_cr_finished") == 0; +#else + for (size_t i = 0; i < bytes_read; ++i) { + if (!my_isxdigit(buf[i])) + return false; + } + return true; +#endif +} + +// |buf| should be |expected_len| + 1 characters in size and nullptr terminated. +void HandleCrashReportId(const char* buf, size_t bytes_read, + size_t expected_len) { + WriteNewline(); + if (!IsValidCrashReportId(buf, bytes_read, expected_len)) { +#if defined(OS_CHROMEOS) + static const char msg[] = + "System crash-reporter failed to process crash report."; +#else + static const char msg[] = "Failed to get crash dump id."; +#endif + WriteLog(msg, sizeof(msg) - 1); + WriteNewline(); + + static const char id_msg[] = "Report Id: "; + WriteLog(id_msg, sizeof(id_msg) - 1); + WriteLog(buf, bytes_read); + WriteNewline(); + return; + } + +#if defined(OS_CHROMEOS) + static const char msg[] = "Crash dump received by crash_reporter\n"; + WriteLog(msg, sizeof(msg) - 1); +#else + // Write crash dump id to stderr. + static const char msg[] = "Crash dump id: "; + WriteLog(msg, sizeof(msg) - 1); + WriteLog(buf, my_strlen(buf)); + WriteNewline(); + + // Write crash dump id to crash log as: seconds_since_epoch,crash_id + struct kernel_timeval tv; + if (g_crash_log_path && !sys_gettimeofday(&tv, nullptr)) { + uint64_t time = kernel_timeval_to_ms(&tv) / 1000; + char time_str[kUint64StringSize]; + const unsigned time_len = my_uint64_len(time); + my_uint64tos(time_str, time, time_len); + + const int kLogOpenFlags = O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC; + int log_fd = sys_open(g_crash_log_path, kLogOpenFlags, 0600); + if (log_fd > 0) { + sys_write(log_fd, time_str, time_len); + sys_write(log_fd, ",", 1); + sys_write(log_fd, buf, my_strlen(buf)); + sys_write(log_fd, "\n", 1); + IGNORE_RET(sys_close(log_fd)); + } + } +#endif +} + +#if defined(OS_CHROMEOS) +const char* GetCrashingProcessName(const BreakpadInfo& info, + google_breakpad::PageAllocator* allocator) { + // Symlink to process binary is at /proc/###/exe. + char linkpath[kUint64StringSize + sizeof("/proc/") + sizeof("/exe")] = + "/proc/"; + uint64_t pid_value_len = my_uint64_len(info.pid); + my_uint64tos(linkpath + sizeof("/proc/") - 1, info.pid, pid_value_len); + linkpath[sizeof("/proc/") - 1 + pid_value_len] = '\0'; + my_strlcat(linkpath, "/exe", sizeof(linkpath)); + + const int kMaxSize = 4096; + char* link = reinterpret_cast(allocator->Alloc(kMaxSize)); + if (link) { + ssize_t size = readlink(linkpath, link, kMaxSize); + if (size < kMaxSize && size > 0) { + // readlink(2) doesn't add a terminating NUL, so do it now. + link[size] = '\0'; + + const char* name = my_strrchr(link, '/'); + if (name) + return name + 1; + return link; + } + } + // Either way too long, or a read error. + return "chrome-crash-unknown-process"; +} +#endif + +void HandleCrashDump(const BreakpadInfo& info) { + int dumpfd; + bool keep_fd = false; + size_t dump_size; + uint8_t* dump_data; + google_breakpad::PageAllocator allocator; + const char* exe_buf = nullptr; + + if (GetCrashReporterClient()->HandleCrashDump(info.filename)) { + return; + } + +#if defined(OS_CHROMEOS) + // Grab the crashing process' name now, when it should still be available. + // If we try to do this later in our grandchild the crashing process has + // already terminated. + exe_buf = GetCrashingProcessName(info, &allocator); +#endif + + if (info.fd != -1) { + // Dump is provided with an open FD. + keep_fd = true; + dumpfd = info.fd; + + // The FD is pointing to the end of the file. + // Rewind, we'll read the data next. + if (lseek(dumpfd, 0, SEEK_SET) == -1) { + static const char msg[] = "Cannot upload crash dump: failed to " + "reposition minidump FD\n"; + WriteLog(msg, sizeof(msg) - 1); + IGNORE_RET(sys_close(dumpfd)); + return; + } + LoadDataFromFD(allocator, info.fd, false, &dump_data, &dump_size); + } else { + // Dump is provided with a path. + keep_fd = false; + LoadDataFromFile(allocator, info.filename, &dumpfd, &dump_data, &dump_size); + } + + // TODO(jcivelli): make log work when using FDs. +#if defined(ADDRESS_SANITIZER) + int logfd; + size_t log_size; + uint8_t* log_data; + // Load the AddressSanitizer log into log_data. + LoadDataFromFile(allocator, info.log_filename, &logfd, &log_data, &log_size); +#endif + + // We need to build a MIME block for uploading to the server. Since we are + // going to fork and run wget, it needs to be written to a temp file. + const int ufd = sys_open("/dev/urandom", O_RDONLY, 0); + if (ufd < 0) { + static const char msg[] = "Cannot upload crash dump because /dev/urandom" + " is missing\n"; + WriteLog(msg, sizeof(msg) - 1); + return; + } + + static const char temp_file_template[] = + "/tmp/chromium-upload-XXXXXXXXXXXXXXXX"; + char temp_file[sizeof(temp_file_template)]; + int temp_file_fd = -1; + if (keep_fd) { + temp_file_fd = dumpfd; + // Rewind the destination, we are going to overwrite it. + if (lseek(dumpfd, 0, SEEK_SET) == -1) { + static const char msg[] = "Cannot upload crash dump: failed to " + "reposition minidump FD (2)\n"; + WriteLog(msg, sizeof(msg) - 1); + IGNORE_RET(sys_close(dumpfd)); + return; + } + } else { + if (info.upload) { + my_memcpy(temp_file, temp_file_template, sizeof(temp_file_template)); + + for (unsigned i = 0; i < 10; ++i) { + uint64_t t; + sys_read(ufd, &t, sizeof(t)); + write_uint64_hex(temp_file + sizeof(temp_file) - (16 + 1), t); + + temp_file_fd = sys_open(temp_file, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (temp_file_fd >= 0) + break; + } + + if (temp_file_fd < 0) { + static const char msg[] = "Failed to create temporary file in /tmp: " + "cannot upload crash dump\n"; + WriteLog(msg, sizeof(msg) - 1); + IGNORE_RET(sys_close(ufd)); + return; + } + } else { + temp_file_fd = sys_open(info.filename, O_WRONLY, 0600); + if (temp_file_fd < 0) { + static const char msg[] = "Failed to save crash dump: failed to open\n"; + WriteLog(msg, sizeof(msg) - 1); + IGNORE_RET(sys_close(ufd)); + return; + } + } + } + + // The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL. + char mime_boundary[28 + 16 + 1]; + my_memset(mime_boundary, '-', 28); + uint64_t boundary_rand; + sys_read(ufd, &boundary_rand, sizeof(boundary_rand)); + write_uint64_hex(mime_boundary + 28, boundary_rand); + mime_boundary[28 + 16] = 0; + IGNORE_RET(sys_close(ufd)); + + // The MIME block looks like this: + // BOUNDARY \r\n + // Content-Disposition: form-data; name="prod" \r\n \r\n + // Chrome_Linux \r\n + // BOUNDARY \r\n + // Content-Disposition: form-data; name="ver" \r\n \r\n + // 1.2.3.4 \r\n + // BOUNDARY \r\n + // + // zero or one: + // Content-Disposition: form-data; name="ptime" \r\n \r\n + // abcdef \r\n + // BOUNDARY \r\n + // + // zero or one: + // Content-Disposition: form-data; name="ptype" \r\n \r\n + // abcdef \r\n + // BOUNDARY \r\n + // + // zero or one: + // Content-Disposition: form-data; name="lsb-release" \r\n \r\n + // abcdef \r\n + // BOUNDARY \r\n + // + // zero or one: + // Content-Disposition: form-data; name="oom-size" \r\n \r\n + // 1234567890 \r\n + // BOUNDARY \r\n + // + // zero or more (up to CrashKeyStorage::num_entries = 64): + // Content-Disposition: form-data; name=crash-key-name \r\n + // crash-key-value \r\n + // BOUNDARY \r\n + // + // Content-Disposition: form-data; name="dump"; filename="dump" \r\n + // Content-Type: application/octet-stream \r\n \r\n + // + // \r\n BOUNDARY -- \r\n + +#if defined(OS_CHROMEOS) + CrashReporterWriter writer(temp_file_fd); +#else + MimeWriter writer(temp_file_fd, mime_boundary); +#endif + { + const char* product_name = ""; + const char* version = ""; + + GetCrashReporterClient()->GetProductNameAndVersion(&product_name, &version); + + writer.AddBoundary(); + writer.AddPairString("prod", product_name); + writer.AddBoundary(); + writer.AddPairString("ver", version); + writer.AddBoundary(); + if (info.pid > 0) { + char pid_value_buf[kUint64StringSize]; + uint64_t pid_value_len = my_uint64_len(info.pid); + my_uint64tos(pid_value_buf, info.pid, pid_value_len); + static const char pid_key_name[] = "pid"; + writer.AddPairData(pid_key_name, sizeof(pid_key_name) - 1, + pid_value_buf, pid_value_len); + writer.AddBoundary(); + } +#if defined(OS_ANDROID) + // Addtional MIME blocks are added for logging on Android devices. + static const char android_build_id[] = "android_build_id"; + static const char android_build_fp[] = "android_build_fp"; + static const char device[] = "device"; + static const char model[] = "model"; + static const char brand[] = "brand"; + static const char exception_info[] = "exception_info"; + + base::android::BuildInfo* android_build_info = + base::android::BuildInfo::GetInstance(); + writer.AddPairString( + android_build_id, android_build_info->android_build_id()); + writer.AddBoundary(); + writer.AddPairString( + android_build_fp, android_build_info->android_build_fp()); + writer.AddBoundary(); + writer.AddPairString(device, android_build_info->device()); + writer.AddBoundary(); + writer.AddPairString(model, android_build_info->model()); + writer.AddBoundary(); + writer.AddPairString(brand, android_build_info->brand()); + writer.AddBoundary(); + if (android_build_info->java_exception_info() != nullptr) { + writer.AddPairString(exception_info, + android_build_info->java_exception_info()); + writer.AddBoundary(); + } +#endif + writer.Flush(); + } + + if (info.process_start_time > 0) { + struct kernel_timeval tv; + if (!sys_gettimeofday(&tv, nullptr)) { + uint64_t time = kernel_timeval_to_ms(&tv); + if (time > info.process_start_time) { + time -= info.process_start_time; + char time_str[kUint64StringSize]; + const unsigned time_len = my_uint64_len(time); + my_uint64tos(time_str, time, time_len); + + static const char process_time_msg[] = "ptime"; + writer.AddPairData(process_time_msg, sizeof(process_time_msg) - 1, + time_str, time_len); + writer.AddBoundary(); + writer.Flush(); + } + } + } + + if (info.process_type_length) { + writer.AddPairString("ptype", info.process_type); + writer.AddBoundary(); + writer.Flush(); + } + + if (info.distro_length) { + static const char distro_msg[] = "lsb-release"; + writer.AddPairString(distro_msg, info.distro); + writer.AddBoundary(); + writer.Flush(); + } + + if (info.oom_size) { + char oom_size_str[kUint64StringSize]; + const unsigned oom_size_len = my_uint64_len(info.oom_size); + my_uint64tos(oom_size_str, info.oom_size, oom_size_len); + static const char oom_size_msg[] = "oom-size"; + writer.AddPairData(oom_size_msg, sizeof(oom_size_msg) - 1, + oom_size_str, oom_size_len); + writer.AddBoundary(); + writer.Flush(); + } + + if (info.crash_keys) { + CrashKeyStorage::Iterator crash_key_iterator(*info.crash_keys); + const CrashKeyStorage::Entry* entry; + while ((entry = crash_key_iterator.Next())) { + writer.AddPairString(entry->key, entry->value); + writer.AddBoundary(); + writer.Flush(); + } + } + + writer.AddFileContents(g_dump_msg, dump_data, dump_size); +#if defined(ADDRESS_SANITIZER) + // Append a multipart boundary and the contents of the AddressSanitizer log. + writer.AddBoundary(); + writer.AddFileContents(g_log_msg, log_data, log_size); +#endif + writer.AddEnd(); + writer.Flush(); + + IGNORE_RET(sys_close(temp_file_fd)); + +#if defined(OS_ANDROID) + if (info.filename) { + size_t filename_length = my_strlen(info.filename); + + // If this was a file, we need to copy it to the right place and use the + // right file name so it gets uploaded by the browser. + const char msg[] = "Output crash dump file:"; + WriteLog(msg, sizeof(msg) - 1); + WriteLog(info.filename, filename_length); + + char pid_buf[kUint64StringSize]; + size_t pid_str_length = my_uint64_len(info.pid); + my_uint64tos(pid_buf, info.pid, pid_str_length); + pid_buf[pid_str_length] = 0; // my_uint64tos() doesn't null-terminate. + + size_t done_filename_len = filename_length + pid_str_length + 1; + char* done_filename = reinterpret_cast( + allocator.Alloc(done_filename_len)); + // Rename the file such that the pid is the suffix in order signal to other + // processes that the minidump is complete. The advantage of using the pid + // as the suffix is that it is trivial to associate the minidump with the + // crashed process. + my_strlcpy(done_filename, info.filename, done_filename_len); + my_strlcat(done_filename, pid_buf, done_filename_len); + // Rename the minidump file to signal that it is complete. + if (rename(info.filename, done_filename)) { + const char failed_msg[] = "Failed to rename:"; + WriteLog(failed_msg, sizeof(failed_msg) - 1); + WriteLog(info.filename, filename_length); + const char to_msg[] = "to"; + WriteLog(to_msg, sizeof(to_msg) - 1); + WriteLog(done_filename, done_filename_len - 1); + } + } +#endif + + if (!info.upload) + return; + + const pid_t child = sys_fork(); + if (!child) { + // Spawned helper process. + // + // This code is called both when a browser is crashing (in which case, + // nothing really matters any more) and when a renderer/plugin crashes, in + // which case we need to continue. + // + // Since we are a multithreaded app, if we were just to fork(), we might + // grab file descriptors which have just been created in another thread and + // hold them open for too long. + // + // Thus, we have to loop and try and close everything. + const int fd = sys_open("/proc/self/fd", O_DIRECTORY | O_RDONLY, 0); + if (fd < 0) { + for (unsigned i = 3; i < 8192; ++i) + IGNORE_RET(sys_close(i)); + } else { + google_breakpad::DirectoryReader reader(fd); + const char* name; + while (reader.GetNextEntry(&name)) { + int i; + if (my_strtoui(&i, name) && i > 2 && i != fd) + IGNORE_RET(sys_close(i)); + reader.PopEntry(); + } + + IGNORE_RET(sys_close(fd)); + } + + IGNORE_RET(sys_setsid()); + + // Leave one end of a pipe in the upload process and watch for it getting + // closed by the upload process exiting. + int fds[2]; + if (sys_pipe(fds) >= 0) { + const pid_t upload_child = sys_fork(); + if (!upload_child) { + // Upload process. + IGNORE_RET(sys_close(fds[0])); + IGNORE_RET(sys_dup2(fds[1], 3)); + ExecUploadProcessOrTerminate(info, temp_file, mime_boundary, exe_buf, + &allocator); + } + + // Helper process. + if (upload_child > 0) { + IGNORE_RET(sys_close(fds[1])); + + const size_t kCrashIdLength = 16; + char id_buf[kCrashIdLength + 1]; + size_t bytes_read = + WaitForCrashReportUploadProcess(fds[0], kCrashIdLength, id_buf); + HandleCrashReportId(id_buf, bytes_read, kCrashIdLength); + + if (sys_waitpid(upload_child, nullptr, WNOHANG) == 0) { + // Upload process is still around, kill it. + sys_kill(upload_child, SIGKILL); + } + } + } + + // Helper process. + IGNORE_RET(sys_unlink(info.filename)); +#if defined(ADDRESS_SANITIZER) + IGNORE_RET(sys_unlink(info.log_filename)); +#endif + IGNORE_RET(sys_unlink(temp_file)); + sys__exit(0); + } + + // Main browser process. + if (child <= 0) + return; + (void) HANDLE_EINTR(sys_waitpid(child, nullptr, 0)); +} + +void InitCrashReporter(const std::string& process_type) { +#if defined(OS_ANDROID) + // This will guarantee that the BuildInfo has been initialized and subsequent + // calls will not require memory allocation. + base::android::BuildInfo::GetInstance(); + + // Handler registration is LIFO. Install the microdump handler first, such + // that if conventional minidump crash reporting is enabled below, it takes + // precedence (i.e. its handler is run first) over the microdump handler. + InitMicrodumpCrashHandlerIfNecessary(process_type); +#endif + // Determine the process type and take appropriate action. + const base::CommandLine& parsed_command_line = + *base::CommandLine::ForCurrentProcess(); + if (parsed_command_line.HasSwitch(switches::kDisableBreakpad)) + return; + + if (process_type.empty()) { + bool enable_breakpad = GetCrashReporterClient()->GetCollectStatsConsent() || + GetCrashReporterClient()->IsRunningUnattended(); + enable_breakpad &= + !parsed_command_line.HasSwitch(switches::kDisableBreakpad); + if (!enable_breakpad) { + enable_breakpad = parsed_command_line.HasSwitch( + switches::kEnableCrashReporterForTesting); + } + if (!enable_breakpad) { + VLOG(1) << "Breakpad disabled"; + return; + } + + InitCrashKeys(); + EnableCrashDumping(GetCrashReporterClient()->IsRunningUnattended()); + } else if (GetCrashReporterClient()->EnableBreakpadForProcess(process_type)) { +#if defined(OS_ANDROID) + NOTREACHED() << "Breakpad initialized with InitCrashReporter() instead of " + "InitNonBrowserCrashReporter in " << process_type << " process."; + return; +#else + // We might be chrooted in a zygote or renderer process so we cannot call + // GetCollectStatsConsent because that needs access the the user's home + // dir. Instead, we set a command line flag for these processes. + // Even though plugins are not chrooted, we share the same code path for + // simplicity. + if (!parsed_command_line.HasSwitch(switches::kEnableCrashReporter)) + return; + InitCrashKeys(); + SetClientIdFromCommandLine(parsed_command_line); + EnableNonBrowserCrashDumping(); + VLOG(1) << "Non Browser crash dumping enabled for: " << process_type; +#endif // #if defined(OS_ANDROID) + } + + PostEnableBreakpadInitialization(); +} + +#if defined(OS_ANDROID) +void InitNonBrowserCrashReporterForAndroid(const std::string& process_type) { + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + + // Handler registration is LIFO. Install the microdump handler first, such + // that if conventional minidump crash reporting is enabled below, it takes + // precedence (i.e. its handler is run first) over the microdump handler. + InitMicrodumpCrashHandlerIfNecessary(process_type); + + if (command_line->HasSwitch(switches::kEnableCrashReporter)) { + // On Android we need to provide a FD to the file where the minidump is + // generated as the renderer and browser run with different UIDs + // (preventing the browser from inspecting the renderer process). + int minidump_fd = base::GlobalDescriptors::GetInstance()->MaybeGet( + GetCrashReporterClient()->GetAndroidMinidumpDescriptor()); + if (minidump_fd < 0) { + NOTREACHED() << "Could not find minidump FD, crash reporting disabled."; + } else { + InitCrashKeys(); + EnableNonBrowserCrashDumping(process_type, minidump_fd); + } + } +} +#endif // OS_ANDROID + +bool IsCrashReporterEnabled() { + return g_is_crash_reporter_enabled; +} + +} // namespace breakpad diff --git a/components/crash/content/app/breakpad_linux.h b/components/crash/content/app/breakpad_linux.h new file mode 100644 index 0000000..4bfe0ce --- /dev/null +++ b/components/crash/content/app/breakpad_linux.h @@ -0,0 +1,32 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Public interface for enabling Breakpad on Linux systems. + +#ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_ +#define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_ + +#include + +#include "build/build_config.h" + +namespace breakpad { + +// Turns on the crash reporter in any process. +extern void InitCrashReporter(const std::string& process_type); + +// Enables the crash reporter in child processes. +#if defined(OS_ANDROID) +extern void InitNonBrowserCrashReporterForAndroid( + const std::string& process_type); +#endif + +// Checks if crash reporting is enabled. Note that this is not the same as +// being opted into metrics reporting (and crash reporting), which controls +// whether InitCrashReporter() is called. +bool IsCrashReporterEnabled(); + +} // namespace breakpad + +#endif // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_ diff --git a/components/crash/content/app/breakpad_linux_impl.h b/components/crash/content/app/breakpad_linux_impl.h new file mode 100644 index 0000000..3c94d72 --- /dev/null +++ b/components/crash/content/app/breakpad_linux_impl.h @@ -0,0 +1,65 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Internal header file for the Linux breakpad implementation. This file is +// shared between crash_handler_host_linux.cc and breakpad_linux.cc. It should +// only be used in files compiled with linux_breakpad=1. + +#ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_IMPL_H_ +#define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_IMPL_H_ + +#include + +#include "base/basictypes.h" +#include "breakpad/src/common/simple_string_dictionary.h" +#include "components/crash/content/app/breakpad_linux.h" + +namespace breakpad { + +typedef google_breakpad::NonAllocatingMap<256, 256, 64> CrashKeyStorage; + +#if defined(ADDRESS_SANITIZER) +static const size_t kMaxAsanReportSize = 1 << 16; +#endif +// Define a preferred limit on minidump sizes, because Crash Server currently +// throws away any larger than 1.2MB (1.2 * 1024 * 1024). A value of -1 means +// no limit. +static const off_t kMaxMinidumpFileSize = 1258291; + +// The size of the iovec used to transfer crash data from a child back to the +// browser. +#if !defined(ADDRESS_SANITIZER) +const size_t kCrashIovSize = 6; +#else +// Additional field to pass the AddressSanitizer log to the crash handler. +const size_t kCrashIovSize = 7; +#endif + +// BreakpadInfo describes a crash report. +// The minidump information can either be contained in a file descriptor (fd) or +// in a file (whose path is in filename). +struct BreakpadInfo { + int fd; // File descriptor to the Breakpad dump data. + const char* filename; // Path to the Breakpad dump data. +#if defined(ADDRESS_SANITIZER) + const char* log_filename; // Path to the ASan log file. + const char* asan_report_str; // ASan report. + unsigned asan_report_length; // Length of |asan_report_length|. +#endif + const char* process_type; // Process type, e.g. "renderer". + unsigned process_type_length; // Length of |process_type|. + const char* distro; // Linux distro string. + unsigned distro_length; // Length of |distro|. + bool upload; // Whether to upload or save crash dump. + uint64_t process_start_time; // Uptime of the crashing process. + size_t oom_size; // Amount of memory requested if OOM. + uint64_t pid; // PID where applicable. + CrashKeyStorage* crash_keys; +}; + +extern void HandleCrashDump(const BreakpadInfo& info); + +} // namespace breakpad + +#endif // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_IMPL_H_ diff --git a/components/crash/content/app/breakpad_mac.h b/components/crash/content/app/breakpad_mac.h new file mode 100644 index 0000000..d392e13 --- /dev/null +++ b/components/crash/content/app/breakpad_mac.h @@ -0,0 +1,27 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_MAC_H_ +#define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_MAC_H_ + +#include + +// This header defines the entry points for Breakpad integration. + +namespace breakpad { + +// Initializes Breakpad. +void InitCrashReporter(const std::string& process_type); + +// Give Breakpad a chance to store information about the current process. +// Extra information requires a parsed command line, so call this after +// CommandLine::Init has been called. +void InitCrashProcessInfo(const std::string& process_type_switch); + +// Is Breakpad enabled? +bool IsCrashReporterEnabled(); + +} // namespace breakpad + +#endif // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_MAC_H_ diff --git a/components/crash/content/app/breakpad_mac.mm b/components/crash/content/app/breakpad_mac.mm new file mode 100644 index 0000000..84ef742 --- /dev/null +++ b/components/crash/content/app/breakpad_mac.mm @@ -0,0 +1,286 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "components/crash/content/app/breakpad_mac.h" + +#include +#import + +#include "base/auto_reset.h" +#include "base/base_switches.h" +#import "base/basictypes.h" +#include "base/command_line.h" +#include "base/debug/crash_logging.h" +#include "base/debug/dump_without_crashing.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#import "base/logging.h" +#include "base/mac/bundle_locations.h" +#include "base/mac/foundation_util.h" +#include "base/mac/mac_util.h" +#include "base/mac/scoped_cftyperef.h" +#import "base/mac/scoped_nsautorelease_pool.h" +#include "base/strings/sys_string_conversions.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread_restrictions.h" +#import "breakpad/src/client/mac/Framework/Breakpad.h" +#include "components/crash/content/app/crash_reporter_client.h" + +using crash_reporter::GetCrashReporterClient; + +namespace breakpad { + +namespace { + +BreakpadRef gBreakpadRef = NULL; + +void SetCrashKeyValue(NSString* key, NSString* value) { + // Comment repeated from header to prevent confusion: + // IMPORTANT: On OS X, the key/value pairs are sent to the crash server + // out of bounds and not recorded on disk in the minidump, this means + // that if you look at the minidump file locally you won't see them! + if (gBreakpadRef == NULL) { + return; + } + + BreakpadAddUploadParameter(gBreakpadRef, key, value); +} + +void ClearCrashKeyValue(NSString* key) { + if (gBreakpadRef == NULL) { + return; + } + + BreakpadRemoveUploadParameter(gBreakpadRef, key); +} + +void SetCrashKeyValueImpl(const base::StringPiece& key, + const base::StringPiece& value) { + @autoreleasepool { + SetCrashKeyValue(base::SysUTF8ToNSString(key.as_string()), + base::SysUTF8ToNSString(value.as_string())); + } +} + +void ClearCrashKeyValueImpl(const base::StringPiece& key) { + @autoreleasepool { + ClearCrashKeyValue(base::SysUTF8ToNSString(key.as_string())); + } +} + +bool FatalMessageHandler(int severity, const char* file, int line, + size_t message_start, const std::string& str) { + // Do not handle non-FATAL. + if (severity != logging::LOG_FATAL) + return false; + + // In case of OOM condition, this code could be reentered when + // constructing and storing the key. Using a static is not + // thread-safe, but if multiple threads are in the process of a + // fatal crash at the same time, this should work. + static bool guarded = false; + if (guarded) + return false; + + base::AutoReset guard(&guarded, true); + + // Only log last path component. This matches logging.cc. + if (file) { + const char* slash = strrchr(file, '/'); + if (slash) + file = slash + 1; + } + + NSString* fatal_key = @"LOG_FATAL"; + NSString* fatal_value = + [NSString stringWithFormat:@"%s:%d: %s", + file, line, str.c_str() + message_start]; + SetCrashKeyValue(fatal_key, fatal_value); + + // Rather than including the code to force the crash here, allow the + // caller to do it. + return false; +} + +// BreakpadGenerateAndSendReport() does not report the current +// thread. This class can be used to spin up a thread to run it. +class DumpHelper : public base::PlatformThread::Delegate { + public: + static void DumpWithoutCrashing() { + DumpHelper dumper; + base::PlatformThreadHandle handle; + if (base::PlatformThread::Create(0, &dumper, &handle)) { + // The entire point of this is to block so that the correct + // stack is logged. + base::ThreadRestrictions::ScopedAllowIO allow_io; + base::PlatformThread::Join(handle); + } + } + + private: + DumpHelper() {} + + void ThreadMain() override { + base::PlatformThread::SetName("CrDumpHelper"); + BreakpadGenerateAndSendReport(gBreakpadRef); + } + + DISALLOW_COPY_AND_ASSIGN(DumpHelper); +}; + +void SIGABRTHandler(int signal) { + // The OSX abort() (link below) masks all signals for the process, + // and all except SIGABRT for the thread. SIGABRT will be masked + // when the SIGABRT is sent, which means at this point only SIGKILL + // and SIGSTOP can be delivered. Unmask others so that the code + // below crashes as desired. + // + // http://www.opensource.apple.com/source/Libc/Libc-825.26/stdlib/FreeBSD/abort.c + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, signal); + pthread_sigmask(SIG_SETMASK, &mask, NULL); + + // Most interesting operations are not safe in a signal handler, just crash. + char* volatile death_ptr = NULL; + *death_ptr = '!'; +} + +} // namespace + +bool IsCrashReporterEnabled() { + return gBreakpadRef != NULL; +} + +// Only called for a branded build of Chrome.app. +void InitCrashReporter(const std::string& process_type) { + DCHECK(!gBreakpadRef); + base::mac::ScopedNSAutoreleasePool autorelease_pool; + + // Check whether crash reporting should be enabled. If enterprise + // configuration management controls crash reporting, it takes precedence. + // Otherwise, check whether the user has consented to stats and crash + // reporting. The browser process can make this determination directly. + // Helper processes may not have access to the disk or to the same data as + // the browser process, so the browser passes the decision to them on the + // command line. + NSBundle* main_bundle = base::mac::FrameworkBundle(); + bool is_browser = !base::mac::IsBackgroundOnlyProcess(); + bool enable_breakpad = false; + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + + if (is_browser) { + // Since the configuration management infrastructure is possibly not + // initialized when this code runs, read the policy preference directly. + if (!GetCrashReporterClient()->ReportingIsEnforcedByPolicy( + &enable_breakpad)) { + // Controlled by the user. The crash reporter may be enabled by + // preference or through an environment variable, but the kDisableBreakpad + // switch overrides both. + enable_breakpad = GetCrashReporterClient()->GetCollectStatsConsent() || + GetCrashReporterClient()->IsRunningUnattended(); + enable_breakpad &= !command_line->HasSwitch(switches::kDisableBreakpad); + } + } else { + // This is a helper process, check the command line switch. + enable_breakpad = command_line->HasSwitch(switches::kEnableCrashReporter); + } + + if (!enable_breakpad) { + VLOG_IF(1, is_browser) << "Breakpad disabled"; + return; + } + + // Tell Breakpad where crash_inspector and crash_report_sender are. + NSString* resource_path = [main_bundle resourcePath]; + NSString *inspector_location = + [resource_path stringByAppendingPathComponent:@"crash_inspector"]; + NSString *reporter_bundle_location = + [resource_path stringByAppendingPathComponent:@"crash_report_sender.app"]; + NSString *reporter_location = + [[NSBundle bundleWithPath:reporter_bundle_location] executablePath]; + + if (!inspector_location || !reporter_location) { + VLOG_IF(1, is_browser && base::mac::AmIBundled()) << "Breakpad disabled"; + return; + } + + NSDictionary* info_dictionary = [main_bundle infoDictionary]; + NSMutableDictionary *breakpad_config = + [[info_dictionary mutableCopy] autorelease]; + [breakpad_config setObject:inspector_location + forKey:@BREAKPAD_INSPECTOR_LOCATION]; + [breakpad_config setObject:reporter_location + forKey:@BREAKPAD_REPORTER_EXE_LOCATION]; + + // In the main application (the browser process), crashes can be passed to + // the system's Crash Reporter. This allows the system to notify the user + // when the application crashes, and provide the user with the option to + // restart it. + if (is_browser) + [breakpad_config setObject:@"NO" forKey:@BREAKPAD_SEND_AND_EXIT]; + + base::FilePath dir_crash_dumps; + GetCrashReporterClient()->GetCrashDumpLocation(&dir_crash_dumps); + [breakpad_config setObject:base::SysUTF8ToNSString(dir_crash_dumps.value()) + forKey:@BREAKPAD_DUMP_DIRECTORY]; + + // Temporarily run Breakpad in-process on 10.10 and later because APIs that + // it depends on got broken (http://crbug.com/386208). + // This can catch crashes in the browser process only. + if (is_browser && base::mac::IsOSYosemiteOrLater()) { + [breakpad_config setObject:[NSNumber numberWithBool:YES] + forKey:@BREAKPAD_IN_PROCESS]; + } + + // Initialize Breakpad. + gBreakpadRef = BreakpadCreate(breakpad_config); + if (!gBreakpadRef) { + LOG_IF(ERROR, base::mac::AmIBundled()) << "Breakpad initialization failed"; + return; + } + + // Initialize the scoped crash key system. + base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueImpl, + &ClearCrashKeyValueImpl); + GetCrashReporterClient()->RegisterCrashKeys(); + + // Set Breakpad metadata values. These values are added to Info.plist during + // the branded Google Chrome.app build. + SetCrashKeyValue(@"ver", [info_dictionary objectForKey:@BREAKPAD_VERSION]); + SetCrashKeyValue(@"prod", [info_dictionary objectForKey:@BREAKPAD_PRODUCT]); + SetCrashKeyValue(@"plat", @"OS X"); + + logging::SetLogMessageHandler(&FatalMessageHandler); + base::debug::SetDumpWithoutCrashingFunction(&DumpHelper::DumpWithoutCrashing); + + // abort() sends SIGABRT, which breakpad does not intercept. + // Register a signal handler to crash in a way breakpad will + // intercept. + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = SIGABRTHandler; + CHECK(0 == sigaction(SIGABRT, &sigact, NULL)); +} + +void InitCrashProcessInfo(const std::string& process_type_switch) { + if (gBreakpadRef == NULL) { + return; + } + + // Determine the process type. + NSString* process_type = @"browser"; + if (!process_type_switch.empty()) { + process_type = base::SysUTF8ToNSString(process_type_switch); + } + + // Store process type in crash dump. + SetCrashKeyValue(@"ptype", process_type); + + NSString* pid_value = + [NSString stringWithFormat:@"%d", static_cast(getpid())]; + SetCrashKeyValue(@"pid", pid_value); +} + +} // namespace breakpad diff --git a/components/crash/content/app/breakpad_mac_stubs.mm b/components/crash/content/app/breakpad_mac_stubs.mm new file mode 100644 index 0000000..329794b --- /dev/null +++ b/components/crash/content/app/breakpad_mac_stubs.mm @@ -0,0 +1,24 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "components/crash/content/app/breakpad_mac.h" + +#import + +// Stubbed out versions of breakpad integration functions so we can compile +// without linking in Breakpad. + +namespace breakpad { + +bool IsCrashReporterEnabled() { + return false; +} + +void InitCrashProcessInfo(const std::string& process_type_switch) { +} + +void InitCrashReporter(const std::string& process_type) { +} + +} // namespace breakpad diff --git a/components/crash/content/app/breakpad_win.cc b/components/crash/content/app/breakpad_win.cc new file mode 100644 index 0000000..5b53ff7 --- /dev/null +++ b/components/crash/content/app/breakpad_win.cc @@ -0,0 +1,745 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crash/content/app/breakpad_win.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "base/base_switches.h" +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/debug/crash_logging.h" +#include "base/debug/dump_without_crashing.h" +#include "base/environment.h" +#include "base/memory/scoped_ptr.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/string16.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/synchronization/lock.h" +#include "base/win/metro.h" +#include "base/win/pe_image.h" +#include "base/win/registry.h" +#include "base/win/win_util.h" +#include "breakpad/src/client/windows/handler/exception_handler.h" +#include "components/crash/content/app/crash_keys_win.h" +#include "components/crash/content/app/crash_reporter_client.h" +#include "components/crash/content/app/hard_error_handler_win.h" +#include "content/public/common/result_codes.h" +#include "sandbox/win/src/nt_internals.h" +#include "sandbox/win/src/sidestep/preamble_patcher.h" + +// userenv.dll is required for GetProfileType(). +#pragma comment(lib, "userenv.lib") + +#pragma intrinsic(_AddressOfReturnAddress) +#pragma intrinsic(_ReturnAddress) + +#ifdef _WIN64 +// See http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx +typedef struct _UNWIND_INFO { + unsigned char Version : 3; + unsigned char Flags : 5; + unsigned char SizeOfProlog; + unsigned char CountOfCodes; + unsigned char FrameRegister : 4; + unsigned char FrameOffset : 4; + ULONG ExceptionHandler; +} UNWIND_INFO, *PUNWIND_INFO; +#endif + +namespace breakpad { + +using crash_reporter::GetCrashReporterClient; + +namespace { + +// Minidump with stacks, PEB, TEB, and unloaded module list. +const MINIDUMP_TYPE kSmallDumpType = static_cast( + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithUnloadedModules); // Get unloaded modules when available. + +// Minidump with all of the above, plus memory referenced from stack. +const MINIDUMP_TYPE kLargerDumpType = static_cast( + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithUnloadedModules | // Get unloaded modules when available. + MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. + +// Large dump with all process memory. +const MINIDUMP_TYPE kFullDumpType = static_cast( + MiniDumpWithFullMemory | // Full memory from process. + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithHandleData | // Get all handle information. + MiniDumpWithUnloadedModules); // Get unloaded modules when available. + +const char kPipeNameVar[] = "CHROME_BREAKPAD_PIPE_NAME"; + +const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; +const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; + +// This is the well known SID for the system principal. +const wchar_t kSystemPrincipalSid[] =L"S-1-5-18"; + +google_breakpad::ExceptionHandler* g_breakpad = NULL; +google_breakpad::ExceptionHandler* g_dumphandler_no_crash = NULL; + +#if !defined(_WIN64) +EXCEPTION_POINTERS g_surrogate_exception_pointers = {0}; +EXCEPTION_RECORD g_surrogate_exception_record = {0}; +CONTEXT g_surrogate_context = {0}; +#endif // !defined(_WIN64) + +typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle, + NTSTATUS ExitStatus); +char* g_real_terminate_process_stub = NULL; + +} // namespace + +// Dumps the current process memory. +extern "C" void __declspec(dllexport) __cdecl DumpProcess() { + if (g_breakpad) { + g_breakpad->WriteMinidump(); + } +} + +// Used for dumping a process state when there is no crash. +extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { + if (g_dumphandler_no_crash) { + g_dumphandler_no_crash->WriteMinidump(); + } +} + +namespace { + +// We need to prevent ICF from folding DumpForHangDebuggingThread() and +// DumpProcessWithoutCrashThread() together, since that makes them +// indistinguishable in crash dumps. We do this by making the function +// bodies unique, and prevent optimization from shuffling things around. +MSVC_DISABLE_OPTIMIZE() +MSVC_PUSH_DISABLE_WARNING(4748) + +DWORD WINAPI DumpProcessWithoutCrashThread(void*) { + DumpProcessWithoutCrash(); + return 0; +} + +// The following two functions do exactly the same thing as the two above. But +// we want the signatures to be different so that we can easily track them in +// crash reports. +// TODO(yzshen): Remove when enough information is collected and the hang rate +// of pepper/renderer processes is reduced. +DWORD WINAPI DumpForHangDebuggingThread(void*) { + DumpProcessWithoutCrash(); + VLOG(1) << "dumped for hang debugging"; + return 0; +} + +MSVC_POP_WARNING() +MSVC_ENABLE_OPTIMIZE() + +} // namespace + +// Injects a thread into a remote process to dump state when there is no crash. +extern "C" HANDLE __declspec(dllexport) __cdecl +InjectDumpProcessWithoutCrash(HANDLE process) { + return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread, + 0, 0, NULL); +} + +extern "C" HANDLE __declspec(dllexport) __cdecl +InjectDumpForHangDebugging(HANDLE process) { + return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, + 0, 0, NULL); +} + +// Returns a string containing a list of all modifiers for the loaded profile. +std::wstring GetProfileType() { + std::wstring profile_type; + DWORD profile_bits = 0; + if (::GetProfileType(&profile_bits)) { + static const struct { + DWORD bit; + const wchar_t* name; + } kBitNames[] = { + { PT_MANDATORY, L"mandatory" }, + { PT_ROAMING, L"roaming" }, + { PT_TEMPORARY, L"temporary" }, + }; + for (size_t i = 0; i < arraysize(kBitNames); ++i) { + const DWORD this_bit = kBitNames[i].bit; + if ((profile_bits & this_bit) != 0) { + profile_type.append(kBitNames[i].name); + profile_bits &= ~this_bit; + if (profile_bits != 0) + profile_type.append(L", "); + } + } + } else { + DWORD last_error = ::GetLastError(); + base::SStringPrintf(&profile_type, L"error %u", last_error); + } + return profile_type; +} + +namespace { + +// This callback is used when we want to get a dump without crashing the +// process. +bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*, + EXCEPTION_POINTERS* ex_info, + MDRawAssertionInfo*, bool succeeded) { + GetCrashReporterClient()->RecordCrashDumpAttemptResult( + false /* is_real_crash */, succeeded); + return true; +} + +// This callback is executed when the browser process has crashed, after +// the crash dump has been created. We need to minimize the amount of work +// done here since we have potentially corrupted process. Our job is to +// spawn another instance of chrome which will show a 'chrome has crashed' +// dialog. This code needs to live in the exe and thus has no access to +// facilities such as the i18n helpers. +bool DumpDoneCallback(const wchar_t*, const wchar_t*, void*, + EXCEPTION_POINTERS* ex_info, + MDRawAssertionInfo*, bool succeeded) { + GetCrashReporterClient()->RecordCrashDumpAttemptResult( + true /* is_real_crash */, succeeded); + // Check if the exception is one of the kind which would not be solved + // by simply restarting chrome. In this case we show a message box with + // and exit silently. Remember that chrome is in a crashed state so we + // can't show our own UI from this process. + if (HardErrorHandler(ex_info)) + return true; + + if (!GetCrashReporterClient()->AboutToRestart()) + return true; + + // Now we just start chrome browser with the same command line. + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi; + if (::CreateProcessW(NULL, ::GetCommandLineW(), NULL, NULL, FALSE, + CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi)) { + ::CloseHandle(pi.hProcess); + ::CloseHandle(pi.hThread); + } + // After this return we will be terminated. The actual return value is + // not used at all. + return true; +} + +// flag to indicate that we are already handling an exception. +volatile LONG handling_exception = 0; + +// This callback is used when there is no crash. Note: Unlike the +// |FilterCallback| below this does not do dupe detection. It is upto the caller +// to implement it. +bool FilterCallbackWhenNoCrash( + void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { + GetCrashReporterClient()->RecordCrashDumpAttempt(false); + return true; +} + +// This callback is executed when the Chrome process has crashed and *before* +// the crash dump is created. To prevent duplicate crash reports we +// make every thread calling this method, except the very first one, +// go to sleep. +bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { + // Capture every thread except the first one in the sleep. We don't + // want multiple threads to concurrently report exceptions. + if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) { + ::Sleep(INFINITE); + } + GetCrashReporterClient()->RecordCrashDumpAttempt(true); + return true; +} + +// Previous unhandled filter. Will be called if not null when we +// intercept a crash. +LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL; + +// Exception filter used when breakpad is not enabled. We just display +// the "Do you want to restart" message and then we call the previous filter. +long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) { + DumpDoneCallback(NULL, NULL, NULL, info, NULL, false); + + if (previous_filter) + return previous_filter(info); + + return EXCEPTION_EXECUTE_HANDLER; +} + +// Exception filter for the service process used when breakpad is not enabled. +// We just display the "Do you want to restart" message and then die +// (without calling the previous filter). +long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) { + DumpDoneCallback(NULL, NULL, NULL, info, NULL, false); + return EXCEPTION_EXECUTE_HANDLER; +} + +#if !defined(COMPONENT_BUILD) +// Installed via base::debug::SetCrashKeyReportingFunctions. +void SetCrashKeyValueForBaseDebug(const base::StringPiece& key, + const base::StringPiece& value) { + DCHECK(CrashKeysWin::keeper()); + CrashKeysWin::keeper()->SetCrashKeyValue(base::UTF8ToUTF16(key), + base::UTF8ToUTF16(value)); +} + +// Installed via base::debug::SetCrashKeyReportingFunctions. +void ClearCrashKeyForBaseDebug(const base::StringPiece& key) { + DCHECK(CrashKeysWin::keeper()); + CrashKeysWin::keeper()->ClearCrashKeyValue(base::UTF8ToUTF16(key)); +} +#endif // !defined(COMPONENT_BUILD) + +} // namespace + +// NOTE: This function is used by SyzyASAN to annotate crash reports. If you +// change the name or signature of this function you will break SyzyASAN +// instrumented releases of Chrome. Please contact syzygy-team@chromium.org +// before doing so! +extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl( + const wchar_t* key, const wchar_t* value) { + CrashKeysWin* keeper = CrashKeysWin::keeper(); + if (!keeper) + return; + + // TODO(siggi): This doesn't look quite right - there's NULL deref potential + // here, and an implicit std::wstring conversion. Fixme. + keeper->SetCrashKeyValue(key, value); +} + +extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl( + const wchar_t* key) { + CrashKeysWin* keeper = CrashKeysWin::keeper(); + if (!keeper) + return; + + // TODO(siggi): This doesn't look quite right - there's NULL deref potential + // here, and an implicit std::wstring conversion. Fixme. + keeper->ClearCrashKeyValue(key); +} + +static bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption, + UINT flags, bool* exit_now) { + // We wrap the call to MessageBoxW with a SEH handler because it some + // machines with CursorXP, PeaDict or with FontExplorer installed it crashes + // uncontrollably here. Being this a best effort deal we better go away. + __try { + *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags)); + } __except(EXCEPTION_EXECUTE_HANDLER) { + // Its not safe to continue executing, exit silently here. + ::TerminateProcess(::GetCurrentProcess(), + GetCrashReporterClient()->GetResultCodeRespawnFailed()); + } + + return true; +} + +// This function is executed by the child process that DumpDoneCallback() +// spawned and basically just shows the 'chrome has crashed' dialog if +// the CHROME_CRASHED environment variable is present. +bool ShowRestartDialogIfCrashed(bool* exit_now) { + // If we are being launched in metro mode don't try to show the dialog. + if (base::win::IsMetroProcess()) + return false; + + base::string16 message; + base::string16 title; + bool is_rtl_locale; + if (!GetCrashReporterClient()->ShouldShowRestartDialog( + &title, &message, &is_rtl_locale)) { + return false; + } + + // If the UI layout is right-to-left, we need to pass the appropriate MB_XXX + // flags so that an RTL message box is displayed. + UINT flags = MB_OKCANCEL | MB_ICONWARNING; + if (is_rtl_locale) + flags |= MB_RIGHT | MB_RTLREADING; + + return WrapMessageBoxWithSEH(message.c_str(), title.c_str(), flags, exit_now); +} + +extern "C" void __declspec(dllexport) TerminateProcessWithoutDump() { + // Patched stub exists based on conditions (See InitCrashReporter). + // As a side note this function also gets called from + // WindowProcExceptionFilter. + if (g_real_terminate_process_stub == NULL) { + ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED); + } else { + NtTerminateProcessPtr real_terminate_proc = + reinterpret_cast( + static_cast(g_real_terminate_process_stub)); + real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED); + } +} + +// Crashes the process after generating a dump for the provided exception. Note +// that the crash reporter should be initialized before calling this function +// for it to do anything. +// NOTE: This function is used by SyzyASAN to invoke a crash. If you change the +// the name or signature of this function you will break SyzyASAN instrumented +// releases of Chrome. Please contact syzygy-team@chromium.org before doing so! +extern "C" int __declspec(dllexport) CrashForException( + EXCEPTION_POINTERS* info) { + if (g_breakpad) { + g_breakpad->WriteMinidumpForException(info); + TerminateProcessWithoutDump(); + } + return EXCEPTION_CONTINUE_SEARCH; +} + +#ifndef _WIN64 +static NTSTATUS WINAPI HookNtTerminateProcess(HANDLE ProcessHandle, + NTSTATUS ExitStatus) { + if (g_breakpad && + (ProcessHandle == ::GetCurrentProcess() || ProcessHandle == NULL)) { + NT_TIB* tib = reinterpret_cast(NtCurrentTeb()); + void* address_on_stack = _AddressOfReturnAddress(); + if (address_on_stack < tib->StackLimit || + address_on_stack > tib->StackBase) { + g_surrogate_exception_record.ExceptionAddress = _ReturnAddress(); + g_surrogate_exception_record.ExceptionCode = DBG_TERMINATE_PROCESS; + g_surrogate_exception_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + CrashForException(&g_surrogate_exception_pointers); + } + } + + NtTerminateProcessPtr real_proc = + reinterpret_cast( + static_cast(g_real_terminate_process_stub)); + return real_proc(ProcessHandle, ExitStatus); +} + +static void InitTerminateProcessHooks() { + NtTerminateProcessPtr terminate_process_func_address = + reinterpret_cast(::GetProcAddress( + ::GetModuleHandle(L"ntdll.dll"), "NtTerminateProcess")); + if (terminate_process_func_address == NULL) + return; + + DWORD old_protect = 0; + if (!::VirtualProtect(terminate_process_func_address, 5, + PAGE_EXECUTE_READWRITE, &old_protect)) + return; + + g_real_terminate_process_stub = reinterpret_cast(VirtualAllocEx( + ::GetCurrentProcess(), NULL, sidestep::kMaxPreambleStubSize, + MEM_COMMIT, PAGE_EXECUTE_READWRITE)); + if (g_real_terminate_process_stub == NULL) + return; + + g_surrogate_exception_pointers.ContextRecord = &g_surrogate_context; + g_surrogate_exception_pointers.ExceptionRecord = + &g_surrogate_exception_record; + + sidestep::SideStepError patch_result = + sidestep::PreamblePatcher::Patch( + terminate_process_func_address, HookNtTerminateProcess, + g_real_terminate_process_stub, sidestep::kMaxPreambleStubSize); + if (patch_result != sidestep::SIDESTEP_SUCCESS) { + CHECK(::VirtualFreeEx(::GetCurrentProcess(), g_real_terminate_process_stub, + 0, MEM_RELEASE)); + CHECK(::VirtualProtect(terminate_process_func_address, 5, old_protect, + &old_protect)); + return; + } + + DWORD dummy = 0; + CHECK(::VirtualProtect(terminate_process_func_address, + 5, + old_protect, + &dummy)); + CHECK(::VirtualProtect(g_real_terminate_process_stub, + sidestep::kMaxPreambleStubSize, + old_protect, + &old_protect)); +} +#endif + +static void InitPipeNameEnvVar(bool is_per_user_install) { + scoped_ptr env(base::Environment::Create()); + if (env->HasVar(kPipeNameVar)) { + // The Breakpad pipe name is already configured: nothing to do. + return; + } + + // Check whether configuration management controls crash reporting. + bool crash_reporting_enabled = true; + bool controlled_by_policy = + GetCrashReporterClient()->ReportingIsEnforcedByPolicy( + &crash_reporting_enabled); + + const base::CommandLine& command = *base::CommandLine::ForCurrentProcess(); + bool use_crash_service = !controlled_by_policy && + (command.HasSwitch(switches::kNoErrorDialogs) || + GetCrashReporterClient()->IsRunningUnattended()); + + std::wstring pipe_name; + if (use_crash_service) { + // Crash reporting is done by crash_service.exe. + pipe_name = kChromePipeName; + } else { + // We want to use the Google Update crash reporting. We need to check if the + // user allows it first (in case the administrator didn't already decide + // via policy). + if (!controlled_by_policy) + crash_reporting_enabled = + GetCrashReporterClient()->GetCollectStatsConsent(); + + if (!crash_reporting_enabled) { + // Crash reporting is disabled, don't set the environment variable. + return; + } + + // Build the pipe name. It can be either: + // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18" + // Per-user install: "NamedPipe\GoogleCrashServices\" + std::wstring user_sid; + if (is_per_user_install) { + if (!base::win::GetUserSidString(&user_sid)) { + return; + } + } else { + user_sid = kSystemPrincipalSid; + } + + pipe_name = kGoogleUpdatePipeName; + pipe_name += user_sid; + } + env->SetVar(kPipeNameVar, base::UTF16ToASCII(pipe_name)); +} + +void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) { + previous_filter = SetUnhandledExceptionFilter(filter); +} + +void InitCrashReporter(const std::string& process_type_switch) { + const base::CommandLine& command = *base::CommandLine::ForCurrentProcess(); + if (command.HasSwitch(switches::kDisableBreakpad)) + return; + + // Disable the message box for assertions. + _CrtSetReportMode(_CRT_ASSERT, 0); + + base::string16 process_type = base::ASCIIToUTF16(process_type_switch); + if (process_type.empty()) + process_type = L"browser"; + + wchar_t exe_path[MAX_PATH]; + exe_path[0] = 0; + GetModuleFileNameW(NULL, exe_path, MAX_PATH); + + bool is_per_user_install = + GetCrashReporterClient()->GetIsPerUserInstall(base::FilePath(exe_path)); + + // This is intentionally leaked. + CrashKeysWin* keeper = new CrashKeysWin(); + + google_breakpad::CustomClientInfo* custom_info = + keeper->GetCustomInfo(exe_path, process_type, GetProfileType(), + base::CommandLine::ForCurrentProcess(), + GetCrashReporterClient()); + +#if !defined(COMPONENT_BUILD) + // chrome/common/child_process_logging_win.cc registers crash keys for + // chrome.dll. In a component build, that is sufficient as chrome.dll and + // chrome.exe share a copy of base (in base.dll). + // In a static build, the EXE must separately initialize the crash keys + // configuration as it has its own statically linked copy of base. + base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueForBaseDebug, + &ClearCrashKeyForBaseDebug); + GetCrashReporterClient()->RegisterCrashKeys(); +#endif + + google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL; + LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL; + // We install the post-dump callback only for the browser and service + // processes. It spawns a new browser/service process. + if (process_type == L"browser") { + callback = &DumpDoneCallback; + default_filter = &ChromeExceptionFilter; + } else if (process_type == L"service") { + callback = &DumpDoneCallback; + default_filter = &ServiceExceptionFilter; + } + + if (process_type == L"browser") { + InitPipeNameEnvVar(is_per_user_install); + GetCrashReporterClient()->InitBrowserCrashDumpsRegKey(); + } + + scoped_ptr env(base::Environment::Create()); + std::string pipe_name_ascii; + if (!env->GetVar(kPipeNameVar, &pipe_name_ascii)) { + // Breakpad is not enabled. Configuration is managed or the user + // did not allow Google Update to send crashes. We need to use + // our default crash handler instead, but only for the + // browser/service processes. + if (default_filter) + InitDefaultCrashCallback(default_filter); + return; + } + base::string16 pipe_name = base::ASCIIToUTF16(pipe_name_ascii); + +#ifdef _WIN64 + // The protocol for connecting to the out-of-process Breakpad crash + // reporter is different for x86-32 and x86-64: the message sizes + // are different because the message struct contains a pointer. As + // a result, there are two different named pipes to connect to. The + // 64-bit one is distinguished with an "-x64" suffix. + pipe_name += L"-x64"; +#endif + + // Get the alternate dump directory. We use the temp path. + wchar_t temp_dir[MAX_PATH] = {0}; + ::GetTempPathW(MAX_PATH, temp_dir); + + MINIDUMP_TYPE dump_type = kSmallDumpType; + // Capture full memory if explicitly instructed to. + if (command.HasSwitch(switches::kFullMemoryCrashReport)) + dump_type = kFullDumpType; + else if (GetCrashReporterClient()->GetShouldDumpLargerDumps( + is_per_user_install)) + dump_type = kLargerDumpType; + + g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, &FilterCallback, + callback, NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL, + dump_type, pipe_name.c_str(), custom_info); + + // Now initialize the non crash dump handler. + g_dumphandler_no_crash = new google_breakpad::ExceptionHandler(temp_dir, + &FilterCallbackWhenNoCrash, + &DumpDoneCallbackWhenNoCrash, + NULL, + // Set the handler to none so this handler would not be added to + // |handler_stack_| in |ExceptionHandler| which is a list of exception + // handlers. + google_breakpad::ExceptionHandler::HANDLER_NONE, + dump_type, pipe_name.c_str(), custom_info); + + // Set the DumpWithoutCrashingFunction for this instance of base.lib. Other + // executable images linked with base should set this again for + // DumpWithoutCrashing to function correctly. + // See chrome_main.cc for example. + base::debug::SetDumpWithoutCrashingFunction(&DumpProcessWithoutCrash); + + if (g_breakpad->IsOutOfProcess()) { + // Tells breakpad to handle breakpoint and single step exceptions. + // This might break JIT debuggers, but at least it will always + // generate a crashdump for these exceptions. + g_breakpad->set_handle_debug_exceptions(true); + +#ifndef _WIN64 + if (process_type != L"browser" && + !GetCrashReporterClient()->IsRunningUnattended()) { + // Initialize the hook TerminateProcess to catch unexpected exits. + InitTerminateProcessHooks(); + } +#endif + } +} + +void ConsumeInvalidHandleExceptions() { + if (g_breakpad) { + g_breakpad->set_consume_invalid_handle_exceptions(true); + } + if (g_dumphandler_no_crash) { + g_dumphandler_no_crash->set_consume_invalid_handle_exceptions(true); + } +} + +// If the user has disabled crash reporting uploads and restarted Chrome, the +// restarted instance will still contain the pipe environment variable, which +// will allow the restarted process to still upload crash reports. This function +// clears the environment variable, so that the restarted Chrome, which inherits +// its environment from the current Chrome, will no longer contain the variable. +extern "C" void __declspec(dllexport) __cdecl + ClearBreakpadPipeEnvironmentVariable() { + scoped_ptr env(base::Environment::Create()); + env->UnSetVar(kPipeNameVar); +} + +#ifdef _WIN64 +int CrashForExceptionInNonABICompliantCodeRange( + PEXCEPTION_RECORD ExceptionRecord, + ULONG64 EstablisherFrame, + PCONTEXT ContextRecord, + PDISPATCHER_CONTEXT DispatcherContext) { + EXCEPTION_POINTERS info = { ExceptionRecord, ContextRecord }; + return CrashForException(&info); +} + +struct ExceptionHandlerRecord { + RUNTIME_FUNCTION runtime_function; + UNWIND_INFO unwind_info; + unsigned char thunk[12]; +}; + +extern "C" void __declspec(dllexport) __cdecl +RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) { + ExceptionHandlerRecord* record = + reinterpret_cast(start); + + // We assume that the first page of the code range is executable and + // committed and reserved for breakpad. What could possibly go wrong? + + // All addresses are 32bit relative offsets to start. + record->runtime_function.BeginAddress = 0; + record->runtime_function.EndAddress = + base::checked_cast(size_in_bytes); + record->runtime_function.UnwindData = + offsetof(ExceptionHandlerRecord, unwind_info); + + // Create unwind info that only specifies an exception handler. + record->unwind_info.Version = 1; + record->unwind_info.Flags = UNW_FLAG_EHANDLER; + record->unwind_info.SizeOfProlog = 0; + record->unwind_info.CountOfCodes = 0; + record->unwind_info.FrameRegister = 0; + record->unwind_info.FrameOffset = 0; + record->unwind_info.ExceptionHandler = + offsetof(ExceptionHandlerRecord, thunk); + + // Hardcoded thunk. + // mov imm64, rax + record->thunk[0] = 0x48; + record->thunk[1] = 0xb8; + void* handler = &CrashForExceptionInNonABICompliantCodeRange; + memcpy(&record->thunk[2], &handler, 8); + + // jmp rax + record->thunk[10] = 0xff; + record->thunk[11] = 0xe0; + + // Protect reserved page against modifications. + DWORD old_protect; + CHECK(VirtualProtect( + start, sizeof(ExceptionHandlerRecord), PAGE_EXECUTE_READ, &old_protect)); + CHECK(RtlAddFunctionTable( + &record->runtime_function, 1, reinterpret_cast(start))); +} + +extern "C" void __declspec(dllexport) __cdecl +UnregisterNonABICompliantCodeRange(void* start) { + ExceptionHandlerRecord* record = + reinterpret_cast(start); + + CHECK(RtlDeleteFunctionTable(&record->runtime_function)); +} +#endif + +} // namespace breakpad diff --git a/components/crash/content/app/breakpad_win.h b/components/crash/content/app/breakpad_win.h new file mode 100644 index 0000000..c4e9349 --- /dev/null +++ b/components/crash/content/app/breakpad_win.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_WIN_H_ +#define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_WIN_H_ + +#include +#include +#include + +namespace breakpad { + +void InitCrashReporter(const std::string& process_type_switch); + +// If chrome has been restarted because it crashed, this function will display +// a dialog asking for permission to continue execution or to exit now. +bool ShowRestartDialogIfCrashed(bool* exit_now); + +// Tells Breakpad that our process is shutting down and to consume +// EXCEPTION_INVALID_HANDLE exceptions which occur if bad handle detection is +// enabled and the sandbox handle closer has previously closed handles owned by +// Windows DLLs. +void ConsumeInvalidHandleExceptions(); + +} // namespace breakpad + +#endif // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_WIN_H_ diff --git a/components/crash/content/app/crash_keys_win.cc b/components/crash/content/app/crash_keys_win.cc new file mode 100644 index 0000000..2c784fa --- /dev/null +++ b/components/crash/content/app/crash_keys_win.cc @@ -0,0 +1,192 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crash/content/app/crash_keys_win.h" + +#include + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "components/crash/content/app/crash_reporter_client.h" + +namespace breakpad { + +using crash_reporter::CrashReporterClient; + +namespace { + +const size_t kMaxPluginPathLength = 256; +const size_t kMaxDynamicEntries = 256; + +} // namespace + +CrashKeysWin* CrashKeysWin::keeper_; + +CrashKeysWin::CrashKeysWin() : dynamic_keys_offset_(0) { + DCHECK_EQ(static_cast(NULL), keeper_); + keeper_ = this; +} + +CrashKeysWin::~CrashKeysWin() { + DCHECK_EQ(this, keeper_); + keeper_ = NULL; +} + +// Appends the plugin path to |g_custom_entries|. +void CrashKeysWin::SetPluginPath(const std::wstring& path) { + if (path.size() > kMaxPluginPathLength) { + // If the path is too long, truncate from the start rather than the end, + // since we want to be able to recover the DLL name. + SetPluginPath(path.substr(path.size() - kMaxPluginPathLength)); + return; + } + + // The chunk size without terminator. + const size_t kChunkSize = static_cast( + google_breakpad::CustomInfoEntry::kValueMaxLength - 1); + + int chunk_index = 0; + size_t chunk_start = 0; // Current position inside |path| + + for (chunk_start = 0; chunk_start < path.size(); chunk_index++) { + size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start); + + custom_entries_.push_back(google_breakpad::CustomInfoEntry( + base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(), + path.substr(chunk_start, chunk_length).c_str())); + + chunk_start += chunk_length; + } +} + +// Appends the breakpad dump path to |g_custom_entries|. +void CrashKeysWin::SetBreakpadDumpPath(CrashReporterClient* crash_client) { + base::FilePath crash_dumps_dir_path; + if (crash_client->GetAlternativeCrashDumpLocation(&crash_dumps_dir_path)) { + custom_entries_.push_back(google_breakpad::CustomInfoEntry( + L"breakpad-dump-location", crash_dumps_dir_path.value().c_str())); + } +} + +// Returns the custom info structure based on the dll in parameter and the +// process type. +google_breakpad::CustomClientInfo* +CrashKeysWin::GetCustomInfo(const std::wstring& exe_path, + const std::wstring& type, + const std::wstring& profile_type, + base::CommandLine* cmd_line, + CrashReporterClient* crash_client) { + base::string16 version, product; + base::string16 special_build; + base::string16 channel_name; + + crash_client->GetProductNameAndVersion( + base::FilePath(exe_path), + &product, + &version, + &special_build, + &channel_name); + + // We only expect this method to be called once per process. + // Common enties + custom_entries_.push_back( + google_breakpad::CustomInfoEntry(L"ver", version.c_str())); + custom_entries_.push_back( + google_breakpad::CustomInfoEntry(L"prod", product.c_str())); + custom_entries_.push_back( + google_breakpad::CustomInfoEntry(L"plat", L"Win32")); + custom_entries_.push_back( + google_breakpad::CustomInfoEntry(L"ptype", type.c_str())); + custom_entries_.push_back( + google_breakpad::CustomInfoEntry( + L"pid", base::IntToString16(::GetCurrentProcessId()).c_str())); + custom_entries_.push_back( + google_breakpad::CustomInfoEntry(L"channel", channel_name.c_str())); + custom_entries_.push_back( + google_breakpad::CustomInfoEntry(L"profile-type", profile_type.c_str())); + + if (!special_build.empty()) { + custom_entries_.push_back( + google_breakpad::CustomInfoEntry(L"special", special_build.c_str())); + } + + if (type == L"plugin" || type == L"ppapi") { + std::wstring plugin_path = cmd_line->GetSwitchValueNative("plugin-path"); + if (!plugin_path.empty()) + SetPluginPath(plugin_path); + } + + // Check whether configuration management controls crash reporting. + bool crash_reporting_enabled = true; + bool controlled_by_policy = crash_client->ReportingIsEnforcedByPolicy( + &crash_reporting_enabled); + bool use_crash_service = !controlled_by_policy && + (cmd_line->HasSwitch(switches::kNoErrorDialogs) || + crash_client->IsRunningUnattended()); + if (use_crash_service) + SetBreakpadDumpPath(crash_client); + + // Create space for dynamic ad-hoc keys. The names and values are set using + // the API defined in base/debug/crash_logging.h. + dynamic_keys_offset_ = custom_entries_.size(); + for (size_t i = 0; i < kMaxDynamicEntries; ++i) { + // The names will be mutated as they are set. Un-numbered since these are + // merely placeholders. The name cannot be empty because Breakpad's + // HTTPUpload will interpret that as an invalid parameter. + custom_entries_.push_back( + google_breakpad::CustomInfoEntry(L"unspecified-crash-key", L"")); + } + + static google_breakpad::CustomClientInfo custom_client_info; + custom_client_info.entries = &custom_entries_.front(); + custom_client_info.count = custom_entries_.size(); + + return &custom_client_info; +} + +void CrashKeysWin::SetCrashKeyValue( + const std::wstring& key, const std::wstring& value) { + // CustomInfoEntry limits the length of key and value. If they exceed + // their maximum length the underlying string handling functions raise + // an exception and prematurely trigger a crash. Truncate here. + std::wstring safe_key(std::wstring(key).substr( + 0, google_breakpad::CustomInfoEntry::kNameMaxLength - 1)); + std::wstring safe_value(std::wstring(value).substr( + 0, google_breakpad::CustomInfoEntry::kValueMaxLength - 1)); + + // If we already have a value for this key, update it; otherwise, insert + // the new value if we have not exhausted the pre-allocated slots for dynamic + // entries. + base::AutoLock lock(lock_); + + DynamicEntriesMap::iterator it = dynamic_entries_.find(safe_key); + google_breakpad::CustomInfoEntry* entry = NULL; + if (it == dynamic_entries_.end()) { + if (dynamic_entries_.size() >= kMaxDynamicEntries) + return; + entry = &custom_entries_[dynamic_keys_offset_++]; + dynamic_entries_.insert(std::make_pair(safe_key, entry)); + } else { + entry = it->second; + } + + entry->set(safe_key.data(), safe_value.data()); +} + +void CrashKeysWin::ClearCrashKeyValue(const std::wstring& key) { + base::AutoLock lock(lock_); + + std::wstring key_string(key); + DynamicEntriesMap::iterator it = dynamic_entries_.find(key_string); + if (it == dynamic_entries_.end()) + return; + + it->second->set_value(NULL); +} + +} // namespace breakpad diff --git a/components/crash/content/app/crash_keys_win.h b/components/crash/content/app/crash_keys_win.h new file mode 100644 index 0000000..870de77 --- /dev/null +++ b/components/crash/content/app/crash_keys_win.h @@ -0,0 +1,87 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASH_KEYS_WIN_H_ +#define COMPONENTS_CRASH_CONTENT_APP_CRASH_KEYS_WIN_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "base/synchronization/lock.h" +#include "breakpad/src/client/windows/common/ipc_protocol.h" +#include "breakpad/src/client/windows/handler/exception_handler.h" + + +namespace base { +class CommandLine; +} // namespace base + +namespace crash_reporter { +class CrashReporterClient; +} + +namespace breakpad { + +// Manages the breakpad key/value pair stash, there may only be one instance +// of this class per process at one time. +class CrashKeysWin { + public: + CrashKeysWin(); + ~CrashKeysWin(); + + // May only be called once. + // |exe_path| is the path to the executable running, which may be used + // to figure out whether this is a user or system install. + // |type| is the process type, or mode this process is running in e.g. + // something like "browser" or "renderer". + // |profile_type| is a string describing the kind of the user's Windows + // profile, e.g. "mandatory", or "roaming" or similar. + // |cmd_line| is the current process' command line consulted for explicit + // crash reporting flags. + // |crash_client| is consulted for crash reporting settings. + google_breakpad::CustomClientInfo* GetCustomInfo( + const std::wstring& exe_path, + const std::wstring& type, + const std::wstring& profile_type, + base::CommandLine* cmd_line, + crash_reporter::CrashReporterClient* crash_client); + + void SetCrashKeyValue(const std::wstring& key, const std::wstring& value); + void ClearCrashKeyValue(const std::wstring& key); + + const std::vector& custom_info_entries() + const { + return custom_entries_; + } + + static CrashKeysWin* keeper() { return keeper_; } + + private: + // One-time initialization of private key/value pairs. + void SetPluginPath(const std::wstring& path); + void SetBreakpadDumpPath(crash_reporter::CrashReporterClient* crash_client); + + // Must not be resized after GetCustomInfo is invoked. + std::vector custom_entries_; + + typedef std::map + DynamicEntriesMap; + base::Lock lock_; + // Keeps track of the next index for a new dynamic entry. + size_t dynamic_keys_offset_; // Under lock_. + // Maintains key->entry information for dynamic key/value entries + // in custom_entries_. + DynamicEntriesMap dynamic_entries_; // Under lock_. + + // Stores the sole instance of this class allowed per process. + static CrashKeysWin* keeper_; + + DISALLOW_COPY_AND_ASSIGN(CrashKeysWin); +}; + +} // namespace breakpad + +#endif // COMPONENTS_CRASH_CONTENT_APP_CRASH_KEYS_WIN_H_ diff --git a/components/crash/content/app/crash_keys_win_unittest.cc b/components/crash/content/app/crash_keys_win_unittest.cc new file mode 100644 index 0000000..19d3942 --- /dev/null +++ b/components/crash/content/app/crash_keys_win_unittest.cc @@ -0,0 +1,142 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crash/content/app/crash_keys_win.h" + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/strings/stringprintf.h" +#include "components/crash/content/app/crash_reporter_client.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace breakpad { + +using testing::_; +using testing::DoAll; +using testing::Return; +using testing::SetArgPointee; + +class MockCrashReporterClient : public crash_reporter::CrashReporterClient { + public: + MOCK_METHOD1(GetAlternativeCrashDumpLocation, + bool(base::FilePath* crash_dir)); + MOCK_METHOD5(GetProductNameAndVersion, void(const base::FilePath& exe_path, + base::string16* product_name, + base::string16* version, + base::string16* special_build, + base::string16* channel_name)); + MOCK_METHOD3(ShouldShowRestartDialog, bool(base::string16* title, + base::string16* message, + bool* is_rtl_locale)); + MOCK_METHOD0(AboutToRestart, bool()); + MOCK_METHOD1(GetDeferredUploadsSupported, bool(bool is_per_user_install)); + MOCK_METHOD1(GetIsPerUserInstall, bool(const base::FilePath& exe_path)); + MOCK_METHOD1(GetShouldDumpLargerDumps, bool(bool is_per_user_install)); + MOCK_METHOD0(GetResultCodeRespawnFailed, int()); + MOCK_METHOD0(InitBrowserCrashDumpsRegKey, void()); + MOCK_METHOD1(RecordCrashDumpAttempt, void(bool is_real_crash)); + + MOCK_METHOD2(GetProductNameAndVersion, void(std::string* product_name, + std::string* version)); + MOCK_METHOD0(GetReporterLogFilename, base::FilePath()); + MOCK_METHOD1(GetCrashDumpLocation, bool(base::FilePath* crash_dir)); + MOCK_METHOD0(RegisterCrashKeys, size_t()); + MOCK_METHOD0(IsRunningUnattended, bool()); + MOCK_METHOD0(GetCollectStatsConsent, bool()); + MOCK_METHOD1(ReportingIsEnforcedByPolicy, bool(bool* breakpad_enabled)); + MOCK_METHOD0(GetAndroidMinidumpDescriptor, int()); + MOCK_METHOD1(EnableBreakpadForProcess, bool(const std::string& process_type)); +}; + +class CrashKeysWinTest : public testing::Test { + public: + + size_t CountKeyValueOccurences( + const google_breakpad::CustomClientInfo* client_info, + const wchar_t* key, const wchar_t* value); + + protected: + testing::StrictMock crash_client_; +}; + +size_t CrashKeysWinTest::CountKeyValueOccurences( + const google_breakpad::CustomClientInfo* client_info, + const wchar_t* key, const wchar_t* value) { + size_t occurrences = 0; + for (size_t i = 0; i < client_info->count; ++i) { + if (wcscmp(client_info->entries[i].name, key) == 0 && + wcscmp(client_info->entries[i].value, value) == 0) { + ++occurrences; + } + } + + return occurrences; +} + +TEST_F(CrashKeysWinTest, RecordsSelf) { + ASSERT_EQ(static_cast(NULL), CrashKeysWin::keeper()); + + { + CrashKeysWin crash_keys; + + ASSERT_EQ(&crash_keys, CrashKeysWin::keeper()); + } + + ASSERT_EQ(static_cast(NULL), CrashKeysWin::keeper()); +} + +// Tests the crash keys set up for the most common official build consumer +// scenario. No policy controls, not running unattended and no explicit +// switches. +TEST_F(CrashKeysWinTest, OfficialLikeKeys) { + CrashKeysWin crash_keys; + + const base::FilePath kExePath(L"C:\\temp\\exe_path.exe"); + // The exe path ought to get passed through to the breakpad client. + EXPECT_CALL(crash_client_, GetProductNameAndVersion(kExePath, _, _, _, _)) + .WillRepeatedly(DoAll( + SetArgPointee<1>(L"SomeProdName"), + SetArgPointee<2>(L"1.2.3.4"), + SetArgPointee<3>(L""), + SetArgPointee<4>(L"-devm"))); + + EXPECT_CALL(crash_client_, GetAlternativeCrashDumpLocation(_)) + .WillRepeatedly(DoAll( + SetArgPointee<0>(base::FilePath(L"C:\\temp")), + Return(false))); + + EXPECT_CALL(crash_client_, ReportingIsEnforcedByPolicy(_)) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(crash_client_, IsRunningUnattended()) + .WillRepeatedly(Return(false)); + + // Provide an empty command line. + base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM); + google_breakpad::CustomClientInfo* info = + crash_keys.GetCustomInfo(kExePath.value(), + L"made_up_type", + L"temporary", + &cmd_line, + &crash_client_); + + ASSERT_TRUE(info != NULL); + ASSERT_TRUE(info->entries != NULL); + + // We expect 7 fixed keys and a "freeboard" of 256 keys for dynamic entries. + EXPECT_EQ(256U + 7U, info->count); + + EXPECT_EQ(1, CountKeyValueOccurences(info, L"ver", L"1.2.3.4")); + EXPECT_EQ(1, CountKeyValueOccurences(info, L"prod", L"SomeProdName")); + EXPECT_EQ(1, CountKeyValueOccurences(info, L"plat", L"Win32")); + EXPECT_EQ(1, CountKeyValueOccurences(info, L"ptype", L"made_up_type")); + std::wstring pid_str(base::StringPrintf(L"%d", ::GetCurrentProcessId())); + EXPECT_EQ(1, CountKeyValueOccurences(info, L"pid", pid_str.c_str())); + EXPECT_EQ(1, CountKeyValueOccurences(info, L"channel", L"-devm")); + EXPECT_EQ(1, CountKeyValueOccurences(info, L"profile-type", L"temporary")); + EXPECT_EQ(256, CountKeyValueOccurences(info, L"unspecified-crash-key", L"")); +} + +} // namespace breakpad diff --git a/components/crash/content/app/crash_reporter_client.cc b/components/crash/content/app/crash_reporter_client.cc new file mode 100644 index 0000000..66388e2 --- /dev/null +++ b/components/crash/content/app/crash_reporter_client.cc @@ -0,0 +1,148 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crash/content/app/crash_reporter_client.h" + +#include "base/files/file_path.h" +#include "base/logging.h" + +namespace crash_reporter { + +namespace { + +CrashReporterClient* g_client = NULL; + +} // namespace + +void SetCrashReporterClient(CrashReporterClient* client) { + g_client = client; +} + +CrashReporterClient* GetCrashReporterClient() { + DCHECK(g_client); + return g_client; +} + +CrashReporterClient::CrashReporterClient() {} +CrashReporterClient::~CrashReporterClient() {} + +#if !defined(OS_MACOSX) +void CrashReporterClient::SetCrashReporterClientIdFromGUID( + const std::string& client_guid) { +} +#endif + +#if defined(OS_WIN) +bool CrashReporterClient::GetAlternativeCrashDumpLocation( + base::FilePath* crash_dir) { + return false; +} + +void CrashReporterClient::GetProductNameAndVersion( + const base::FilePath& exe_path, + base::string16* product_name, + base::string16* version, + base::string16* special_build, + base::string16* channel_name) { +} + +bool CrashReporterClient::ShouldShowRestartDialog(base::string16* title, + base::string16* message, + bool* is_rtl_locale) { + return false; +} + +bool CrashReporterClient::AboutToRestart() { + return false; +} + +bool CrashReporterClient::GetDeferredUploadsSupported(bool is_per_usr_install) { + return false; +} + +bool CrashReporterClient::GetIsPerUserInstall(const base::FilePath& exe_path) { + return true; +} + +bool CrashReporterClient::GetShouldDumpLargerDumps(bool is_per_user_install) { + return false; +} + +int CrashReporterClient::GetResultCodeRespawnFailed() { + return 0; +} + +void CrashReporterClient::InitBrowserCrashDumpsRegKey() { +} + +void CrashReporterClient::RecordCrashDumpAttempt(bool is_real_crash) { +} + +void CrashReporterClient::RecordCrashDumpAttemptResult(bool is_real_crash, + bool succeeded) { +} +#endif + +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) +void CrashReporterClient::GetProductNameAndVersion(const char** product_name, + const char** version) { +} + +base::FilePath CrashReporterClient::GetReporterLogFilename() { + return base::FilePath(); +} + +bool CrashReporterClient::HandleCrashDump(const char* crashdump_filename) { + return false; +} +#endif + +bool CrashReporterClient::GetCrashDumpLocation(base::FilePath* crash_dir) { + return false; +} + +size_t CrashReporterClient::RegisterCrashKeys() { + return 0; +} + +bool CrashReporterClient::IsRunningUnattended() { + return true; +} + +bool CrashReporterClient::GetCollectStatsConsent() { + return false; +} + +#if defined(OS_WIN) || defined(OS_MACOSX) +bool CrashReporterClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) { + return false; +} +#endif + +#if defined(OS_ANDROID) +int CrashReporterClient::GetAndroidMinidumpDescriptor() { + return 0; +} + +bool CrashReporterClient::ShouldEnableBreakpadMicrodumps() { +// Always enable microdumps on Android when stripping unwind tables. Rationale: +// when unwind tables are stripped out (to save binary size) the stack traces +// produced locally in the case of a crash / CHECK are meaningless. In order to +// provide meaningful development diagnostics (and keep the binary size savings) +// on Android we attach a secondary crash handler which serializes a reduced +// form of logcat on the console. +#if defined(NO_UNWIND_TABLES) + return true; +#else + return false; +#endif +} +#endif + +bool CrashReporterClient::EnableBreakpadForProcess( + const std::string& process_type) { + return false; +} + +} // namespace crash_reporter diff --git a/components/crash/content/app/crash_reporter_client.h b/components/crash/content/app/crash_reporter_client.h new file mode 100644 index 0000000..4a96954 --- /dev/null +++ b/components/crash/content/app/crash_reporter_client.h @@ -0,0 +1,158 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASH_REPORTER_CLIENT_H_ +#define COMPONENTS_CRASH_CONTENT_APP_CRASH_REPORTER_CLIENT_H_ + +#include + +#include "base/strings/string16.h" +#include "build/build_config.h" + +namespace base { +class FilePath; +} + +#if defined(OS_MACOSX) +// We don't want to directly include +// breakpad/src/client/mac/Framework/Breakpad.h here, so we repeat the +// definition of BreakpadRef. +// +// On Mac, when compiling without breakpad support, a stub implementation is +// compiled in. Not having any includes of the breakpad library allows for +// reusing this header for the stub. +typedef void* BreakpadRef; +#endif + +namespace crash_reporter { + +class CrashReporterClient; + +// Setter and getter for the client. The client should be set early, before any +// crash reporter code is called, and should stay alive throughout the entire +// runtime. +void SetCrashReporterClient(CrashReporterClient* client); + +#if defined(CRASH_IMPLEMENTATION) +// The components's embedder API should only be used by the component. +CrashReporterClient* GetCrashReporterClient(); +#endif + +// Interface that the embedder implements. +class CrashReporterClient { + public: + CrashReporterClient(); + virtual ~CrashReporterClient(); + +#if !defined(OS_MACOSX) + // Sets the crash reporting client ID, a unique identifier for the client + // that is sending crash reports. After it is set, it should not be changed. + // |client_guid| may either be a full GUID or a GUID that was already stripped + // from its dashes. + // + // On Mac OS X, this is the responsibility of Crashpad, and can not be set + // directly by the client. + virtual void SetCrashReporterClientIdFromGUID(const std::string& client_guid); +#endif + +#if defined(OS_WIN) + // Returns true if an alternative location to store the minidump files was + // specified. Returns true if |crash_dir| was set. + virtual bool GetAlternativeCrashDumpLocation(base::FilePath* crash_dir); + + // Returns a textual description of the product type and version to include + // in the crash report. + virtual void GetProductNameAndVersion(const base::FilePath& exe_path, + base::string16* product_name, + base::string16* version, + base::string16* special_build, + base::string16* channel_name); + + // Returns true if a restart dialog should be displayed. In that case, + // |message| and |title| are set to a message to display in a dialog box with + // the given title before restarting, and |is_rtl_locale| indicates whether + // to display the text as RTL. + virtual bool ShouldShowRestartDialog(base::string16* title, + base::string16* message, + bool* is_rtl_locale); + + // Returns true if it is ok to restart the application. Invoked right before + // restarting after a crash. + virtual bool AboutToRestart(); + + // Returns true if the crash report uploader supports deferred uploads. + virtual bool GetDeferredUploadsSupported(bool is_per_user_install); + + // Returns true if the running binary is a per-user installation. + virtual bool GetIsPerUserInstall(const base::FilePath& exe_path); + + // Returns true if larger crash dumps should be dumped. + virtual bool GetShouldDumpLargerDumps(bool is_per_user_install); + + // Returns the result code to return when breakpad failed to respawn a + // crashed process. + virtual int GetResultCodeRespawnFailed(); + + // Invoked when initializing the crash reporter in the browser process. + virtual void InitBrowserCrashDumpsRegKey(); + + // Invoked before attempting to write a minidump. + virtual void RecordCrashDumpAttempt(bool is_real_crash); + + // Invoked with the results of a minidump attempt. + virtual void RecordCrashDumpAttemptResult(bool is_real_crash, bool succeeded); +#endif + +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) + // Returns a textual description of the product type and version to include + // in the crash report. Neither out parameter should be set to NULL. + virtual void GetProductNameAndVersion(const char** product_name, + const char** version); + + virtual base::FilePath GetReporterLogFilename(); + + // Custom crash minidump handler after the minidump is generated. + // Returns true if the minidump is handled (client); otherwise, return false + // to fallback to default handler. + // WARNING: this handler runs in a compromised context. It may not call into + // libc nor allocate memory normally. + virtual bool HandleCrashDump(const char* crashdump_filename); +#endif + + // The location where minidump files should be written. Returns true if + // |crash_dir| was set. + virtual bool GetCrashDumpLocation(base::FilePath* crash_dir); + + // Register all of the potential crash keys that can be sent to the crash + // reporting server. Returns the size of the union of all keys. + virtual size_t RegisterCrashKeys(); + + // Returns true if running in unattended mode (for automated testing). + virtual bool IsRunningUnattended(); + + // Returns true if the user has given consent to collect stats. + virtual bool GetCollectStatsConsent(); + +#if defined(OS_WIN) || defined(OS_MACOSX) + // Returns true if crash reporting is enforced via management policies. In + // that case, |breakpad_enabled| is set to the value enforced by policies. + virtual bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled); +#endif + +#if defined(OS_ANDROID) + // Returns the descriptor key of the android minidump global descriptor. + virtual int GetAndroidMinidumpDescriptor(); + + // Returns true if breakpad microdumps should be enabled. This orthogonal to + // the standard minidump uploader (which depends on the user consent). + virtual bool ShouldEnableBreakpadMicrodumps(); +#endif + + // Returns true if breakpad should run in the given process type. + virtual bool EnableBreakpadForProcess(const std::string& process_type); +}; + +} // namespace crash_reporter + +#endif // COMPONENTS_CRASH_CONTENT_APP_CRASH_REPORTER_CLIENT_H_ diff --git a/components/crash/content/app/crashpad_mac.h b/components/crash/content/app/crashpad_mac.h new file mode 100644 index 0000000..b46cce5 --- /dev/null +++ b/components/crash/content/app/crashpad_mac.h @@ -0,0 +1,54 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_MAC_H_ +#define COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_MAC_H_ + +#include + +#include +#include + +namespace crash_reporter { + +// Initializes Crashpad in a way that is appropriate for process_type. If +// process_type is empty, initializes Crashpad for the browser process, which +// starts crashpad_handler and sets it as the exception handler. Other process +// types inherit this exception handler from the browser, but still need to +// perform additional initialization. +void InitializeCrashpad(const std::string& process_type); + +// Enables or disables crash report upload. This is a property of the Crashpad +// database. In a newly-created database, uploads will be disabled. This +// function only has an effect when called in the browser process. Its effect is +// immediate and applies to all other process types, including processes that +// are already running. +void SetUploadsEnabled(bool enabled); + +// Determines whether uploads are enabled or disabled. This information is only +// available in the browser process. +bool GetUploadsEnabled(); + +struct UploadedReport { + std::string local_id; + std::string remote_id; + time_t creation_time; +}; + +// Obtains a list of reports uploaded to the collection server. This function +// only operates when called in the browser process. All reports in the Crashpad +// database that have been successfully uploaded will be included in this list. +// The list will be sorted in descending order by report creation time (newest +// reports first). +// +// TODO(mark): The about:crashes UI expects to show only uploaded reports. If it +// is ever enhanced to work well with un-uploaded reports, those should be +// returned as well. Un-uploaded reports may have a pending upload, may have +// experienced upload failure, or may have been collected while uploads were +// disabled. +void GetUploadedReports(std::vector* uploaded_reports); + +} // namespace crash_reporter + +#endif // COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_MAC_H_ diff --git a/components/crash/content/app/crashpad_mac.mm b/components/crash/content/app/crashpad_mac.mm new file mode 100644 index 0000000..1082db4 --- /dev/null +++ b/components/crash/content/app/crashpad_mac.mm @@ -0,0 +1,251 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crash/content/app/crashpad_mac.h" + +#include +#include + +#include +#include +#include + +#include "base/auto_reset.h" +#include "base/debug/crash_logging.h" +#include "base/debug/dump_without_crashing.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/mac/bundle_locations.h" +#include "base/mac/foundation_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "components/crash/content/app/crash_reporter_client.h" +#include "third_party/crashpad/crashpad/client/crash_report_database.h" +#include "third_party/crashpad/crashpad/client/crashpad_client.h" +#include "third_party/crashpad/crashpad/client/crashpad_info.h" +#include "third_party/crashpad/crashpad/client/settings.h" +#include "third_party/crashpad/crashpad/client/simple_string_dictionary.h" +#include "third_party/crashpad/crashpad/client/simulate_crash.h" + +namespace crash_reporter { + +namespace { + +crashpad::SimpleStringDictionary* g_simple_string_dictionary; +crashpad::CrashReportDatabase* g_database; + +void SetCrashKeyValue(const base::StringPiece& key, + const base::StringPiece& value) { + g_simple_string_dictionary->SetKeyValue(key.data(), value.data()); +} + +void ClearCrashKey(const base::StringPiece& key) { + g_simple_string_dictionary->RemoveKey(key.data()); +} + +bool LogMessageHandler(int severity, + const char* file, + int line, + size_t message_start, + const std::string& string) { + // Only handle FATAL. + if (severity != logging::LOG_FATAL) { + return false; + } + + // In case of an out-of-memory condition, this code could be reentered when + // constructing and storing the key. Using a static is not thread-safe, but if + // multiple threads are in the process of a fatal crash at the same time, this + // should work. + static bool guarded = false; + if (guarded) { + return false; + } + base::AutoReset guard(&guarded, true); + + // Only log last path component. This matches logging.cc. + if (file) { + const char* slash = strrchr(file, '/'); + if (slash) { + file = slash + 1; + } + } + + std::string message = base::StringPrintf("%s:%d: %s", file, line, + string.c_str() + message_start); + SetCrashKeyValue("LOG_FATAL", message); + + // Rather than including the code to force the crash here, allow the caller to + // do it. + return false; +} + +void DumpWithoutCrashing() { + CRASHPAD_SIMULATE_CRASH(); +} + +} // namespace + +void InitializeCrashpad(const std::string& process_type) { + static bool initialized = false; + DCHECK(!initialized); + initialized = true; + + const bool browser_process = process_type.empty(); + CrashReporterClient* crash_reporter_client = GetCrashReporterClient(); + + base::FilePath database_path; // Only valid in the browser process. + + if (browser_process) { + @autoreleasepool { + base::FilePath framework_bundle_path = base::mac::FrameworkBundlePath(); + base::FilePath handler_path = + framework_bundle_path.Append("Helpers").Append("crashpad_handler"); + + // Is there a way to recover if this fails? + crash_reporter_client->GetCrashDumpLocation(&database_path); + + // TODO(mark): Reading the Breakpad keys is temporary and transitional. At + // the very least, they should be renamed to Crashpad. For the time being, + // this isn't the worst thing: Crashpad is still uploading to a + // Breakpad-type server, after all. + NSBundle* framework_bundle = base::mac::FrameworkBundle(); + NSString* product = base::mac::ObjCCast( + [framework_bundle objectForInfoDictionaryKey:@"BreakpadProduct"]); + NSString* version = base::mac::ObjCCast( + [framework_bundle objectForInfoDictionaryKey:@"BreakpadVersion"]); + NSString* url_ns = base::mac::ObjCCast( + [framework_bundle objectForInfoDictionaryKey:@"BreakpadURL"]); + + std::string url = base::SysNSStringToUTF8(url_ns); + + std::map process_annotations; + process_annotations["prod"] = base::SysNSStringToUTF8(product); + process_annotations["ver"] = base::SysNSStringToUTF8(version); + process_annotations["plat"] = std::string("OS X"); + + crashpad::CrashpadClient crashpad_client; + if (crashpad_client.StartHandler(handler_path, database_path, url, + process_annotations, + std::vector())) { + crashpad_client.UseHandler(); + } + } // @autoreleasepool + } + + crashpad::CrashpadInfo* crashpad_info = + crashpad::CrashpadInfo::GetCrashpadInfo(); + +#if defined(NDEBUG) + const bool is_debug_build = false; +#else + const bool is_debug_build = true; +#endif + + // Disable forwarding to the system's crash reporter in processes other than + // the browser process. For the browser, the system's crash reporter presents + // the crash UI to the user, so it's desirable there. Additionally, having + // crash reports appear in ~/Library/Logs/DiagnosticReports provides a + // fallback. Forwarding is turned off for debug-mode builds even for the + // browser process, because the system's crash reporter can take a very long + // time to chew on symbols. + if (!browser_process || is_debug_build) { + crashpad_info->set_system_crash_reporter_forwarding( + crashpad::TriState::kDisabled); + } + + g_simple_string_dictionary = new crashpad::SimpleStringDictionary(); + crashpad_info->set_simple_annotations(g_simple_string_dictionary); + + base::debug::SetCrashKeyReportingFunctions(SetCrashKeyValue, ClearCrashKey); + crash_reporter_client->RegisterCrashKeys(); + + SetCrashKeyValue("ptype", browser_process ? base::StringPiece("browser") + : base::StringPiece(process_type)); + SetCrashKeyValue("pid", base::IntToString(getpid())); + + logging::SetLogMessageHandler(LogMessageHandler); + + // If clients called CRASHPAD_SIMULATE_CRASH() instead of + // base::debug::DumpWithoutCrashing(), these dumps would appear as crashes in + // the correct function, at the correct file and line. This would be + // preferable to having all occurrences show up in DumpWithoutCrashing() at + // the same file and line. + base::debug::SetDumpWithoutCrashingFunction(DumpWithoutCrashing); + + if (browser_process) { + g_database = + crashpad::CrashReportDatabase::Initialize(database_path).release(); + + bool enable_uploads = false; + if (!crash_reporter_client->ReportingIsEnforcedByPolicy(&enable_uploads)) { + enable_uploads = crash_reporter_client->GetCollectStatsConsent() && + !crash_reporter_client->IsRunningUnattended(); + // Breakpad provided a --disable-breakpad switch to disable crash dumping + // (not just uploading) here. Crashpad doesn't need it: dumping is enabled + // unconditionally and uploading is gated on consent, which tests/bots + // shouldn't have. As a precaution, we also force disable uploading on + // bots even if consent is present. + } + + SetUploadsEnabled(enable_uploads); + } +} + +void SetUploadsEnabled(bool enable_uploads) { + if (g_database) { + crashpad::Settings* settings = g_database->GetSettings(); + settings->SetUploadsEnabled(enable_uploads); + } +} + +bool GetUploadsEnabled() { + if (g_database) { + crashpad::Settings* settings = g_database->GetSettings(); + bool enable_uploads; + if (settings->GetUploadsEnabled(&enable_uploads)) { + return enable_uploads; + } + } + + return false; +} + +void GetUploadedReports(std::vector* uploaded_reports) { + uploaded_reports->clear(); + + if (!g_database) { + return; + } + + std::vector completed_reports; + crashpad::CrashReportDatabase::OperationStatus status = + g_database->GetCompletedReports(&completed_reports); + if (status != crashpad::CrashReportDatabase::kNoError) { + return; + } + + for (const crashpad::CrashReportDatabase::Report& completed_report : + completed_reports) { + if (completed_report.uploaded) { + UploadedReport uploaded_report; + uploaded_report.local_id = completed_report.uuid.ToString(); + uploaded_report.remote_id = completed_report.id; + uploaded_report.creation_time = completed_report.creation_time; + + uploaded_reports->push_back(uploaded_report); + } + } + + struct { + bool operator()(const UploadedReport& a, const UploadedReport& b) { + return a.creation_time >= b.creation_time; + } + } sort_by_time; + std::sort(uploaded_reports->begin(), uploaded_reports->end(), sort_by_time); +} + +} // namespace crash_reporter diff --git a/components/crash/content/app/hard_error_handler_win.cc b/components/crash/content/app/hard_error_handler_win.cc new file mode 100644 index 0000000..a310666 --- /dev/null +++ b/components/crash/content/app/hard_error_handler_win.cc @@ -0,0 +1,112 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crash/content/app/hard_error_handler_win.h" + +#include +#include + +#include "base/basictypes.h" +#include "base/strings/string_util.h" +#include "components/crash/content/app/crash_reporter_client.h" + +namespace breakpad { + +using crash_reporter::GetCrashReporterClient; + +namespace { +const DWORD kExceptionModuleNotFound = VcppException(ERROR_SEVERITY_ERROR, + ERROR_MOD_NOT_FOUND); +const DWORD kExceptionEntryPtNotFound = VcppException(ERROR_SEVERITY_ERROR, + ERROR_PROC_NOT_FOUND); +// This is defined in but we can't include this file here. +const DWORD FACILITY_GRAPHICS_KERNEL = 0x1E; +const DWORD NT_STATUS_ENTRYPOINT_NOT_FOUND = 0xC0000139; +const DWORD NT_STATUS_DLL_NOT_FOUND = 0xC0000135; + +// We assume that exception codes are NT_STATUS codes. +DWORD FacilityFromException(DWORD exception_code) { + return (exception_code >> 16) & 0x0FFF; +} + +// This is not a generic function. It only works with some |nt_status| values. +// Check the strings here http://msdn.microsoft.com/en-us/library/cc704588.aspx +// before attempting to use this function. +void RaiseHardErrorMsg(long nt_status, const std::string& p1, + const std::string& p2) { + // If headless just exit silently. + if (GetCrashReporterClient()->IsRunningUnattended()) + return; + + HMODULE ntdll = ::GetModuleHandleA("NTDLL.DLL"); + wchar_t* msg_template = NULL; + size_t count = ::FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_HMODULE, + ntdll, + nt_status, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&msg_template), + 0, + NULL); + + if (!count) + return; + count += p1.size() + p2.size() + 1; + base::string16 message; + ::wsprintf(base::WriteInto(&message, count), msg_template, + p1.c_str(), p2.c_str()); + // The MB_SERVICE_NOTIFICATION causes this message to be displayed by + // csrss. This means that we are not creating windows or pumping WM messages + // in this process. + ::MessageBox(NULL, message.c_str(), + L"chrome.exe", + MB_OK | MB_SERVICE_NOTIFICATION); + ::LocalFree(msg_template); +} + +void ModuleNotFoundHardError(const EXCEPTION_RECORD* ex_record) { + DelayLoadInfo* dli = reinterpret_cast( + ex_record->ExceptionInformation[0]); + if (!dli->szDll) + return; + RaiseHardErrorMsg(NT_STATUS_DLL_NOT_FOUND, dli->szDll, std::string()); +} + +void EntryPointNotFoundHardError(const EXCEPTION_RECORD* ex_record) { + DelayLoadInfo* dli = reinterpret_cast( + ex_record->ExceptionInformation[0]); + if (!dli->dlp.fImportByName) + return; + if (!dli->dlp.szProcName) + return; + if (!dli->szDll) + return; + RaiseHardErrorMsg(NT_STATUS_ENTRYPOINT_NOT_FOUND, + dli->dlp.szProcName, dli->szDll); +} + +} // namespace + +bool HardErrorHandler(EXCEPTION_POINTERS* ex_info) { + if (!ex_info) + return false; + if (!ex_info->ExceptionRecord) + return false; + + long exception = ex_info->ExceptionRecord->ExceptionCode; + if (exception == kExceptionModuleNotFound) { + ModuleNotFoundHardError(ex_info->ExceptionRecord); + return true; + } else if (exception == kExceptionEntryPtNotFound) { + EntryPointNotFoundHardError(ex_info->ExceptionRecord); + return true; + } else if (FacilityFromException(exception) == FACILITY_GRAPHICS_KERNEL) { + RaiseHardErrorMsg(exception, std::string(), std::string()); + return true; + } + return false; +} + +} // namespace breakpad diff --git a/components/crash/content/app/hard_error_handler_win.h b/components/crash/content/app/hard_error_handler_win.h new file mode 100644 index 0000000..f1afc6b --- /dev/null +++ b/components/crash/content/app/hard_error_handler_win.h @@ -0,0 +1,34 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRASH_CONTENT_APP_HARD_ERROR_HANDLER_WIN_H_ +#define COMPONENTS_CRASH_CONTENT_APP_HARD_ERROR_HANDLER_WIN_H_ + +#include + +namespace breakpad { + +// This function is in charge of displaying a dialog box that informs the +// user of a fatal condition in chrome. It is meant to be called from +// breakpad's unhandled exception handler after the crash dump has been +// created. The return value will be true if we are to retry launching +// chrome (and show the 'chrome has crashed' dialog) or to silently exit. +// +// This function only handles a few known exceptions, currently: +// - Failure to load a delayload dll. +// - Failure to bind to a delayloaded import. +// - Fatal Graphics card failure (aura build only). +// +// If any of these conditions are encountered, a message box shown by +// the operating system CSRSS process via NtRaiseHardError is invoked. +// The wording and localization is up to the operating system. +// +// Do not call this function for memory related errors like heap corruption +// or stack exahustion. This function assumes that memory allocations are +// possible. +bool HardErrorHandler(EXCEPTION_POINTERS* ex_info); + +} // namespace breakpad + +#endif // COMPONENTS_CRASH_CONTENT_APP_HARD_ERROR_HANDLER_WIN_H_ diff --git a/components/crash/content/browser/BUILD.gn b/components/crash/content/browser/BUILD.gn new file mode 100644 index 0000000..1af0d89 --- /dev/null +++ b/components/crash/content/browser/BUILD.gn @@ -0,0 +1,38 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if (is_android) { + import("//build/config/android/config.gni") +} + +source_set("browser") { + sources = [ + "crash_dump_manager_android.cc", + "crash_dump_manager_android.h", + ] + + if (is_linux || is_android) { + set_sources_assignment_filter([]) + + # Want this file on both Linux and Android. + sources += [ + "crash_handler_host_linux.cc", + "crash_handler_host_linux.h", + ] + } + + deps = [ + "//base", + "//components/crash/content/app", + "//content/public/browser", + "//content/public/common", + ] + + # This is not in the GYP build but this target includes breakpad client + # headers, so add the dependency here. + if (is_posix && !is_ios) { + configs += [ "//breakpad:client_config" ] + public_configs = [ "//breakpad:client_config" ] + } +} diff --git a/components/crash/content/browser/DEPS b/components/crash/content/browser/DEPS new file mode 100644 index 0000000..c24130e --- /dev/null +++ b/components/crash/content/browser/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+content/public/browser", + "+content/public/common", +] diff --git a/components/crash/content/browser/crash_dump_manager_android.cc b/components/crash/content/browser/crash_dump_manager_android.cc new file mode 100644 index 0000000..62e77ad --- /dev/null +++ b/components/crash/content/browser/crash_dump_manager_android.cc @@ -0,0 +1,174 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crash/content/browser/crash_dump_manager_android.h" + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/format_macros.h" +#include "base/logging.h" +#include "base/posix/global_descriptors.h" +#include "base/process/process.h" +#include "base/rand_util.h" +#include "base/stl_util.h" +#include "base/strings/stringprintf.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/child_process_data.h" +#include "content/public/browser/file_descriptor_info.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/render_process_host.h" + +using content::BrowserThread; + +namespace breakpad { + +// static +CrashDumpManager* CrashDumpManager::instance_ = NULL; + +// static +CrashDumpManager* CrashDumpManager::GetInstance() { + CHECK(instance_); + return instance_; +} + +CrashDumpManager::CrashDumpManager(const base::FilePath& crash_dump_dir) + : crash_dump_dir_(crash_dump_dir) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(!instance_); + + instance_ = this; + + notification_registrar_.Add(this, + content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, + content::NotificationService::AllSources()); + notification_registrar_.Add(this, + content::NOTIFICATION_RENDERER_PROCESS_CLOSED, + content::NotificationService::AllSources()); + + BrowserChildProcessObserver::Add(this); +} + +CrashDumpManager::~CrashDumpManager() { + instance_ = NULL; + + BrowserChildProcessObserver::Remove(this); +} + +base::File CrashDumpManager::CreateMinidumpFile(int child_process_id) { + DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); + base::FilePath minidump_path; + if (!base::CreateTemporaryFile(&minidump_path)) + return base::File(); + + // We need read permission as the minidump is generated in several phases + // and needs to be read at some point. + int flags = base::File::FLAG_OPEN | base::File::FLAG_READ | + base::File::FLAG_WRITE; + base::File minidump_file(minidump_path, flags); + if (!minidump_file.IsValid()) { + LOG(ERROR) << "Failed to create temporary file, crash won't be reported."; + return base::File(); + } + + { + base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_); + DCHECK(!ContainsKey(child_process_id_to_minidump_path_, child_process_id)); + child_process_id_to_minidump_path_[child_process_id] = minidump_path; + } + return minidump_file.Pass(); +} + +// static +void CrashDumpManager::ProcessMinidump(const base::FilePath& minidump_path, + base::ProcessHandle pid) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + CHECK(instance_); + int64 file_size = 0; + int r = base::GetFileSize(minidump_path, &file_size); + DCHECK(r) << "Failed to retrieve size for minidump " + << minidump_path.value(); + + if (file_size == 0) { + // Empty minidump, this process did not crash. Just remove the file. + r = base::DeleteFile(minidump_path, false); + DCHECK(r) << "Failed to delete temporary minidump file " + << minidump_path.value(); + return; + } + + // We are dealing with a valid minidump. Copy it to the crash report + // directory from where Java code will upload it later on. + if (instance_->crash_dump_dir_.empty()) { + NOTREACHED() << "Failed to retrieve the crash dump directory."; + return; + } + const uint64 rand = base::RandUint64(); + const std::string filename = + base::StringPrintf("chromium-renderer-minidump-%016" PRIx64 ".dmp%d", + rand, pid); + base::FilePath dest_path = instance_->crash_dump_dir_.Append(filename); + r = base::Move(minidump_path, dest_path); + if (!r) { + LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value() + << " to " << dest_path.value(); + base::DeleteFile(minidump_path, false); + return; + } + VLOG(1) << "Crash minidump successfully generated: " << + instance_->crash_dump_dir_.Append(filename).value(); +} + +void CrashDumpManager::BrowserChildProcessHostDisconnected( + const content::ChildProcessData& data) { + OnChildExit(data.id, data.handle); +} + +void CrashDumpManager::BrowserChildProcessCrashed( + const content::ChildProcessData& data, + int exit_code) { + OnChildExit(data.id, data.handle); +} + +void CrashDumpManager::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: + // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer + // process is cleanly shutdown. However, we need to fallthrough so that + // we close the minidump_fd we kept open. + case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { + content::RenderProcessHost* rph = + content::Source(source).ptr(); + OnChildExit(rph->GetID(), rph->GetHandle()); + break; + } + default: + NOTREACHED(); + return; + } +} + +void CrashDumpManager::OnChildExit(int child_process_id, + base::ProcessHandle pid) { + base::FilePath minidump_path; + { + base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_); + ChildProcessIDToMinidumpPath::iterator iter = + child_process_id_to_minidump_path_.find(child_process_id); + if (iter == child_process_id_to_minidump_path_.end()) { + // We might get a NOTIFICATION_RENDERER_PROCESS_TERMINATED and a + // NOTIFICATION_RENDERER_PROCESS_CLOSED. + return; + } + minidump_path = iter->second; + child_process_id_to_minidump_path_.erase(iter); + } + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&CrashDumpManager::ProcessMinidump, minidump_path, pid)); +} + +} // namespace breakpad diff --git a/components/crash/content/browser/crash_dump_manager_android.h b/components/crash/content/browser/crash_dump_manager_android.h new file mode 100644 index 0000000..e3f2391 --- /dev/null +++ b/components/crash/content/browser/crash_dump_manager_android.h @@ -0,0 +1,85 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_ +#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_ + +#include + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/process/process.h" +#include "base/synchronization/lock.h" +#include "content/public/browser/browser_child_process_observer.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" + +namespace content { +class RenderProcessHost; +} + +namespace breakpad { + +// This class manages the crash minidumps. +// On Android, because of process isolation, each renderer process runs with a +// different UID. As a result, we cannot generate the minidumps in the browser +// (as the browser process does not have access to some system files for the +// crashed process). So the minidump is generated in the renderer process. +// Since the isolated process cannot open files, we provide it on creation with +// a file descriptor where to write the minidump in the event of a crash. +// This class creates these file descriptors and associates them with render +// processes and take the appropriate action when the render process terminates. +class CrashDumpManager : public content::BrowserChildProcessObserver, + public content::NotificationObserver { + public: + // The embedder should create a single instance of the CrashDumpManager. + static CrashDumpManager* GetInstance(); + + // Should be created on the UI thread. + explicit CrashDumpManager(const base::FilePath& crash_dump_dir); + + ~CrashDumpManager() override; + + // Returns a file that should be used to generate a minidump for the process + // |child_process_id|. + base::File CreateMinidumpFile(int child_process_id); + + private: + typedef std::map ChildProcessIDToMinidumpPath; + + static void ProcessMinidump(const base::FilePath& minidump_path, + base::ProcessHandle pid); + + // content::BrowserChildProcessObserver implementation: + void BrowserChildProcessHostDisconnected( + const content::ChildProcessData& data) override; + void BrowserChildProcessCrashed( + const content::ChildProcessData& data, + int exit_code) override; + + // NotificationObserver implementation: + void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) override; + + // Called on child process exit (including crash). + void OnChildExit(int child_process_id, base::ProcessHandle pid); + + content::NotificationRegistrar notification_registrar_; + + // This map should only be accessed with its lock aquired as it is accessed + // from the PROCESS_LAUNCHER and UI threads. + base::Lock child_process_id_to_minidump_path_lock_; + ChildProcessIDToMinidumpPath child_process_id_to_minidump_path_; + + base::FilePath crash_dump_dir_; + + static CrashDumpManager* instance_; + + DISALLOW_COPY_AND_ASSIGN(CrashDumpManager); +}; + +} // namespace breakpad + +#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_ diff --git a/components/crash/content/browser/crash_handler_host_linux.cc b/components/crash/content/browser/crash_handler_host_linux.cc new file mode 100644 index 0000000..a29a3ed --- /dev/null +++ b/components/crash/content/browser/crash_handler_host_linux.cc @@ -0,0 +1,437 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crash/content/browser/crash_handler_host_linux.h" + +#include +#include +#include +#include +#include + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_file.h" +#include "base/format_macros.h" +#include "base/linux_util.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/posix/eintr_wrapper.h" +#include "base/rand_util.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread.h" +#include "breakpad/src/client/linux/handler/exception_handler.h" +#include "breakpad/src/client/linux/minidump_writer/linux_dumper.h" +#include "breakpad/src/client/linux/minidump_writer/minidump_writer.h" +#include "components/crash/content/app/breakpad_linux_impl.h" +#include "content/public/browser/browser_thread.h" + +#if defined(OS_ANDROID) && !defined(__LP64__) +#include + +#define SYS_read __NR_read +#endif + +using content::BrowserThread; +using google_breakpad::ExceptionHandler; + +namespace breakpad { + +namespace { + +const size_t kNumFDs = 1; +// The length of the control message: +const size_t kControlMsgSize = + CMSG_SPACE(kNumFDs * sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); +// The length of the regular payload: +const size_t kCrashContextSize = sizeof(ExceptionHandler::CrashContext); + +// Handles the crash dump and frees the allocated BreakpadInfo struct. +void CrashDumpTask(CrashHandlerHostLinux* handler, + scoped_ptr info) { + if (handler->IsShuttingDown() && info->upload) { + base::DeleteFile(base::FilePath(info->filename), false); +#if defined(ADDRESS_SANITIZER) + base::DeleteFile(base::FilePath(info->log_filename), false); +#endif + return; + } + + HandleCrashDump(*info); + delete[] info->filename; +#if defined(ADDRESS_SANITIZER) + delete[] info->log_filename; + delete[] info->asan_report_str; +#endif + delete[] info->process_type; + delete[] info->distro; + delete info->crash_keys; +} + +} // namespace + +// Since instances of CrashHandlerHostLinux are leaked, they are only destroyed +// at the end of the processes lifetime, which is greater in span than the +// lifetime of the IO message loop. Thus, all calls to base::Bind() use +// non-refcounted pointers. + +CrashHandlerHostLinux::CrashHandlerHostLinux(const std::string& process_type, + const base::FilePath& dumps_path, + bool upload) + : process_type_(process_type), + dumps_path_(dumps_path), + upload_(upload), + shutting_down_(false), + worker_pool_token_(BrowserThread::GetBlockingPool()->GetSequenceToken()) { + int fds[2]; + // We use SOCK_SEQPACKET rather than SOCK_DGRAM to prevent the process from + // sending datagrams to other sockets on the system. The sandbox may prevent + // the process from calling socket() to create new sockets, but it'll still + // inherit some sockets. With PF_UNIX+SOCK_DGRAM, it can call sendmsg to send + // a datagram to any (abstract) socket on the same system. With + // SOCK_SEQPACKET, this is prevented. + CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); + static const int on = 1; + + // Enable passcred on the server end of the socket + CHECK_EQ(0, setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))); + + process_socket_ = fds[0]; + browser_socket_ = fds[1]; + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&CrashHandlerHostLinux::Init, base::Unretained(this))); +} + +CrashHandlerHostLinux::~CrashHandlerHostLinux() { + close(process_socket_); + close(browser_socket_); +} + +void CrashHandlerHostLinux::StartUploaderThread() { + uploader_thread_.reset( + new base::Thread(process_type_ + "_crash_uploader")); + uploader_thread_->Start(); +} + +void CrashHandlerHostLinux::Init() { + base::MessageLoopForIO* ml = base::MessageLoopForIO::current(); + CHECK(ml->WatchFileDescriptor( + browser_socket_, true /* persistent */, + base::MessageLoopForIO::WATCH_READ, + &file_descriptor_watcher_, this)); + ml->AddDestructionObserver(this); +} + +void CrashHandlerHostLinux::OnFileCanWriteWithoutBlocking(int fd) { + NOTREACHED(); +} + +void CrashHandlerHostLinux::OnFileCanReadWithoutBlocking(int fd) { + DCHECK_EQ(browser_socket_, fd); + + // A process has crashed and has signaled us by writing a datagram + // to the death signal socket. The datagram contains the crash context needed + // for writing the minidump as well as a file descriptor and a credentials + // block so that they can't lie about their pid. + // + // The message sender is in components/crash/content/app/breakpad_linux.cc. + + struct msghdr msg = {0}; + struct iovec iov[kCrashIovSize]; + + scoped_ptr crash_context(new char[kCrashContextSize]); +#if defined(ADDRESS_SANITIZER) + scoped_ptr asan_report(new char[kMaxAsanReportSize + 1]); +#endif + + scoped_ptr crash_keys(new CrashKeyStorage); + google_breakpad::SerializedNonAllocatingMap* serialized_crash_keys; + size_t crash_keys_size = crash_keys->Serialize( + const_cast( + &serialized_crash_keys)); + + char* tid_buf_addr = NULL; + int tid_fd = -1; + uint64_t uptime; + size_t oom_size; + char control[kControlMsgSize]; + const ssize_t expected_msg_size = + kCrashContextSize + + sizeof(tid_buf_addr) + sizeof(tid_fd) + + sizeof(uptime) + +#if defined(ADDRESS_SANITIZER) + kMaxAsanReportSize + 1 + +#endif + sizeof(oom_size) + + crash_keys_size; + iov[0].iov_base = crash_context.get(); + iov[0].iov_len = kCrashContextSize; + iov[1].iov_base = &tid_buf_addr; + iov[1].iov_len = sizeof(tid_buf_addr); + iov[2].iov_base = &tid_fd; + iov[2].iov_len = sizeof(tid_fd); + iov[3].iov_base = &uptime; + iov[3].iov_len = sizeof(uptime); + iov[4].iov_base = &oom_size; + iov[4].iov_len = sizeof(oom_size); + iov[5].iov_base = serialized_crash_keys; + iov[5].iov_len = crash_keys_size; +#if !defined(ADDRESS_SANITIZER) + static_assert(5 == kCrashIovSize - 1, "kCrashIovSize should equal 6"); +#else + iov[6].iov_base = asan_report.get(); + iov[6].iov_len = kMaxAsanReportSize + 1; + static_assert(6 == kCrashIovSize - 1, "kCrashIovSize should equal 7"); +#endif + msg.msg_iov = iov; + msg.msg_iovlen = kCrashIovSize; + msg.msg_control = control; + msg.msg_controllen = kControlMsgSize; + + const ssize_t msg_size = HANDLE_EINTR(recvmsg(browser_socket_, &msg, 0)); + if (msg_size < 0) { + LOG(ERROR) << "Error reading from death signal socket. Crash dumping" + << " is disabled." + << " msg_size:" << msg_size + << " errno:" << errno; + file_descriptor_watcher_.StopWatchingFileDescriptor(); + return; + } + const bool bad_message = (msg_size != expected_msg_size || + msg.msg_controllen != kControlMsgSize || + msg.msg_flags & ~MSG_TRUNC); + base::ScopedFD signal_fd; + pid_t crashing_pid = -1; + if (msg.msg_controllen > 0) { + // Walk the control payload and extract the file descriptor and + // validated pid. + for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; + hdr = CMSG_NXTHDR(&msg, hdr)) { + if (hdr->cmsg_level != SOL_SOCKET) + continue; + if (hdr->cmsg_type == SCM_RIGHTS) { + const size_t len = hdr->cmsg_len - + (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); + DCHECK_EQ(0U, len % sizeof(int)); + const size_t num_fds = len / sizeof(int); + if (num_fds != kNumFDs) { + // A nasty process could try and send us too many descriptors and + // force a leak. + LOG(ERROR) << "Death signal contained wrong number of descriptors;" + << " num_fds:" << num_fds; + for (size_t i = 0; i < num_fds; ++i) + close(reinterpret_cast(CMSG_DATA(hdr))[i]); + return; + } + DCHECK(!signal_fd.is_valid()); + int fd = reinterpret_cast(CMSG_DATA(hdr))[0]; + DCHECK_GE(fd, 0); // The kernel should never send a negative fd. + signal_fd.reset(fd); + } else if (hdr->cmsg_type == SCM_CREDENTIALS) { + DCHECK_EQ(-1, crashing_pid); + const struct ucred *cred = + reinterpret_cast(CMSG_DATA(hdr)); + crashing_pid = cred->pid; + } + } + } + + if (bad_message) { + LOG(ERROR) << "Received death signal message with the wrong size;" + << " msg.msg_controllen:" << msg.msg_controllen + << " msg.msg_flags:" << msg.msg_flags + << " kCrashContextSize:" << kCrashContextSize + << " kControlMsgSize:" << kControlMsgSize; + return; + } + if (crashing_pid == -1 || !signal_fd.is_valid()) { + LOG(ERROR) << "Death signal message didn't contain all expected control" + << " messages"; + return; + } + + // The crashing TID set inside the compromised context via + // sys_gettid() in ExceptionHandler::HandleSignal might be wrong (if + // the kernel supports PID namespacing) and may need to be + // translated. + // + // We expect the crashing thread to be in sys_read(), waiting for us to + // write to |signal_fd|. Most newer kernels where we have the different pid + // namespaces also have /proc/[pid]/syscall, so we can look through + // |actual_crashing_pid|'s thread group and find the thread that's in the + // read syscall with the right arguments. + + std::string expected_syscall_data; + // /proc/[pid]/syscall is formatted as follows: + // syscall_number arg1 ... arg6 sp pc + // but we just check syscall_number through arg3. + base::StringAppendF(&expected_syscall_data, "%d 0x%x %p 0x1 ", + SYS_read, tid_fd, tid_buf_addr); + bool syscall_supported = false; + pid_t crashing_tid = + base::FindThreadIDWithSyscall(crashing_pid, + expected_syscall_data, + &syscall_supported); + if (crashing_tid == -1) { + // We didn't find the thread we want. Maybe it didn't reach + // sys_read() yet or the thread went away. We'll just take a + // guess here and assume the crashing thread is the thread group + // leader. If procfs syscall is not supported by the kernel, then + // we assume the kernel also does not support TID namespacing and + // trust the TID passed by the crashing process. + LOG(WARNING) << "Could not translate tid - assuming crashing thread is " + "thread group leader; syscall_supported=" << syscall_supported; + crashing_tid = crashing_pid; + } + + ExceptionHandler::CrashContext* bad_context = + reinterpret_cast(crash_context.get()); + bad_context->tid = crashing_tid; + + scoped_ptr info(new BreakpadInfo); + + info->fd = -1; + info->process_type_length = process_type_.length(); + // Freed in CrashDumpTask(). + char* process_type_str = new char[info->process_type_length + 1]; + process_type_.copy(process_type_str, info->process_type_length); + process_type_str[info->process_type_length] = '\0'; + info->process_type = process_type_str; + + // Memory released from scoped_ptrs below are also freed in CrashDumpTask(). + info->crash_keys = crash_keys.release(); +#if defined(ADDRESS_SANITIZER) + asan_report[kMaxAsanReportSize] = '\0'; + info->asan_report_str = asan_report.release(); + info->asan_report_length = strlen(info->asan_report_str); +#endif + + info->process_start_time = uptime; + info->oom_size = oom_size; +#if defined(OS_ANDROID) + // Nothing gets uploaded in android. + info->upload = false; +#else + info->upload = upload_; +#endif + + + BrowserThread::GetBlockingPool()->PostSequencedWorkerTask( + worker_pool_token_, + FROM_HERE, + base::Bind(&CrashHandlerHostLinux::WriteDumpFile, + base::Unretained(this), + base::Passed(&info), + base::Passed(&crash_context), + crashing_pid, + signal_fd.release())); +} + +void CrashHandlerHostLinux::WriteDumpFile(scoped_ptr info, + scoped_ptr crash_context, + pid_t crashing_pid, + int signal_fd) { + DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread( + worker_pool_token_)); + + // Set |info->distro| here because base::GetLinuxDistro() needs to run on a + // blocking thread. + std::string distro = base::GetLinuxDistro(); + info->distro_length = distro.length(); + // Freed in CrashDumpTask(). + char* distro_str = new char[info->distro_length + 1]; + distro.copy(distro_str, info->distro_length); + distro_str[info->distro_length] = '\0'; + info->distro = distro_str; + + base::FilePath dumps_path("/tmp"); + PathService::Get(base::DIR_TEMP, &dumps_path); + if (!info->upload) + dumps_path = dumps_path_; + const std::string minidump_filename = + base::StringPrintf("%s/chromium-%s-minidump-%016" PRIx64 ".dmp", + dumps_path.value().c_str(), + process_type_.c_str(), + base::RandUint64()); + + if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), + kMaxMinidumpFileSize, + crashing_pid, + crash_context.get(), + kCrashContextSize, + google_breakpad::MappingList(), + google_breakpad::AppMemoryList())) { + LOG(ERROR) << "Failed to write crash dump for pid " << crashing_pid; + } +#if defined(ADDRESS_SANITIZER) + // Create a temporary file holding the AddressSanitizer report. + const base::FilePath log_path = + base::FilePath(minidump_filename).ReplaceExtension("log"); + base::WriteFile(log_path, info->asan_report_str, info->asan_report_length); +#endif + + // Freed in CrashDumpTask(). + char* minidump_filename_str = new char[minidump_filename.length() + 1]; + minidump_filename.copy(minidump_filename_str, minidump_filename.length()); + minidump_filename_str[minidump_filename.length()] = '\0'; + info->filename = minidump_filename_str; +#if defined(ADDRESS_SANITIZER) + // Freed in CrashDumpTask(). + char* minidump_log_filename_str = new char[minidump_filename.length() + 1]; + minidump_filename.copy(minidump_log_filename_str, minidump_filename.length()); + memcpy(minidump_log_filename_str + minidump_filename.length() - 3, "log", 3); + minidump_log_filename_str[minidump_filename.length()] = '\0'; + info->log_filename = minidump_log_filename_str; +#endif + info->pid = crashing_pid; + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&CrashHandlerHostLinux::QueueCrashDumpTask, + base::Unretained(this), + base::Passed(&info), + signal_fd)); +} + +void CrashHandlerHostLinux::QueueCrashDumpTask(scoped_ptr info, + int signal_fd) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + // Send the done signal to the process: it can exit now. + struct msghdr msg = {0}; + struct iovec done_iov; + done_iov.iov_base = const_cast("\x42"); + done_iov.iov_len = 1; + msg.msg_iov = &done_iov; + msg.msg_iovlen = 1; + + HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); + close(signal_fd); + + uploader_thread_->task_runner()->PostTask( + FROM_HERE, + base::Bind(&CrashDumpTask, base::Unretained(this), base::Passed(&info))); +} + +void CrashHandlerHostLinux::WillDestroyCurrentMessageLoop() { + file_descriptor_watcher_.StopWatchingFileDescriptor(); + + // If we are quitting and there are crash dumps in the queue, turn them into + // no-ops. + shutting_down_ = true; + uploader_thread_->Stop(); +} + +bool CrashHandlerHostLinux::IsShuttingDown() const { + return shutting_down_; +} + +} // namespace breakpad diff --git a/components/crash/content/browser/crash_handler_host_linux.h b/components/crash/content/browser/crash_handler_host_linux.h new file mode 100644 index 0000000..92ef522 --- /dev/null +++ b/components/crash/content/browser/crash_handler_host_linux.h @@ -0,0 +1,93 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_HANDLER_HOST_LINUX_H_ +#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_HANDLER_HOST_LINUX_H_ + +#include + +#include + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/sequenced_worker_pool.h" + +namespace base { +class Thread; +} + +namespace breakpad { + +struct BreakpadInfo; + +// This is the host for processes which run breakpad inside the sandbox on +// Linux or Android. We perform the crash dump from the browser because it +// allows us to be outside the sandbox. +// +// Processes signal that they need to be dumped by sending a datagram over a +// UNIX domain socket. All processes of the same type share the client end of +// this socket which is installed in their descriptor table before exec. +class CrashHandlerHostLinux : public base::MessageLoopForIO::Watcher, + public base::MessageLoop::DestructionObserver { + public: + CrashHandlerHostLinux(const std::string& process_type, + const base::FilePath& dumps_path, + bool upload); + ~CrashHandlerHostLinux() override; + + // Starts the uploader thread. Must be called immediately after creating the + // class. + void StartUploaderThread(); + + // Get the file descriptor which processes should be given in order to signal + // crashes to the browser. + int GetDeathSignalSocket() const { + return process_socket_; + } + + // MessagePumbLibevent::Watcher impl: + void OnFileCanWriteWithoutBlocking(int fd) override; + void OnFileCanReadWithoutBlocking(int fd) override; + + // MessageLoop::DestructionObserver impl: + void WillDestroyCurrentMessageLoop() override; + + // Whether we are shutting down or not. + bool IsShuttingDown() const; + + private: + void Init(); + + // Do work in a sequenced worker pool for OnFileCanReadWithoutBlocking(). + void WriteDumpFile(scoped_ptr info, + scoped_ptr crash_context, + pid_t crashing_pid, + int signal_fd); + + // Continue OnFileCanReadWithoutBlocking()'s work on the IO thread. + void QueueCrashDumpTask(scoped_ptr info, int signal_fd); + + std::string process_type_; + base::FilePath dumps_path_; + bool upload_; + + int process_socket_; + int browser_socket_; + + base::MessageLoopForIO::FileDescriptorWatcher file_descriptor_watcher_; + scoped_ptr uploader_thread_; + bool shutting_down_; + + // Unique sequence token so that writing crash dump won't be blocked + // by other tasks. + base::SequencedWorkerPool::SequenceToken worker_pool_token_; + + DISALLOW_COPY_AND_ASSIGN(CrashHandlerHostLinux); +}; + +} // namespace breakpad + +#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_HANDLER_HOST_LINUX_H_ diff --git a/components/crash/content/tools/BUILD.gn b/components/crash/content/tools/BUILD.gn new file mode 100644 index 0000000..82f282b --- /dev/null +++ b/components/crash/content/tools/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if (is_win) { + source_set("crash_service") { + sources = [ + "crash_service.cc", + "crash_service.h", + ] + + deps = [ + "//base", + "//breakpad:breakpad_handler", + "//breakpad:breakpad_sender", + ] + } +} diff --git a/components/crash/content/tools/crash_service.cc b/components/crash/content/tools/crash_service.cc new file mode 100644 index 0000000..ed55bbf --- /dev/null +++ b/components/crash/content/tools/crash_service.cc @@ -0,0 +1,487 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/crash/content/tools/crash_service.h" + +#include + +#include +#include +#include + +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/win/windows_version.h" +#include "breakpad/src/client/windows/crash_generation/client_info.h" +#include "breakpad/src/client/windows/crash_generation/crash_generation_server.h" +#include "breakpad/src/client/windows/sender/crash_report_sender.h" + +namespace breakpad { + +namespace { + +const wchar_t kTestPipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; + +const wchar_t kCrashReportURL[] = L"https://clients2.google.com/cr/report"; +const wchar_t kCheckPointFile[] = L"crash_checkpoint.txt"; + +typedef std::map CrashMap; + +bool CustomInfoToMap(const google_breakpad::ClientInfo* client_info, + const std::wstring& reporter_tag, CrashMap* map) { + google_breakpad::CustomClientInfo info = client_info->GetCustomInfo(); + + for (uintptr_t i = 0; i < info.count; ++i) { + (*map)[info.entries[i].name] = info.entries[i].value; + } + + (*map)[L"rept"] = reporter_tag; + + return !map->empty(); +} + +bool WriteCustomInfoToFile(const std::wstring& dump_path, const CrashMap& map) { + std::wstring file_path(dump_path); + size_t last_dot = file_path.rfind(L'.'); + if (last_dot == std::wstring::npos) + return false; + file_path.resize(last_dot); + file_path += L".txt"; + + std::wofstream file(file_path.c_str(), + std::ios_base::out | std::ios_base::app | std::ios::binary); + if (!file.is_open()) + return false; + + CrashMap::const_iterator pos; + for (pos = map.begin(); pos != map.end(); ++pos) { + std::wstring line = pos->first; + line += L':'; + line += pos->second; + line += L'\n'; + file.write(line.c_str(), static_cast(line.length())); + } + return true; +} + +// The window procedure task is to handle when a) the user logs off. +// b) the system shuts down or c) when the user closes the window. +LRESULT __stdcall CrashSvcWndProc(HWND hwnd, UINT message, + WPARAM wparam, LPARAM lparam) { + switch (message) { + case WM_CLOSE: + case WM_ENDSESSION: + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hwnd, message, wparam, lparam); + } + return 0; +} + +// This is the main and only application window. +HWND g_top_window = NULL; + +bool CreateTopWindow(HINSTANCE instance, bool visible) { + WNDCLASSEXW wcx = {0}; + wcx.cbSize = sizeof(wcx); + wcx.style = CS_HREDRAW | CS_VREDRAW; + wcx.lpfnWndProc = CrashSvcWndProc; + wcx.hInstance = instance; + wcx.lpszClassName = L"crash_svc_class"; + ::RegisterClassExW(&wcx); + DWORD style = visible ? WS_POPUPWINDOW | WS_VISIBLE : WS_OVERLAPPED; + + // The window size is zero but being a popup window still shows in the + // task bar and can be closed using the system menu or using task manager. + HWND window = CreateWindowExW(0, wcx.lpszClassName, L"crash service", style, + CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, + NULL, NULL, instance, NULL); + if (!window) + return false; + + ::UpdateWindow(window); + VLOG(1) << "window handle is " << window; + g_top_window = window; + return true; +} + +// Simple helper class to keep the process alive until the current request +// finishes. +class ProcessingLock { + public: + ProcessingLock() { + ::InterlockedIncrement(&op_count_); + } + ~ProcessingLock() { + ::InterlockedDecrement(&op_count_); + } + static bool IsWorking() { + return (op_count_ != 0); + } + private: + static volatile LONG op_count_; +}; + +volatile LONG ProcessingLock::op_count_ = 0; + +// This structure contains the information that the worker thread needs to +// send a crash dump to the server. +struct DumpJobInfo { + DWORD pid; + CrashService* self; + CrashMap map; + std::wstring dump_path; + + DumpJobInfo(DWORD process_id, CrashService* service, + const CrashMap& crash_map, const std::wstring& path) + : pid(process_id), self(service), map(crash_map), dump_path(path) { + } +}; + +} // namespace + +// Command line switches: +const char CrashService::kMaxReports[] = "max-reports"; +const char CrashService::kNoWindow[] = "no-window"; +const char CrashService::kReporterTag[] = "reporter"; +const char CrashService::kDumpsDir[] = "dumps-dir"; +const char CrashService::kPipeName[] = "pipe-name"; + +CrashService::CrashService() + : dumper_(NULL), + sender_(NULL), + requests_handled_(0), + requests_sent_(0), + clients_connected_(0), + clients_terminated_(0) { +} + +CrashService::~CrashService() { + base::AutoLock lock(sending_); + delete dumper_; + delete sender_; +} + +bool CrashService::Initialize(const base::FilePath& operating_dir, + const base::FilePath& dumps_path) { + using google_breakpad::CrashReportSender; + using google_breakpad::CrashGenerationServer; + + std::wstring pipe_name = kTestPipeName; + int max_reports = -1; + + // The checkpoint file allows CrashReportSender to enforce the the maximum + // reports per day quota. Does not seem to serve any other purpose. + base::FilePath checkpoint_path = operating_dir.Append(kCheckPointFile); + + base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess(); + + base::FilePath dumps_path_to_use = dumps_path; + + if (cmd_line.HasSwitch(kDumpsDir)) { + dumps_path_to_use = + base::FilePath(cmd_line.GetSwitchValueNative(kDumpsDir)); + } + + // We can override the send reports quota with a command line switch. + if (cmd_line.HasSwitch(kMaxReports)) + max_reports = _wtoi(cmd_line.GetSwitchValueNative(kMaxReports).c_str()); + + // Allow the global pipe name to be overridden for better testability. + if (cmd_line.HasSwitch(kPipeName)) + pipe_name = cmd_line.GetSwitchValueNative(kPipeName); + +#ifdef _WIN64 + pipe_name += L"-x64"; +#endif + + if (max_reports > 0) { + // Create the http sender object. + sender_ = new CrashReportSender(checkpoint_path.value()); + sender_->set_max_reports_per_day(max_reports); + } + + SECURITY_ATTRIBUTES security_attributes = {0}; + SECURITY_ATTRIBUTES* security_attributes_actual = NULL; + + if (base::win::GetVersion() >= base::win::VERSION_VISTA) { + SECURITY_DESCRIPTOR* security_descriptor = + reinterpret_cast( + GetSecurityDescriptorForLowIntegrity()); + DCHECK(security_descriptor != NULL); + + security_attributes.nLength = sizeof(security_attributes); + security_attributes.lpSecurityDescriptor = security_descriptor; + security_attributes.bInheritHandle = FALSE; + + security_attributes_actual = &security_attributes; + } + + // Create the OOP crash generator object. + dumper_ = new CrashGenerationServer(pipe_name, security_attributes_actual, + &CrashService::OnClientConnected, this, + &CrashService::OnClientDumpRequest, this, + &CrashService::OnClientExited, this, + NULL, NULL, + true, &dumps_path_to_use.value()); + + if (!dumper_) { + LOG(ERROR) << "could not create dumper"; + if (security_attributes.lpSecurityDescriptor) + LocalFree(security_attributes.lpSecurityDescriptor); + return false; + } + + if (!CreateTopWindow(::GetModuleHandleW(NULL), + !cmd_line.HasSwitch(kNoWindow))) { + LOG(ERROR) << "could not create window"; + if (security_attributes.lpSecurityDescriptor) + LocalFree(security_attributes.lpSecurityDescriptor); + return false; + } + + reporter_tag_ = L"crash svc"; + if (cmd_line.HasSwitch(kReporterTag)) + reporter_tag_ = cmd_line.GetSwitchValueNative(kReporterTag); + + // Log basic information. + VLOG(1) << "pipe name is " << pipe_name + << "\ndumps at " << dumps_path_to_use.value(); + + if (sender_) { + VLOG(1) << "checkpoint is " << checkpoint_path.value() + << "\nserver is " << kCrashReportURL + << "\nmaximum " << sender_->max_reports_per_day() << " reports/day" + << "\nreporter is " << reporter_tag_; + } + // Start servicing clients. + if (!dumper_->Start()) { + LOG(ERROR) << "could not start dumper"; + if (security_attributes.lpSecurityDescriptor) + LocalFree(security_attributes.lpSecurityDescriptor); + return false; + } + + if (security_attributes.lpSecurityDescriptor) + LocalFree(security_attributes.lpSecurityDescriptor); + + // This is throwaway code. We don't need to sync with the browser process + // once Google Update is updated to a version supporting OOP crash handling. + // Create or open an event to signal the browser process that the crash + // service is initialized. + HANDLE running_event = + ::CreateEventW(NULL, TRUE, TRUE, L"g_chrome_crash_svc"); + // If the browser already had the event open, the CreateEvent call did not + // signal it. We need to do it manually. + ::SetEvent(running_event); + + return true; +} + +void CrashService::OnClientConnected(void* context, + const google_breakpad::ClientInfo* client_info) { + ProcessingLock lock; + VLOG(1) << "client start. pid = " << client_info->pid(); + CrashService* self = static_cast(context); + ::InterlockedIncrement(&self->clients_connected_); +} + +void CrashService::OnClientExited(void* context, + const google_breakpad::ClientInfo* client_info) { + ProcessingLock lock; + VLOG(1) << "client end. pid = " << client_info->pid(); + CrashService* self = static_cast(context); + ::InterlockedIncrement(&self->clients_terminated_); + + if (!self->sender_) + return; + + // When we are instructed to send reports we need to exit if there are + // no more clients to service. The next client that runs will start us. + // Only chrome.exe starts crash_service with a non-zero max_reports. + if (self->clients_connected_ > self->clients_terminated_) + return; + if (self->sender_->max_reports_per_day() > 0) { + // Wait for the other thread to send crashes, if applicable. The sender + // thread takes the sending_ lock, so the sleep is just to give it a + // chance to start. + ::Sleep(1000); + base::AutoLock lock(self->sending_); + // Some people can restart chrome very fast, check again if we have + // a new client before exiting for real. + if (self->clients_connected_ == self->clients_terminated_) { + VLOG(1) << "zero clients. exiting"; + ::PostMessage(g_top_window, WM_CLOSE, 0, 0); + } + } +} + +void CrashService::OnClientDumpRequest(void* context, + const google_breakpad::ClientInfo* client_info, + const std::wstring* file_path) { + ProcessingLock lock; + + if (!file_path) { + LOG(ERROR) << "dump with no file path"; + return; + } + if (!client_info) { + LOG(ERROR) << "dump with no client info"; + return; + } + + CrashService* self = static_cast(context); + if (!self) { + LOG(ERROR) << "dump with no context"; + return; + } + + CrashMap map; + CustomInfoToMap(client_info, self->reporter_tag_, &map); + + // Move dump file to the directory under client breakpad dump location. + base::FilePath dump_location = base::FilePath(*file_path); + CrashMap::const_iterator it = map.find(L"breakpad-dump-location"); + if (it != map.end()) { + base::FilePath alternate_dump_location = base::FilePath(it->second); + base::CreateDirectoryW(alternate_dump_location); + alternate_dump_location = alternate_dump_location.Append( + dump_location.BaseName()); + base::Move(dump_location, alternate_dump_location); + dump_location = alternate_dump_location; + } + + DWORD pid = client_info->pid(); + VLOG(1) << "dump for pid = " << pid << " is " << dump_location.value(); + + if (!WriteCustomInfoToFile(dump_location.value(), map)) { + LOG(ERROR) << "could not write custom info file"; + } + + if (!self->sender_) + return; + + // Send the crash dump using a worker thread. This operation has retry + // logic in case there is no internet connection at the time. + DumpJobInfo* dump_job = new DumpJobInfo(pid, self, map, + dump_location.value()); + if (!::QueueUserWorkItem(&CrashService::AsyncSendDump, + dump_job, WT_EXECUTELONGFUNCTION)) { + LOG(ERROR) << "could not queue job"; + } +} + +// We are going to try sending the report several times. If we can't send, +// we sleep from one minute to several hours depending on the retry round. +unsigned long CrashService::AsyncSendDump(void* context) { + if (!context) + return 0; + + DumpJobInfo* info = static_cast(context); + + std::wstring report_id = L""; + + const DWORD kOneMinute = 60*1000; + const DWORD kOneHour = 60*kOneMinute; + + const DWORD kSleepSchedule[] = { + 24*kOneHour, + 8*kOneHour, + 4*kOneHour, + kOneHour, + 15*kOneMinute, + 0}; + + int retry_round = arraysize(kSleepSchedule) - 1; + + do { + ::Sleep(kSleepSchedule[retry_round]); + { + // Take the server lock while sending. This also prevent early + // termination of the service object. + base::AutoLock lock(info->self->sending_); + VLOG(1) << "trying to send report for pid = " << info->pid; + google_breakpad::ReportResult send_result + = info->self->sender_->SendCrashReport(kCrashReportURL, info->map, + info->dump_path, &report_id); + switch (send_result) { + case google_breakpad::RESULT_FAILED: + report_id = L""; + break; + case google_breakpad::RESULT_REJECTED: + report_id = L""; + ++info->self->requests_handled_; + retry_round = 0; + break; + case google_breakpad::RESULT_SUCCEEDED: + ++info->self->requests_sent_; + ++info->self->requests_handled_; + retry_round = 0; + break; + case google_breakpad::RESULT_THROTTLED: + report_id = L""; + break; + default: + report_id = L""; + break; + }; + } + + VLOG(1) << "dump for pid =" << info->pid << " crash2 id =" << report_id; + --retry_round; + } while (retry_round >= 0); + + if (!::DeleteFileW(info->dump_path.c_str())) + LOG(WARNING) << "could not delete " << info->dump_path; + + delete info; + return 0; +} + +int CrashService::ProcessingLoop() { + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + VLOG(1) << "session ending.."; + while (ProcessingLock::IsWorking()) { + ::Sleep(50); + } + + VLOG(1) << "clients connected :" << clients_connected_ + << "\nclients terminated :" << clients_terminated_ + << "\ndumps serviced :" << requests_handled_ + << "\ndumps reported :" << requests_sent_; + + return static_cast(msg.wParam); +} + +PSECURITY_DESCRIPTOR CrashService::GetSecurityDescriptorForLowIntegrity() { + // Build the SDDL string for the label. + std::wstring sddl = L"S:(ML;;NW;;;S-1-16-4096)"; + + PSECURITY_DESCRIPTOR sec_desc = NULL; + + PACL sacl = NULL; + BOOL sacl_present = FALSE; + BOOL sacl_defaulted = FALSE; + + if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(), + SDDL_REVISION, + &sec_desc, NULL)) { + if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl, + &sacl_defaulted)) { + return sec_desc; + } + } + + return NULL; +} + +} // namespace breakpad diff --git a/components/crash/content/tools/crash_service.h b/components/crash/content/tools/crash_service.h new file mode 100644 index 0000000..39335e3 --- /dev/null +++ b/components/crash/content/tools/crash_service.h @@ -0,0 +1,125 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CRASH_CONTENT_TOOLS_CRASH_SERVICE_H_ +#define COMPONENTS_CRASH_CONTENT_TOOLS_CRASH_SERVICE_H_ + +#include + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/synchronization/lock.h" + +namespace google_breakpad { + +class CrashReportSender; +class CrashGenerationServer; +class ClientInfo; + +} + +namespace breakpad { + +// This class implements an out-of-process crash server. It uses breakpad's +// CrashGenerationServer and CrashReportSender to generate and then send the +// crash dumps. Internally, it uses OS specific pipe to allow applications to +// register for crash dumps and later on when a registered application crashes +// it will signal an event that causes this code to wake up and perform a +// crash dump on the signaling process. The dump is then stored on disk and +// possibly sent to the crash2 servers. +class CrashService { + public: + CrashService(); + ~CrashService(); + + // Starts servicing crash dumps. Returns false if it failed. Do not use + // other members in that case. |operating_dir| is where the CrashService + // should store breakpad's checkpoint file. |dumps_path| is the directory + // where the crash dumps should be stored. + bool Initialize(const base::FilePath& operating_dir, + const base::FilePath& dumps_path); + + // Command line switches: + // + // --max-reports= + // Allows to override the maximum number for reports per day. Normally + // the crash dumps are never sent so if you want to send any you must + // specify a positive number here. + static const char kMaxReports[]; + // --no-window + // Does not create a visible window on the desktop. The window does not have + // any other functionality other than allowing the crash service to be + // gracefully closed. + static const char kNoWindow[]; + // --reporter= + // Allows to specify a custom string that appears on the detail crash report + // page in the crash server. This should be a 25 chars or less string. + // The default tag if not specified is 'crash svc'. + static const char kReporterTag[]; + // --dumps-dir= + // Override the directory to which crash dump files will be written. + static const char kDumpsDir[]; + // --pipe-name= + // Override the name of the Windows named pipe on which we will + // listen for crash dump request messages. + static const char kPipeName[]; + + // Returns number of crash dumps handled. + int requests_handled() const { + return requests_handled_; + } + // Returns number of crash clients registered. + int clients_connected() const { + return clients_connected_; + } + // Returns number of crash clients terminated. + int clients_terminated() const { + return clients_terminated_; + } + + // Starts the processing loop. This function does not return unless the + // user is logging off or the user closes the crash service window. The + // return value is a good number to pass in ExitProcess(). + int ProcessingLoop(); + + private: + static void OnClientConnected(void* context, + const google_breakpad::ClientInfo* client_info); + + static void OnClientDumpRequest( + void* context, + const google_breakpad::ClientInfo* client_info, + const std::wstring* file_path); + + static void OnClientExited(void* context, + const google_breakpad::ClientInfo* client_info); + + // This routine sends the crash dump to the server. It takes the sending_ + // lock when it is performing the send. + static unsigned long __stdcall AsyncSendDump(void* context); + + // Returns the security descriptor which access to low integrity processes + // The caller is supposed to free the security descriptor by calling + // LocalFree. + PSECURITY_DESCRIPTOR GetSecurityDescriptorForLowIntegrity(); + + google_breakpad::CrashGenerationServer* dumper_; + google_breakpad::CrashReportSender* sender_; + + // the extra tag sent to the server with each dump. + std::wstring reporter_tag_; + + // clients serviced statistics: + int requests_handled_; + int requests_sent_; + volatile long clients_connected_; + volatile long clients_terminated_; + base::Lock sending_; + + DISALLOW_COPY_AND_ASSIGN(CrashService); +}; + +} // namespace breakpad + +#endif // COMPONENTS_CRASH_CONTENT_TOOLS_CRASH_SERVICE_H_ diff --git a/components/crash/content/tools/dmp2minidump.py b/components/crash/content/tools/dmp2minidump.py new file mode 100755 index 0000000..7823d48 --- /dev/null +++ b/components/crash/content/tools/dmp2minidump.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# Copyright 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""A tool to extract minidumps from dmp crash dumps.""" + +import os +import sys +from cgi import parse_multipart + + +def ProcessDump(dump_file, minidump_file): + """Extracts the part of the dump file that minidump_stackwalk can read. + + The dump files generated by the breakpad integration multi-part form data + that include the minidump as file attachment. + + Args: + dump_file: the dump file that needs to be processed. + minidump_file: the file to write the minidump to. + """ + try: + dump = open(dump_file, 'rb') + boundary = dump.readline().strip()[2:] + data = parse_multipart(dump, {'boundary': boundary}) + except: + print 'Failed to read dmp file %s' % dump_file + return + + if not 'upload_file_minidump' in data: + print 'Could not find minidump file in dump.' + return + + f = open(minidump_file, 'w') + f.write("\r\n".join(data['upload_file_minidump'])) + f.close() + + +def main(): + if len(sys.argv) != 3: + print 'Usage: %s [dmp file] [minidump]' % sys.argv[0] + print '' + print 'Extracts the minidump stored in the crash dump file' + return 1 + + ProcessDump(sys.argv[1], sys.argv[2]) + + +if '__main__' == __name__: + sys.exit(main()) diff --git a/components/crash/content/tools/generate_breakpad_symbols.py b/components/crash/content/tools/generate_breakpad_symbols.py new file mode 100755 index 0000000..5f8fb85 --- /dev/null +++ b/components/crash/content/tools/generate_breakpad_symbols.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +# Copyright 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""A tool to generate symbols for a binary suitable for breakpad. + +Currently, the tool only supports Linux, Android, and Mac. Support for other +platforms is planned. +""" + +import errno +import optparse +import os +import Queue +import re +import shutil +import subprocess +import sys +import threading + + +CONCURRENT_TASKS=4 + + +def GetCommandOutput(command): + """Runs the command list, returning its output. + + Prints the given command (which should be a list of one or more strings), + then runs it and returns its output (stdout) as a string. + + From chromium_utils. + """ + devnull = open(os.devnull, 'w') + proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull, + bufsize=1) + output = proc.communicate()[0] + return output + + +def GetDumpSymsBinary(build_dir=None): + """Returns the path to the dump_syms binary.""" + DUMP_SYMS = 'dump_syms' + dump_syms_bin = os.path.join(os.path.expanduser(build_dir), DUMP_SYMS) + if not os.access(dump_syms_bin, os.X_OK): + print 'Cannot find %s.' % dump_syms_bin + return None + + return dump_syms_bin + + +def Resolve(path, exe_path, loader_path, rpaths): + """Resolve a dyld path. + + @executable_path is replaced with |exe_path| + @loader_path is replaced with |loader_path| + @rpath is replaced with the first path in |rpaths| where the referenced file + is found + """ + path = path.replace('@loader_path', loader_path) + path = path.replace('@executable_path', exe_path) + if path.find('@rpath') != -1: + for rpath in rpaths: + new_path = Resolve(path.replace('@rpath', rpath), exe_path, loader_path, + []) + if os.access(new_path, os.X_OK): + return new_path + return '' + return path + + +def GetSharedLibraryDependenciesLinux(binary): + """Return absolute paths to all shared library dependecies of the binary. + + This implementation assumes that we're running on a Linux system.""" + ldd = GetCommandOutput(['ldd', binary]) + lib_re = re.compile('\t.* => (.+) \(.*\)$') + result = [] + for line in ldd.splitlines(): + m = lib_re.match(line) + if m: + result.append(m.group(1)) + return result + + +def GetSharedLibraryDependenciesMac(binary, exe_path): + """Return absolute paths to all shared library dependecies of the binary. + + This implementation assumes that we're running on a Mac system.""" + loader_path = os.path.dirname(binary) + otool = GetCommandOutput(['otool', '-l', binary]).splitlines() + rpaths = [] + for idx, line in enumerate(otool): + if line.find('cmd LC_RPATH') != -1: + m = re.match(' *path (.*) \(offset .*\)$', otool[idx+2]) + rpaths.append(m.group(1)) + + otool = GetCommandOutput(['otool', '-L', binary]).splitlines() + lib_re = re.compile('\t(.*) \(compatibility .*\)$') + deps = [] + for line in otool: + m = lib_re.match(line) + if m: + dep = Resolve(m.group(1), exe_path, loader_path, rpaths) + if dep: + deps.append(os.path.normpath(dep)) + return deps + + +def GetSharedLibraryDependencies(options, binary, exe_path): + """Return absolute paths to all shared library dependecies of the binary.""" + deps = [] + if sys.platform.startswith('linux'): + deps = GetSharedLibraryDependenciesLinux(binary) + elif sys.platform == 'darwin': + deps = GetSharedLibraryDependenciesMac(binary, exe_path) + else: + print "Platform not supported." + sys.exit(1) + + result = [] + build_dir = os.path.abspath(options.build_dir) + for dep in deps: + if (os.access(dep, os.X_OK) and + os.path.abspath(os.path.dirname(dep)).startswith(build_dir)): + result.append(dep) + return result + + +def mkdir_p(path): + """Simulates mkdir -p.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(path): + pass + else: raise + + +def GenerateSymbols(options, binaries): + """Dumps the symbols of binary and places them in the given directory.""" + + queue = Queue.Queue() + print_lock = threading.Lock() + + def _Worker(): + while True: + binary = queue.get() + + should_dump_syms = True + reason = "no reason" + + output_path = os.path.join( + options.symbols_dir, os.path.basename(binary)) + if os.path.isdir(output_path): + if os.path.getmtime(binary) < os.path.getmtime(output_path): + should_dump_syms = False + reason = "symbols are more current than binary" + + if not should_dump_syms: + if options.verbose: + with print_lock: + print "Skipping %s (%s)" % (binary, reason) + queue.task_done() + continue + + if options.verbose: + with print_lock: + print "Generating symbols for %s" % binary + + if os.path.isdir(output_path): + os.utime(output_path, None) + + syms = GetCommandOutput([GetDumpSymsBinary(options.build_dir), '-r', + binary]) + module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\n", syms) + output_path = os.path.join(options.symbols_dir, module_line.group(2), + module_line.group(1)) + mkdir_p(output_path) + symbol_file = "%s.sym" % module_line.group(2) + try: + f = open(os.path.join(output_path, symbol_file), 'w') + f.write(syms) + f.close() + except Exception, e: + # Not much we can do about this. + with print_lock: + print e + + queue.task_done() + + for binary in binaries: + queue.put(binary) + + for _ in range(options.jobs): + t = threading.Thread(target=_Worker) + t.daemon = True + t.start() + + queue.join() + + +def main(): + parser = optparse.OptionParser() + parser.add_option('', '--build-dir', default='', + help='The build output directory.') + parser.add_option('', '--symbols-dir', default='', + help='The directory where to write the symbols file.') + parser.add_option('', '--binary', default='', + help='The path of the binary to generate symbols for.') + parser.add_option('', '--clear', default=False, action='store_true', + help='Clear the symbols directory before writing new ' + 'symbols.') + parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store', + type='int', help='Number of parallel tasks to run.') + parser.add_option('-v', '--verbose', action='store_true', + help='Print verbose status output.') + + (options, _) = parser.parse_args() + + if not options.symbols_dir: + print "Required option --symbols-dir missing." + return 1 + + if not options.build_dir: + print "Required option --build-dir missing." + return 1 + + if not options.binary: + print "Required option --binary missing." + return 1 + + if not os.access(options.binary, os.X_OK): + print "Cannot find %s." % options.binary + return 1 + + if options.clear: + try: + shutil.rmtree(options.symbols_dir) + except: + pass + + if not GetDumpSymsBinary(options.build_dir): + return 1 + + # Build the transitive closure of all dependencies. + binaries = set([options.binary]) + queue = [options.binary] + exe_path = os.path.dirname(options.binary) + while queue: + deps = GetSharedLibraryDependencies(options, queue.pop(0), exe_path) + new_deps = set(deps) - binaries + binaries |= new_deps + queue.extend(list(new_deps)) + + GenerateSymbols(options, binaries) + + return 0 + + +if '__main__' == __name__: + sys.exit(main()) diff --git a/components/crash/tools/BUILD.gn b/components/crash/tools/BUILD.gn deleted file mode 100644 index 82f282b..0000000 --- a/components/crash/tools/BUILD.gn +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -if (is_win) { - source_set("crash_service") { - sources = [ - "crash_service.cc", - "crash_service.h", - ] - - deps = [ - "//base", - "//breakpad:breakpad_handler", - "//breakpad:breakpad_sender", - ] - } -} diff --git a/components/crash/tools/crash_service.cc b/components/crash/tools/crash_service.cc deleted file mode 100644 index f1536fc..0000000 --- a/components/crash/tools/crash_service.cc +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/crash/tools/crash_service.h" - -#include - -#include -#include -#include - -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/win/windows_version.h" -#include "breakpad/src/client/windows/crash_generation/client_info.h" -#include "breakpad/src/client/windows/crash_generation/crash_generation_server.h" -#include "breakpad/src/client/windows/sender/crash_report_sender.h" - -namespace breakpad { - -namespace { - -const wchar_t kTestPipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; - -const wchar_t kCrashReportURL[] = L"https://clients2.google.com/cr/report"; -const wchar_t kCheckPointFile[] = L"crash_checkpoint.txt"; - -typedef std::map CrashMap; - -bool CustomInfoToMap(const google_breakpad::ClientInfo* client_info, - const std::wstring& reporter_tag, CrashMap* map) { - google_breakpad::CustomClientInfo info = client_info->GetCustomInfo(); - - for (uintptr_t i = 0; i < info.count; ++i) { - (*map)[info.entries[i].name] = info.entries[i].value; - } - - (*map)[L"rept"] = reporter_tag; - - return !map->empty(); -} - -bool WriteCustomInfoToFile(const std::wstring& dump_path, const CrashMap& map) { - std::wstring file_path(dump_path); - size_t last_dot = file_path.rfind(L'.'); - if (last_dot == std::wstring::npos) - return false; - file_path.resize(last_dot); - file_path += L".txt"; - - std::wofstream file(file_path.c_str(), - std::ios_base::out | std::ios_base::app | std::ios::binary); - if (!file.is_open()) - return false; - - CrashMap::const_iterator pos; - for (pos = map.begin(); pos != map.end(); ++pos) { - std::wstring line = pos->first; - line += L':'; - line += pos->second; - line += L'\n'; - file.write(line.c_str(), static_cast(line.length())); - } - return true; -} - -// The window procedure task is to handle when a) the user logs off. -// b) the system shuts down or c) when the user closes the window. -LRESULT __stdcall CrashSvcWndProc(HWND hwnd, UINT message, - WPARAM wparam, LPARAM lparam) { - switch (message) { - case WM_CLOSE: - case WM_ENDSESSION: - case WM_DESTROY: - PostQuitMessage(0); - break; - default: - return DefWindowProc(hwnd, message, wparam, lparam); - } - return 0; -} - -// This is the main and only application window. -HWND g_top_window = NULL; - -bool CreateTopWindow(HINSTANCE instance, bool visible) { - WNDCLASSEXW wcx = {0}; - wcx.cbSize = sizeof(wcx); - wcx.style = CS_HREDRAW | CS_VREDRAW; - wcx.lpfnWndProc = CrashSvcWndProc; - wcx.hInstance = instance; - wcx.lpszClassName = L"crash_svc_class"; - ::RegisterClassExW(&wcx); - DWORD style = visible ? WS_POPUPWINDOW | WS_VISIBLE : WS_OVERLAPPED; - - // The window size is zero but being a popup window still shows in the - // task bar and can be closed using the system menu or using task manager. - HWND window = CreateWindowExW(0, wcx.lpszClassName, L"crash service", style, - CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, - NULL, NULL, instance, NULL); - if (!window) - return false; - - ::UpdateWindow(window); - VLOG(1) << "window handle is " << window; - g_top_window = window; - return true; -} - -// Simple helper class to keep the process alive until the current request -// finishes. -class ProcessingLock { - public: - ProcessingLock() { - ::InterlockedIncrement(&op_count_); - } - ~ProcessingLock() { - ::InterlockedDecrement(&op_count_); - } - static bool IsWorking() { - return (op_count_ != 0); - } - private: - static volatile LONG op_count_; -}; - -volatile LONG ProcessingLock::op_count_ = 0; - -// This structure contains the information that the worker thread needs to -// send a crash dump to the server. -struct DumpJobInfo { - DWORD pid; - CrashService* self; - CrashMap map; - std::wstring dump_path; - - DumpJobInfo(DWORD process_id, CrashService* service, - const CrashMap& crash_map, const std::wstring& path) - : pid(process_id), self(service), map(crash_map), dump_path(path) { - } -}; - -} // namespace - -// Command line switches: -const char CrashService::kMaxReports[] = "max-reports"; -const char CrashService::kNoWindow[] = "no-window"; -const char CrashService::kReporterTag[] = "reporter"; -const char CrashService::kDumpsDir[] = "dumps-dir"; -const char CrashService::kPipeName[] = "pipe-name"; - -CrashService::CrashService() - : dumper_(NULL), - sender_(NULL), - requests_handled_(0), - requests_sent_(0), - clients_connected_(0), - clients_terminated_(0) { -} - -CrashService::~CrashService() { - base::AutoLock lock(sending_); - delete dumper_; - delete sender_; -} - -bool CrashService::Initialize(const base::FilePath& operating_dir, - const base::FilePath& dumps_path) { - using google_breakpad::CrashReportSender; - using google_breakpad::CrashGenerationServer; - - std::wstring pipe_name = kTestPipeName; - int max_reports = -1; - - // The checkpoint file allows CrashReportSender to enforce the the maximum - // reports per day quota. Does not seem to serve any other purpose. - base::FilePath checkpoint_path = operating_dir.Append(kCheckPointFile); - - base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess(); - - base::FilePath dumps_path_to_use = dumps_path; - - if (cmd_line.HasSwitch(kDumpsDir)) { - dumps_path_to_use = - base::FilePath(cmd_line.GetSwitchValueNative(kDumpsDir)); - } - - // We can override the send reports quota with a command line switch. - if (cmd_line.HasSwitch(kMaxReports)) - max_reports = _wtoi(cmd_line.GetSwitchValueNative(kMaxReports).c_str()); - - // Allow the global pipe name to be overridden for better testability. - if (cmd_line.HasSwitch(kPipeName)) - pipe_name = cmd_line.GetSwitchValueNative(kPipeName); - -#ifdef _WIN64 - pipe_name += L"-x64"; -#endif - - if (max_reports > 0) { - // Create the http sender object. - sender_ = new CrashReportSender(checkpoint_path.value()); - sender_->set_max_reports_per_day(max_reports); - } - - SECURITY_ATTRIBUTES security_attributes = {0}; - SECURITY_ATTRIBUTES* security_attributes_actual = NULL; - - if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - SECURITY_DESCRIPTOR* security_descriptor = - reinterpret_cast( - GetSecurityDescriptorForLowIntegrity()); - DCHECK(security_descriptor != NULL); - - security_attributes.nLength = sizeof(security_attributes); - security_attributes.lpSecurityDescriptor = security_descriptor; - security_attributes.bInheritHandle = FALSE; - - security_attributes_actual = &security_attributes; - } - - // Create the OOP crash generator object. - dumper_ = new CrashGenerationServer(pipe_name, security_attributes_actual, - &CrashService::OnClientConnected, this, - &CrashService::OnClientDumpRequest, this, - &CrashService::OnClientExited, this, - NULL, NULL, - true, &dumps_path_to_use.value()); - - if (!dumper_) { - LOG(ERROR) << "could not create dumper"; - if (security_attributes.lpSecurityDescriptor) - LocalFree(security_attributes.lpSecurityDescriptor); - return false; - } - - if (!CreateTopWindow(::GetModuleHandleW(NULL), - !cmd_line.HasSwitch(kNoWindow))) { - LOG(ERROR) << "could not create window"; - if (security_attributes.lpSecurityDescriptor) - LocalFree(security_attributes.lpSecurityDescriptor); - return false; - } - - reporter_tag_ = L"crash svc"; - if (cmd_line.HasSwitch(kReporterTag)) - reporter_tag_ = cmd_line.GetSwitchValueNative(kReporterTag); - - // Log basic information. - VLOG(1) << "pipe name is " << pipe_name - << "\ndumps at " << dumps_path_to_use.value(); - - if (sender_) { - VLOG(1) << "checkpoint is " << checkpoint_path.value() - << "\nserver is " << kCrashReportURL - << "\nmaximum " << sender_->max_reports_per_day() << " reports/day" - << "\nreporter is " << reporter_tag_; - } - // Start servicing clients. - if (!dumper_->Start()) { - LOG(ERROR) << "could not start dumper"; - if (security_attributes.lpSecurityDescriptor) - LocalFree(security_attributes.lpSecurityDescriptor); - return false; - } - - if (security_attributes.lpSecurityDescriptor) - LocalFree(security_attributes.lpSecurityDescriptor); - - // This is throwaway code. We don't need to sync with the browser process - // once Google Update is updated to a version supporting OOP crash handling. - // Create or open an event to signal the browser process that the crash - // service is initialized. - HANDLE running_event = - ::CreateEventW(NULL, TRUE, TRUE, L"g_chrome_crash_svc"); - // If the browser already had the event open, the CreateEvent call did not - // signal it. We need to do it manually. - ::SetEvent(running_event); - - return true; -} - -void CrashService::OnClientConnected(void* context, - const google_breakpad::ClientInfo* client_info) { - ProcessingLock lock; - VLOG(1) << "client start. pid = " << client_info->pid(); - CrashService* self = static_cast(context); - ::InterlockedIncrement(&self->clients_connected_); -} - -void CrashService::OnClientExited(void* context, - const google_breakpad::ClientInfo* client_info) { - ProcessingLock lock; - VLOG(1) << "client end. pid = " << client_info->pid(); - CrashService* self = static_cast(context); - ::InterlockedIncrement(&self->clients_terminated_); - - if (!self->sender_) - return; - - // When we are instructed to send reports we need to exit if there are - // no more clients to service. The next client that runs will start us. - // Only chrome.exe starts crash_service with a non-zero max_reports. - if (self->clients_connected_ > self->clients_terminated_) - return; - if (self->sender_->max_reports_per_day() > 0) { - // Wait for the other thread to send crashes, if applicable. The sender - // thread takes the sending_ lock, so the sleep is just to give it a - // chance to start. - ::Sleep(1000); - base::AutoLock lock(self->sending_); - // Some people can restart chrome very fast, check again if we have - // a new client before exiting for real. - if (self->clients_connected_ == self->clients_terminated_) { - VLOG(1) << "zero clients. exiting"; - ::PostMessage(g_top_window, WM_CLOSE, 0, 0); - } - } -} - -void CrashService::OnClientDumpRequest(void* context, - const google_breakpad::ClientInfo* client_info, - const std::wstring* file_path) { - ProcessingLock lock; - - if (!file_path) { - LOG(ERROR) << "dump with no file path"; - return; - } - if (!client_info) { - LOG(ERROR) << "dump with no client info"; - return; - } - - CrashService* self = static_cast(context); - if (!self) { - LOG(ERROR) << "dump with no context"; - return; - } - - CrashMap map; - CustomInfoToMap(client_info, self->reporter_tag_, &map); - - // Move dump file to the directory under client breakpad dump location. - base::FilePath dump_location = base::FilePath(*file_path); - CrashMap::const_iterator it = map.find(L"breakpad-dump-location"); - if (it != map.end()) { - base::FilePath alternate_dump_location = base::FilePath(it->second); - base::CreateDirectoryW(alternate_dump_location); - alternate_dump_location = alternate_dump_location.Append( - dump_location.BaseName()); - base::Move(dump_location, alternate_dump_location); - dump_location = alternate_dump_location; - } - - DWORD pid = client_info->pid(); - VLOG(1) << "dump for pid = " << pid << " is " << dump_location.value(); - - if (!WriteCustomInfoToFile(dump_location.value(), map)) { - LOG(ERROR) << "could not write custom info file"; - } - - if (!self->sender_) - return; - - // Send the crash dump using a worker thread. This operation has retry - // logic in case there is no internet connection at the time. - DumpJobInfo* dump_job = new DumpJobInfo(pid, self, map, - dump_location.value()); - if (!::QueueUserWorkItem(&CrashService::AsyncSendDump, - dump_job, WT_EXECUTELONGFUNCTION)) { - LOG(ERROR) << "could not queue job"; - } -} - -// We are going to try sending the report several times. If we can't send, -// we sleep from one minute to several hours depending on the retry round. -unsigned long CrashService::AsyncSendDump(void* context) { - if (!context) - return 0; - - DumpJobInfo* info = static_cast(context); - - std::wstring report_id = L""; - - const DWORD kOneMinute = 60*1000; - const DWORD kOneHour = 60*kOneMinute; - - const DWORD kSleepSchedule[] = { - 24*kOneHour, - 8*kOneHour, - 4*kOneHour, - kOneHour, - 15*kOneMinute, - 0}; - - int retry_round = arraysize(kSleepSchedule) - 1; - - do { - ::Sleep(kSleepSchedule[retry_round]); - { - // Take the server lock while sending. This also prevent early - // termination of the service object. - base::AutoLock lock(info->self->sending_); - VLOG(1) << "trying to send report for pid = " << info->pid; - google_breakpad::ReportResult send_result - = info->self->sender_->SendCrashReport(kCrashReportURL, info->map, - info->dump_path, &report_id); - switch (send_result) { - case google_breakpad::RESULT_FAILED: - report_id = L""; - break; - case google_breakpad::RESULT_REJECTED: - report_id = L""; - ++info->self->requests_handled_; - retry_round = 0; - break; - case google_breakpad::RESULT_SUCCEEDED: - ++info->self->requests_sent_; - ++info->self->requests_handled_; - retry_round = 0; - break; - case google_breakpad::RESULT_THROTTLED: - report_id = L""; - break; - default: - report_id = L""; - break; - }; - } - - VLOG(1) << "dump for pid =" << info->pid << " crash2 id =" << report_id; - --retry_round; - } while (retry_round >= 0); - - if (!::DeleteFileW(info->dump_path.c_str())) - LOG(WARNING) << "could not delete " << info->dump_path; - - delete info; - return 0; -} - -int CrashService::ProcessingLoop() { - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - VLOG(1) << "session ending.."; - while (ProcessingLock::IsWorking()) { - ::Sleep(50); - } - - VLOG(1) << "clients connected :" << clients_connected_ - << "\nclients terminated :" << clients_terminated_ - << "\ndumps serviced :" << requests_handled_ - << "\ndumps reported :" << requests_sent_; - - return static_cast(msg.wParam); -} - -PSECURITY_DESCRIPTOR CrashService::GetSecurityDescriptorForLowIntegrity() { - // Build the SDDL string for the label. - std::wstring sddl = L"S:(ML;;NW;;;S-1-16-4096)"; - - PSECURITY_DESCRIPTOR sec_desc = NULL; - - PACL sacl = NULL; - BOOL sacl_present = FALSE; - BOOL sacl_defaulted = FALSE; - - if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(), - SDDL_REVISION, - &sec_desc, NULL)) { - if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl, - &sacl_defaulted)) { - return sec_desc; - } - } - - return NULL; -} - -} // namespace breakpad diff --git a/components/crash/tools/crash_service.h b/components/crash/tools/crash_service.h deleted file mode 100644 index 81b28bd..0000000 --- a/components/crash/tools/crash_service.h +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRASH_TOOLS_CRASH_SERVICE_H_ -#define COMPONENTS_CRASH_TOOLS_CRASH_SERVICE_H_ - -#include - -#include "base/basictypes.h" -#include "base/files/file_path.h" -#include "base/synchronization/lock.h" - -namespace google_breakpad { - -class CrashReportSender; -class CrashGenerationServer; -class ClientInfo; - -} - -namespace breakpad { - -// This class implements an out-of-process crash server. It uses breakpad's -// CrashGenerationServer and CrashReportSender to generate and then send the -// crash dumps. Internally, it uses OS specific pipe to allow applications to -// register for crash dumps and later on when a registered application crashes -// it will signal an event that causes this code to wake up and perform a -// crash dump on the signaling process. The dump is then stored on disk and -// possibly sent to the crash2 servers. -class CrashService { - public: - CrashService(); - ~CrashService(); - - // Starts servicing crash dumps. Returns false if it failed. Do not use - // other members in that case. |operating_dir| is where the CrashService - // should store breakpad's checkpoint file. |dumps_path| is the directory - // where the crash dumps should be stored. - bool Initialize(const base::FilePath& operating_dir, - const base::FilePath& dumps_path); - - // Command line switches: - // - // --max-reports= - // Allows to override the maximum number for reports per day. Normally - // the crash dumps are never sent so if you want to send any you must - // specify a positive number here. - static const char kMaxReports[]; - // --no-window - // Does not create a visible window on the desktop. The window does not have - // any other functionality other than allowing the crash service to be - // gracefully closed. - static const char kNoWindow[]; - // --reporter= - // Allows to specify a custom string that appears on the detail crash report - // page in the crash server. This should be a 25 chars or less string. - // The default tag if not specified is 'crash svc'. - static const char kReporterTag[]; - // --dumps-dir= - // Override the directory to which crash dump files will be written. - static const char kDumpsDir[]; - // --pipe-name= - // Override the name of the Windows named pipe on which we will - // listen for crash dump request messages. - static const char kPipeName[]; - - // Returns number of crash dumps handled. - int requests_handled() const { - return requests_handled_; - } - // Returns number of crash clients registered. - int clients_connected() const { - return clients_connected_; - } - // Returns number of crash clients terminated. - int clients_terminated() const { - return clients_terminated_; - } - - // Starts the processing loop. This function does not return unless the - // user is logging off or the user closes the crash service window. The - // return value is a good number to pass in ExitProcess(). - int ProcessingLoop(); - - private: - static void OnClientConnected(void* context, - const google_breakpad::ClientInfo* client_info); - - static void OnClientDumpRequest( - void* context, - const google_breakpad::ClientInfo* client_info, - const std::wstring* file_path); - - static void OnClientExited(void* context, - const google_breakpad::ClientInfo* client_info); - - // This routine sends the crash dump to the server. It takes the sending_ - // lock when it is performing the send. - static unsigned long __stdcall AsyncSendDump(void* context); - - // Returns the security descriptor which access to low integrity processes - // The caller is supposed to free the security descriptor by calling - // LocalFree. - PSECURITY_DESCRIPTOR GetSecurityDescriptorForLowIntegrity(); - - google_breakpad::CrashGenerationServer* dumper_; - google_breakpad::CrashReportSender* sender_; - - // the extra tag sent to the server with each dump. - std::wstring reporter_tag_; - - // clients serviced statistics: - int requests_handled_; - int requests_sent_; - volatile long clients_connected_; - volatile long clients_terminated_; - base::Lock sending_; - - DISALLOW_COPY_AND_ASSIGN(CrashService); -}; - -} // namespace breakpad - -#endif // COMPONENTS_CRASH_TOOLS_CRASH_SERVICE_H_ diff --git a/components/crash/tools/dmp2minidump.py b/components/crash/tools/dmp2minidump.py deleted file mode 100755 index 7823d48..0000000 --- a/components/crash/tools/dmp2minidump.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""A tool to extract minidumps from dmp crash dumps.""" - -import os -import sys -from cgi import parse_multipart - - -def ProcessDump(dump_file, minidump_file): - """Extracts the part of the dump file that minidump_stackwalk can read. - - The dump files generated by the breakpad integration multi-part form data - that include the minidump as file attachment. - - Args: - dump_file: the dump file that needs to be processed. - minidump_file: the file to write the minidump to. - """ - try: - dump = open(dump_file, 'rb') - boundary = dump.readline().strip()[2:] - data = parse_multipart(dump, {'boundary': boundary}) - except: - print 'Failed to read dmp file %s' % dump_file - return - - if not 'upload_file_minidump' in data: - print 'Could not find minidump file in dump.' - return - - f = open(minidump_file, 'w') - f.write("\r\n".join(data['upload_file_minidump'])) - f.close() - - -def main(): - if len(sys.argv) != 3: - print 'Usage: %s [dmp file] [minidump]' % sys.argv[0] - print '' - print 'Extracts the minidump stored in the crash dump file' - return 1 - - ProcessDump(sys.argv[1], sys.argv[2]) - - -if '__main__' == __name__: - sys.exit(main()) diff --git a/components/crash/tools/generate_breakpad_symbols.py b/components/crash/tools/generate_breakpad_symbols.py deleted file mode 100755 index 5f8fb85..0000000 --- a/components/crash/tools/generate_breakpad_symbols.py +++ /dev/null @@ -1,262 +0,0 @@ -#!/usr/bin/env python -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""A tool to generate symbols for a binary suitable for breakpad. - -Currently, the tool only supports Linux, Android, and Mac. Support for other -platforms is planned. -""" - -import errno -import optparse -import os -import Queue -import re -import shutil -import subprocess -import sys -import threading - - -CONCURRENT_TASKS=4 - - -def GetCommandOutput(command): - """Runs the command list, returning its output. - - Prints the given command (which should be a list of one or more strings), - then runs it and returns its output (stdout) as a string. - - From chromium_utils. - """ - devnull = open(os.devnull, 'w') - proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull, - bufsize=1) - output = proc.communicate()[0] - return output - - -def GetDumpSymsBinary(build_dir=None): - """Returns the path to the dump_syms binary.""" - DUMP_SYMS = 'dump_syms' - dump_syms_bin = os.path.join(os.path.expanduser(build_dir), DUMP_SYMS) - if not os.access(dump_syms_bin, os.X_OK): - print 'Cannot find %s.' % dump_syms_bin - return None - - return dump_syms_bin - - -def Resolve(path, exe_path, loader_path, rpaths): - """Resolve a dyld path. - - @executable_path is replaced with |exe_path| - @loader_path is replaced with |loader_path| - @rpath is replaced with the first path in |rpaths| where the referenced file - is found - """ - path = path.replace('@loader_path', loader_path) - path = path.replace('@executable_path', exe_path) - if path.find('@rpath') != -1: - for rpath in rpaths: - new_path = Resolve(path.replace('@rpath', rpath), exe_path, loader_path, - []) - if os.access(new_path, os.X_OK): - return new_path - return '' - return path - - -def GetSharedLibraryDependenciesLinux(binary): - """Return absolute paths to all shared library dependecies of the binary. - - This implementation assumes that we're running on a Linux system.""" - ldd = GetCommandOutput(['ldd', binary]) - lib_re = re.compile('\t.* => (.+) \(.*\)$') - result = [] - for line in ldd.splitlines(): - m = lib_re.match(line) - if m: - result.append(m.group(1)) - return result - - -def GetSharedLibraryDependenciesMac(binary, exe_path): - """Return absolute paths to all shared library dependecies of the binary. - - This implementation assumes that we're running on a Mac system.""" - loader_path = os.path.dirname(binary) - otool = GetCommandOutput(['otool', '-l', binary]).splitlines() - rpaths = [] - for idx, line in enumerate(otool): - if line.find('cmd LC_RPATH') != -1: - m = re.match(' *path (.*) \(offset .*\)$', otool[idx+2]) - rpaths.append(m.group(1)) - - otool = GetCommandOutput(['otool', '-L', binary]).splitlines() - lib_re = re.compile('\t(.*) \(compatibility .*\)$') - deps = [] - for line in otool: - m = lib_re.match(line) - if m: - dep = Resolve(m.group(1), exe_path, loader_path, rpaths) - if dep: - deps.append(os.path.normpath(dep)) - return deps - - -def GetSharedLibraryDependencies(options, binary, exe_path): - """Return absolute paths to all shared library dependecies of the binary.""" - deps = [] - if sys.platform.startswith('linux'): - deps = GetSharedLibraryDependenciesLinux(binary) - elif sys.platform == 'darwin': - deps = GetSharedLibraryDependenciesMac(binary, exe_path) - else: - print "Platform not supported." - sys.exit(1) - - result = [] - build_dir = os.path.abspath(options.build_dir) - for dep in deps: - if (os.access(dep, os.X_OK) and - os.path.abspath(os.path.dirname(dep)).startswith(build_dir)): - result.append(dep) - return result - - -def mkdir_p(path): - """Simulates mkdir -p.""" - try: - os.makedirs(path) - except OSError as e: - if e.errno == errno.EEXIST and os.path.isdir(path): - pass - else: raise - - -def GenerateSymbols(options, binaries): - """Dumps the symbols of binary and places them in the given directory.""" - - queue = Queue.Queue() - print_lock = threading.Lock() - - def _Worker(): - while True: - binary = queue.get() - - should_dump_syms = True - reason = "no reason" - - output_path = os.path.join( - options.symbols_dir, os.path.basename(binary)) - if os.path.isdir(output_path): - if os.path.getmtime(binary) < os.path.getmtime(output_path): - should_dump_syms = False - reason = "symbols are more current than binary" - - if not should_dump_syms: - if options.verbose: - with print_lock: - print "Skipping %s (%s)" % (binary, reason) - queue.task_done() - continue - - if options.verbose: - with print_lock: - print "Generating symbols for %s" % binary - - if os.path.isdir(output_path): - os.utime(output_path, None) - - syms = GetCommandOutput([GetDumpSymsBinary(options.build_dir), '-r', - binary]) - module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\n", syms) - output_path = os.path.join(options.symbols_dir, module_line.group(2), - module_line.group(1)) - mkdir_p(output_path) - symbol_file = "%s.sym" % module_line.group(2) - try: - f = open(os.path.join(output_path, symbol_file), 'w') - f.write(syms) - f.close() - except Exception, e: - # Not much we can do about this. - with print_lock: - print e - - queue.task_done() - - for binary in binaries: - queue.put(binary) - - for _ in range(options.jobs): - t = threading.Thread(target=_Worker) - t.daemon = True - t.start() - - queue.join() - - -def main(): - parser = optparse.OptionParser() - parser.add_option('', '--build-dir', default='', - help='The build output directory.') - parser.add_option('', '--symbols-dir', default='', - help='The directory where to write the symbols file.') - parser.add_option('', '--binary', default='', - help='The path of the binary to generate symbols for.') - parser.add_option('', '--clear', default=False, action='store_true', - help='Clear the symbols directory before writing new ' - 'symbols.') - parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store', - type='int', help='Number of parallel tasks to run.') - parser.add_option('-v', '--verbose', action='store_true', - help='Print verbose status output.') - - (options, _) = parser.parse_args() - - if not options.symbols_dir: - print "Required option --symbols-dir missing." - return 1 - - if not options.build_dir: - print "Required option --build-dir missing." - return 1 - - if not options.binary: - print "Required option --binary missing." - return 1 - - if not os.access(options.binary, os.X_OK): - print "Cannot find %s." % options.binary - return 1 - - if options.clear: - try: - shutil.rmtree(options.symbols_dir) - except: - pass - - if not GetDumpSymsBinary(options.build_dir): - return 1 - - # Build the transitive closure of all dependencies. - binaries = set([options.binary]) - queue = [options.binary] - exe_path = os.path.dirname(options.binary) - while queue: - deps = GetSharedLibraryDependencies(options, queue.pop(0), exe_path) - new_deps = set(deps) - binaries - binaries |= new_deps - queue.extend(list(new_deps)) - - GenerateSymbols(options, binaries) - - return 0 - - -if '__main__' == __name__: - sys.exit(main()) diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn index 010758e..1ee13ab 100644 --- a/content/shell/BUILD.gn +++ b/content/shell/BUILD.gn @@ -201,7 +201,7 @@ static_library("content_shell_lib") { "//base/allocator", "//base/third_party/dynamic_annotations", "//cc", - "//components/crash/app", + "//components/crash/content/app", "//components/devtools_discovery", "//components/devtools_http_handler", "//components/plugins/renderer", @@ -272,7 +272,7 @@ static_library("content_shell_lib") { } if (is_posix && !is_mac) { - deps += [ "//components/crash/browser" ] + deps += [ "//components/crash/content/browser" ] } if (use_aura) { @@ -458,7 +458,7 @@ if (is_win) { deps = [ "//base", "//build/config/sanitizers:deps", - "//components/crash/tools:crash_service", + "//components/crash/content/tools:crash_service", ] configs -= [ "//build/config/win:console" ] diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn index b722f0b..98261c5 100644 --- a/content/shell/android/BUILD.gn +++ b/content/shell/android/BUILD.gn @@ -24,7 +24,7 @@ shared_library("libcontent_shell_content_view") { "//build/config/sanitizers:deps", "//content/shell:content_shell_lib", "//content/shell:pak", - "//components/crash/browser", + "//components/crash/content/browser", "//skia", "//media/base/android", ] diff --git a/content/shell/app/shell_crash_reporter_client.h b/content/shell/app/shell_crash_reporter_client.h index 882ecf0..89e5773 100644 --- a/content/shell/app/shell_crash_reporter_client.h +++ b/content/shell/app/shell_crash_reporter_client.h @@ -6,7 +6,7 @@ #define CONTENT_SHELL_APP_SHELL_CRASH_REPORTER_CLIENT_H_ #include "base/compiler_specific.h" -#include "components/crash/app/crash_reporter_client.h" +#include "components/crash/content/app/crash_reporter_client.h" namespace content { diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc index 2eb438a..e5914f8 100644 --- a/content/shell/app/shell_main_delegate.cc +++ b/content/shell/app/shell_main_delegate.cc @@ -57,7 +57,7 @@ #if defined(OS_MACOSX) #include "base/mac/os_crash_dumps.h" -#include "components/crash/app/breakpad_mac.h" +#include "components/crash/content/app/breakpad_mac.h" #include "content/shell/app/paths_mac.h" #include "content/shell/app/shell_main_delegate_mac.h" #endif // OS_MACOSX @@ -66,12 +66,12 @@ #include #include #include "base/logging_win.h" -#include "components/crash/app/breakpad_win.h" +#include "components/crash/content/app/breakpad_win.h" #include "content/shell/common/v8_breakpad_support_win.h" #endif #if defined(OS_POSIX) && !defined(OS_MACOSX) -#include "components/crash/app/breakpad_linux.h" +#include "components/crash/content/app/breakpad_linux.h" #endif namespace { diff --git a/content/shell/browser/layout_test/layout_test_browser_main_parts.cc b/content/shell/browser/layout_test/layout_test_browser_main_parts.cc index 04361ef..69dede2 100644 --- a/content/shell/browser/layout_test/layout_test_browser_main_parts.cc +++ b/content/shell/browser/layout_test/layout_test_browser_main_parts.cc @@ -35,7 +35,7 @@ #endif #if defined(OS_ANDROID) -#include "components/crash/browser/crash_dump_manager_android.h" +#include "components/crash/content/browser/crash_dump_manager_android.h" #include "net/android/network_change_notifier_factory_android.h" #include "net/base/network_change_notifier.h" #endif diff --git a/content/shell/browser/shell_browser_main_parts.cc b/content/shell/browser/shell_browser_main_parts.cc index 68f52e2..df1e8cf 100644 --- a/content/shell/browser/shell_browser_main_parts.cc +++ b/content/shell/browser/shell_browser_main_parts.cc @@ -31,7 +31,7 @@ #include "url/gurl.h" #if defined(OS_ANDROID) -#include "components/crash/browser/crash_dump_manager_android.h" +#include "components/crash/content/browser/crash_dump_manager_android.h" #include "net/android/network_change_notifier_factory_android.h" #include "net/base/network_change_notifier.h" #endif diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc index 98f66c8..2f928a9 100644 --- a/content/shell/browser/shell_content_browser_client.cc +++ b/content/shell/browser/shell_content_browser_client.cc @@ -40,14 +40,14 @@ #if defined(OS_ANDROID) #include "base/android/apk_assets.h" #include "base/android/path_utils.h" -#include "components/crash/browser/crash_dump_manager_android.h" +#include "components/crash/content/browser/crash_dump_manager_android.h" #include "content/shell/android/shell_descriptors.h" #endif #if defined(OS_POSIX) && !defined(OS_MACOSX) #include "base/debug/leak_annotations.h" -#include "components/crash/app/breakpad_linux.h" -#include "components/crash/browser/crash_handler_host_linux.h" +#include "components/crash/content/app/breakpad_linux.h" +#include "components/crash/content/browser/crash_handler_host_linux.h" #include "content/public/common/content_descriptors.h" #endif diff --git a/content/shell/tools/content_shell_crash_service.cc b/content/shell/tools/content_shell_crash_service.cc index 897c6fb..70c8141 100644 --- a/content/shell/tools/content_shell_crash_service.cc +++ b/content/shell/tools/content_shell_crash_service.cc @@ -10,7 +10,7 @@ #include "base/command_line.h" #include "base/files/file_path.h" #include "base/logging.h" -#include "components/crash/tools/crash_service.h" +#include "components/crash/content/tools/crash_service.h" int __stdcall wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd_line, int show_mode) { diff --git a/remoting/base/breakpad_win.cc b/remoting/base/breakpad_win.cc index ce39d91..926d3af 100644 --- a/remoting/base/breakpad_win.cc +++ b/remoting/base/breakpad_win.cc @@ -4,7 +4,7 @@ // This module contains the necessary code to register the Breakpad exception // handler. This implementation is based on Chrome crash reporting code. See: -// - src/components/crash/app/breakpad_win.cc +// - src/components/crash/content/app/breakpad_win.cc // - src/chrome/installer/setup/setup_main.cc #include "remoting/base/breakpad.h" diff --git a/tools/msan/blacklist.txt b/tools/msan/blacklist.txt index a00f740..9957886 100644 --- a/tools/msan/blacklist.txt +++ b/tools/msan/blacklist.txt @@ -22,7 +22,7 @@ fun:unpack_RGB888 # False positives due to use of linux_syscall_support. http://crbug.com/394028 src:*/breakpad/src/* -src:*/components/crash/app/breakpad_linux.cc +src:*/components/crash/content/app/breakpad_linux.cc # False positives due to an MSan bug. http://crbug.com/418986 fun:*SchedGetParamThread* -- cgit v1.1