blob: 15eae769243416837de930676a0e58b95029c10e [file] [log] [blame]
#include "utils.hpp"
#include <fmt/printf.h>
#include <array>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <memory>
#include <optional>
#include <regex>
#include <string>
#include <string_view>
#include <vector>
namespace boot_time_monitor
{
namespace fs = std::filesystem;
std::optional<int64_t> Util::getUpTimeInMs()
{
constexpr int32_t kMillisecondPerSecond = 1000;
std::ifstream fin("/proc/uptime");
if (!fin.is_open())
{
fmt::print(stderr, "[{}]: Can not read \"/proc/uptime\"\n",
__FUNCTION__);
return std::nullopt;
}
double uptimeSec;
fin >> uptimeSec;
fin.close();
return static_cast<int64_t>(uptimeSec * kMillisecondPerSecond);
}
inline int64_t Util::getWallTimeInMs()
{
return std::chrono::system_clock::now().time_since_epoch() /
std::chrono::milliseconds(1);
}
inline bool Util::isValidName(std::string_view name)
{
return !std::regex_search(name.data(), std::regex("[^0-9a-zA-Z_]"));
}
inline std::string Util::getCPPath(std::string_view nodeName,
bool wantCompleted)
{
return kBTMonitorDir + nodeName.data() + "_" + kCheckpointFile +
(wantCompleted ? kCompletedSuffix : "");
}
inline std::string Util::getDurPath(std::string_view nodeName,
bool wantCompleted)
{
return kBTMonitorDir + nodeName.data() + "_" + kDurationFile +
(wantCompleted ? kCompletedSuffix : "");
}
FileUtil::FileUtil(std::string_view filename) : filename(filename)
{
fs::path dir = fs::path(filename).remove_filename();
if (!dir.empty() && !fs::exists(dir))
{
fs::create_directories(dir);
}
ofs = std::make_unique<std::ofstream>(
filename.data(), std::fstream::out | std::fstream::app);
if (!ofs->good())
{
throw std::invalid_argument("ofstream is not available.");
}
}
void FileUtil::addCheckpoint(std::string_view name, int64_t wallTimeInMs,
int64_t monoTimeInMs)
{
(*ofs) << name << "," << wallTimeInMs << "," << monoTimeInMs << std::endl;
ofs->flush();
}
void FileUtil::addDuration(std::string_view name, int64_t durationInMs)
{
(*ofs) << name << "," << durationInMs << std::endl;
ofs->flush();
}
void FileUtil::completeCurrent()
{
ofs->close();
fs::rename(filename, filename + kCompletedSuffix);
// Reopen for next reboot
ofs->open(filename.c_str(), std::fstream::out | std::fstream::app);
if (!ofs->good())
{
throw std::invalid_argument("ofstream is not available.");
}
}
std::unique_ptr<std::vector<Checkpoint>>
FileUtil::loadCheckpoints(bool loadCompleted)
{
std::unique_ptr<std::vector<Checkpoint>> result =
std::make_unique<std::vector<Checkpoint>>();
std::string cpFilename = filename + (loadCompleted ? kCompletedSuffix : "");
std::ifstream ifs(cpFilename);
if (!ifs.good())
{
fmt::print("[{}] Failed to load `{}`. Skip it.\n", __FUNCTION__,
cpFilename);
return result;
}
constexpr uint32_t kBufSize = 1024;
constexpr uint32_t kMatchCount = 4;
std::array<char, kBufSize> buf;
std::cmatch match;
const std::regex reg("^([0-9a-zA-Z_]*),([0-9]+),([0-9]+)$");
while (ifs.getline(buf.data(), kBufSize))
{
if (regex_match(buf.data(), match, reg))
{
if (match.size() != kMatchCount)
{
// This line is problematic. Skip it.
continue;
}
result->emplace_back(match[1].str(), std::stoll(match[2].str()),
std::stoll(match[3].str()));
}
}
return result;
}
std::unique_ptr<std::vector<Duration>>
FileUtil::loadDurations(bool loadCompleted)
{
std::unique_ptr<std::vector<Duration>> result =
std::make_unique<std::vector<Duration>>();
std::string durFilename = filename +
(loadCompleted ? kCompletedSuffix : "");
std::ifstream ifs(durFilename);
if (!ifs.good())
{
fmt::print("[{}] Failed to load `{}`. Skip it.\n", __FUNCTION__,
durFilename);
return result;
}
constexpr uint32_t kBufSize = 1024;
constexpr uint32_t kMatchCount = 3;
std::array<char, kBufSize> buf;
std::cmatch match;
const std::regex reg("^([0-9a-zA-Z_]*),([0-9]+)$");
while (ifs.getline(buf.data(), kBufSize))
{
if (regex_match(buf.data(), match, reg))
{
if (match.size() != kMatchCount)
{
// This line is problematic. Skip it.
continue;
}
result->emplace_back(match[1].str(), std::stoll(match[2].str()));
}
}
return result;
}
bool FileUtil::isEmpty()
{
const auto result = ofs->tellp();
if (result < 0)
{
throw std::runtime_error("File can't be `tellp`");
}
return result == 0;
}
} // namespace boot_time_monitor