Commit 002523c9 authored by Jasper Chapman-Black's avatar Jasper Chapman-Black Committed by Commit Bot

SuperSize: Caspian: Add group by template

This is the first step in adding canned queries to Caspian. Refactored
TreeBuilder to replace group_by_component with a more arbitrary
Symbol -> parent path transformation.

Leaving generated file classification to a future CL.

Change-Id: Ia142df715ba911d9629ad89aa5fa0d714394410a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1924604Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Commit-Queue: Andrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#717702}
parent 04cb0791
......@@ -52,8 +52,8 @@ void LoadBeforeSizeFile(const char* compressed, size_t size) {
ParseSizeInfo(compressed, size, before_info.get());
}
void BuildTree(bool group_by_component,
bool method_count_mode,
void BuildTree(bool method_count_mode,
const char* group_by,
const char* include_regex_str,
const char* exclude_regex_str,
const char* include_sections,
......@@ -129,7 +129,26 @@ void BuildTree(bool group_by_component,
} else {
builder.reset(new TreeBuilder(info.get()));
}
builder->Build(group_by_component, method_count_mode, filters);
std::unique_ptr<BaseLens> lens;
char sep = '/';
std::cout << "group_by=" << group_by << std::endl;
if (!strcmp(group_by, "source_path")) {
lens = std::make_unique<IdPathLens>();
} else if (!strcmp(group_by, "component")) {
lens = std::make_unique<ComponentLens>();
sep = '>';
} else if (!strcmp(group_by, "template")) {
lens = std::make_unique<TemplateLens>();
filters.push_back([](const BaseSymbol& sym) -> bool {
return sym.IsTemplate() && sym.IsNative();
});
} else {
// TODO(jaspercb): Support group by generated path type.
std::cerr << "Unsupported group_by=" << group_by << std::endl;
exit(1);
}
builder->Build(std::move(lens), sep, method_count_mode, filters);
}
const char* Open(const char* path) {
......
......@@ -343,6 +343,14 @@ void TreeNode::WriteIntoJson(
(*out)["size"] = this->size;
(*out)["flags"] = this->flags;
this->node_stats.WriteIntoJson(&(*out)["childStats"]);
const size_t kMaxChildNodesToExpand = 1000;
if (this->children.size() > kMaxChildNodesToExpand) {
// When the tree is very flat, don't expand child nodes to avoid cost of
// sending thousands of children and grandchildren to renderer.
depth = 0;
}
if (depth < 0 && this->children.size() > 1) {
(*out)["children"] = Json::Value(); // null
} else {
......
......@@ -90,6 +90,8 @@ class BaseSymbol {
return aliases ? aliases->size() : 1;
}
bool IsTemplate() const { return Name().size() != TemplateName().size(); }
bool IsOverhead() const { return FullName().substr(0, 10) == "Overhead: "; }
bool IsBss() const { return Section() == SectionId::kBss; }
......
......@@ -36,6 +36,36 @@ std::string_view DirName(std::string_view path, char sep, char othersep) {
}
} // namespace
std::string_view IdPathLens::ParentName(
const BaseSymbol& symbol,
std::deque<std::string>* owned_strings) {
const char* source_path = symbol.SourcePath();
return source_path && *source_path ? source_path : symbol.ObjectPath();
}
std::string_view ComponentLens::ParentName(
const BaseSymbol& symbol,
std::deque<std::string>* owned_strings) {
std::string component;
if (symbol.Component() && *symbol.Component()) {
component = symbol.Component();
} else {
component = kNoComponent;
}
owned_strings->push_back(component + std::string(1, kComponentSep) +
std::string(symbol.SourcePath()));
return owned_strings->back();
}
std::string_view TemplateLens::ParentName(
const BaseSymbol& symbol,
std::deque<std::string>* owned_strings) {
owned_strings->push_back(std::string(symbol.Name()) + std::string(1, '/') +
std::string(symbol.SourcePath()));
return owned_strings->back();
// return symbol.Name();
}
TreeBuilder::TreeBuilder(SizeInfo* size_info) {
symbols_.reserve(size_info->raw_symbols.size());
for (const Symbol& sym : size_info->raw_symbols) {
......@@ -53,13 +83,14 @@ TreeBuilder::TreeBuilder(DeltaSizeInfo* size_info) {
TreeBuilder::~TreeBuilder() = default;
void TreeBuilder::Build(
bool group_by_component,
std::unique_ptr<BaseLens> lens,
char separator,
bool method_count_mode,
std::vector<std::function<bool(const BaseSymbol&)>> filters) {
group_by_component_ = group_by_component;
lens_ = std::move(lens);
method_count_mode_ = method_count_mode;
filters_ = filters;
sep_ = group_by_component ? kComponentSep : kPathSep;
sep_ = separator;
// Initialize tree root.
root_.container_type = ContainerType::kDirectory;
......@@ -71,10 +102,7 @@ void TreeBuilder::Build(
symbols_by_source_path;
for (const BaseSymbol* sym : symbols_) {
if (ShouldIncludeSymbol(*sym)) {
std::string_view key = sym->SourcePath();
if (key == nullptr) {
key = sym->ObjectPath();
}
std::string_view key = lens_->ParentName(*sym, &owned_strings_);
symbols_by_source_path[key].push_back(sym);
}
}
......@@ -142,17 +170,6 @@ void TreeBuilder::AddFileEntry(const std::string_view source_path,
} else {
file_node->id_path = source_path;
}
if (group_by_component_) {
std::string component;
if (symbols[0]->Component() && *symbols[0]->Component()) {
component = symbols[0]->Component();
} else {
component = kNoComponent;
}
owned_strings_.push_back(component + std::string(1, kComponentSep) +
std::string(file_node->id_path));
file_node->id_path = owned_strings_.back();
}
file_node->short_name_index =
LastSeparatorIndex(file_node->id_path, sep_, kPathSep) + 1;
......@@ -172,7 +189,6 @@ void TreeBuilder::AddFileEntry(const std::string_view source_path,
AttachToParent(symbol_node, file_node);
}
// TODO: Only add if there are unfiltered symbols in this file.
TreeNode* orphan_node = file_node;
while (orphan_node != &root_) {
orphan_node = GetOrMakeParentNode(orphan_node);
......
......@@ -15,12 +15,39 @@
#include "tools/binary_size/libsupersize/caspian/model.h"
namespace caspian {
class BaseLens {
public:
virtual ~BaseLens() = default;
virtual std::string_view ParentName(
const BaseSymbol& symbol,
std::deque<std::string>* owned_strings) = 0;
};
class IdPathLens : public BaseLens {
public:
std::string_view ParentName(const BaseSymbol& symbol,
std::deque<std::string>* owned_strings) override;
};
class ComponentLens : public BaseLens {
public:
std::string_view ParentName(const BaseSymbol& symbol,
std::deque<std::string>* owned_strings) override;
};
class TemplateLens : public BaseLens {
public:
std::string_view ParentName(const BaseSymbol& symbol,
std::deque<std::string>* owned_strings) override;
};
class TreeBuilder {
public:
TreeBuilder(SizeInfo* size_info);
TreeBuilder(DeltaSizeInfo* size_info);
~TreeBuilder();
void Build(bool group_by_component,
void Build(std::unique_ptr<BaseLens> lens,
char separator,
bool method_count_mode,
std::vector<std::function<bool(const BaseSymbol&)>> filters);
Json::Value Open(const char* path);
......@@ -52,7 +79,7 @@ class TreeBuilder {
// in |owned_strings_|.
// Deque is used for stability, to support string_view.
std::deque<std::string> owned_strings_;
bool group_by_component_;
std::unique_ptr<BaseLens> lens_;
bool method_count_mode_;
// The current path separator: '>' if grouping by component, '/' otherwise.
// Note that we split paths on '/' no matter the value of separator, since
......
......@@ -177,11 +177,10 @@ async function buildTree(
const BuildTree = Module.cwrap(
'BuildTree', 'void',
['bool', 'bool', 'string', 'string', 'string', 'number', 'number']);
['bool', 'string', 'string', 'string', 'string', 'number', 'number']);
const start_time = Date.now();
const groupByComponent = groupBy === 'component';
BuildTree(
groupByComponent, methodCountMode, includeRegex, excludeRegex,
methodCountMode, groupBy, includeRegex, excludeRegex,
includeSections, minSymbolSize, flagToFilter);
console.log(
'Constructed tree in ' + (Date.now() - start_time) / 1000.0 +
......
......@@ -116,12 +116,16 @@ if ('serviceWorker' in navigator) {
<legend class="subhead">Group symbols by</legend>
<div class="radio-wrapper">
<input type="radio" id="sourcepath" name="group_by" value="source_path" checked>
<label class="radio-label" for="sourcepath">Source path</label>
<label class="radio-label" for="sourcepath">Nothing</label>
</div>
<div class="radio-wrapper">
<input type="radio" id="component" name="group_by" value="component">
<label class="radio-label" for="component">Component</label>
</div>
<div class="radio-wrapper">
<input type="radio" id="template" name="group_by" value="template">
<label class="radio-label" for="template">Template</label>
</div>
</fieldset>
<fieldset>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment