blob: 26d09c832ec0722de6c0dddf6430c1a8c4647a9a [file] [log] [blame]
// Copyright 2020 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/forensics/exceptions/handler/crash_reporter.h"
#include <lib/async/cpp/task.h>
#include <lib/fidl/cpp/string.h>
#include <lib/fpromise/promise.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/exception.h>
#include <optional>
#include "src/developer/forensics/exceptions/handler/component_lookup.h"
#include "src/developer/forensics/exceptions/handler/minidump.h"
#include "src/developer/forensics/exceptions/handler/report_builder.h"
#include "src/lib/fsl/handles/object_info.h"
#include "src/lib/fxl/strings/join_strings.h"
namespace forensics {
namespace exceptions {
namespace handler {
namespace {
using fuchsia::feedback::CrashReport;
// Either resets the exception immediately if the process only has one thread or with a 5s delay
// otherwise.
void ResetException(async_dispatcher_t* dispatcher, zx::exception exception,
const zx::process& process) {
if (!process.is_valid()) {
FX_LOGS(ERROR) << "Process for exception is invalid";
exception.reset();
return;
}
size_t num_threads{0};
if (const zx_status_t status = zx_object_get_info(process.get(), ZX_INFO_PROCESS_THREADS, nullptr,
0u, nullptr, &num_threads);
status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to get thread info from process " << process.get();
exception.reset();
return;
}
if (num_threads > 1) {
// If the process has multiple threads, delay resetting |exception| for 5 seconds. If one of the
// other threads is in an exception, releasing |exception| immediately may result in the process
// being terminated by the kernel before the minidump for the other thread is generated.
async::PostDelayedTask(
dispatcher, [exception = std::move(exception)]() mutable { exception.reset(); },
zx::sec(5));
} else {
exception.reset();
}
}
} // namespace
CrashReporter::CrashReporter(async_dispatcher_t* dispatcher,
std::shared_ptr<sys::ServiceDirectory> services,
zx::duration component_lookup_timeout)
: dispatcher_(dispatcher),
executor_(dispatcher_),
services_(services),
component_lookup_timeout_(component_lookup_timeout) {}
void CrashReporter::Send(zx::exception exception, zx::process crashed_process,
zx::thread crashed_thread, SendCallback callback) {
CrashReportBuilder builder;
builder.SetProcess(crashed_process).SetThread(crashed_thread);
if (exception.is_valid()) {
std::optional<ExceptionReason> exception_reason{std::nullopt};
std::optional<std::string> gwp_asan_exception_type;
zx::vmo minidump = GenerateMinidump(exception, &exception_reason, &gwp_asan_exception_type);
ResetException(dispatcher_, std::move(exception), crashed_process);
if (minidump.is_valid()) {
builder.SetMinidump(std::move(minidump));
} else {
builder.SetProcessTerminated();
}
builder.SetExceptionReason(exception_reason);
if (gwp_asan_exception_type.has_value()) {
builder.SetGwpAsanExceptionType(gwp_asan_exception_type.value());
}
} else {
builder.SetExceptionExpired();
}
const auto thread_koid = fsl::GetKoid(crashed_thread.get());
auto file_crash_report =
GetComponentInfo(dispatcher_, services_, component_lookup_timeout_, thread_koid)
.then(
[dispatcher = dispatcher_, services = services_, builder = std::move(builder),
callback = std::move(callback)](::fpromise::result<ComponentInfo>& result) mutable {
ComponentInfo component_info;
if (result.is_ok()) {
component_info = result.take_value();
}
builder.SetComponentInfo(component_info);
fuchsia::feedback::CrashReporterPtr crash_reporter;
if (builder.ProcessName() == "feedback.cm") {
// Delay connecting to the crash reporter by 5 seconds if the crashed process is
// the crash reporter. This gives the system time to route the request to a new
// instance of the crash reporter instead of sending it into oblivion.
if (const zx_status_t status = async::PostDelayedTask(
dispatcher,
[services,
connection_request = crash_reporter.NewRequest(dispatcher)]() mutable {
services->Connect(std::move(connection_request));
},
zx::sec(5));
status != ZX_OK) {
FX_PLOGS(ERROR, status)
<< "Failed to delay connecting to the crash reporter, connecting now";
services->Connect(crash_reporter.NewRequest(dispatcher));
}
} else {
services->Connect(crash_reporter.NewRequest(dispatcher));
}
crash_reporter->File(builder.Consume(),
[](fuchsia::feedback::CrashReporter_File_Result result) {});
::fidl::StringPtr moniker = std::nullopt;
if (!component_info.moniker.empty()) {
moniker = component_info.moniker;
}
callback(std::move(moniker));
return ::fpromise::ok();
});
executor_.schedule_task(std::move(file_crash_report));
}
} // namespace handler
} // namespace exceptions
} // namespace forensics