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

SuperSize: Caspian: Add component view mode

Plus some JavaScript scaffolding to avoid re-uncompressing the .size
file on every buildTree() invocation.

Bug: 1011921
Change-Id: I839fb8a8e0ea0b1509620b391a1dd3153a157e9a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1888371
Commit-Queue: Jasper Chapman-Black <jaspercb@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710901}
parent 7df0a99f
......@@ -39,8 +39,8 @@ void LoadSizeFile(const char* compressed, size_t size) {
ParseSizeInfo(compressed, size, &info);
}
void BuildTree() {
builder.reset(new TreeBuilder(&info));
void BuildTree(bool group_by_component) {
builder.reset(new TreeBuilder(&info, group_by_component));
builder->Build();
}
......
......@@ -2,28 +2,61 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "tree_builder.h"
#include "tools/binary_size/libsupersize/caspian/tree_builder.h"
#include <algorithm>
#include <iostream>
#include <map>
namespace caspian {
/** Name used by a directory created to hold symbols with no name. */
namespace {
/** Name used by a directory created to hold symbols with no name. */
constexpr const char* kNoName = "(No path)";
constexpr const char* kNoComponent = "(No component)";
const char kPathSep = '/';
const char kComponentSep = '>';
size_t LastSeparatorIndex(std::string_view str, char sep, char othersep) {
size_t sep_idx = str.find_last_of(sep);
size_t path_idx = str.find_last_of(othersep);
if (sep_idx != std::string_view::npos && path_idx != std::string_view::npos) {
return std::max(sep_idx, path_idx);
} else if (sep_idx == std::string_view::npos) {
return path_idx;
} else {
return sep_idx;
}
}
std::string_view DirName(std::string_view path, char sep, char othersep) {
auto end = LastSeparatorIndex(path, sep, othersep);
if (end != std::string_view::npos) {
std::string_view ret = path;
ret.remove_suffix(path.size() - end);
return ret;
}
return "";
}
} // namespace
TreeBuilder::TreeBuilder(SizeInfo* size_info, bool group_by_component)
: size_info_(size_info),
group_by_component_(group_by_component),
sep_(group_by_component ? kComponentSep : kPathSep) {}
TreeBuilder::TreeBuilder(SizeInfo* size_info) : _size_info(size_info) {}
TreeBuilder::~TreeBuilder() = default;
void TreeBuilder::Build() {
// Initialize tree root.
_root.container_type = ContainerType::kDirectory;
_root.id_path = "/";
_parents[""] = &_root;
root_.container_type = ContainerType::kDirectory;
owned_strings_.emplace_back(1, sep_);
root_.id_path = owned_strings_.back();
_parents[""] = &root_;
// Group symbols by source path.
std::unordered_map<std::string_view, std::vector<const Symbol*>> symbols;
for (auto& sym : _size_info->raw_symbols) {
for (auto& sym : size_info_->raw_symbols) {
std::string_view key = sym.source_path;
if (key == nullptr) {
key = sym.object_path;
......@@ -56,12 +89,26 @@ void TreeBuilder::AddFileEntry(const std::string_view source_path,
// Creates a single file node with a child for each symbol in that file.
TreeNode* file_node = new TreeNode();
file_node->container_type = ContainerType::kFile;
if (source_path.empty()) {
file_node->id_path = kNoName;
} else {
file_node->id_path = source_path;
}
file_node->short_name_index = source_path.find_last_of('/') + 1;
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;
_parents[file_node->id_path] = file_node;
// TODO: Initialize file type, source path, component
......@@ -72,13 +119,13 @@ void TreeBuilder::AddFileEntry(const std::string_view source_path,
symbol_node->id_path = sym->full_name;
symbol_node->size = sym->size;
symbol_node->node_stats = NodeStats(
_size_info->ShortSectionName(sym->section_name), 1, 0, sym->size);
size_info_->ShortSectionName(sym->section_name), 1, 0, sym->size);
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) {
while (orphan_node != &root_) {
orphan_node = GetOrMakeParentNode(orphan_node);
}
......@@ -90,16 +137,16 @@ TreeNode* TreeBuilder::GetOrMakeParentNode(TreeNode* child_node) {
if (child_node->id_path.empty()) {
parent_path = kNoName;
} else {
parent_path = DirName(child_node->id_path, '/');
parent_path = DirName(child_node->id_path, sep_, kPathSep);
}
TreeNode*& parent = _parents[parent_path];
if (parent == nullptr) {
parent = new TreeNode();
parent->id_path = parent_path;
parent->short_name_index = parent_path.find_last_of('/') + 1;
// TODO: Container type might be a component instead of a directory.
parent->container_type = ContainerType::kDirectory;
parent->short_name_index =
LastSeparatorIndex(parent_path, sep_, kPathSep) + 1;
parent->container_type = ContainerTypeFromChild(child_node->id_path);
}
if (child_node->parent != parent) {
AttachToParent(child_node, parent);
......@@ -127,14 +174,18 @@ void TreeBuilder::AttachToParent(TreeNode* child, TreeNode* parent) {
}
}
std::string_view TreeBuilder::DirName(std::string_view path, char sep) {
auto end = path.find_last_of(sep);
if (end != std::string_view::npos) {
std::string_view ret = path;
ret.remove_suffix(path.size() - end);
return ret;
ContainerType TreeBuilder::ContainerTypeFromChild(
std::string_view child_id_path) const {
// When grouping by component, id paths use '>' separators for components and
// '/' separators for the file tree - e.g. Blink>third_party/blink/common...
// We know that Blink is a component because its children have the form
// Blink>third_party rather than Blink/third_party.
size_t idx = LastSeparatorIndex(child_id_path, sep_, kPathSep);
if (idx == std::string_view::npos || child_id_path[idx] == kPathSep) {
return ContainerType::kDirectory;
} else {
return ContainerType::kComponent;
}
return "";
}
void TreeBuilder::JoinDexMethodClasses(TreeNode* node) {
......
......@@ -5,38 +5,56 @@
#ifndef TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_TREE_BUILDER_H_
#define TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_TREE_BUILDER_H_
#include "model.h"
#include <deque>
#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "tools/binary_size/libsupersize/caspian/model.h"
namespace caspian {
class TreeBuilder {
public:
TreeBuilder(SizeInfo* size_info);
TreeBuilder(SizeInfo* size_info, bool group_by_component);
~TreeBuilder();
void Build();
Json::Value Open(const char* path);
private:
std::string_view GetPath(const Symbol* symbol);
void AddFileEntry(const std::string_view source_path,
const std::vector<const Symbol*>& symbols);
TreeNode* GetOrMakeParentNode(TreeNode* child_node);
void AttachToParent(TreeNode* child, TreeNode* parent);
std::string_view DirName(std::string_view path, char sep);
ContainerType ContainerTypeFromChild(std::string_view child_id_path) const;
// Merges dex method symbols into containers based on the class of the dex
// method.
void JoinDexMethodClasses(TreeNode* node);
TreeNode _root;
TreeNode root_;
// TODO: A full hash table might be overkill here - could walk tree to find
// node.
std::unordered_map<std::string_view, TreeNode*> _parents;
SizeInfo* _size_info = nullptr;
SizeInfo* size_info_ = nullptr;
// Contained TreeNode hold lightweight string_views to fields in SizeInfo.
// If grouping by component, this isn't possible: TreeNode id_paths are not
// substrings of SizeInfo-owned strings. In that case, the strings are stored
// in |owned_strings_|.
// Deque is used for stability, to support string_view.
std::deque<std::string> owned_strings_;
bool group_by_component_;
// The current path separator: '>' if grouping by component, '/' otherwise.
// Note that we split paths on '/' no matter the value of separator, since
// when grouping by component, paths look like Component>path/to/file.
char sep_;
}; // TreeBuilder
} // namespace caspian
#endif // TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_TREE_BUILDER_H_
......@@ -141,19 +141,25 @@ function Open(name) {
// Placeholder input name until supplied via setInput()
const fetcher = new DataFetcher('data.size');
let sizeFileLoaded = false;
async function buildTree(
groupBy, filterTest, highlightTest, methodCountMode, onProgress) {
let sizeBuffer = await fetcher.loadSizeBuffer();
let heapBuffer = mallocBuffer(sizeBuffer);
console.log('Passing ' + sizeBuffer.byteLength + ' bytes to WebAssembly');
let LoadSizeFile = Module.cwrap('LoadSizeFile', 'bool', ['number', 'number']);
let start_time = Date.now();
LoadSizeFile(heapBuffer.byteOffset, sizeBuffer.byteLength);
console.log('Loaded size file in ' +
(Date.now() - start_time)/1000.0 + ' seconds');
Module._free(heapBuffer.byteOffset);
if (!sizeFileLoaded) {
let sizeBuffer = await fetcher.loadSizeBuffer();
let heapBuffer = mallocBuffer(sizeBuffer);
console.log('Passing ' + sizeBuffer.byteLength + ' bytes to WebAssembly');
let LoadSizeFile = Module.cwrap(
'LoadSizeFile', 'bool', ['number', 'number']);
let start_time = Date.now();
LoadSizeFile(heapBuffer.byteOffset, sizeBuffer.byteLength);
console.log('Loaded size file in ' +
(Date.now() - start_time)/1000.0 + ' seconds');
Module._free(heapBuffer.byteOffset);
sizeFileLoaded = true;
}
/**
* Creates data to post to the UI thread. Defaults will be used for the root
......@@ -182,9 +188,10 @@ async function buildTree(
return message;
}
let BuildTree = Module.cwrap('BuildTree', 'void', []);
start_time = Date.now();
BuildTree();
let BuildTree = Module.cwrap('BuildTree', 'void', ['bool']);
let start_time = Date.now();
const groupByComponent = groupBy === "component";
BuildTree(groupByComponent);
console.log('Constructed tree in ' +
(Date.now() - start_time)/1000.0 + ' seconds');
......
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