From d4d2f8557aac557743edc17b5501eb19c0463aee Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 29 Aug 2018 09:00:15 -0700 Subject: [PATCH] android: Add client methods to start a Java handler These methods use /system/bin/app_process{32,64} to load a Java class supplied by the embedding application. It is expected that the supplied class loads a native library containing Crashpad's handler code and passes its arguments to crashpad::HandlerMain(). Bug: crashpad:30 Change-Id: Ic0f9a1439007047b06f07f5ec7d5de9a9d4a19a2 Reviewed-on: https://chromium-review.googlesource.com/1194400 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- client/client_argv_handling.cc | 14 ++-- client/client_argv_handling.h | 10 +-- client/crashpad_client.h | 78 +++++++++++++++++++++++ client/crashpad_client_fuchsia.cc | 2 +- client/crashpad_client_linux.cc | 102 ++++++++++++++++++++++++++++-- 5 files changed, 189 insertions(+), 17 deletions(-) diff --git a/client/client_argv_handling.cc b/client/client_argv_handling.cc index 6933aac6..742a0008 100644 --- a/client/client_argv_handling.cc +++ b/client/client_argv_handling.cc @@ -61,14 +61,14 @@ std::vector BuildHandlerArgvStrings( return argv_strings; } -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()); +void StringVectorToCStringVector(const std::vector& strings, + std::vector* c_strings) { + c_strings->clear(); + c_strings->reserve(strings.size() + 1); + for (const auto& str : strings) { + c_strings->push_back(str.c_str()); } - argv->push_back(nullptr); + c_strings->push_back(nullptr); } } // namespace crashpad diff --git a/client/client_argv_handling.h b/client/client_argv_handling.h index d380b1a0..04c66d31 100644 --- a/client/client_argv_handling.h +++ b/client/client_argv_handling.h @@ -40,11 +40,11 @@ std::vector BuildHandlerArgvStrings( //! \brief Flattens a string vector into a const char* vector suitable for use //! in an exec() call. //! -//! \param[in] argv_strings Arguments to be passed to child process, typically -//! created by BuildHandlerArgvStrings(). -//! \param[out] argv argv suitable for starting the child process. -void ConvertArgvStrings(const std::vector& argv_strings, - std::vector* argv); +//! \param[in] strings A vector of string data. This vector must remain valid +//! for the lifetime of \a c_strings. +//! \param[out] c_strings A vector of pointers to the string data in \a strings. +void StringVectorToCStringVector(const std::vector& strings, + std::vector* c_strings); } // namespace crashpad diff --git a/client/crashpad_client.h b/client/crashpad_client.h index ae409d43..154d9ace 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -112,6 +112,84 @@ class CrashpadClient { bool restartable, bool asynchronous_start); +#if defined(OS_ANDROID) || DOXYGEN + //! \brief Installs a signal handler to execute `/system/bin/app_process` and + //! load a Java class in response to a crash. + //! + //! \param[in] class_name The fully qualified class name to load, which must + //! define a `main()` method to be invoked by `app_process`. Arguments + //! will be passed to this method as though it were the Crashpad handler. + //! This class is expected to load a native library defining + //! crashpad::HandlerMain() and pass the arguments to it. + //! \param[in] env A vector of environment variables of the form `var=value` + //! defining the environment in which to execute `app_process`. If this + //! value is `nullptr`, the application's environment at the time of the + //! crash will be used. + //! \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. + static bool StartJavaHandlerAtCrash( + const std::string& class_name, + const std::vector* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments); + + //! \brief Executes `/system/bin/app_process` and loads a Java class. + //! + //! \param[in] class_name The fully qualified class name to load, which must + //! define a `main()` method to be invoked by `app_process`. Arguments + //! will be passed to this method as though it were the Crashpad handler. + //! This class is expected to load a native library defining + //! crashpad::HandlerMain() and pass the arguments to it. + //! \param[in] env A vector of environment variables of the form `var=value` + //! defining the environment in which to execute `app_process`. If this + //! value is `nullptr`, the application's current environment will be + //! used. + //! \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. + static bool StartJavaHandlerForClient( + const std::string& class_name, + const std::vector* env, + 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_ANDROID || DOXYGEN + #if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN //! \brief Installs a signal handler to launch a handler process in reponse to //! a crash. diff --git a/client/crashpad_client_fuchsia.cc b/client/crashpad_client_fuchsia.cc index 0eba01d6..523a3e90 100644 --- a/client/crashpad_client_fuchsia.cc +++ b/client/crashpad_client_fuchsia.cc @@ -62,7 +62,7 @@ bool CrashpadClient::StartHandler( handler, database, metrics_dir, url, annotations, arguments); std::vector argv; - ConvertArgvStrings(argv_strings, &argv); + StringVectorToCStringVector(argv_strings, &argv); // Follow the same protocol as devmgr and crashlogger in Zircon (that is, // process handle as handle 0, with type USER0, exception port handle as diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index ec5396f3..1e9d6518 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -45,6 +45,39 @@ std::string FormatArgumentAddress(const std::string& name, void* addr) { return base::StringPrintf("--%s=%p", name.c_str(), addr); } +#if defined(OS_ANDROID) + +std::vector BuildAppProcessArgs( + const std::string& class_name, + 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; +#if defined(ARCH_CPU_64_BIT) + argv.push_back("/system/bin/app_process64"); +#else + argv.push_back("/system/bin/app_process32"); +#endif + argv.push_back("/system/bin"); + argv.push_back("--application"); + argv.push_back(class_name); + + std::vector handler_argv = BuildHandlerArgvStrings( + base::FilePath(), database, metrics_dir, url, annotations, arguments); + + if (socket != kInvalidFileHandle) { + handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket)); + } + + argv.insert(argv.end(), handler_argv.begin() + 1, handler_argv.end()); + return argv; +} + +#endif // OS_ANDROID + class SignalHandler { public: virtual void HandleCrashFatal(int signo, @@ -73,13 +106,20 @@ class LaunchAtCrashHandler : public SignalHandler { return instance; } - bool Initialize(std::vector* argv_in) { + bool Initialize(std::vector* argv_in, + const std::vector* envp) { argv_strings_.swap(*argv_in); + if (envp) { + envp_strings_ = *envp; + StringVectorToCStringVector(envp_strings_, &envp_); + set_envp_ = true; + } + argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception", &exception_information_)); - ConvertArgvStrings(argv_strings_, &argv_); + StringVectorToCStringVector(argv_strings_, &argv_); return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); } @@ -107,7 +147,13 @@ class LaunchAtCrashHandler : public SignalHandler { return false; } if (pid == 0) { - execv(argv_[0], const_cast(argv_.data())); + if (set_envp_) { + execve(argv_[0], + const_cast(argv_.data()), + const_cast(envp_.data())); + } else { + execv(argv_[0], const_cast(argv_.data())); + } _exit(EXIT_FAILURE); } @@ -135,6 +181,9 @@ class LaunchAtCrashHandler : public SignalHandler { std::vector argv_strings_; std::vector argv_; + std::vector envp_strings_; + std::vector envp_; + bool set_envp_ = false; ExceptionInformation exception_information_; DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler); @@ -167,6 +216,51 @@ bool CrashpadClient::StartHandler( return false; } +#if defined(OS_ANDROID) + +// static +bool CrashpadClient::StartJavaHandlerAtCrash( + const std::string& class_name, + const std::vector* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments) { + std::vector argv = BuildAppProcessArgs(class_name, + database, + metrics_dir, + url, + annotations, + arguments, + kInvalidFileHandle); + + auto signal_handler = LaunchAtCrashHandler::Get(); + if (signal_handler->Initialize(&argv, env)) { + DCHECK(!g_crash_handler); + g_crash_handler = signal_handler; + return true; + } + return false; +} + +// static +bool CrashpadClient::StartJavaHandlerForClient( + const std::string& class_name, + const std::vector* env, + 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 = BuildAppProcessArgs( + class_name, database, metrics_dir, url, annotations, arguments, socket); + return DoubleForkAndExec(argv, env, socket, false, nullptr); +} + +#endif + // static bool CrashpadClient::StartHandlerAtCrash( const base::FilePath& handler, @@ -179,7 +273,7 @@ bool CrashpadClient::StartHandlerAtCrash( handler, database, metrics_dir, url, annotations, arguments); auto signal_handler = LaunchAtCrashHandler::Get(); - if (signal_handler->Initialize(&argv)) { + if (signal_handler->Initialize(&argv, nullptr)) { DCHECK(!g_crash_handler); g_crash_handler = signal_handler; return true;