#ifdef RUY_PROFILER
#include "ruy/profiler/treeview.h"
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <functional>
#include <memory>
#include <vector>
namespace ruy {
namespace profiler {
namespace {
void SortNode(TreeView::Node* node) {
using NodePtr = std::unique_ptr<TreeView::Node>;
std::sort(node->children.begin(), node->children.end(),
[](const NodePtr& n1, const NodePtr& n2) {
return n1->weight > n2->weight;
});
for (const auto& child : node->children) {
SortNode(child.get());
}
}
void AddStack(const detail::Stack& stack, TreeView::Node* node, int level) {
node->weight++;
if (stack.size == level) {
return;
}
TreeView::Node* child_to_add_to = nullptr;
for (const auto& child : node->children) {
if (child->label == stack.labels[level]) {
child_to_add_to = child.get();
break;
}
}
if (!child_to_add_to) {
child_to_add_to = new TreeView::Node;
child_to_add_to->label = stack.labels[level];
node->children.emplace_back(child_to_add_to);
}
AddStack(stack, child_to_add_to, level + 1);
}
void AddOther(TreeView::Node* node) {
int top_level_children_weight = 0;
for (const auto& child : node->children) {
AddOther(child.get());
top_level_children_weight += child->weight;
}
if (top_level_children_weight != 0 &&
top_level_children_weight != node->weight) {
auto* new_child = new TreeView::Node;
new_child->label = Label("[other]");
new_child->weight = node->weight - top_level_children_weight;
node->children.emplace_back(new_child);
}
}
}
void TreeView::Populate(const std::vector<char>& samples_buf_) {
thread_roots_.clear();
const char* buf_ptr = samples_buf_.data();
const char* const buf_ptr_end = buf_ptr + samples_buf_.size();
while (buf_ptr < buf_ptr_end) {
detail::Stack stack;
detail::ReadFromBuffer(buf_ptr, &stack);
assert(stack.size > 0);
buf_ptr += GetBufferSize(stack);
const int id = stack.id;
if (!thread_roots_[id]) {
thread_roots_[id].reset(new Node);
}
AddStack(stack, thread_roots_[id].get(), 0);
}
for (const auto& thread_root : thread_roots_) {
std::uint32_t id = thread_root.first;
Node* root = thread_root.second.get();
AddOther(root);
SortNode(root);
root->label.Set("Thread %x (%d samples)", id, root->weight);
}
}
void PrintTreeBelow(const TreeView::Node& node, const TreeView::Node& root,
int level) {
if (&node == &root) {
printf("%s\n\n", node.label.Formatted().c_str());
} else {
for (int i = 1; i < level; i++) {
printf(" ");
}
printf("* %.2f%% %s\n", 100.0f * node.weight / root.weight,
node.label.Formatted().c_str());
}
for (const auto& child : node.children) {
PrintTreeBelow(*child, root, level + 1);
}
}
void Print(const TreeView& treeview) {
printf("\n");
printf("Profile (%d threads):\n\n",
static_cast<int>(treeview.thread_roots().size()));
for (const auto& thread_root : treeview.thread_roots()) {
const TreeView::Node& root = *thread_root.second;
PrintTreeBelow(root, root, 0);
printf("\n");
}
}
int DepthOfTreeBelow(const TreeView::Node& node) {
if (node.children.empty()) {
return 0;
} else {
int max_child_depth = 0;
for (const auto& child : node.children) {
max_child_depth = std::max(max_child_depth, DepthOfTreeBelow(*child));
}
return 1 + max_child_depth;
}
}
int WeightBelowNodeMatchingFunction(
const TreeView::Node& node,
const std::function<bool(const Label&)>& match) {
int weight = 0;
if (match(node.label)) {
weight += node.weight;
}
for (const auto& child : node.children) {
weight += WeightBelowNodeMatchingFunction(*child, match);
}
return weight;
}
int WeightBelowNodeMatchingUnformatted(const TreeView::Node& node,
const std::string& format) {
return WeightBelowNodeMatchingFunction(
node, [&format](const Label& label) { return label.format() == format; });
}
int WeightBelowNodeMatchingFormatted(const TreeView::Node& node,
const std::string& formatted) {
return WeightBelowNodeMatchingFunction(
node, [&formatted](const Label& label) {
return label.Formatted() == formatted;
});
}
void CollapseNode(const TreeView::Node& node_in, int depth,
TreeView::Node* node_out) {
node_out->label = node_in.label;
node_out->weight = node_in.weight;
node_out->children.clear();
if (depth > 0) {
for (const auto& child_in : node_in.children) {
auto* child_out = new TreeView::Node;
node_out->children.emplace_back(child_out);
CollapseNode(*child_in, depth - 1, child_out);
}
}
}
void CollapseSubnodesMatchingFunction(
const TreeView::Node& node_in, int depth,
const std::function<bool(const Label&)>& match, TreeView::Node* node_out) {
if (match(node_in.label)) {
CollapseNode(node_in, depth, node_out);
} else {
node_out->label = node_in.label;
node_out->weight = node_in.weight;
node_out->children.clear();
for (const auto& child_in : node_in.children) {
auto* child_out = new TreeView::Node;
node_out->children.emplace_back(child_out);
CollapseSubnodesMatchingFunction(*child_in, depth, match, child_out);
}
}
}
void CollapseNodesMatchingFunction(
const TreeView& treeview_in, int depth,
const std::function<bool(const Label&)>& match, TreeView* treeview_out) {
treeview_out->mutable_thread_roots()->clear();
for (const auto& thread_root_in : treeview_in.thread_roots()) {
std::uint32_t id = thread_root_in.first;
const auto& root_in = *thread_root_in.second;
auto* root_out = new TreeView::Node;
treeview_out->mutable_thread_roots()->emplace(
id, std::unique_ptr<TreeView::Node>(root_out));
CollapseSubnodesMatchingFunction(root_in, depth, match, root_out);
}
}
void CollapseNodesMatchingUnformatted(const TreeView& treeview_in, int depth,
const std::string& format,
TreeView* treeview_out) {
CollapseNodesMatchingFunction(
treeview_in, depth,
[&format](const Label& label) { return label.format() == format; },
treeview_out);
}
void CollapseNodesMatchingFormatted(const TreeView& treeview_in, int depth,
const std::string& formatted,
TreeView* treeview_out) {
CollapseNodesMatchingFunction(
treeview_in, depth,
[&formatted](const Label& label) {
return label.Formatted() == formatted;
},
treeview_out);
}
}
}
#endif