|  | // Copyright 2017 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/fallback_crash_handling_win.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/base_switches.h" | 
|  | #include "base/command_line.h" | 
|  | #include "base/logging.h" | 
|  | #include "components/crash/content/app/crash_switches.h" | 
|  | #include "components/crash/content/app/fallback_crash_handler_launcher_win.h" | 
|  | #include "components/crash/content/app/fallback_crash_handler_win.h" | 
|  |  | 
|  | namespace crash_reporter { | 
|  |  | 
|  | namespace switches { | 
|  | const char kFallbackCrashHandler[] = "fallback-handler"; | 
|  | const char kPrefetchArgument[] = "/prefetch:7"; | 
|  | } | 
|  |  | 
|  | const uint32_t kFallbackCrashTerminationCode = 0xFFFF8001; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Intentionally leaked on program exit. | 
|  | FallbackCrashHandlerLauncher* g_fallback_crash_handler_launcher = nullptr; | 
|  |  | 
|  | LONG WINAPI FallbackUnhandledExceptionFilter(EXCEPTION_POINTERS* exc_ptrs) { | 
|  | if (!g_fallback_crash_handler_launcher) | 
|  | return EXCEPTION_CONTINUE_SEARCH; | 
|  |  | 
|  | return g_fallback_crash_handler_launcher->LaunchAndWaitForHandler(exc_ptrs); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool SetupFallbackCrashHandling(const base::CommandLine& command_line) { | 
|  | DCHECK(!g_fallback_crash_handler_launcher); | 
|  |  | 
|  | // Run the same program. | 
|  | base::CommandLine base_command_line(command_line.GetProgram()); | 
|  | base_command_line.AppendSwitchASCII("type", switches::kFallbackCrashHandler); | 
|  |  | 
|  | // This is to support testing under gtest. | 
|  | if (command_line.HasSwitch(::switches::kTestChildProcess)) { | 
|  | base_command_line.AppendSwitchASCII( | 
|  | ::switches::kTestChildProcess, | 
|  | command_line.GetSwitchValueASCII(::switches::kTestChildProcess)); | 
|  | } | 
|  |  | 
|  | // All Chrome processes need a prefetch argument. | 
|  | base_command_line.AppendArg(switches::kPrefetchArgument); | 
|  |  | 
|  | // Get the database path. | 
|  | base::FilePath database_path = command_line.GetSwitchValuePath("database"); | 
|  | if (database_path.empty()) { | 
|  | NOTREACHED(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<FallbackCrashHandlerLauncher> fallback_launcher( | 
|  | new FallbackCrashHandlerLauncher()); | 
|  |  | 
|  | if (!fallback_launcher->Initialize(base_command_line, database_path)) { | 
|  | NOTREACHED(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // This is necessary because chrome_elf stubs out the | 
|  | // SetUnhandledExceptionFilter in the IAT of chrome.exe. | 
|  | using SetUnhandledExceptionFilterFunction = | 
|  | PTOP_LEVEL_EXCEPTION_FILTER(WINAPI*)(PTOP_LEVEL_EXCEPTION_FILTER filter); | 
|  | HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); | 
|  | if (!kernel32) | 
|  | return false; | 
|  |  | 
|  | SetUnhandledExceptionFilterFunction set_unhandled_exception_filter = | 
|  | reinterpret_cast<SetUnhandledExceptionFilterFunction>( | 
|  | GetProcAddress(kernel32, "SetUnhandledExceptionFilter")); | 
|  | if (!set_unhandled_exception_filter) | 
|  | return false; | 
|  |  | 
|  | // Success, pass ownership to the global. | 
|  | g_fallback_crash_handler_launcher = fallback_launcher.release(); | 
|  |  | 
|  | set_unhandled_exception_filter(&FallbackUnhandledExceptionFilter); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | int RunAsFallbackCrashHandler(const base::CommandLine& command_line, | 
|  | std::string product_name, | 
|  | std::string version, | 
|  | std::string channel_name) { | 
|  | FallbackCrashHandler fallback_handler; | 
|  |  | 
|  | if (!fallback_handler.ParseCommandLine(command_line)) { | 
|  | // TODO(siggi): Figure out how to UMA from this process, if need be. | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (!fallback_handler.GenerateCrashDump( | 
|  | product_name, version, channel_name, | 
|  | crash_reporter::switches::kCrashpadHandler)) { | 
|  | // TODO(siggi): Figure out how to UMA from this process, if need be. | 
|  | return 2; | 
|  | } | 
|  |  | 
|  | if (!fallback_handler.process().Terminate(kFallbackCrashTerminationCode, | 
|  | false)) { | 
|  | return 3; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | }  // namespace crash_reporter |