foundationdb/monitoring/actor_flamegraph.cpp

187 lines
4.6 KiB
C++

#include <cstdint>
#include <unordered_map>
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <stack>
#include <memory>
#include <sstream>
#include <algorithm>
namespace {
void usage(const char* execName, std::ostream& out) {
out << "USAGE: " << execName << " [OPTIONS] [--] file [file]..." << std::endl;
out << '\t' << "-h|--help: print this help" << std::endl;
}
struct Error {
const char* msg = "";
bool isFatal = false;
const char* what() const { return msg; }
};
struct Actor {
template <class Str>
explicit Actor(std::unordered_map<std::string, unsigned long>& results, unsigned long id, Str&& name) : results(results), id(id), name(std::forward<Str>(name)) {}
Actor(const Actor&) = delete;
~Actor() { collect(); }
std::unordered_map<std::string, unsigned long>& results;
unsigned long id;
std::string name;
std::deque<std::string> stack;
unsigned long runTime = 0;
unsigned long lastStart = 0;
void enter(unsigned long time) { lastStart = time; }
void exit(unsigned long time) { runTime += time - lastStart; }
void collect() {
std::stringstream ss;
for (auto i = stack.begin(); i != stack.end();) {
int num = 0;
auto name = *i;
for (; i != stack.end() && *i == name ; ++i) {
++num;
}
ss << name;
if (num > 1) {
ss << " ("<< num << ')';
}
ss << ';';
}
ss << name;
auto myStack = ss.str();
results[myStack] += std::max(1ul, runTime);
}
};
class Traces {
constexpr static int OP_CREATE = 0;
constexpr static int OP_DESTROY = 1;
constexpr static int OP_ENTER = 2;
constexpr static int OP_EXIT = 3;
std::stack<std::shared_ptr<Actor>> currentStack;
std::unordered_map<unsigned long, std::shared_ptr<Actor>> actors;
std::unordered_map<std::string, unsigned long> results;
std::vector<std::string> split(const std::string& str, char delim) {
std::vector<std::string> res;
std::string::size_type pos = 0;
while (pos < str.size()) {
auto e = str.find(delim, pos);
if (e == std::string::npos) {
res.emplace_back(str.substr(pos));
break;
}
res.emplace_back(str.substr(pos, e - pos));
pos = e + 1;
}
return res;
}
public:
void print(std::ostream& out) const {
for (const auto& r : results) {
out << r.first << ' ' << r.second << std::endl;
}
}
void operator()(std::istream& in) {
int lineNo = 0;
std::string line;
while (std::getline(in, line)) {
++lineNo;
if (line.empty()) {
continue;
}
auto v = split(line, ';');
if (v.size() != 4) {
Error e;
e.msg = "Could not parse line";
throw e;
}
unsigned long timestamp = std::stoul(v[0]);
int op = std::stoi(v[1]);
const auto& name = v[2];
unsigned long id = std::stoul(v[3]);
if (op == OP_CREATE) {
actors[id] = std::make_shared<Actor>(results, id, name);
auto& actor = actors[id];
if (!currentStack.empty()) {
actor->stack = currentStack.top()->stack;
actor->stack.push_back(currentStack.top()->name);
}
} else if (op == OP_DESTROY) {
if (actors.count(id)) {
actors.erase(id);
}
} else if (op == OP_ENTER) {
if (actors.count(id) == 0) {
actors[id] = std::make_shared<Actor>(results, id, name);
}
currentStack.push(actors[id]);
actors[id]->enter(timestamp);
} else if (op == OP_EXIT) {
if (!currentStack.empty()) {
if (currentStack.top()->id != id) {
std::cerr << "WARNING: Unbalanced stack at line " << lineNo << std::endl;
} else {
currentStack.top()->exit(timestamp);
currentStack.pop();
}
}
}
}
std::cout << "DONE" << std::endl;
while (!currentStack.empty()) {
currentStack.pop();
}
actors.clear();
}
};
} // namespace
int main(int argc, char* argv[]) {
std::vector<std::string> files;
bool endOfArgs = false;
for (int i = 1; i < argc; ++i) {
std::string arg(argv[i]);
if (endOfArgs) {
files.emplace_back(arg);
} else if (arg == "--") {
endOfArgs = true;
} else if (arg == "-h" || arg == "--") {
usage(argv[0], std::cout);
return 0;
} else if (arg[0] != '-') {
files.emplace_back(arg);
} else {
std::cerr << "Unknown argument \"" << arg << "\"" << std::endl;
usage(argv[0], std::cerr);
return 1;
}
}
if (files.empty()) {
std::cerr << "ERROR: No file" << std::endl;
}
Traces traces;
for (const auto& file : files) {
std::fstream in(file.c_str(), std::ios_base::in);
if (!in) {
std::cerr << "Error: can't open file: " << file << std::endl;
return 1;
}
try {
traces(in);
} catch (Error& e) {
std::cerr << (e.isFatal ? "FATAL: " : "ERROR: ") << e.what() << std::endl;
if (e.isFatal) {
return 1;
}
}
}
traces.print(std::cout);
return 0;
}