blob: 6b6c4953b86c01ac19ca3cb43c91cfdef339b6ad [file] [log] [blame] [edit]
// Copyright 2010 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The kernel collector reports kernel panics or other kernel-level issues that
// caused machine reboot, like EFI crashes and BIOS crashes.
// The kernel collector runs on boot, via the crash-boot-collect service.
#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_H_
#define CRASH_REPORTER_KERNEL_COLLECTOR_H_
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <base/files/file_path.h>
#include <base/memory/ref_counted.h>
#include <base/memory/scoped_refptr.h>
#include <base/types/expected.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include <metrics/metrics_library.h>
#include "crash-reporter/crash_collector.h"
#include "crash-reporter/kernel_util.h"
enum class CrashCollectionStatus;
// These correspond to strings returned from kmsg_dump_reason_str() in the
// kernel.
enum class PstoreRecordType {
kPanic,
kOops,
kEmergency,
kShutdown,
kUnknown,
// Below this point are for errors while parsing the pstore record.
kParseFailed,
};
std::ostream& operator<<(std::ostream& out, PstoreRecordType record_type);
// Kernel crash collector.
class KernelCollector : public CrashCollector {
public:
explicit KernelCollector(
const scoped_refptr<
base::RefCountedData<std::unique_ptr<MetricsLibraryInterface>>>&
metrics_lib);
KernelCollector(const KernelCollector&) = delete;
KernelCollector& operator=(const KernelCollector&) = delete;
~KernelCollector() override;
void OverrideEventLogPath(const base::FilePath& file_path);
void OverrideBiosLogPath(const base::FilePath& file_path);
void OverridePreservedDumpPath(const base::FilePath& file_path);
void OverrideWatchdogSysPath(const base::FilePath& file_path);
// Enable collection.
bool Enable();
// Returns true if the kernel collection currently enabled.
bool is_enabled() const { return is_enabled_; }
// Collects efi crashes. Returns 1 status per EFI crash found *or*
// a vector containing the single element kNoCrashFound.
std::vector<CrashCollectionStatus> CollectEfiCrashes(bool use_saved_lsb);
// Collects ramoops crash.
CrashCollectionStatus CollectRamoopsCrash(bool use_saved_lsb);
// Given the returned results of CollectEfiCrash and CollectRamoopsCrash,
// was there actually a kernel crash available to collect?
static bool WasKernelCrash(
const std::vector<CrashCollectionStatus>& efi_crash_statuses,
CrashCollectionStatus ramoops_crash_status);
// Set the architecture of the crash dumps we are looking at.
void set_arch(kernel_util::ArchKind arch) { arch_ = arch; }
kernel_util::ArchKind arch() const { return arch_; }
// Returns the severity level and product group of the crash.
CrashCollector::ComputedCrashSeverity ComputeSeverity(
const std::string& exec_name) override;
// Helper functions for parsing pstore record headers.
static PstoreRecordType StringToPstoreRecordType(std::string_view record);
static std::string PstoreRecordTypeToString(PstoreRecordType record_type);
static constexpr char const* kDumpDriverEfiNames[] = {"efi", "efi_pstore"};
static constexpr size_t numKDumpDriverEfiNames =
sizeof(kDumpDriverEfiNames) / sizeof(*kDumpDriverEfiNames);
static constexpr char kDumpDriverRamoopsName[] = "ramoops";
protected:
// This class represents a single pstore crash record. Depending on the
// backend used (EFI, ramoops, etc.) a pstore record may be split up across
// many files, each of which has an ID as the last number (for example
// dmesg-efi-150989600314002 has an id of 150989600314002). Regardless of the
// backend used, the pstore filesystem exposes records in a common format
// where each file has a header indicating the type of record (panic, oops,
// etc.) and the part of the record the file corresponds to. For example, a
// header "Panic#2 Part#4" indicates that the file is for the fourth part of
// the second panic recorded in the backend. The remaining data in the file is
// the record contents for that part.
class PstoreCrash {
public:
explicit PstoreCrash(uint64_t id,
uint32_t max_part,
std::string_view backend,
const KernelCollector* collector)
: id_(id),
max_part_(max_part),
backend_(std::string(backend)),
collector_(collector) {}
PstoreCrash(const PstoreCrash&) = default;
PstoreCrash& operator=(const PstoreCrash&) = default;
virtual ~PstoreCrash();
// Load crash data into |contents|.
// Returns true iff all parts of crashes are copied to |contents|.
// In case of failure |contents| might be modified.
bool Load(std::string& contents) const;
// Get type of crash.
PstoreRecordType GetType() const;
// Remove pstore file(s) for this crash.
void Remove() const;
// Collect crash data into |crash| when crash type is a panic.
CrashCollectionStatus CollectCrash(std::string& crash) const;
// Returns crash id.
uint64_t GetId() const { return id_; }
// Returns id for |part|.
virtual constexpr uint64_t GetIdForPart(uint32_t part) const { return id_; }
protected:
// Returns largest part for a record.
uint32_t GetMaxPart() const { return max_part_; }
void SetMaxPart(uint32_t max_part) { max_part_ = max_part; }
const KernelCollector* GetCollector() const { return collector_; }
// Returns the filepath in pstore for this record's |part|.
base::FilePath GetFilePath(uint32_t part) const;
private:
uint64_t id_;
uint32_t max_part_;
std::string backend_;
const KernelCollector* collector_;
};
// This class represents a single ramoops crash record. A ramoops record
// only ever has one part and thus one file in the pstore filesystem.
class RamoopsCrash : public PstoreCrash {
public:
explicit RamoopsCrash(uint64_t id, const KernelCollector* collector)
: PstoreCrash(id, 1, kDumpDriverRamoopsName, collector) {}
RamoopsCrash(const RamoopsCrash&) = default;
RamoopsCrash& operator=(const RamoopsCrash&) = default;
~RamoopsCrash() override;
};
// This class represents a single EFI crash record. An EFI crash record may be
// split across multiple files, each for a different part of the record.
class EfiCrash : public PstoreCrash {
public:
explicit EfiCrash(uint64_t id,
std::string efi_driver_name,
const KernelCollector* collector)
: PstoreCrash(id, GetPart(id), efi_driver_name, collector),
timestamp_(GetTimestamp(id)),
crash_count_(GetCrashCount(id)) {}
EfiCrash(const EfiCrash&) = default;
EfiCrash& operator=(const EfiCrash&) = default;
~EfiCrash() override;
// Updates part from crash id iff it's greater.
void UpdateMaxPart(uint64_t id) {
uint32_t part = GetPart(id);
if (part > GetMaxPart()) {
SetMaxPart(part);
}
}
constexpr uint64_t GetIdForPart(uint32_t part) const override {
return GenerateId(timestamp_, part, crash_count_);
}
// Helper functions for parsing and generating efi crash id.
// Get efi crash id for given part.
static constexpr uint64_t GetIdForPart(uint64_t id, uint32_t part) {
return GenerateId(GetTimestamp(id), part, GetCrashCount(id));
}
// Get crash count from efi crash id.
static constexpr uint32_t GetCrashCount(uint64_t id) {
return id % kMaxDumpRecord;
}
// Get part number from efi crash id.
static constexpr uint32_t GetPart(uint64_t id) {
return (id / kMaxDumpRecord) % kMaxPart;
}
// Get timestamp from efi crash id.
static constexpr uint64_t GetTimestamp(uint64_t id) {
return (id / (kMaxDumpRecord * kMaxPart));
}
// Generates efi crash id from timestamp, part, crash count.
// EFI File name is of format dmesg-efi-<crash_id>. Since one kernel crash
// is split into multiple parts, <crash_id> is derived by
// crash_id = (timestamp * 100 + part) * 1000 + crash_count.
// See efi-pstore driver (https://goo.gl/1YBeCD) for more information.
// e.g. File "dmesg-efi-150989600314002" represents part 14 of crash 2.
static constexpr uint64_t GenerateId(uint64_t timestamp,
uint32_t part,
uint32_t crash_count) {
return (timestamp * kMaxPart + part) * kMaxDumpRecord + crash_count;
}
static constexpr size_t kMaxDumpRecord = 1000;
static constexpr size_t kMaxPart = 100;
private:
uint64_t timestamp_;
uint32_t crash_count_;
};
private:
friend class KernelCollectorTest;
FRIEND_TEST(KernelCollectorTest, LoadCorruptDump);
FRIEND_TEST(KernelCollectorTest, LoadBiosLog);
FRIEND_TEST(KernelCollectorTest, CollectOK);
FRIEND_TEST(KernelCollectorTest, ParseEfiCrashId);
FRIEND_TEST(KernelCollectorTest, GetEfiCrashType);
FRIEND_TEST(KernelCollectorTest, LoadEfiCrash);
FRIEND_TEST(KernelCollectorTest, RemoveEfiCrash);
FRIEND_TEST(KernelCollectorTest, GetRamoopsCrashType);
FRIEND_TEST(KernelCollectorTest, LoadRamoopsCrash);
FRIEND_TEST(KernelCollectorTest, RemoveRamoopsCrash);
FRIEND_TEST(KernelCollectorTest, LastRebootWasNoCError);
virtual bool DumpDirMounted();
bool LoadPreservedDump(std::string* contents);
bool LoadLastBootBiosLog(std::string* contents);
bool LastRebootWasBiosCrash(const std::string& dump);
bool LastRebootWasNoCError(const std::string& dump);
base::expected<bool, CrashCollectionStatus> LastRebootWasWatchdog(
std::string& signature);
bool LoadConsoleRamoops(std::string* contents);
base::FilePath GetDumpRecordPath(const char* type,
const char* driver,
size_t record);
bool LoadParameters();
// Read a record to string, modified from file_utils since that didn't
// provide a way to restrict the read length.
// Return value indicates (only) error state:
// * false when we get an error (can't read from dump location).
// * true if no error occured.
// Not finding a valid record is not an error state and is signaled by the
// record_found output parameter.
bool ReadRecordToString(std::string* contents,
size_t current_record,
bool* record_found);
void AddLogFile(const char* log_name,
const std::string& log_data,
const base::FilePath& log_path);
CrashCollectionStatus HandleCrash(const std::string& kernel_dump,
const std::string& bios_dump,
const std::string& hypervisor_dump,
const std::string& signature);
std::vector<EfiCrash> _FindDriverEfiCrashes(const char* driver_name) const;
std::vector<EfiCrash> FindEfiCrashes() const;
bool is_enabled_;
base::FilePath eventlog_path_;
base::FilePath dump_path_;
base::FilePath bios_log_path_;
base::FilePath watchdogsys_path_;
size_t records_;
// The architecture of kernel dump strings we are working with.
kernel_util::ArchKind arch_;
};
#endif // CRASH_REPORTER_KERNEL_COLLECTOR_H_