blob: 417428aca402edff29e8edc6175435ad5657ab40 [file] [log] [blame]
// Copyright (c) 2011 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.
// Implementation of bootstat_log(), part of the Chromium OS 'bootstat'
// facility.
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/strings/stringprintf.h>
#include <brillo/brillo_export.h>
#include <rootdev/rootdev.h>
#include "bootstat/bootstat.h"
namespace bootstat {
//
// Default path to directory where output statistics will be stored.
//
static const char kDefaultOutputDirectoryName[] = "/tmp";
//
// Paths to the statistics files we snapshot as part of the data to
// be logged.
//
static const char kDefaultUptimeStatisticsFileName[] = "/proc/uptime";
// TODO(drinkcat): Cache function output (we only need to evaluate it once)
base::FilePath BootStatSystem::GetDiskStatisticsFilePath() const {
char boot_path[PATH_MAX];
int ret = rootdev(boot_path, sizeof(boot_path),
true, // Do full resolution.
false); // Do not remove partition number.
if (ret < 0)
return base::FilePath();
// The general idea is to use the the root device's sysfs entry to
// get the path to the root disk's sysfs entry.
// Example:
// - rootdev() returns "/dev/sda3"
// - Use /sys/class/block/sda3/../ to get to root disk (sda) sysfs entry.
// This is because /sys/class/block/sda3 is a symlink that maps to:
// /sys/devices/pci.../.../ata./host./target.../.../block/sda/sda3
base::FilePath root_device_name = base::FilePath(boot_path).BaseName();
base::FilePath stat_path = base::FilePath("/sys/class/block")
.Append(root_device_name)
.Append("../stat");
// Normalize the path as some functions refuse to follow symlink/`..`.
base::FilePath norm;
if (!base::NormalizeFilePath(stat_path, &norm))
return base::FilePath();
return norm;
}
BootStat::BootStat()
: BootStat(base::FilePath(kDefaultOutputDirectoryName),
kDefaultUptimeStatisticsFileName,
std::make_unique<BootStatSystem>()) {}
BootStat::BootStat(const base::FilePath& output_directory_path,
const std::string& uptime_statistics_file_path,
std::unique_ptr<BootStatSystem> boot_stat_system)
: output_directory_path_(output_directory_path),
uptime_statistics_file_path_(uptime_statistics_file_path),
boot_stat_system_(std::move(boot_stat_system)) {}
BootStat::~BootStat() = default;
base::ScopedFD BootStat::OpenEventFile(const std::string& output_name_prefix,
const std::string& event_name) const {
const mode_t kFileCreationMode =
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
//
// For those not up on the more esoteric features of printf
// formats: the "%.*s" format is used to truncate the event name
// to the proper number of characters..
//
std::string output_file =
base::StringPrintf("%s-%.*s", output_name_prefix.c_str(),
BOOTSTAT_MAX_EVENT_LEN - 1, event_name.c_str());
base::FilePath output_path = output_directory_path_.Append(output_file);
int output_fd =
HANDLE_EINTR(open(output_path.value().c_str(),
O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW | O_CLOEXEC,
kFileCreationMode));
return base::ScopedFD(output_fd);
}
bool BootStat::LogDiskEvent(const std::string& event_name) const {
base::FilePath disk_statistics_file_path =
boot_stat_system_->GetDiskStatisticsFilePath();
if (disk_statistics_file_path.empty())
return false;
std::string data;
if (!base::ReadFileToString(disk_statistics_file_path, &data))
return false;
base::ScopedFD output_fd = OpenEventFile("disk", event_name);
if (!output_fd.is_valid())
return false;
return base::WriteFileDescriptor(output_fd.get(), data.c_str(), data.size());
}
// TODO(drinkcat): Either merge the common parts of this function with
// LogDiskEvent, or use clock_gettime.
bool BootStat::LogUptimeEvent(const std::string& event_name) const {
std::string data;
if (!base::ReadFileToString(base::FilePath(uptime_statistics_file_path_),
&data))
return false;
base::ScopedFD output_fd = OpenEventFile("uptime", event_name);
if (!output_fd.is_valid())
return false;
return base::WriteFileDescriptor(output_fd.get(), data.c_str(), data.size());
}
// API functions.
bool BootStat::LogEvent(const std::string& event_name) const {
bool ret = true;
ret &= LogDiskEvent(event_name);
ret &= LogUptimeEvent(event_name);
return ret;
}
}; // namespace bootstat
BRILLO_EXPORT
void bootstat_log(const char* event_name) {
bootstat::BootStat().LogEvent(event_name);
}