From 574936540df33f434e82fa86565b9a5aabf89e34 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 1 Feb 2018 10:39:32 -0800 Subject: [PATCH] linux: Add CrashpadClient methods to start the handler This change includes methods to install a signal handler to launch the handler process at crash time or to launch the handler on behalf of another process. Bug: crashpad:30 Change-Id: I503c788cb3648852d09e9e8c1fe5099ca07a0277 Reviewed-on: https://chromium-review.googlesource.com/759406 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- client/BUILD.gn | 4 + client/client.gyp | 1 + client/crashpad_client.h | 65 ++++++++++ client/crashpad_client_linux.cc | 204 ++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 client/crashpad_client_linux.cc diff --git a/client/BUILD.gn b/client/BUILD.gn index f7fd288f..f054e7ca 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -45,6 +45,10 @@ static_library("client") { ] } + if (crashpad_is_linux || crashpad_is_android) { + sources += [ "crashpad_client_linux.cc" ] + } + if (crashpad_is_win) { sources += [ "crash_report_database_win.cc", diff --git a/client/client.gyp b/client/client.gyp index 4b14c4bf..f75f9c40 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -40,6 +40,7 @@ 'crash_report_database_mac.mm', 'crash_report_database_win.cc', 'crashpad_client.h', + 'crashpad_client_linux.cc', 'crashpad_client_mac.cc', 'crashpad_client_win.cc', 'crashpad_info.cc', diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 7799bd9c..1f5bae65 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -105,6 +105,71 @@ class CrashpadClient { bool restartable, bool asynchronous_start); +#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN + //! \brief Installs a signal handler to launch a handler process in reponse to + //! a crash. + //! + //! The handler process will create a crash dump for this process and exit. + //! + //! \param[in] handler The path to a Crashpad handler executable. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartHandlerAtCrash( + 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); + + //! \brief Starts a handler process with an initial client. + //! + //! This method allows a process to launch the handler process on behalf of + //! another process. + //! + //! \param[in] handler The path to a Crashpad handler executable. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! \param[in] socket The server end of a socket pair. The client end should + //! be used with an ExceptionHandlerClient. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartHandlerForClient( + 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, + int socket); +#endif // OS_LINUX || OS_ANDROID || DOXYGEN + #if defined(OS_MACOSX) || DOXYGEN //! \brief Sets the process’ crash handler to a Mach service registered with //! the bootstrap server. diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc new file mode 100644 index 00000000..a36d9229 --- /dev/null +++ b/client/crashpad_client_linux.cc @@ -0,0 +1,204 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "client/crashpad_client.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "util/file/file_io.h" +#include "util/linux/exception_handler_client.h" +#include "util/linux/exception_information.h" +#include "util/misc/from_pointer_cast.h" +#include "util/posix/double_fork_and_exec.h" +#include "util/posix/signals.h" + +namespace crashpad { + +namespace { + +std::string FormatArgumentString(const std::string& name, + const std::string& value) { + return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); +} + +std::string FormatArgumentInt(const std::string& name, int value) { + return base::StringPrintf("--%s=%d", name.c_str(), value); +} + +std::string FormatArgumentAddress(const std::string& name, void* addr) { + return base::StringPrintf("--%s=%p", name.c_str(), addr); +} + +void BuildHandlerArgvStrings( + 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, + std::vector* argv_strings) { + argv_strings->clear(); + + argv_strings->push_back(handler.value()); + for (const auto& argument : arguments) { + argv_strings->push_back(argument); + } + + if (!database.empty()) { + argv_strings->push_back(FormatArgumentString("database", database.value())); + } + + if (!metrics_dir.empty()) { + argv_strings->push_back( + FormatArgumentString("metrics-dir", metrics_dir.value())); + } + + if (!url.empty()) { + argv_strings->push_back(FormatArgumentString("url", url)); + } + + for (const auto& kv : annotations) { + argv_strings->push_back( + FormatArgumentString("annotation", kv.first + '=' + kv.second)); + } +} + +void ConvertArgvStrings(const std::vector argv_strings, + std::vector* argv) { + argv->clear(); + argv->reserve(argv_strings.size() + 1); + for (const auto& arg : argv_strings) { + argv->push_back(arg.c_str()); + } + argv->push_back(nullptr); +} + +// Launches a single use handler to snapshot this process. +class LaunchAtCrashHandler { + public: + static LaunchAtCrashHandler* Get() { + static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler(); + return instance; + } + + bool Initialize(std::vector* argv_in) { + argv_strings_.swap(*argv_in); + + argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception", + &exception_information_)); + + ConvertArgvStrings(argv_strings_, &argv_); + return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); + } + + private: + LaunchAtCrashHandler() = default; + + ~LaunchAtCrashHandler() = delete; + + static void HandleCrash(int signo, siginfo_t* siginfo, void* context) { + auto state = Get(); + auto exception_information = &state->exception_information_; + + exception_information->siginfo_address = + FromPointerCastsiginfo_address)>( + siginfo); + exception_information->context_address = + FromPointerCastcontext_address)>( + context); + exception_information->thread_id = syscall(SYS_gettid); + + pid_t pid = fork(); + if (pid < 0) { + return; + } + if (pid == 0) { + execv(state->argv_[0], const_cast(state->argv_.data())); + return; + } + + int status; + waitpid(pid, &status, 0); + } + + std::vector argv_strings_; + std::vector argv_; + ExceptionInformation exception_information_; + + DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler); +}; + +} // namespace + +CrashpadClient::CrashpadClient() {} + +CrashpadClient::~CrashpadClient() {} + +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) { + // TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever + // supports accepting new connections. + // https://crashpad.chromium.org/bug/30 + NOTREACHED(); + return false; +} + +bool CrashpadClient::StartHandlerAtCrash( + 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) { + std::vector argv; + BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments, &argv); + + auto signal_handler = LaunchAtCrashHandler::Get(); + return signal_handler->Initialize(&argv); +} + +bool CrashpadClient::StartHandlerForClient( + 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, + int socket) { + std::vector argv; + BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments, &argv); + + argv.push_back(FormatArgumentInt("initial-client", socket)); + + return DoubleForkAndExec(argv, socket, true, nullptr); +} + +} // namespace crashpad