fuchsia: InitializeException, and write dump in exception handler

Implements InitializeException() in ProcessSnapshot, and pulls it all
together writing the dump in crash handler. Sample output at crash
00163eff624e653e on the staging server.

Also adds a child-retrieve helper to koid_utilities.

Bug: crashpad:196
Change-Id: I4bee7655e81e3243ac0ae896ff0caea7ce4acdad
Reviewed-on: https://chromium-review.googlesource.com/1044771
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Scott Graham 2018-05-04 16:59:20 -07:00 committed by Commit Bot
parent c82309f0e5
commit 02adab2e80
6 changed files with 198 additions and 21 deletions

View File

@ -14,14 +14,148 @@
#include "handler/fuchsia/crash_report_exception_handler.h"
#include <zircon/syscalls/exception.h>
#include "base/fuchsia/fuchsia_logging.h"
#include "client/settings.h"
#include "minidump/minidump_file_writer.h"
#include "minidump/minidump_user_extension_stream_data_source.h"
#include "snapshot/fuchsia/process_snapshot_fuchsia.h"
#include "util/fuchsia/koid_utilities.h"
#include "util/fuchsia/scoped_task_suspend.h"
namespace crashpad {
namespace {
struct ScopedZxTaskResumeAfterException {
ScopedZxTaskResumeAfterException(zx_handle_t thread) : thread_(thread) {}
~ScopedZxTaskResumeAfterException() {
DCHECK_NE(thread_, ZX_HANDLE_INVALID);
// Resuming with ZX_RESUME_TRY_NEXT chains to the next handler. In normal
// operation, there won't be another beyond this one, which will result in
// the kernel terminating the process.
zx_status_t status =
zx_task_resume(thread_, ZX_RESUME_EXCEPTION | ZX_RESUME_TRY_NEXT);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_task_resume";
}
}
private:
zx_handle_t thread_; // weak
};
} // namespace
CrashReportExceptionHandler::CrashReportExceptionHandler(
CrashReportDatabase* database,
CrashReportUploadThread* upload_thread,
const std::map<std::string, std::string>* process_annotations,
const UserStreamDataSources* user_stream_data_sources) {}
const UserStreamDataSources* user_stream_data_sources)
: database_(database),
upload_thread_(upload_thread),
process_annotations_(process_annotations),
user_stream_data_sources_(user_stream_data_sources) {}
CrashReportExceptionHandler::~CrashReportExceptionHandler() {}
bool CrashReportExceptionHandler::HandleException(uint32_t type,
uint64_t process_id,
uint64_t thread_id) {
// TODO(scottmg): This function needs to be instrumented with metrics calls,
// https://crashpad.chromium.org/bug/230.
base::ScopedZxHandle process(GetProcessFromKoid(process_id));
if (!process.is_valid()) {
// There's no way to zx_task_resume() the thread if the process retrieval
// fails. Assume that the process has been already killed, and bail.
return false;
}
ScopedTaskSuspend suspend(process.get());
base::ScopedZxHandle thread(GetChildHandleByKoid(process.get(), thread_id));
if (!thread.is_valid()) {
return false;
}
// Now that the thread has been successfully retrieved, it is possible to
// correctly call zx_task_resume() to continue exception processing, even if
// something else during this function fails.
ScopedZxTaskResumeAfterException resume(thread.get());
ProcessSnapshotFuchsia process_snapshot;
if (!process_snapshot.Initialize(process.get())) {
return false;
}
CrashpadInfoClientOptions client_options;
process_snapshot.GetCrashpadOptions(&client_options);
if (client_options.crashpad_handler_behavior != TriState::kDisabled) {
zx_exception_report_t report;
zx_status_t status = zx_object_get_info(thread.get(),
ZX_INFO_THREAD_EXCEPTION_REPORT,
&report,
sizeof(report),
nullptr,
nullptr);
if (status != ZX_OK) {
ZX_LOG(ERROR, status)
<< "zx_object_get_info ZX_INFO_THREAD_EXCEPTION_REPORT";
return false;
}
DCHECK_EQ(type, report.header.type);
if (!process_snapshot.InitializeException(thread_id, report)) {
return false;
}
UUID client_id;
Settings* const settings = database_->GetSettings();
if (settings) {
// If GetSettings() or GetClientID() fails, something else will log a
// message and client_id will be left at its default value, all zeroes,
// which is appropriate.
settings->GetClientID(&client_id);
}
process_snapshot.SetClientID(client_id);
process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
CrashReportDatabase::OperationStatus database_status =
database_->PrepareNewCrashReport(&new_report);
if (database_status != CrashReportDatabase::kNoError) {
return false;
}
process_snapshot.SetReportID(new_report->ReportID());
MinidumpFileWriter minidump;
minidump.InitializeFromSnapshot(&process_snapshot);
AddUserExtensionStreams(
user_stream_data_sources_, &process_snapshot, &minidump);
if (!minidump.WriteEverything(new_report->Writer())) {
return false;
}
UUID uuid;
database_status =
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
if (database_status != CrashReportDatabase::kNoError) {
return false;
}
if (upload_thread_) {
upload_thread_->ReportPending(uuid);
}
}
return true;
}
} // namespace crashpad

View File

@ -28,7 +28,7 @@
namespace crashpad {
//! \brief An exception handler that writes crash reports for exception messages
//! to a CrashReportDatabase. This class is not yet implemented.
//! to a CrashReportDatabase.
class CrashReportExceptionHandler {
public:
//! \brief Creates a new object that will store crash reports in \a database.
@ -64,19 +64,19 @@ class CrashReportExceptionHandler {
//! This function is expected to call `zx_task_resume()` in order to complete
//! handling of the exception.
//!
//! \note TODO(scottmg): This is not yet implemented.
//!
//! \param[in] type The type of exception, a `ZX_EXCP_*` value.
//! \param[in] pid The koid of the process which sustained the exception.
//! \param[in] tid The koid of the thread which sustained the exception.
//! \param[in] process_id The koid of the process which sustained the
//! exception.
//! \param[in] thread_id The koid of the thread which sustained the exception.
//! \return `true` on success, or `false` with an error logged.
bool HandleException(uint32_t type,
uint64_t pid,
uint64_t tid) {
return false;
}
bool HandleException(uint32_t type, uint64_t process_id, uint64_t thread_id);
private:
CrashReportDatabase* database_; // weak
CrashReportUploadThread* upload_thread_; // weak
const std::map<std::string, std::string>* process_annotations_; // weak
const UserStreamDataSources* user_stream_data_sources_; // weak
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
};

View File

@ -46,6 +46,15 @@ bool ProcessSnapshotFuchsia::Initialize(zx_handle_t process) {
return true;
}
bool ProcessSnapshotFuchsia::InitializeException(
zx_koid_t thread_id,
const zx_exception_report_t& report) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
exception_.reset(new internal::ExceptionSnapshotFuchsia());
exception_->Initialize(&process_reader_, thread_id, report);
return true;
}
void ProcessSnapshotFuchsia::GetCrashpadOptions(
CrashpadInfoClientOptions* options) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
@ -161,8 +170,7 @@ std::vector<UnloadedModuleSnapshot> ProcessSnapshotFuchsia::UnloadedModules()
const ExceptionSnapshot* ProcessSnapshotFuchsia::Exception() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// TODO(scottmg): https://crashpad.chromium.org/bug/196
return nullptr;
return exception_.get();
}
std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotFuchsia::MemoryMap()

View File

@ -17,6 +17,7 @@
#include <sys/time.h>
#include <zircon/types.h>
#include <zircon/syscalls/exception.h>
#include <memory>
#include <vector>
@ -25,6 +26,7 @@
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/elf/elf_image_reader.h"
#include "snapshot/elf/module_snapshot_elf.h"
#include "snapshot/fuchsia/exception_snapshot_fuchsia.h"
#include "snapshot/fuchsia/process_reader_fuchsia.h"
#include "snapshot/fuchsia/system_snapshot_fuchsia.h"
#include "snapshot/fuchsia/thread_snapshot_fuchsia.h"
@ -49,6 +51,24 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot {
//! an appropriate message logged.
bool Initialize(zx_handle_t process);
//! \brief Initializes the object's exception.
//!
//! This populates the data to be returned by Exception(). The thread
//! identified by \a thread_id must be in an exception.
//!
//! This method must not be called until after a successful call to
//! Initialize().
//!
//! \param[in] thread_id Koid of the thread which sustained the exception.
//! \param[in] report The `zx_exception_report_t` for the thread which
//! sustained the exception.
//! \return `true` if the exception information could be initialized, `false`
//! otherwise with an appropriate message logged. When this method returns
//! `false`, the ProcessSnapshotFuchsia objects validity remains
//! unchanged.
bool InitializeException(zx_koid_t thread_id,
const zx_exception_report_t& report);
//! \brief Returns options from CrashpadInfo structures found in modules in
//! the process.
//!
@ -110,6 +130,7 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot {
internal::SystemSnapshotFuchsia system_;
std::vector<std::unique_ptr<internal::ThreadSnapshotFuchsia>> threads_;
std::vector<std::unique_ptr<internal::ModuleSnapshotElf>> modules_;
std::unique_ptr<internal::ExceptionSnapshotFuchsia> exception_;
ProcessReaderFuchsia process_reader_;
std::map<std::string, std::string> annotations_simple_map_;
UUID report_id_;

View File

@ -113,19 +113,24 @@ std::vector<base::ScopedZxHandle> GetHandlesForChildKoids(
const std::vector<zx_koid_t>& koids) {
std::vector<base::ScopedZxHandle> result;
result.reserve(koids.size());
for (zx_koid_t koid : koids) {
zx_handle_t handle;
if (zx_object_get_child(parent, koid, ZX_RIGHT_SAME_RIGHTS, &handle) ==
ZX_OK) {
result.emplace_back(base::ScopedZxHandle(handle));
} else {
result.push_back(base::ScopedZxHandle());
}
result.emplace_back(GetChildHandleByKoid(parent, koid));
}
return result;
}
base::ScopedZxHandle GetChildHandleByKoid(zx_handle_t parent,
zx_koid_t child_koid) {
zx_handle_t handle;
zx_status_t status =
zx_object_get_child(parent, child_koid, ZX_RIGHT_SAME_RIGHTS, &handle);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_object_get_child";
return base::ScopedZxHandle();
}
return base::ScopedZxHandle(handle);
}
zx_koid_t GetKoidForHandle(zx_handle_t object) {
zx_info_handle_basic_t info;
zx_status_t status = zx_object_get_info(

View File

@ -68,6 +68,15 @@ std::vector<base::ScopedZxHandle> GetHandlesForChildKoids(
zx_handle_t parent,
const std::vector<zx_koid_t>& koids);
//! \brief Retrieve the child of a parent handle, based on koid.
//!
//! \param[in] parent The parent object to which the child belongs.
//! \param[in] child_koid The koid of the child to retrieve.
//! \return A handle representing \a child_koid, or `ZX_HANDLE_INVALID` if the
//! handle could not be retrieved, in which case an error will be logged.
base::ScopedZxHandle GetChildHandleByKoid(zx_handle_t parent,
zx_koid_t child_koid);
//! \brief Gets a process handle given the process' koid.
//!
//! \param[in] koid The process id.