blob: 46b7bac75ee64a6b2ef8fd88aa4f0eca58fef385 [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 "crash-reporter/arc_java_collector.h"
#include <ctime>
#include <memory>
#include <utility>
#include <base/bind.h>
#include <base/files/file.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/time/time.h>
#include "crash-reporter/arc_util.h"
#include "crash-reporter/util.h"
using base::File;
using base::FilePath;
namespace {
constexpr char kArcJavaCollectorName[] = "ARC_java";
} // namespace
ArcJavaCollector::ArcJavaCollector()
: CrashCollector(kArcJavaCollectorName,
kAlwaysUseUserCrashDirectory,
kNormalCrashSendMode,
kArcJavaCollectorName) {}
bool ArcJavaCollector::HandleCrash(
const std::string& crash_type,
const arc_util::BuildProperty& build_property,
base::TimeDelta uptime) {
std::ostringstream message;
message << "Received " << crash_type << " notification";
std::string contents;
if (!base::ReadStreamToString(stdin, &contents)) {
PLOG(ERROR) << "Failed to read crash log";
return false;
}
if (contents.empty()) {
LOG(ERROR) << "crash log was empty";
return false;
}
CrashLogHeaderMap map;
std::string exception_info, log;
if (!arc_util::ParseCrashLog(crash_type, contents, &map, &exception_info,
&log)) {
LOG(ERROR) << "Failed to parse crash log";
return false;
}
const auto exec = arc_util::GetCrashLogHeader(map, arc_util::kProcessKey);
message << " for " << exec;
LogCrash(message.str(), "handling");
bool out_of_capacity = false;
if (!CreateReportForJavaCrash(crash_type, build_property, map, exception_info,
log, uptime, &out_of_capacity)) {
if (!out_of_capacity) {
EnqueueCollectionErrorLog(kErrorSystemIssue, exec);
}
return false;
}
return true;
}
std::string ArcJavaCollector::GetProductVersion() const {
return arc_util::GetProductVersion();
}
void ArcJavaCollector::AddArcMetaData(const std::string& process,
const std::string& crash_type,
base::TimeDelta uptime) {
for (const auto& metadata :
arc_util::ListBasicARCRelatedMetadata(process, crash_type)) {
AddCrashMetaUploadData(metadata.first, metadata.second);
}
AddCrashMetaUploadData(arc_util::kChromeOsVersionField, GetOsVersion());
#if USE_ARCPP
if (uptime.is_zero()) {
SetUpDBus();
if (!arc_util::GetArcContainerUptime(session_manager_proxy_.get(),
&uptime)) {
uptime = base::TimeDelta();
}
}
#endif // USE_ARCPP
if (!uptime.is_zero()) {
AddCrashMetaUploadData(arc_util::kUptimeField,
arc_util::FormatDuration(uptime));
}
if (arc_util::IsSilentReport(crash_type))
AddCrashMetaData(arc_util::kSilentKey, "true");
}
bool ArcJavaCollector::CreateReportForJavaCrash(
const std::string& crash_type,
const arc_util::BuildProperty& build_property,
const CrashLogHeaderMap& map,
const std::string& exception_info,
const std::string& log,
base::TimeDelta uptime,
bool* out_of_capacity) {
FilePath crash_dir;
if (!GetCreatedCrashDirectoryByEuid(geteuid(), &crash_dir, out_of_capacity)) {
LOG(ERROR) << "Failed to create or find crash directory";
return false;
}
const auto process = arc_util::GetCrashLogHeader(map, arc_util::kProcessKey);
pid_t dt = arc_util::CreateRandomPID();
const auto basename = FormatDumpBasename(process, std::time(nullptr), dt);
const FilePath log_path = GetCrashPath(crash_dir, basename, "log");
const int size = static_cast<int>(log.size());
if (WriteNewFile(log_path, log) != size) {
PLOG(ERROR) << "Failed to write log";
return false;
}
AddArcMetaData(process, crash_type, uptime);
for (auto metadata : arc_util::ListMetadataForBuildProperty(build_property)) {
AddCrashMetaUploadData(metadata.first, metadata.second);
}
for (const auto& mapping : arc_util::kHeaderToFieldMapping) {
if (map.count(mapping.first)) {
AddCrashMetaUploadData(mapping.second,
arc_util::GetCrashLogHeader(map, mapping.first));
}
}
if (exception_info.empty()) {
if (const char* const tag = arc_util::GetSubjectTag(crash_type)) {
std::ostringstream out;
out << '[' << tag << ']';
const auto it = map.find(arc_util::kSubjectKey);
if (it != map.end())
out << ' ' << it->second;
AddCrashMetaData(arc_util::kSignatureField, out.str());
} else {
LOG(ERROR) << "Invalid crash type: " << crash_type;
return false;
}
} else {
const FilePath info_path = GetCrashPath(crash_dir, basename, "info");
const int size = static_cast<int>(exception_info.size());
if (WriteNewFile(info_path, exception_info) != size) {
PLOG(ERROR) << "Failed to write exception info";
return false;
}
AddCrashMetaUploadText(arc_util::kExceptionInfoField,
info_path.BaseName().value());
}
const FilePath meta_path = GetCrashPath(crash_dir, basename, "meta");
FinishCrash(meta_path, process, log_path.BaseName().value());
return true;
}
// static
CollectorInfo ArcJavaCollector::GetHandlerInfo(
const std::string& arc_java_crash,
const arc_util::BuildProperty& build_property,
int64_t uptime_millis) {
auto arc_java_collector = std::make_shared<ArcJavaCollector>();
return {
.collector = arc_java_collector,
.handlers = {{
// This handles Java app crashes of ARC++ and ARCVM.
.should_handle = !arc_java_crash.empty(),
.cb = base::BindRepeating(
&ArcJavaCollector::HandleCrash, arc_java_collector,
arc_java_crash, build_property,
base::TimeDelta::FromMilliseconds(uptime_millis)),
}},
};
}