blob: 9993745e5425a44a4daa602e258eceba1810af42 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
#include "src/developer/exception_broker/exception_broker.h"
#include <lib/fit/defer.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <third_party/crashpad/util/file/string_file.h>
#include "fuchsia/exception/cpp/fidl.h"
#include "src/developer/exception_broker/crash_report_generation.h"
#include "src/developer/exception_broker/json_utils.h"
#include "src/lib/files/directory.h"
#include "src/lib/files/file.h"
#include "src/lib/fxl/strings/join_strings.h"
namespace forensics {
namespace exceptions {
namespace {
using fuchsia::exception::ExceptionInfo;
using fuchsia::exception::ProcessException;
constexpr char kEnableJitdConfigPath[] = "/config/data/enable_jitd_on_startup.json";
} // namespace
std::unique_ptr<ExceptionBroker> ExceptionBroker::Create(
async_dispatcher_t* dispatcher, std::shared_ptr<sys::ServiceDirectory> services,
const char* override_filepath) {
auto broker = std::unique_ptr<ExceptionBroker>(new ExceptionBroker(services));
// Check if JITD should be enabled at startup. For now existence means it's activated.
if (!override_filepath)
override_filepath = kEnableJitdConfigPath;
if (files::IsFile(override_filepath)) {
broker->limbo_manager().SetActive(true);
std::string file_content;
if (!files::ReadFileToString(override_filepath, &file_content)) {
FX_LOGS(WARNING) << "Could not read the config file.";
} else {
broker->limbo_manager().set_filters(ExtractFilters(file_content));
}
}
return broker;
}
ExceptionBroker::ExceptionBroker(std::shared_ptr<sys::ServiceDirectory> services)
: services_(std::move(services)), weak_factory_(this) {
FX_DCHECK(services_);
}
fxl::WeakPtr<ExceptionBroker> ExceptionBroker::GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
// OnException -------------------------------------------------------------------------------------
void ExceptionBroker::OnException(zx::exception exception, ExceptionInfo info,
OnExceptionCallback cb) {
// Always call the callback when we're done.
auto defer_cb = fit::defer([cb = std::move(cb)]() { cb(); });
ProcessException process_exception = {};
process_exception.set_exception(std::move(exception));
process_exception.set_info(std::move(info));
zx_status_t status;
zx::process process;
status = process_exception.exception().get_process(&process);
if (status != ZX_OK) {
FX_PLOGS(WARNING, status) << "Could not obtain process handle for exception.";
} else {
process_exception.set_process(std::move(process));
}
zx::thread thread;
status = process_exception.exception().get_thread(&thread);
if (status != ZX_OK) {
FX_PLOGS(WARNING, status) << "Could not obtain thread handle for exception.";
} else {
process_exception.set_thread(std::move(thread));
}
if (!limbo_manager_.active()) {
FileCrashReport(std::move(process_exception));
} else {
limbo_manager_.AddToLimbo(std::move(process_exception));
}
}
// ExceptionBroker implementation ------------------------------------------------------------------
void ExceptionBroker::FileCrashReport(ProcessException process_exception) {
std::string process_name;
zx::vmo minidump_vmo = GenerateMinidumpVMO(process_exception.exception(), &process_name);
CrashReportBuilder builder(process_name);
if (minidump_vmo.is_valid()) {
builder.SetMinidump(std::move(minidump_vmo));
}
uint64_t id = next_connection_id_++;
crash_report_builders_.emplace(id, std::move(builder));
auto introspect_ptr = services_->Connect<fuchsia::sys::internal::Introspect>();
introspect_connections_[id] = std::move(introspect_ptr);
auto& introspect = introspect_connections_[id];
introspect.set_error_handler([id, broker = GetWeakPtr()](zx_status_t status) {
FX_PLOGS(ERROR, status) << "Lost connection to fuchsia.sys.internal.Introspect";
// If the broker is not there anymore, there is nothing more we can do.
if (!broker)
return;
broker->FileCrashReport(id);
// Remove the connection after we have filed the crash report. The connection must be removed at
// the end of the function because the InterfacePtr that owns the lambda is destroyed when the
// connection is removed.
broker->introspect_connections_.erase(id);
});
using fuchsia::sys::internal::Introspect_FindComponentByProcessKoid_Result;
const zx_koid_t process_koid = process_exception.info().process_koid;
introspect->FindComponentByProcessKoid(
process_koid,
// |process_exception| is moved into the call back to keep it alive until after the component
// information of the crashed process has been collected or has failed to be collected,
// otherwise the kernel would terminate the process.
[id, process_exception = std::move(process_exception),
broker = GetWeakPtr()](Introspect_FindComponentByProcessKoid_Result result) {
// If the broker is not there anymore, there is nothing more we can do.
if (!broker)
return;
auto& builder = broker->crash_report_builders_.at(id);
if (result.is_response()) {
if (!result.response().component_info.has_component_url()) {
FX_LOGS(ERROR) << "Did not receive a component url";
} else {
builder.SetComponentUrl(result.response().component_info.component_url());
}
if (!result.response().component_info.has_realm_path()) {
FX_LOGS(ERROR) << "Did not receive a realm path";
} else {
builder.SetRealmPath(
"/" + fxl::JoinStrings(result.response().component_info.realm_path(), "/"));
}
} else if (result.err() != ZX_ERR_NOT_FOUND) {
FX_PLOGS(ERROR, result.err()) << "Failed FindComponentByProcessKoid";
}
broker->FileCrashReport(id);
// Remove the connection after we have filed the crash report. The connection must be
// removed at the end of the function because the InterfacePtr that owns the lambda, and the
// reference to |broker|, is destroyed when the connection is removed.
broker->introspect_connections_.erase(id);
});
}
void ExceptionBroker::FileCrashReport(uint64_t id) {
if (crash_report_builders_.find(id) == crash_report_builders_.end()) {
return;
}
// Create a new connection to the crash reporter and keep track of it.
auto crash_reporter_ptr = services_->Connect<fuchsia::feedback::CrashReporter>();
crash_reporter_connections_[id] = std::move(crash_reporter_ptr);
// Get the ref to the crash reporter.
auto& crash_reporter = crash_reporter_connections_[id];
crash_reporter.set_error_handler([id, broker = GetWeakPtr()](zx_status_t status) {
FX_PLOGS(ERROR, status) << "Lost connection to fuchsia.feedback.CrashReporter";
// If the broker is not there anymore, there is nothing more we can do.
if (!broker)
return;
// Remove the connection after we have removed the exception. The connection must be
// removed at the end of the function because the InterfacePtr that owns the lambda, and the
// reference to |broker|, is destroyed when the connection is removed.
broker->crash_reporter_connections_.erase(id);
});
auto report = crash_report_builders_.at(id).Consume();
crash_report_builders_.erase(id);
crash_reporter->File(
std::move(report), [id, program_name = report.program_name(), broker = GetWeakPtr()](
fuchsia::feedback::CrashReporter_File_Result result) {
if (result.is_err())
FX_PLOGS(ERROR, result.err()) << "Error filing crash report for " << program_name;
// If the broker is not there anymore, there is nothing more we can do.
if (!broker)
return;
// Remove the connection after we have removed the exception. The connection must be
// removed at the end of the function because the InterfacePtr that owns the lambda, and the
// reference to |broker|, is destroyed when the connection is removed.
broker->crash_reporter_connections_.erase(id);
});
}
} // namespace exceptions
} // namespace forensics