| #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 |