blob: c1d99ce0cbf8da00a9812fa8ade0dd68d7a7914f [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/feedback/reboot_info/reporter.h"
#include <lib/async/cpp/task.h>
#include <lib/fit/result.h>
#include <lib/zx/time.h>
#include <zircon/types.h>
#include <string>
#include <vector>
#include "src/developer/feedback/reboot_info/reboot_reason.h"
#include "src/developer/feedback/utils/errors.h"
#include "src/lib/files/file.h"
#include "src/lib/fsl/vmo/file.h"
#include "src/lib/fsl/vmo/strings.h"
#include "src/lib/fxl/logging.h"
#include "src/lib/syslog/cpp/logger.h"
namespace feedback {
namespace {
constexpr char kHasReportedOnPath[] = "/tmp/has_reported_on_reboot_log.txt";
} // namespace
Reporter::Reporter(async_dispatcher_t* dispatcher, std::shared_ptr<sys::ServiceDirectory> services)
: dispatcher_(dispatcher),
executor_(dispatcher),
crash_reporter_(dispatcher, services),
cobalt_(dispatcher, services) {}
void Reporter::ReportOn(const RebootLog& reboot_log, zx::duration crash_reporting_delay) {
if (files::IsFile(kHasReportedOnPath)) {
FX_LOGS(INFO)
<< "Reboot log has already been reported on in another instance of this component "
"for this boot cycle";
return;
}
if (!files::WriteFile(kHasReportedOnPath, /*data=*/"", /*size=*/0)) {
FX_LOGS(ERROR) << "Failed to record reboot log as reported on";
}
// TODO(49689): Start logging Cobalt events and filing crash reports for non-parseable reboot
// logs.
if (reboot_log.RebootReason() == RebootReason::kNotParseable && !reboot_log.HasRebootLogStr()) {
FX_LOGS(ERROR) << "Error parsing reboot log";
return;
}
cobalt_.LogOccurrence(ToCobaltRebootReason(reboot_log.RebootReason()));
// We don't want to file a crash report on graceful or cold reboots.
if (IsGraceful(reboot_log.RebootReason()) || reboot_log.RebootReason() == RebootReason::kCold) {
return;
}
executor_.schedule_task(FileCrashReport(reboot_log, crash_reporting_delay));
}
namespace {
fuchsia::feedback::CrashReport CreateCrashReport(const RebootLog& reboot_log) {
// Build the crash report.
fuchsia::feedback::GenericCrashReport generic_report;
generic_report.set_crash_signature(ToCrashSignature(reboot_log.RebootReason()));
fuchsia::feedback::SpecificCrashReport specific_report;
specific_report.set_generic(std::move(generic_report));
fuchsia::feedback::CrashReport report;
report.set_program_name(ToCrashProgramName(reboot_log.RebootReason()));
if (reboot_log.HasUptime()) {
report.set_program_uptime(reboot_log.Uptime().get());
}
report.set_specific_report(std::move(specific_report));
// Build the crash report attachments.
if (reboot_log.HasRebootLogStr()) {
fsl::SizedVmo vmo;
if (fsl::VmoFromString(reboot_log.RebootLogStr(), &vmo)) {
std::vector<fuchsia::feedback::Attachment> attachments(1);
attachments.back().key = "reboot_crash_log";
attachments.back().value = std::move(vmo).ToTransport();
report.set_attachments(std::move(attachments));
}
}
return report;
}
} // namespace
::fit::promise<void> Reporter::FileCrashReport(const RebootLog& reboot_log,
const zx::duration delay) {
auto report = CreateCrashReport(reboot_log);
delayed_crash_reporting_.Reset([this, report = std::move(report)]() mutable {
crash_reporter_->File(std::move(report), [this](::fit::result<void, zx_status_t> result) {
if (crash_reporter_.IsAlreadyDone()) {
return;
}
if (result.is_error()) {
FX_PLOGS(ERROR, result.error()) << "fuchsia.feedback.CrashReporter/File returned an error";
crash_reporter_.CompleteError(Error::kBadValue);
} else {
crash_reporter_.CompleteOk();
}
});
});
if (const zx_status_t status = async::PostDelayedTask(
dispatcher_, [cb = delayed_crash_reporting_.callback()] { cb(); }, delay);
status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to post delayed task, no crash reporting";
crash_reporter_.CompleteError(Error::kAsyncTaskPostFailure);
}
return crash_reporter_.WaitForDone().then([this](const ::fit::result<void, Error>& result) {
delayed_crash_reporting_.Cancel();
if (result.is_error()) {
FX_LOGS(ERROR) << "Failed to file a crash report: " << ToString(result.error());
}
return ::fit::ok();
});
}
} // namespace feedback