工程编译好了,修改了winMain函数入口点代码。使之可以弹框打印出ppid/pid/command_line.
启动工程后,遇到弹框,遇到主进程或感兴趣的子进程,附加上去单步观摩代码实现。
chromium工程启动后,首先会启动一个崩溃信息处理程序. 先观摩了这个子进程的创建和子进程的实现。
chromium代码封装的层次挺深的,先看个大概。
chromium的修改都在vs2017中进行。
chromium的编译在cmd中运行
“Z:\chromium\src>autoninja -C out\Default_68_0_3440_84 chrome”
调试和单步用附加弹框的chrome.exe进行
修改点: Z:\chromium\src\chrome\app\chrome_exe_main_win.cc
// 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.
#include
#include
#include
#include
#include
#include
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string16.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/win/registry.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "base/process/process_handle.h" // add by ls
#include "chrome/app/main_dll_loader_win.h"
#include "chrome/browser/policy/policy_path_parser.h"
#include "chrome/browser/win/chrome_process_finder.h"
#include "chrome/common/chrome_paths_internal.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/install_static/initialize_from_primary_module.h"
#include "chrome/install_static/install_util.h"
#include "chrome/install_static/user_data_dir.h"
#include "chrome_elf/chrome_elf_main.h"
#include "components/crash/content/app/crash_switches.h"
#include "components/crash/content/app/crashpad.h"
#include "components/crash/content/app/fallback_crash_handling_win.h"
#include "components/crash/content/app/run_as_crashpad_handler_win.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#if !defined(WIN_CONSOLE_APP)
int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prev, wchar_t*, int) {
#else
int main() {
HINSTANCE instance = GetModuleHandle(nullptr);
#endif
base::ProcessId my_ppid = base::GetParentProcessId(base::GetCurrentProcessHandle());
base::ProcessId my_pid = base::GetCurrentProcId();
DWORD my_mb_style = MB_ICONEXCLAMATION | MB_OK | MB_TASKMODAL | MB_APPLMODAL | MB_TOPMOST;
// 为了抢在建立崩溃处理子进程之前调试
if (1) {
wchar_t sz_buf[1024] = { L'\0' };
memset(sz_buf, 0, sizeof(wchar_t) * 1024);
wsprintf(sz_buf, L"[ppid/pid] = [%d/%d]", my_ppid, my_pid);
MessageBox(NULL, sz_buf, L"new chromium entry wWinMain", my_mb_style);
}
install_static::InitializeFromPrimaryModule();
SignalInitializeCrashReporting(); // create sub process : --type=crashpad-handler
// Initialize the CommandLine singleton from the environment.
base::CommandLine::Init(0, nullptr);
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
// show pocess's ppid/pid/command_line
if (NULL != command_line) {
// StringType GetArgumentsString()
// 如果没有命令行参数的chromium进程,就是主进程
base::CommandLine::StringType str_cmd_line = command_line->GetArgumentsString();
int len = str_cmd_line.size();
wchar_t* psz_buf = new wchar_t[len + 4096];
if (NULL != psz_buf) {
memset(psz_buf, 0, sizeof(wchar_t) * (len + 4096));
wsprintf(psz_buf, L"ppid/pid/cmd_line => %d/%d/[%s]", my_ppid, my_pid, str_cmd_line.c_str());
MessageBox(NULL, psz_buf, L"new chromium entry - command_line", my_mb_style);
delete[] psz_buf;
psz_buf = NULL;
}
else {
MessageBox(NULL, L"failed new memory", L"new chromium entry", my_mb_style);
}
}
else {
// print pid
wchar_t sz_buf[1024] = { L'\0' };
memset(sz_buf, 0, sizeof(wchar_t) * 1024);
wsprintf(sz_buf, L"[ppid/pid] = [%d/%d]", my_ppid, my_pid);
MessageBox(NULL, sz_buf, L"new chromium entry", my_mb_style);
}
const std::string process_type =
command_line->GetSwitchValueASCII(switches::kProcessType);
// Confirm that an explicit prefetch profile is used for all process types
// except for the browser process. Any new process type will have to assign
// itself a prefetch id. See kPrefetchArgument* constants in
// content_switches.cc for details.
DCHECK(process_type.empty() ||
HasValidWindowsPrefetchArgument(*command_line));
if (process_type == crash_reporter::switches::kCrashpadHandler) {
crash_reporter::SetupFallbackCrashHandling(*command_line);
// The handler process must always be passed the user data dir on the
// command line.
DCHECK(command_line->HasSwitch(switches::kUserDataDir));
base::FilePath user_data_dir =
command_line->GetSwitchValuePath(switches::kUserDataDir);
return crash_reporter::RunAsCrashpadHandler(
*base::CommandLine::ForCurrentProcess(), user_data_dir,
switches::kProcessType, switches::kUserDataDir);
} else if (process_type == crash_reporter::switches::kFallbackCrashHandler) {
return RunFallbackCrashHandler(*command_line);
}
const base::TimeTicks exe_entry_point_ticks = base::TimeTicks::Now();
// Signal Chrome Elf that Chrome has begun to start.
SignalChromeElf();
// The exit manager is in charge of calling the dtors of singletons.
base::AtExitManager exit_manager;
base::win::EnableHighDPISupport();
if (AttemptFastNotify(*command_line))
return 0;
RemoveAppCompatFlagsEntry();
// Load and launch the chrome dll. *Everything* happens inside.
VLOG(1) << "About to load main DLL.";
MainDllLoader* loader = MakeMainDllLoader();
int rc = loader->Launch(instance, exe_entry_point_ticks);
loader->RelaunchChromeBrowserWithNewCommandLineIfNeeded();
delete loader;
return rc;
}
wWinMain()
install_static::InitializeFromPrimaryModule();
SignalInitializeCrashReporting(); // create sub process : --type=crashpad-handler
崩溃报告子进程的实现(子进程的创建和子进程的实现)在Z:\chromium\src\third_party\crashpad\crashpad\client\crashpad_client_win.cc
// This function is a temporary workaround for https://crbug.com/655788. We
// need to come up with a better way to initialize crash reporting that can
// happen inside DllMain().
void SignalInitializeCrashReporting() {
if (!elf_crash::InitializeCrashReporting()) { // create sub process
#ifdef _DEBUG
assert(false);
#endif // _DEBUG
}
}
namespace elf_crash {
// NOTE: This function will be called from DllMain during DLL_PROCESS_ATTACH
// (while we have the loader lock), so do not misbehave.
bool InitializeCrashReporting() {
#ifdef _DEBUG
assert(g_crash_reports == nullptr);
assert(g_set_unhandled_exception_filter == nullptr);
#endif // _DEBUG
// No global objects with destructors, so using global pointers.
// DllMain on detach will clean these up.
g_crash_reports = new std::vector;
g_set_unhandled_exception_filter = new elf_hook::IATHook();
ChromeCrashReporterClient::InitializeCrashReportingForProcess(); // create sub process
g_crash_helper_enabled = true;
return true;
}
#if !defined(NACL_WIN64)
// static
void ChromeCrashReporterClient::InitializeCrashReportingForProcess() {
static ChromeCrashReporterClient* instance = nullptr;
if (instance)
return;
instance = new ChromeCrashReporterClient();
ANNOTATE_LEAKING_OBJECT_PTR(instance);
std::wstring process_type = install_static::GetSwitchValueFromCommandLine(
::GetCommandLine(), install_static::kProcessType);
// Don't set up Crashpad crash reporting in the Crashpad handler itself, nor
// in the fallback crash handler for the Crashpad handler process.
if (process_type != install_static::kCrashpadHandler &&
process_type != install_static::kFallbackHandler) {
crash_reporter::SetCrashReporterClient(instance);
std::wstring user_data_dir;
if (process_type.empty())
install_static::GetUserDataDirectory(&user_data_dir, nullptr);
crash_reporter::InitializeCrashpadWithEmbeddedHandler(
process_type.empty(), install_static::UTF16ToUTF8(process_type),
install_static::UTF16ToUTF8(user_data_dir), base::FilePath());
}
}
#endif // !defined(NACL_WIN64)
#if defined(OS_WIN)
void InitializeCrashpadWithEmbeddedHandler(bool initial_client,
const std::string& process_type,
const std::string& user_data_dir,
const base::FilePath& exe_path) {
// create sub process
InitializeCrashpadImpl(initial_client, process_type, user_data_dir, exe_path,
true);
}
#endif // OS_WIN
void InitializeCrashpadImpl(bool initial_client,
const std::string& process_type,
const std::string& user_data_dir,
const base::FilePath& exe_path,
bool embedded_handler) {
static bool initialized = false;
DCHECK(!initialized);
initialized = true;
const bool browser_process = process_type.empty();
CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
if (initial_client) {
#if defined(OS_MACOSX)
// "relauncher" is hard-coded because it's a Chrome --type, but this
// component can't see Chrome's switches. This is only used for argument
// sanitization.
DCHECK(browser_process || process_type == "relauncher");
#elif defined(OS_WIN)
// "Chrome Installer" is the name historically used for installer binaries
// as processed by the backend.
DCHECK(browser_process || process_type == "Chrome Installer" ||
process_type == "notification-helper");
#elif defined(OS_LINUX) || defined(OS_ANDROID)
DCHECK(browser_process);
#else
#error Port.
#endif // OS_MACOSX
} else {
DCHECK(!browser_process);
}
// database_path is only valid in the browser process.
// create sub process
base::FilePath database_path = internal::PlatformCrashpadInitialization(
initial_client, browser_process, embedded_handler, user_data_dir,
exe_path);
base::FilePath PlatformCrashpadInitialization(bool initial_client,
bool browser_process,
bool embedded_handler,
const std::string& user_data_dir,
const base::FilePath& exe_path) {
base::FilePath database_path; // Only valid in the browser process.
base::FilePath metrics_path; // Only valid in the browser process.
const char kPipeNameVar[] = "CHROME_CRASHPAD_PIPE_NAME";
const char kServerUrlVar[] = "CHROME_CRASHPAD_SERVER_URL";
std::unique_ptr env(base::Environment::Create());
if (initial_client) {
CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
base::string16 database_path_str;
if (crash_reporter_client->GetCrashDumpLocation(&database_path_str))
database_path = base::FilePath(database_path_str);
base::string16 metrics_path_str;
if (crash_reporter_client->GetCrashMetricsLocation(&metrics_path_str)) {
metrics_path = base::FilePath(metrics_path_str);
CHECK(base::CreateDirectoryAndGetError(metrics_path, nullptr));
}
std::map process_annotations;
GetPlatformCrashpadAnnotations(&process_annotations);
#if defined(GOOGLE_CHROME_BUILD)
std::string url = "https://clients2.google.com/cr/report";
#else
std::string url;
#endif
// Allow the crash server to be overridden for testing. If the variable
// isn't present in the environment then the default URL will remain.
env->GetVar(kServerUrlVar, &url);
base::FilePath exe_file(exe_path);
if (exe_file.empty()) {
wchar_t exe_file_path[MAX_PATH] = {};
CHECK(::GetModuleFileName(nullptr, exe_file_path,
arraysize(exe_file_path)));
exe_file = base::FilePath(exe_file_path);
}
if (crash_reporter_client->GetShouldDumpLargerDumps()) {
const uint32_t kIndirectMemoryLimit = 4 * 1024 * 1024;
crashpad::CrashpadInfo::GetCrashpadInfo()
->set_gather_indirectly_referenced_memory(
crashpad::TriState::kEnabled, kIndirectMemoryLimit);
}
// If the handler is embedded in the binary (e.g. chrome, setup), we
// reinvoke it with --type=crashpad-handler. Otherwise, we use the
// standalone crashpad_handler.exe (for tests, etc.).
std::vector start_arguments;
if (embedded_handler) {
start_arguments.push_back(std::string("--type=") +
switches::kCrashpadHandler);
if (!user_data_dir.empty()) {
start_arguments.push_back(std::string("--user-data-dir=") +
user_data_dir);
}
// The prefetch argument added here has to be documented in
// chrome_switches.cc, below the kPrefetchArgument* constants. A constant
// can't be used here because crashpad can't depend on Chrome.
start_arguments.push_back("/prefetch:7");
} else {
base::FilePath exe_dir = exe_file.DirName();
exe_file = exe_dir.Append(FILE_PATH_LITERAL("crashpad_handler.exe"));
}
std::vector arguments(start_arguments);
if (crash_reporter_client->ShouldMonitorCrashHandlerExpensively()) {
arguments.push_back("--monitor-self");
for (const std::string& start_argument : start_arguments) {
arguments.push_back(std::string("--monitor-self-argument=") +
start_argument);
}
}
// Set up --monitor-self-annotation even in the absence of --monitor-self so
// that minidumps produced by Crashpad's generate_dump tool will contain
// these annotations.
arguments.push_back(std::string("--monitor-self-annotation=ptype=") +
switches::kCrashpadHandler);
// create sub process
GetCrashpadClient().StartHandler(exe_file, database_path, metrics_path, url,
process_annotations, arguments, false,
false);
bool CrashpadClient::StartHandler(
const base::FilePath& handler,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map& annotations,
const std::vector& arguments,
bool restartable,
bool asynchronous_start) {
DCHECK(ipc_pipe_.empty());
// Both the pipe and the signalling events have to be created on the main
// thread (not the spawning thread) so that they're valid after we return from
// this function.
ScopedFileHANDLE ipc_pipe_handle;
CreatePipe(&ipc_pipe_, &ipc_pipe_handle);
SECURITY_ATTRIBUTES security_attributes = {0};
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
security_attributes.bInheritHandle = true;
g_signal_exception =
CreateEvent(&security_attributes, false /* auto reset */, false, nullptr);
g_signal_non_crash_dump =
CreateEvent(&security_attributes, false /* auto reset */, false, nullptr);
g_non_crash_dump_done =
CreateEvent(&security_attributes, false /* auto reset */, false, nullptr);
CommonInProcessInitialization();
RegisterHandlers();
auto data = new BackgroundHandlerStartThreadData(handler,
database,
metrics_dir,
url,
annotations,
arguments,
ipc_pipe_,
std::move(ipc_pipe_handle));
if (asynchronous_start) {
// It is important that the current thread not be synchronized with the
// thread that is created here. StartHandler() needs to be callable inside a
// DllMain(). In that case, the background thread will not start until the
// current DllMain() completes, which would cause deadlock if it was waited
// upon.
handler_start_thread_.reset(CreateThread(nullptr,
0,
&BackgroundHandlerStartThreadProc,
reinterpret_cast(data),
0,
nullptr));
if (!handler_start_thread_.is_valid()) {
PLOG(ERROR) << "CreateThread";
SetHandlerStartupState(StartupState::kFailed);
return false;
}
// In asynchronous mode, we can't report on the overall success or failure
// of initialization at this point.
return true;
} else {
// start sub process
return StartHandlerProcess(
std::unique_ptr(data));
}
}
bool StartHandlerProcess(
std::unique_ptr data) {
ScopedCallSetHandlerStartupState scoped_startup_state_caller;
std::wstring command_line;
AppendCommandLineArgument(data->handler.value(), &command_line);
for (const std::string& argument : data->arguments) {
AppendCommandLineArgument(base::UTF8ToUTF16(argument), &command_line);
}
if (!data->database.value().empty()) {
AppendCommandLineArgument(
FormatArgumentString("database", data->database.value()),
&command_line);
}
if (!data->metrics_dir.value().empty()) {
AppendCommandLineArgument(
FormatArgumentString("metrics-dir", data->metrics_dir.value()),
&command_line);
}
if (!data->url.empty()) {
AppendCommandLineArgument(
FormatArgumentString("url", base::UTF8ToUTF16(data->url)),
&command_line);
}
for (const auto& kv : data->annotations) {
AppendCommandLineArgument(
FormatArgumentString("annotation",
base::UTF8ToUTF16(kv.first + '=' + kv.second)),
&command_line);
}
ScopedKernelHANDLE this_process(
OpenProcess(kXPProcessAllAccess, true, GetCurrentProcessId()));
if (!this_process.is_valid()) {
PLOG(ERROR) << "OpenProcess";
return false;
}
InitialClientData initial_client_data(
g_signal_exception,
g_signal_non_crash_dump,
g_non_crash_dump_done,
data->ipc_pipe_handle.get(),
this_process.get(),
FromPointerCast(&g_crash_exception_information),
FromPointerCast(&g_non_crash_exception_information),
FromPointerCast(&g_critical_section_with_debug_info));
AppendCommandLineArgument(
base::UTF8ToUTF16(std::string("--initial-client-data=") +
initial_client_data.StringRepresentation()),
&command_line);
BOOL rv;
DWORD creation_flags;
STARTUPINFOEX startup_info = {};
startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
startup_info.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startup_info.StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
startup_info.StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
std::vector handle_list;
std::unique_ptr proc_thread_attribute_list_storage;
ScopedProcThreadAttributeList proc_thread_attribute_list_owner;
static const auto initialize_proc_thread_attribute_list =
GET_FUNCTION(L"kernel32.dll", ::InitializeProcThreadAttributeList);
static const auto update_proc_thread_attribute =
initialize_proc_thread_attribute_list
? GET_FUNCTION(L"kernel32.dll", ::UpdateProcThreadAttribute)
: nullptr;
if (!initialize_proc_thread_attribute_list || !update_proc_thread_attribute) {
// The OS doesn’t allow handle inheritance to be restricted, so the handler
// will inherit every inheritable handle.
creation_flags = 0;
startup_info.StartupInfo.cb = sizeof(startup_info.StartupInfo);
} else {
// Restrict handle inheritance to just those needed in the handler.
creation_flags = EXTENDED_STARTUPINFO_PRESENT;
startup_info.StartupInfo.cb = sizeof(startup_info);
SIZE_T size;
rv = initialize_proc_thread_attribute_list(nullptr, 1, 0, &size);
if (rv) {
LOG(ERROR) << "InitializeProcThreadAttributeList (size) succeeded, "
"expected failure";
return false;
} else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
PLOG(ERROR) << "InitializeProcThreadAttributeList (size)";
return false;
}
proc_thread_attribute_list_storage.reset(new uint8_t[size]);
startup_info.lpAttributeList =
reinterpret_cast(
proc_thread_attribute_list_storage.get());
rv = initialize_proc_thread_attribute_list(
startup_info.lpAttributeList, 1, 0, &size);
if (!rv) {
PLOG(ERROR) << "InitializeProcThreadAttributeList";
return false;
}
proc_thread_attribute_list_owner.reset(startup_info.lpAttributeList);
handle_list.reserve(8);
handle_list.push_back(g_signal_exception);
handle_list.push_back(g_signal_non_crash_dump);
handle_list.push_back(g_non_crash_dump_done);
handle_list.push_back(data->ipc_pipe_handle.get());
handle_list.push_back(this_process.get());
AddHandleToListIfValidAndInheritable(&handle_list,
startup_info.StartupInfo.hStdInput);
AddHandleToListIfValidAndInheritable(&handle_list,
startup_info.StartupInfo.hStdOutput);
AddHandleToListIfValidAndInheritable(&handle_list,
startup_info.StartupInfo.hStdError);
rv = update_proc_thread_attribute(
startup_info.lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
&handle_list[0],
handle_list.size() * sizeof(handle_list[0]),
nullptr,
nullptr);
if (!rv) {
PLOG(ERROR) << "UpdateProcThreadAttribute";
return false;
}
}
PROCESS_INFORMATION process_info;
// create sub process
rv = CreateProcess(data->handler.value().c_str(),
&command_line[0],
nullptr,
nullptr,
true,
creation_flags,
nullptr,
nullptr,
&startup_info.StartupInfo,
&process_info);
wWinMain()
得到process_type = “crashpad-handler”
const std::string process_type =
command_line->GetSwitchValueASCII(switches::kProcessType);
无限等待要检测的进程结束,然后崩溃处理子进程就退出了
if (process_type == crash_reporter::switches::kCrashpadHandler) {
crash_reporter::SetupFallbackCrashHandling(*command_line);
// The handler process must always be passed the user data dir on the
// command line.
DCHECK(command_line->HasSwitch(switches::kUserDataDir));
base::FilePath user_data_dir =
command_line->GetSwitchValuePath(switches::kUserDataDir);
// 进入一个循环,死等目标进程结束
return crash_reporter::RunAsCrashpadHandler(
*base::CommandLine::ForCurrentProcess(), user_data_dir,
switches::kProcessType, switches::kUserDataDir);
} else if (process_type == crash_reporter::switches::kFallbackCrashHandler) {
return RunFallbackCrashHandler(*command_line);
}
int RunAsCrashpadHandler(const base::CommandLine& command_line,
const base::FilePath& user_data_dir,
const char* process_type_switch,
const char* user_data_dir_switch) {
// Make sure this process terminates on OOM in the same mode as other Chrome
// processes.
base::EnableTerminationOnOutOfMemory();
// If the handler is started with --monitor-self, it'll need a ptype
// annotation set. It'll normally set one itself by being invoked with
// --monitor-self-annotation=ptype=crashpad-handler, but that leaves a window
// during self-monitoring initialization when the ptype is not set at all, so
// provide one here.
const std::string process_type =
command_line.GetSwitchValueASCII(process_type_switch);
if (!process_type.empty()) {
crashpad::SimpleStringDictionary* annotations =
new crashpad::SimpleStringDictionary();
annotations->SetKeyValue("ptype", process_type.c_str());
crashpad::CrashpadInfo* crashpad_info =
crashpad::CrashpadInfo::GetCrashpadInfo();
DCHECK(!crashpad_info->simple_annotations());
crashpad_info->set_simple_annotations(annotations);
}
std::vector argv = command_line.argv();
const base::string16 process_type_arg_prefix =
base::string16(L"--") + base::UTF8ToUTF16(process_type_switch) + L"=";
const base::string16 user_data_dir_arg_prefix =
base::string16(L"--") + base::UTF8ToUTF16(user_data_dir_switch) + L"=";
argv.erase(
std::remove_if(argv.begin(), argv.end(),
[&process_type_arg_prefix,
&user_data_dir_arg_prefix](const base::string16& str) {
return base::StartsWith(str, process_type_arg_prefix,
base::CompareCase::SENSITIVE) ||
base::StartsWith(str, user_data_dir_arg_prefix,
base::CompareCase::SENSITIVE) ||
(!str.empty() && str[0] == L'/');
}),
argv.end());
std::unique_ptr argv_as_utf8(new char*[argv.size() + 1]);
std::vector storage;
storage.reserve(argv.size());
for (size_t i = 0; i < argv.size(); ++i) {
storage.push_back(base::UTF16ToUTF8(argv[i]));
argv_as_utf8[i] = &storage[i][0];
}
argv_as_utf8[argv.size()] = nullptr;
argv.clear();
crashpad::UserStreamDataSources user_stream_data_sources;
// Interpret an empty user data directory as a missing value.
if (!user_data_dir.empty()) {
// Register an extension to collect stability information. The extension
// will be invoked for any registered process' crashes, but information only
// exists for instrumented browser processes.
user_stream_data_sources.push_back(
std::make_unique(
user_data_dir));
}
// 死等目标进程结束
return crashpad::HandlerMain(static_cast(storage.size()),
argv_as_utf8.get(), &user_stream_data_sources);
}
崩溃处理子进程也启动了一个子进程,用途未知,没有去跟了。
int HandlerMain(int argc,
char* argv[],
const UserStreamDataSources* user_stream_sources) {
InstallCrashHandler();
CallMetricsRecordNormalExit metrics_record_normal_exit;
const base::FilePath argv0(
ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
const base::FilePath me(argv0.BaseName());
enum OptionFlags {
// Long options without short equivalents.
kOptionLastChar = 255,
kOptionAnnotation,
kOptionDatabase,
#if defined(OS_MACOSX)
kOptionHandshakeFD,
#endif // OS_MACOSX
#if defined(OS_WIN)
kOptionInitialClientData,
#endif // OS_WIN
#if defined(OS_MACOSX)
kOptionMachService,
#endif // OS_MACOSX
kOptionMetrics,
kOptionMonitorSelf,
kOptionMonitorSelfAnnotation,
kOptionMonitorSelfArgument,
kOptionNoIdentifyClientViaUrl,
kOptionNoPeriodicTasks,
kOptionNoRateLimit,
kOptionNoUploadGzip,
#if defined(OS_WIN)
kOptionPipeName,
#endif // OS_WIN
#if defined(OS_MACOSX)
kOptionResetOwnCrashExceptionPortToSystemDefault,
#endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID)
kOptionTraceParentWithException,
kOptionInitialClientFD,
#endif
kOptionURL,
// Standard options.
kOptionHelp = -2,
kOptionVersion = -3,
};
static constexpr option long_options[] = {
{"annotation", required_argument, nullptr, kOptionAnnotation},
{"database", required_argument, nullptr, kOptionDatabase},
#if defined(OS_MACOSX)
{"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
#endif // OS_MACOSX
#if defined(OS_WIN)
{"initial-client-data",
required_argument,
nullptr,
kOptionInitialClientData},
#endif // OS_MACOSX
#if defined(OS_MACOSX)
{"mach-service", required_argument, nullptr, kOptionMachService},
#endif // OS_MACOSX
{"metrics-dir", required_argument, nullptr, kOptionMetrics},
{"monitor-self", no_argument, nullptr, kOptionMonitorSelf},
{"monitor-self-annotation",
required_argument,
nullptr,
kOptionMonitorSelfAnnotation},
{"monitor-self-argument",
required_argument,
nullptr,
kOptionMonitorSelfArgument},
{"no-identify-client-via-url",
no_argument,
nullptr,
kOptionNoIdentifyClientViaUrl},
{"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks},
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
{"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip},
#if defined(OS_WIN)
{"pipe-name", required_argument, nullptr, kOptionPipeName},
#endif // OS_WIN
#if defined(OS_MACOSX)
{"reset-own-crash-exception-port-to-system-default",
no_argument,
nullptr,
kOptionResetOwnCrashExceptionPortToSystemDefault},
#endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID)
{"trace-parent-with-exception",
required_argument,
nullptr,
kOptionTraceParentWithException},
{"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD},
#endif // OS_LINUX || OS_ANDROID
{"url", required_argument, nullptr, kOptionURL},
{"help", no_argument, nullptr, kOptionHelp},
{"version", no_argument, nullptr, kOptionVersion},
{nullptr, 0, nullptr, 0},
};
Options options = {};
#if defined(OS_MACOSX)
options.handshake_fd = -1;
#endif
options.identify_client_via_url = true;
options.periodic_tasks = true;
options.rate_limit = true;
options.upload_gzip = true;
#if defined(OS_LINUX) || defined(OS_ANDROID)
options.exception_information_address = 0;
options.initial_client_fd = kInvalidFileHandle;
#endif
int opt;
while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
switch (opt) {
case kOptionAnnotation: {
if (!AddKeyValueToMap(&options.annotations, optarg, "--annotation")) {
return ExitFailure();
}
break;
}
case kOptionDatabase: {
options.database = base::FilePath(
ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
break;
}
#if defined(OS_MACOSX)
case kOptionHandshakeFD: {
if (!StringToNumber(optarg, &options.handshake_fd) ||
options.handshake_fd < 0) {
ToolSupport::UsageHint(me,
"--handshake-fd requires a file descriptor");
return ExitFailure();
}
break;
}
case kOptionMachService: {
options.mach_service = optarg;
break;
}
#endif // OS_MACOSX
#if defined(OS_WIN)
case kOptionInitialClientData: {
if (!options.initial_client_data.InitializeFromString(optarg)) {
ToolSupport::UsageHint(
me, "failed to parse --initial-client-data");
return ExitFailure();
}
break;
}
#endif // OS_WIN
case kOptionMetrics: {
options.metrics_dir = base::FilePath(
ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
break;
}
case kOptionMonitorSelf: {
options.monitor_self = true;
break;
}
case kOptionMonitorSelfAnnotation: {
if (!AddKeyValueToMap(&options.monitor_self_annotations,
optarg,
"--monitor-self-annotation")) {
return ExitFailure();
}
break;
}
case kOptionMonitorSelfArgument: {
options.monitor_self_arguments.push_back(optarg);
break;
}
case kOptionNoIdentifyClientViaUrl: {
options.identify_client_via_url = false;
break;
}
case kOptionNoPeriodicTasks: {
options.periodic_tasks = false;
break;
}
case kOptionNoRateLimit: {
options.rate_limit = false;
break;
}
case kOptionNoUploadGzip: {
options.upload_gzip = false;
break;
}
#if defined(OS_WIN)
case kOptionPipeName: {
options.pipe_name = optarg;
break;
}
#endif // OS_WIN
#if defined(OS_MACOSX)
case kOptionResetOwnCrashExceptionPortToSystemDefault: {
options.reset_own_crash_exception_port_to_system_default = true;
break;
}
#endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID)
case kOptionTraceParentWithException: {
if (!StringToNumber(optarg, &options.exception_information_address)) {
ToolSupport::UsageHint(
me, "failed to parse --trace-parent-with-exception");
return ExitFailure();
}
break;
}
case kOptionInitialClientFD: {
if (!base::StringToInt(optarg, &options.initial_client_fd)) {
ToolSupport::UsageHint(me, "failed to parse --initial-client-fd");
return ExitFailure();
}
break;
}
#endif // OS_LINUX || OS_ANDROID
case kOptionURL: {
options.url = optarg;
break;
}
case kOptionHelp: {
Usage(me);
MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
return EXIT_SUCCESS;
}
case kOptionVersion: {
ToolSupport::Version(me);
MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
return EXIT_SUCCESS;
}
default: {
ToolSupport::UsageHint(me, nullptr);
return ExitFailure();
}
}
}
argc -= optind;
argv += optind;
#if defined(OS_MACOSX)
if (options.handshake_fd < 0 && options.mach_service.empty()) {
ToolSupport::UsageHint(me, "--handshake-fd or --mach-service is required");
return ExitFailure();
}
if (options.handshake_fd >= 0 && !options.mach_service.empty()) {
ToolSupport::UsageHint(
me, "--handshake-fd and --mach-service are incompatible");
return ExitFailure();
}
#elif defined(OS_WIN)
if (!options.initial_client_data.IsValid() && options.pipe_name.empty()) {
ToolSupport::UsageHint(me,
"--initial-client-data or --pipe-name is required");
return ExitFailure();
}
if (options.initial_client_data.IsValid() && !options.pipe_name.empty()) {
ToolSupport::UsageHint(
me, "--initial-client-data and --pipe-name are incompatible");
return ExitFailure();
}
#elif defined(OS_LINUX) || defined(OS_ANDROID)
if (!options.exception_information_address &&
options.initial_client_fd == kInvalidFileHandle) {
ToolSupport::UsageHint(
me,
"--exception_information_address or --initial_client_fd is required");
return ExitFailure();
}
#endif // OS_MACOSX
if (options.database.empty()) {
ToolSupport::UsageHint(me, "--database is required");
return ExitFailure();
}
if (argc) {
ToolSupport::UsageHint(me, nullptr);
return ExitFailure();
}
#if defined(OS_MACOSX)
if (options.reset_own_crash_exception_port_to_system_default) {
CrashpadClient::UseSystemDefaultHandler();
}
#endif // OS_MACOSX
if (options.monitor_self) {
MonitorSelf(options);
}
if (!options.monitor_self_annotations.empty()) {
// Establish these annotations even if --monitor-self is not present, in
// case something such as generate_dump wants to try to access them later.
//
// If the handler is part of a multi-purpose executable, simple annotations
// may already be present for this module. If they are, use them.
CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
SimpleStringDictionary* module_annotations =
crashpad_info->simple_annotations();
if (!module_annotations) {
module_annotations = new SimpleStringDictionary();
crashpad_info->set_simple_annotations(module_annotations);
}
for (const auto& iterator : options.monitor_self_annotations) {
module_annotations->SetKeyValue(iterator.first.c_str(),
iterator.second.c_str());
}
}
std::unique_ptr database(
CrashReportDatabase::Initialize(options.database));
if (!database) {
return ExitFailure();
}
ScopedStoppable upload_thread;
if (!options.url.empty()) {
// TODO(scottmg): options.rate_limit should be removed when we have a
// configurable database setting to control upload limiting.
// See https://crashpad.chromium.org/bug/23.
CrashReportUploadThread::Options upload_thread_options;
upload_thread_options.identify_client_via_url =
options.identify_client_via_url;
upload_thread_options.rate_limit = options.rate_limit;
upload_thread_options.upload_gzip = options.upload_gzip;
upload_thread_options.watch_pending_reports = options.periodic_tasks;
upload_thread.Reset(new CrashReportUploadThread(
database.get(), options.url, upload_thread_options));
upload_thread.Get()->Start();
}
CrashReportExceptionHandler exception_handler(
database.get(),
static_cast(upload_thread.Get()),
&options.annotations,
user_stream_sources);
#if defined(OS_LINUX) || defined(OS_ANDROID)
if (options.exception_information_address) {
return exception_handler.HandleException(getppid(),
options.exception_information_address) ?
EXIT_SUCCESS : ExitFailure();
}
#endif // OS_LINUX || OS_ANDROID
ScopedStoppable prune_thread;
if (options.periodic_tasks) {
prune_thread.Reset(new PruneCrashReportThread(
database.get(), PruneCondition::GetDefault()));
prune_thread.Get()->Start();
}
#if defined(OS_MACOSX)
if (options.mach_service.empty()) {
// Don’t do this when being run by launchd. See launchd.plist(5).
CloseStdinAndStdout();
}
base::mac::ScopedMachReceiveRight receive_right;
if (options.handshake_fd >= 0) {
receive_right.reset(
ChildPortHandshake::RunServerForFD(
base::ScopedFD(options.handshake_fd),
ChildPortHandshake::PortRightType::kReceiveRight));
} else if (!options.mach_service.empty()) {
receive_right = BootstrapCheckIn(options.mach_service);
}
if (!receive_right.is_valid()) {
return ExitFailure();
}
ExceptionHandlerServer exception_handler_server(
std::move(receive_right), !options.mach_service.empty());
base::AutoReset reset_g_exception_handler_server(
&g_exception_handler_server, &exception_handler_server);
struct sigaction old_sigterm_action;
ScopedResetSIGTERM reset_sigterm;
if (!options.mach_service.empty()) {
// When running from launchd, no no-senders notification could ever be
// triggered, because launchd maintains a send right to the service. When
// launchd wants the job to exit, it will send a SIGTERM. See
// launchd.plist(5).
//
// Set up a SIGTERM handler that will call exception_handler_server.Stop().
// This replaces the HandleTerminateSignal handler for SIGTERM.
if (Signals::InstallHandler(
SIGTERM, HandleSIGTERM, 0, &old_sigterm_action)) {
reset_sigterm.reset(&old_sigterm_action);
}
}
RecordFileLimitAnnotation();
#elif defined(OS_WIN)
// Shut down as late as possible relative to programs we're watching.
if (!SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY))
PLOG(ERROR) << "SetProcessShutdownParameters";
ExceptionHandlerServer exception_handler_server(!options.pipe_name.empty());
if (!options.pipe_name.empty()) {
exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name));
}
#elif defined(OS_FUCHSIA)
// These handles are logically "moved" into these variables when retrieved by
// zx_get_startup_handle(). Both are given to ExceptionHandlerServer which
// owns them in this process. There is currently no "connect-later" mode on
// Fuchsia, all the binding must be done by the client before starting
// crashpad_handler.
base::ScopedZxHandle root_job(zx_get_startup_handle(PA_HND(PA_USER0, 0)));
if (!root_job.is_valid()) {
LOG(ERROR) << "no process handle passed in startup handle 0";
return EXIT_FAILURE;
}
base::ScopedZxHandle exception_port(
zx_get_startup_handle(PA_HND(PA_USER0, 1)));
if (!exception_port.is_valid()) {
LOG(ERROR) << "no exception port handle passed in startup handle 1";
return EXIT_FAILURE;
}
ExceptionHandlerServer exception_handler_server(std::move(root_job),
std::move(exception_port));
#elif defined(OS_LINUX) || defined(OS_ANDROID)
ExceptionHandlerServer exception_handler_server;
#endif // OS_MACOSX
base::GlobalHistogramAllocator* histogram_allocator = nullptr;
if (!options.metrics_dir.empty()) {
static constexpr char kMetricsName[] = "CrashpadMetrics";
constexpr size_t kMetricsFileSize = 1 << 20;
if (base::GlobalHistogramAllocator::CreateWithActiveFileInDir(
options.metrics_dir, kMetricsFileSize, 0, kMetricsName)) {
histogram_allocator = base::GlobalHistogramAllocator::Get();
histogram_allocator->CreateTrackingHistograms(kMetricsName);
}
}
Metrics::HandlerLifetimeMilestone(Metrics::LifetimeMilestone::kStarted);
#if defined(OS_WIN)
if (options.initial_client_data.IsValid()) {
exception_handler_server.InitializeWithInheritedDataForInitialClient(
options.initial_client_data, &exception_handler);
}
#elif defined(OS_LINUX) || defined(OS_ANDROID)
if (options.initial_client_fd == kInvalidFileHandle ||
!exception_handler_server.InitializeWithClient(
ScopedFileHandle(options.initial_client_fd))) {
return ExitFailure();
}
#endif // OS_WIN
// 死等目标进程结束
exception_handler_server.Run(&exception_handler);
return EXIT_SUCCESS;
}
// Z:\chromium\src\third_party\crashpad\crashpad\util\win\exception_handler_server.cc
void ExceptionHandlerServer::Run(Delegate* delegate) {
uint64_t shutdown_token = base::RandUint64();
ScopedKernelHANDLE thread_handles[kPipeInstances];
for (size_t i = 0; i < arraysize(thread_handles); ++i) {
HANDLE pipe;
if (first_pipe_instance_.is_valid()) {
pipe = first_pipe_instance_.release();
} else {
pipe = CreateNamedPipeInstance(pipe_name_, i == 0);
PCHECK(pipe != INVALID_HANDLE_VALUE) << "CreateNamedPipe";
}
// Ownership of this object (and the pipe instance) is given to the new
// thread. We close the thread handles at the end of the scope. They clean
// up the context object and the pipe instance on termination.
internal::PipeServiceContext* context =
new internal::PipeServiceContext(port_.get(),
pipe,
delegate,
&clients_lock_,
&clients_,
shutdown_token);
thread_handles[i].reset(
CreateThread(nullptr, 0, &PipeServiceProc, context, 0, nullptr));
PCHECK(thread_handles[i].is_valid()) << "CreateThread";
}
delegate->ExceptionHandlerServerStarted();
// This is the main loop of the server. Most work is done on the threadpool,
// other than process end handling which is posted back to this main thread,
// as we must unregister the threadpool waits here.
// 在主程序和其他子程序运行时,就在这个循环中打转,所有进程都结束后,才跳出循环返回上一级,去崩溃进程处理结束。
for (;;) {
OVERLAPPED* ov = nullptr;
ULONG_PTR key = 0;
DWORD bytes = 0;
GetQueuedCompletionStatus(port_.get(), &bytes, &key, &ov, INFINITE);
if (!key) {
// Shutting down.
break;
}
// Otherwise, this is a request to unregister and destroy the given client.
// delete'ing the ClientData blocks in UnregisterWaitEx to ensure all
// outstanding threadpool waits are complete. This is important because the
// process handle can be signalled *before* the dump request is signalled.
internal::ClientData* client = reinterpret_cast(key);
base::AutoLock lock(clients_lock_);
clients_.erase(client);
delete client;
if (!persistent_ && clients_.empty())
break;
}