blob: 8b5064ba93122456c83a362e7669f07723633576 [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/report_builder.h"
#include <lib/syslog/cpp/macros.h>
#include <optional>
#include "src/lib/fsl/handles/object_info.h"
#include "src/lib/fxl/strings/join_strings.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace forensics {
namespace exceptions {
namespace handler {
namespace {
std::string Sanitize(std::string process_name) {
// Determine if ".cm" is present in |process_name| and use the substring that precedes it. This
// works for components v1 and v2 because their processes will end with ".cmx" and ".cm"
// respectively.
const size_t cm_pos = process_name.find(".cm");
if (cm_pos != std::string::npos) {
process_name = process_name.substr(0, cm_pos);
}
return process_name;
}
} // namespace
CrashReportBuilder& CrashReportBuilder::SetProcess(const zx::process& process) {
if (!process.is_valid()) {
process_name_ = "unknown_process";
return *this;
}
process_name_ = fsl::GetObjectName(process.get());
process_koid_ = fsl::GetKoid(process.get());
// Get the crashed process uptime
zx_info_process_t process_info;
if (const zx_status_t status =
process.get_info(ZX_INFO_PROCESS, &process_info, sizeof(process_info), nullptr, nullptr);
status == ZX_OK) {
if (!(process_info.flags & ZX_INFO_PROCESS_FLAG_STARTED)) {
FX_LOGS(WARNING) << "Cannot get the start time from crashed process "
<< process_name_.value();
return *this;
}
const zx_time_t crashed_process_uptime = zx_clock_get_monotonic() - process_info.start_time;
if (crashed_process_uptime >= 0) {
process_uptime_ = crashed_process_uptime;
} else {
FX_LOGS(WARNING) << "Invalid uptime = " << crashed_process_uptime << ", for crashed process "
<< process_name_.value();
}
}
return *this;
}
CrashReportBuilder& CrashReportBuilder::SetThread(const zx::thread& thread) {
thread_name_ = (thread.is_valid()) ? fsl::GetObjectName(thread.get()) : "unknown_thread";
if (thread.is_valid()) {
thread_koid_ = fsl::GetKoid(thread.get());
}
return *this;
}
CrashReportBuilder& CrashReportBuilder::SetMinidump(zx::vmo minidump) {
FX_CHECK(minidump.is_valid());
minidump_ = std::move(minidump);
return *this;
}
CrashReportBuilder& CrashReportBuilder::SetExceptionReason(
const std::optional<ExceptionReason>& exception_reason) {
exception_reason_ = exception_reason;
return *this;
}
CrashReportBuilder& CrashReportBuilder::SetComponentInfo(const ComponentInfo& component_info) {
if (!component_info.url.empty()) {
component_url_ = component_info.url;
}
if (!component_info.realm_path.empty()) {
realm_path_ = component_info.realm_path;
}
return *this;
}
CrashReportBuilder& CrashReportBuilder::SetExceptionExpired() {
exception_expired_ = true;
return *this;
}
CrashReportBuilder& CrashReportBuilder::SetProcessTerminated() {
process_already_terminated_ = true;
return *this;
}
CrashReportBuilder& CrashReportBuilder::SetGwpAsanExceptionType(std::string exception_type) {
gwp_asan_exception_type_ = std::move(exception_type);
return *this;
}
const std::optional<std::string>& CrashReportBuilder::ProcessName() const { return process_name_; }
fuchsia::feedback::CrashReport CrashReportBuilder::Consume() {
FX_CHECK(process_name_.has_value()) << "Need a process name";
FX_CHECK(thread_name_.has_value()) << "Need a thread name";
FX_CHECK(is_valid_) << "Consume can only be called once";
is_valid_ = false;
using namespace fuchsia::feedback;
CrashReport crash_report;
// Always fatal crash.
crash_report.set_is_fatal(true);
// Program name.
const std::string program_name =
(component_url_.has_value()) ? component_url_.value() : process_name_.value();
crash_report.set_program_name(program_name.substr(0, fuchsia::feedback::MAX_PROGRAM_NAME_LENGTH));
// Program uptime.
if (process_uptime_.has_value()) {
crash_report.set_program_uptime(process_uptime_.value());
}
// Extra annotations.
auto AddAnnotation = [&crash_report](const std::string& key, const std::string& value) {
crash_report.mutable_annotations()->push_back(Annotation{.key = key, .value = value});
};
if (!component_url_.has_value()) {
AddAnnotation("debug.crash.component.url.set", "false");
}
if (realm_path_.has_value()) {
AddAnnotation("crash.realm-path", realm_path_.value());
}
if (gwp_asan_exception_type_.has_value()) {
AddAnnotation("crash.gwp_asan.exception-type", gwp_asan_exception_type_.value());
}
// Crash signature overwrite on channel/port overflow.
if (exception_reason_.has_value()) {
switch (exception_reason_.value()) {
case ExceptionReason::kChannelOverflow:
crash_report.set_crash_signature(fxl::StringPrintf(
"fuchsia-%s-channel-overflow", Sanitize(process_name_.value()).c_str()));
break;
case ExceptionReason::kPortObserverOverflow:
crash_report.set_crash_signature(fxl::StringPrintf(
"fuchsia-%s-port-observer-overflow", Sanitize(process_name_.value()).c_str()));
break;
case ExceptionReason::kPortPacketOverflow:
crash_report.set_crash_signature(fxl::StringPrintf(
"fuchsia-%s-port-packet-overflow", Sanitize(process_name_.value()).c_str()));
break;
case ExceptionReason::kPageFaultIo:
crash_report.set_crash_signature("fuchsia-page_fault-io");
break;
case ExceptionReason::kPageFaultIoDataIntegrity:
crash_report.set_crash_signature("fuchsia-page_fault-io_data_integrity");
break;
case ExceptionReason::kPageFaultBadState:
crash_report.set_crash_signature("fuchsia-page_fault-bad_state");
break;
}
}
// Process and thread names/koids.
NativeCrashReport native_crash_report;
native_crash_report.set_process_name(process_name_.value());
if (process_koid_.has_value()) {
native_crash_report.set_process_koid(process_koid_.value());
}
native_crash_report.set_thread_name(thread_name_.value());
if (thread_koid_.has_value()) {
native_crash_report.set_thread_koid(thread_koid_.value());
}
// Minidump.
FX_CHECK(minidump_.has_value() || exception_expired_ || process_already_terminated_);
if (minidump_.has_value()) {
fuchsia::mem::Buffer mem_buffer;
minidump_.value().get_size(&mem_buffer.size);
mem_buffer.vmo = std::move(minidump_.value());
minidump_ = std::nullopt;
native_crash_report.set_minidump(std::move(mem_buffer));
} else if (exception_expired_) {
crash_report.set_crash_signature("fuchsia-no-minidump-exception-expired");
} else if (process_already_terminated_) {
crash_report.set_crash_signature("fuchsia-no-minidump-process-terminated");
}
crash_report.set_specific_report(SpecificCrashReport::WithNative(std::move(native_crash_report)));
return crash_report;
}
} // namespace handler
} // namespace exceptions
} // namespace forensics