blob: 2509ae347d0dd5631b8abcffae15766f6605beb2 [file] [log] [blame]
#include "utils.hpp"
#include <fmt/printf.h>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#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 : "");
}
std::optional<uint32_t> Util::readMem4Bytes(uint32_t target)
{
// Typically the pageSize will be 4K/8K for 32 bit operating systems
uint32_t pageSize = boost::interprocess::mapped_region::get_page_size();
const uint32_t kPageMask = pageSize - 1;
uint32_t pageOffset = target & (~kPageMask);
uint32_t offsetInPage = target & kPageMask;
// Map `/dev/mem` to a region.
std::unique_ptr<boost::interprocess::file_mapping> fileMapping;
std::unique_ptr<boost::interprocess::mapped_region> MappedRegion;
try
{
fileMapping = std::make_unique<boost::interprocess::file_mapping>(
"/dev/mem", boost::interprocess::read_only);
// No need to unmap in the end.
MappedRegion = std::make_unique<boost::interprocess::mapped_region>(
*fileMapping, boost::interprocess::read_only, pageOffset,
pageSize * 2);
// MappedRegion->get_address() returns (void*) which needs to covert
// into (char*) to make `+ offsetInPage` work.
// Then converts it again into (uint32_t*) since we read 4 bytes.
return *(reinterpret_cast<uint32_t*>(
static_cast<char*>(MappedRegion->get_address()) + offsetInPage));
}
catch (const std::exception& e)
{
fmt::print(stderr, "[{}]: Throw exception: %s\n", __FUNCTION__,
e.what());
return std::nullopt;
}
fmt::print(stderr, "[{}]: Shouldn't go to this line.\n", __FUNCTION__);
return std::nullopt;
}
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