Commit 59582aea authored by Joshua Peraza's avatar Joshua Peraza Committed by Commit Bot

Add tools for dumping WebView embedder apps

Adds DumpWithoutCrashingForClient() which enables generating a dump on
behalf of the embedding app (represented by AwDebugCrashReporterClient)

This also includes refactoring of native code in minidump_uploader
to expose MimeifyReport() and WriteBodyToFile(), to be used by
AwDebug::DumpWithoutCrashing() in the followup CL.

Change-Id: If528bf060b6acaa51d0facaf1f1adf784c1852e6
Reviewed-on: https://chromium-review.googlesource.com/c/1269099
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Reviewed-by: default avatarTobias Sargeant <tobiasjs@chromium.org>
Reviewed-by: default avatarMark Mentovai <mark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#612955}
parent 59f9c856
...@@ -11,6 +11,7 @@ include_rules = [ ...@@ -11,6 +11,7 @@ include_rules = [
"+components/autofill/core/browser", "+components/autofill/core/browser",
"+components/autofill/core/common", "+components/autofill/core/common",
"+components/cdm/browser", "+components/cdm/browser",
"+components/crash/content/app",
"+components/crash/content/browser", "+components/crash/content/browser",
"+components/crash/core", "+components/crash/core",
"+components/download/public/common", "+components/download/public/common",
......
...@@ -2,15 +2,20 @@ ...@@ -2,15 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "android_webview/common/aw_channel.h"
#include "android_webview/common/crash_reporter/aw_crash_reporter_client.h" #include "android_webview/common/crash_reporter/aw_crash_reporter_client.h"
#include "android_webview/common/crash_reporter/crash_keys.h" #include "android_webview/common/crash_reporter/crash_keys.h"
#include "base/android/jni_android.h" #include "base/android/jni_android.h"
#include "base/android/jni_string.h" #include "base/android/jni_string.h"
#include "base/android/path_utils.h"
#include "base/debug/dump_without_crashing.h" #include "base/debug/dump_without_crashing.h"
#include "base/files/file.h" #include "base/files/file.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "components/crash/content/app/crash_reporter_client.h"
#include "components/crash/core/common/crash_key.h" #include "components/crash/core/common/crash_key.h"
#include "components/version_info/version_info.h"
#include "components/version_info/version_info_values.h"
#include "jni/AwDebug_jni.h" #include "jni/AwDebug_jni.h"
using base::android::ConvertJavaStringToUTF16; using base::android::ConvertJavaStringToUTF16;
...@@ -20,6 +25,45 @@ using base::android::ScopedJavaLocalRef; ...@@ -20,6 +25,45 @@ using base::android::ScopedJavaLocalRef;
namespace android_webview { namespace android_webview {
namespace {
class AwDebugCrashReporterClient
: public ::crash_reporter::CrashReporterClient {
public:
AwDebugCrashReporterClient() = default;
~AwDebugCrashReporterClient() override = default;
void GetProductNameAndVersion(std::string* product_name,
std::string* version,
std::string* channel) override {
*product_name = "AndroidWebView";
*version = PRODUCT_VERSION;
*channel =
version_info::GetChannelString(android_webview::GetChannelOrStable());
}
bool GetCrashDumpLocation(base::FilePath* debug_dir) override {
base::FilePath cache_dir;
if (!base::android::GetCacheDirectory(&cache_dir)) {
return false;
}
*debug_dir = cache_dir.Append(FILE_PATH_LITERAL("WebView")).Append("Debug");
return true;
}
void GetSanitizationInformation(const char* const** annotations_whitelist,
void** target_module,
bool* sanitize_stacks) override {
*annotations_whitelist = crash_keys::kWebViewCrashKeyWhiteList;
*target_module = nullptr;
*sanitize_stacks = true;
}
DISALLOW_COPY_AND_ASSIGN(AwDebugCrashReporterClient);
};
} // namespace
static jboolean JNI_AwDebug_DumpWithoutCrashing( static jboolean JNI_AwDebug_DumpWithoutCrashing(
JNIEnv* env, JNIEnv* env,
const JavaParamRef<jstring>& dump_path) { const JavaParamRef<jstring>& dump_path) {
......
...@@ -148,6 +148,14 @@ base::FilePath::StringType::const_pointer GetCrashpadDatabasePathImpl(); ...@@ -148,6 +148,14 @@ base::FilePath::StringType::const_pointer GetCrashpadDatabasePathImpl();
void DumpProcessWithoutCrashing(task_t task_port); void DumpProcessWithoutCrashing(task_t task_port);
#endif #endif
#if defined(OS_ANDROID)
// This is used by WebView to generate a dump on behalf of the embedding app.
// This function can only be called from the browser process. Returns `true` on
// success.
class CrashReporterClient;
bool DumpWithoutCrashingForClient(CrashReporterClient* client);
#endif // OS_ANDROID
namespace internal { namespace internal {
#if defined(OS_WIN) #if defined(OS_WIN)
......
...@@ -43,18 +43,40 @@ ...@@ -43,18 +43,40 @@
namespace crashpad { namespace crashpad {
namespace { namespace {
bool SetSanitizationInfo(SanitizationInformation* info) { bool SetSanitizationInfo(crash_reporter::CrashReporterClient* client,
SanitizationInformation* info) {
const char* const* whitelist = nullptr; const char* const* whitelist = nullptr;
void* target_module = nullptr; void* target_module = nullptr;
bool sanitize_stacks = false; bool sanitize_stacks = false;
crash_reporter::GetCrashReporterClient()->GetSanitizationInformation( client->GetSanitizationInformation(&whitelist, &target_module,
&whitelist, &target_module, &sanitize_stacks); &sanitize_stacks);
info->annotations_whitelist_address = FromPointerCast<VMAddress>(whitelist); info->annotations_whitelist_address = FromPointerCast<VMAddress>(whitelist);
info->target_module_address = FromPointerCast<VMAddress>(target_module); info->target_module_address = FromPointerCast<VMAddress>(target_module);
info->sanitize_stacks = sanitize_stacks; info->sanitize_stacks = sanitize_stacks;
return whitelist != nullptr || target_module != nullptr || sanitize_stacks; return whitelist != nullptr || target_module != nullptr || sanitize_stacks;
} }
void SetExceptionInformation(siginfo_t* siginfo,
ucontext_t* context,
ExceptionInformation* info) {
info->siginfo_address =
FromPointerCast<decltype(info->siginfo_address)>(siginfo);
info->context_address =
FromPointerCast<decltype(info->context_address)>(context);
info->thread_id = sandbox::sys_gettid();
}
void SetClientInformation(ExceptionInformation* exception,
SanitizationInformation* sanitization,
ClientInformation* info) {
info->exception_information_address =
FromPointerCast<decltype(info->exception_information_address)>(exception);
info->sanitization_information_address =
FromPointerCast<decltype(info->sanitization_information_address)>(
sanitization);
}
// A signal handler for non-browser processes in the sandbox. // A signal handler for non-browser processes in the sandbox.
// Sends a message to a crashpad::CrashHandlerHost to handle the crash. // Sends a message to a crashpad::CrashHandlerHost to handle the crash.
class SandboxedHandler { class SandboxedHandler {
...@@ -65,7 +87,8 @@ class SandboxedHandler { ...@@ -65,7 +87,8 @@ class SandboxedHandler {
} }
bool Initialize() { bool Initialize() {
SetSanitizationInfo(&sanitization_); SetSanitizationInfo(crash_reporter::GetCrashReporterClient(),
&sanitization_);
server_fd_ = base::GlobalDescriptors::GetInstance()->Get( server_fd_ = base::GlobalDescriptors::GetInstance()->Get(
service_manager::kCrashDumpSignal); service_manager::kCrashDumpSignal);
...@@ -124,22 +147,12 @@ class SandboxedHandler { ...@@ -124,22 +147,12 @@ class SandboxedHandler {
base::ScopedFD connection; base::ScopedFD connection;
if (state->ConnectToHandler(signo, &connection) == 0) { if (state->ConnectToHandler(signo, &connection) == 0) {
ExceptionInformation exception_information; ExceptionInformation exception_information;
exception_information.siginfo_address = SetExceptionInformation(siginfo, static_cast<ucontext_t*>(context),
FromPointerCast<decltype(exception_information.siginfo_address)>(
siginfo);
exception_information.context_address =
FromPointerCast<decltype(exception_information.context_address)>(
context);
exception_information.thread_id = sandbox::sys_gettid();
ClientInformation info = {};
info.exception_information_address =
FromPointerCast<decltype(info.exception_information_address)>(
&exception_information); &exception_information);
info.sanitization_information_address = ClientInformation info;
FromPointerCast<decltype(info.sanitization_information_address)>( SetClientInformation(&exception_information, &state->sanitization_,
&state->sanitization_); &info);
ExceptionHandlerClient handler_client(connection.get()); ExceptionHandlerClient handler_client(connection.get());
handler_client.SetCanSetPtracer(false); handler_client.SetCanSetPtracer(false);
...@@ -251,12 +264,12 @@ const char kCrashpadJavaMain[] = ...@@ -251,12 +264,12 @@ const char kCrashpadJavaMain[] =
#endif // OS_ANDROID #endif // OS_ANDROID
void BuildHandlerArgs(base::FilePath* database_path, void BuildHandlerArgs(CrashReporterClient* crash_reporter_client,
base::FilePath* database_path,
base::FilePath* metrics_path, base::FilePath* metrics_path,
std::string* url, std::string* url,
std::map<std::string, std::string>* process_annotations, std::map<std::string, std::string>* process_annotations,
std::vector<std::string>* arguments) { std::vector<std::string>* arguments) {
CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
crash_reporter_client->GetCrashDumpLocation(database_path); crash_reporter_client->GetCrashDumpLocation(database_path);
crash_reporter_client->GetCrashMetricsLocation(metrics_path); crash_reporter_client->GetCrashMetricsLocation(metrics_path);
...@@ -358,8 +371,8 @@ class HandlerStarter { ...@@ -358,8 +371,8 @@ class HandlerStarter {
std::string url; std::string url;
std::map<std::string, std::string> process_annotations; std::map<std::string, std::string> process_annotations;
std::vector<std::string> arguments; std::vector<std::string> arguments;
BuildHandlerArgs(&database_path, &metrics_path, &url, &process_annotations, BuildHandlerArgs(GetCrashReporterClient(), &database_path, &metrics_path,
&arguments); &url, &process_annotations, &arguments);
base::FilePath exe_dir; base::FilePath exe_dir;
base::FilePath handler_path; base::FilePath handler_path;
...@@ -367,7 +380,8 @@ class HandlerStarter { ...@@ -367,7 +380,8 @@ class HandlerStarter {
return database_path; return database_path;
} }
if (crashpad::SetSanitizationInfo(&browser_sanitization_info_)) { if (crashpad::SetSanitizationInfo(GetCrashReporterClient(),
&browser_sanitization_info_)) {
arguments.push_back(base::StringPrintf("--sanitization-information=%p", arguments.push_back(base::StringPrintf("--sanitization-information=%p",
&browser_sanitization_info_)); &browser_sanitization_info_));
} }
...@@ -399,14 +413,14 @@ class HandlerStarter { ...@@ -399,14 +413,14 @@ class HandlerStarter {
return database_path; return database_path;
} }
bool StartHandlerForClient(int fd) { bool StartHandlerForClient(CrashReporterClient* client, int fd) {
base::FilePath database_path; base::FilePath database_path;
base::FilePath metrics_path; base::FilePath metrics_path;
std::string url; std::string url;
std::map<std::string, std::string> process_annotations; std::map<std::string, std::string> process_annotations;
std::vector<std::string> arguments; std::vector<std::string> arguments;
BuildHandlerArgs(&database_path, &metrics_path, &url, &process_annotations, BuildHandlerArgs(client, &database_path, &metrics_path, &url,
&arguments); &process_annotations, &arguments);
base::FilePath exe_dir; base::FilePath exe_dir;
base::FilePath handler_path; base::FilePath handler_path;
...@@ -449,12 +463,58 @@ class HandlerStarter { ...@@ -449,12 +463,58 @@ class HandlerStarter {
DISALLOW_COPY_AND_ASSIGN(HandlerStarter); DISALLOW_COPY_AND_ASSIGN(HandlerStarter);
}; };
bool ConnectToHandler(CrashReporterClient* client, base::ScopedFD* connection) {
int fds[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
PLOG(ERROR) << "socketpair";
return false;
}
base::ScopedFD local_connection(fds[0]);
base::ScopedFD handlers_socket(fds[1]);
if (!HandlerStarter::Get()->StartHandlerForClient(client,
handlers_socket.get())) {
return false;
}
*connection = std::move(local_connection);
return true;
}
} // namespace } // namespace
bool DumpWithoutCrashingForClient(CrashReporterClient* client) {
base::ScopedFD connection;
if (!ConnectToHandler(client, &connection)) {
return false;
}
siginfo_t siginfo;
siginfo.si_signo = crashpad::Signals::kSimulatedSigno;
siginfo.si_errno = 0;
siginfo.si_code = 0;
ucontext_t context;
crashpad::CaptureContext(&context);
crashpad::SanitizationInformation sanitization;
crashpad::SetSanitizationInfo(client, &sanitization);
crashpad::ExceptionInformation exception;
crashpad::SetExceptionInformation(&siginfo, &context, &exception);
crashpad::ClientInformation info;
crashpad::SetClientInformation(&exception, &sanitization, &info);
crashpad::ExceptionHandlerClient handler_client(connection.get());
return handler_client.RequestCrashDump(info) == 0;
}
namespace internal { namespace internal {
bool StartHandlerForClient(int fd) { bool StartHandlerForClient(int fd) {
return HandlerStarter::Get()->StartHandlerForClient(fd); return HandlerStarter::Get()->StartHandlerForClient(GetCrashReporterClient(),
fd);
} }
base::FilePath PlatformCrashpadInitialization( base::FilePath PlatformCrashpadInitialization(
......
...@@ -18,6 +18,13 @@ ...@@ -18,6 +18,13 @@
namespace minidump_uploader { namespace minidump_uploader {
bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport& report,
crashpad::HTTPMultipartBuilder* http_multipart_builder,
pid_t* pid);
bool WriteBodyToFile(crashpad::HTTPBodyStream* body,
crashpad::FileWriterInterface* writer);
namespace { namespace {
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
...@@ -32,9 +39,32 @@ enum class ProcessedMinidumpCounts { ...@@ -32,9 +39,32 @@ enum class ProcessedMinidumpCounts {
}; };
#endif // OS_ANDROID #endif // OS_ANDROID
bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report, bool MimeifyReportAndWriteToDirectory(
const crashpad::CrashReportDatabase::UploadReport& report,
const base::FilePath& dest_dir) { const base::FilePath& dest_dir) {
crashpad::FileReader* reader = report->Reader(); crashpad::HTTPMultipartBuilder builder;
pid_t pid;
if (!MimeifyReport(report, &builder, &pid)) {
return false;
}
crashpad::FileWriter writer;
if (!writer.Open(dest_dir.Append(base::StringPrintf(
"%s.dmp%d", report.uuid.ToString().c_str(), pid)),
crashpad::FileWriteMode::kCreateOrFail,
crashpad::FilePermissions::kOwnerOnly)) {
return false;
}
return WriteBodyToFile(builder.GetBodyStream().get(), &writer);
}
} // namespace
bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport& report,
crashpad::HTTPMultipartBuilder* http_multipart_builder,
pid_t* pid) {
crashpad::FileReader* reader = report.Reader();
crashpad::FileOffset start_offset = reader->SeekGet(); crashpad::FileOffset start_offset = reader->SeekGet();
if (start_offset < 0) { if (start_offset < 0) {
return false; return false;
...@@ -55,8 +85,6 @@ bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report, ...@@ -55,8 +85,6 @@ bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report,
return false; return false;
} }
crashpad::HTTPMultipartBuilder http_multipart_builder;
static constexpr char kMinidumpKey[] = "upload_file_minidump"; static constexpr char kMinidumpKey[] = "upload_file_minidump";
static constexpr char kPtypeKey[] = "ptype"; static constexpr char kPtypeKey[] = "ptype";
...@@ -65,7 +93,7 @@ bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report, ...@@ -65,7 +93,7 @@ bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report,
LOG(WARNING) << "reserved key " << kv.first << ", discarding value " LOG(WARNING) << "reserved key " << kv.first << ", discarding value "
<< kv.second; << kv.second;
} else { } else {
http_multipart_builder.SetFormData(kv.first, kv.second); http_multipart_builder->SetFormData(kv.first, kv.second);
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
if (kv.first == kPtypeKey) { if (kv.first == kPtypeKey) {
ProcessedMinidumpCounts count_type; ProcessedMinidumpCounts count_type;
...@@ -85,31 +113,24 @@ bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report, ...@@ -85,31 +113,24 @@ bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report,
} }
} }
http_multipart_builder.SetFileAttachment(kMinidumpKey, http_multipart_builder->SetFileAttachment(kMinidumpKey,
report->uuid.ToString() + ".dmp", report.uuid.ToString() + ".dmp",
reader, "application/octet-stream"); reader, "application/octet-stream");
std::unique_ptr<crashpad::HTTPBodyStream> body = *pid = minidump_process_snapshot.ProcessID();
http_multipart_builder.GetBodyStream(); return true;
crashpad::FileWriter writer; }
if (!writer.Open(dest_dir.Append(base::StringPrintf(
"%s.dmp%d", report->uuid.ToString().c_str(),
minidump_process_snapshot.ProcessID())),
crashpad::FileWriteMode::kCreateOrFail,
crashpad::FilePermissions::kOwnerOnly)) {
return false;
}
bool WriteBodyToFile(crashpad::HTTPBodyStream* body,
crashpad::FileWriterInterface* writer) {
uint8_t buffer[4096]; uint8_t buffer[4096];
crashpad::FileOperationResult bytes_read; crashpad::FileOperationResult bytes_read;
while ((bytes_read = body->GetBytesBuffer(buffer, sizeof(buffer))) > 0) { while ((bytes_read = body->GetBytesBuffer(buffer, sizeof(buffer))) > 0) {
writer.Write(buffer, bytes_read); writer->Write(buffer, bytes_read);
} }
return bytes_read == 0; return bytes_read == 0;
} }
} // namespace
void RewriteMinidumpsAsMIMEs(const base::FilePath& src_dir, void RewriteMinidumpsAsMIMEs(const base::FilePath& src_dir,
const base::FilePath& dest_dir) { const base::FilePath& dest_dir) {
std::unique_ptr<crashpad::CrashReportDatabase> db = std::unique_ptr<crashpad::CrashReportDatabase> db =
...@@ -135,7 +156,7 @@ void RewriteMinidumpsAsMIMEs(const base::FilePath& src_dir, ...@@ -135,7 +156,7 @@ void RewriteMinidumpsAsMIMEs(const base::FilePath& src_dir,
continue; continue;
case crashpad::CrashReportDatabase::kNoError: case crashpad::CrashReportDatabase::kNoError:
if (MimeifyReport(upload_report.get(), dest_dir)) { if (MimeifyReportAndWriteToDirectory(*upload_report.get(), dest_dir)) {
db->RecordUploadComplete(std::move(upload_report), std::string()); db->RecordUploadComplete(std::move(upload_report), std::string());
} else { } else {
crashpad::Metrics::CrashUploadSkipped( crashpad::Metrics::CrashUploadSkipped(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment