blob: 59aefcba5373b0131c6f7bc2e32e3dfd31af5166 [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "vm_tools/concierge/mglru_util.h"
#include <sstream>
#include <utility>
namespace vm_tools::concierge::mglru {
namespace {
// Parses a single generation from the input stream and adds it to the supplied
// node. The input stream is expected to be at the starting position for the
// generation, otherwise this function will fail. On failure the stream is reset
// to a good state and set to its original position.
bool ParseGeneration(std::stringstream& mglru_stream, MglruNode& node) {
std::streampos starting_position = mglru_stream.tellg();
MglruGeneration gen;
mglru_stream >> gen.sequence_num >> gen.timestamp_msec >> gen.anon >>
gen.file;
if (mglru_stream.fail()) {
mglru_stream.clear();
mglru_stream.seekg(starting_position);
return false;
}
node.generations.emplace_back(std::move(gen));
return true;
}
// Parses a single node from the input stream and adds it to the supplied memcg.
// The input stream is expected to be at the starting position for the node,
// otherwise this function will fail. On failure the stream is reset to a good
// state and set to its original position.
bool ParseNode(std::stringstream& mglru_stream, MglruMemcg& memcg) {
std::streampos starting_position = mglru_stream.tellg();
std::string token;
mglru_stream >> token;
// A node always starts with the node identifier token.
if (token != "node") {
mglru_stream.clear();
mglru_stream.seekg(starting_position);
return false;
}
MglruNode new_node;
// After the node identifier is the node ID.
mglru_stream >> new_node.id;
if (mglru_stream.fail()) {
mglru_stream.clear();
mglru_stream.seekg(starting_position);
return false;
}
// Next is one or more generations. Parse generations until we encounter a
// failure. The first failure indicates the end of the list of generations.
while (ParseGeneration(mglru_stream, new_node)) {
}
// If no generations were parsed, there is an error.
if (new_node.generations.size() == 0) {
mglru_stream.clear();
mglru_stream.seekg(starting_position);
return false;
}
// Add new Node to last CG
memcg.nodes.emplace_back(std::move(new_node));
return true;
}
// Parses a single memcg from the input stream and adds it to the supplied
// stats. The input stream is expected to be at the starting position for the
// memcg, otherwise this function will fail. On failure the stream is reset to a
// good state and set to its original position.
bool ParseMemcg(std::stringstream& mglru_stream, MglruStats& parsed_stats) {
std::streampos starting_position = mglru_stream.tellg();
std::string token;
mglru_stream >> token;
// The first token of a memcg is always the memcg identifier.
if (token != "memcg") {
mglru_stream.clear();
mglru_stream.seekg(starting_position);
return false;
}
MglruMemcg new_memcg;
// After the memcg identifier is the id
mglru_stream >> new_memcg.id;
if (mglru_stream.fail()) {
mglru_stream.clear();
mglru_stream.seekg(starting_position);
return false;
}
// After the id is a list of one or more nodes. Parse nodes until failure. The
// first failure indicates the end of the list of nodes.
while (ParseNode(mglru_stream, new_memcg)) {
}
// If no nodes were parsed, then there was an error.
if (new_memcg.nodes.size() == 0) {
mglru_stream.clear();
mglru_stream.seekg(starting_position);
return false;
}
// Add the newly parsed memcg to the stats
parsed_stats.cgs.emplace_back(std::move(new_memcg));
return true;
}
} // namespace
std::optional<MglruStats> ParseStatsFromString(const std::string admin_file) {
std::stringstream contents_stream(admin_file);
MglruStats parsed_stats{};
// The MGLRU stats file is a list of one or more memcgs. Parse until failure.
// The first failure indicates the end of the list of memcgs.
while (ParseMemcg(contents_stream, parsed_stats)) {
}
std::string token;
contents_stream >> token;
// If the parsing did not consume the entire input file, or if no memcgs were
// parsed, then something went wrong.
if (token != "" || !contents_stream.eof() || parsed_stats.cgs.size() == 0) {
return std::nullopt;
}
return parsed_stats;
}
std::string StatsToString(const MglruStats& stats) {
std::stringstream output;
for (const MglruMemcg& cg : stats.cgs) {
output << "memcg " << cg.id << '\n';
for (const MglruNode& node : cg.nodes) {
output << " node " << node.id << '\n';
for (const MglruGeneration& gen : node.generations) {
output << " " << gen.sequence_num << " " << gen.timestamp_msec
<< " " << gen.anon << " " << gen.file << '\n';
}
}
}
return output.str();
}
} // namespace vm_tools::concierge::mglru