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 <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2018-08-29 09:00:15 -07:00 committed by Commit Bot
parent 0204fbd38b
commit d4d2f8557a
5 changed files with 189 additions and 17 deletions

View File

@ -61,14 +61,14 @@ std::vector<std::string> BuildHandlerArgvStrings(
return argv_strings; return argv_strings;
} }
void ConvertArgvStrings(const std::vector<std::string>& argv_strings, void StringVectorToCStringVector(const std::vector<std::string>& strings,
std::vector<const char*>* argv) { std::vector<const char*>* c_strings) {
argv->clear(); c_strings->clear();
argv->reserve(argv_strings.size() + 1); c_strings->reserve(strings.size() + 1);
for (const auto& arg : argv_strings) { for (const auto& str : strings) {
argv->push_back(arg.c_str()); c_strings->push_back(str.c_str());
} }
argv->push_back(nullptr); c_strings->push_back(nullptr);
} }
} // namespace crashpad } // namespace crashpad

View File

@ -40,11 +40,11 @@ std::vector<std::string> BuildHandlerArgvStrings(
//! \brief Flattens a string vector into a const char* vector suitable for use //! \brief Flattens a string vector into a const char* vector suitable for use
//! in an exec() call. //! in an exec() call.
//! //!
//! \param[in] argv_strings Arguments to be passed to child process, typically //! \param[in] strings A vector of string data. This vector must remain valid
//! created by BuildHandlerArgvStrings(). //! for the lifetime of \a c_strings.
//! \param[out] argv argv suitable for starting the child process. //! \param[out] c_strings A vector of pointers to the string data in \a strings.
void ConvertArgvStrings(const std::vector<std::string>& argv_strings, void StringVectorToCStringVector(const std::vector<std::string>& strings,
std::vector<const char*>* argv); std::vector<const char*>* c_strings);
} // namespace crashpad } // namespace crashpad

View File

@ -112,6 +112,84 @@ class CrashpadClient {
bool restartable, bool restartable,
bool asynchronous_start); 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<std::string>* env,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& 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<std::string>* env,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments,
int socket);
#endif // OS_ANDROID || DOXYGEN
#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN #if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN
//! \brief Installs a signal handler to launch a handler process in reponse to //! \brief Installs a signal handler to launch a handler process in reponse to
//! a crash. //! a crash.

View File

@ -62,7 +62,7 @@ bool CrashpadClient::StartHandler(
handler, database, metrics_dir, url, annotations, arguments); handler, database, metrics_dir, url, annotations, arguments);
std::vector<const char*> argv; std::vector<const char*> argv;
ConvertArgvStrings(argv_strings, &argv); StringVectorToCStringVector(argv_strings, &argv);
// Follow the same protocol as devmgr and crashlogger in Zircon (that is, // Follow the same protocol as devmgr and crashlogger in Zircon (that is,
// process handle as handle 0, with type USER0, exception port handle as // process handle as handle 0, with type USER0, exception port handle as

View File

@ -45,6 +45,39 @@ std::string FormatArgumentAddress(const std::string& name, void* addr) {
return base::StringPrintf("--%s=%p", name.c_str(), addr); return base::StringPrintf("--%s=%p", name.c_str(), addr);
} }
#if defined(OS_ANDROID)
std::vector<std::string> BuildAppProcessArgs(
const std::string& class_name,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments,
int socket) {
std::vector<std::string> 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<std::string> 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 { class SignalHandler {
public: public:
virtual void HandleCrashFatal(int signo, virtual void HandleCrashFatal(int signo,
@ -73,13 +106,20 @@ class LaunchAtCrashHandler : public SignalHandler {
return instance; return instance;
} }
bool Initialize(std::vector<std::string>* argv_in) { bool Initialize(std::vector<std::string>* argv_in,
const std::vector<std::string>* envp) {
argv_strings_.swap(*argv_in); 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", argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception",
&exception_information_)); &exception_information_));
ConvertArgvStrings(argv_strings_, &argv_); StringVectorToCStringVector(argv_strings_, &argv_);
return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr);
} }
@ -107,7 +147,13 @@ class LaunchAtCrashHandler : public SignalHandler {
return false; return false;
} }
if (pid == 0) { if (pid == 0) {
execv(argv_[0], const_cast<char* const*>(argv_.data())); if (set_envp_) {
execve(argv_[0],
const_cast<char* const*>(argv_.data()),
const_cast<char* const*>(envp_.data()));
} else {
execv(argv_[0], const_cast<char* const*>(argv_.data()));
}
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
@ -135,6 +181,9 @@ class LaunchAtCrashHandler : public SignalHandler {
std::vector<std::string> argv_strings_; std::vector<std::string> argv_strings_;
std::vector<const char*> argv_; std::vector<const char*> argv_;
std::vector<std::string> envp_strings_;
std::vector<const char*> envp_;
bool set_envp_ = false;
ExceptionInformation exception_information_; ExceptionInformation exception_information_;
DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler); DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler);
@ -167,6 +216,51 @@ bool CrashpadClient::StartHandler(
return false; return false;
} }
#if defined(OS_ANDROID)
// static
bool CrashpadClient::StartJavaHandlerAtCrash(
const std::string& class_name,
const std::vector<std::string>* env,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments) {
std::vector<std::string> 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<std::string>* env,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments,
int socket) {
std::vector<std::string> argv = BuildAppProcessArgs(
class_name, database, metrics_dir, url, annotations, arguments, socket);
return DoubleForkAndExec(argv, env, socket, false, nullptr);
}
#endif
// static // static
bool CrashpadClient::StartHandlerAtCrash( bool CrashpadClient::StartHandlerAtCrash(
const base::FilePath& handler, const base::FilePath& handler,
@ -179,7 +273,7 @@ bool CrashpadClient::StartHandlerAtCrash(
handler, database, metrics_dir, url, annotations, arguments); handler, database, metrics_dir, url, annotations, arguments);
auto signal_handler = LaunchAtCrashHandler::Get(); auto signal_handler = LaunchAtCrashHandler::Get();
if (signal_handler->Initialize(&argv)) { if (signal_handler->Initialize(&argv, nullptr)) {
DCHECK(!g_crash_handler); DCHECK(!g_crash_handler);
g_crash_handler = signal_handler; g_crash_handler = signal_handler;
return true; return true;