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

SuperSize: Caspian: Record size contributions by symbol section

Bug: 1011921
Change-Id: I7091f5fa8c42abe4ed779d5ac0d761ea3bf26eef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1876983
Commit-Queue: Jasper Chapman-Black <jaspercb@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709599}
parent af3592a0
......@@ -18,43 +18,26 @@
#include "tools/binary_size/libsupersize/caspian/file_format.h"
#include "tools/binary_size/libsupersize/caspian/model.h"
namespace caspian {
namespace {
caspian::SizeInfo info;
SizeInfo info;
caspian::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, caspian::TreeNode*> parents;
std::unordered_map<std::string_view, TreeNode*> parents;
std::unique_ptr<Json::StreamWriter> writer;
/** Name used by a directory created to hold symbols with no name. */
const char* _NO_NAME = "(No path)";
const char* NO_NAME = "(No path)";
std::string ToJson(const Json::Value& value) {
std::string JsonSerialize(const Json::Value& value) {
std::stringstream s;
writer->write(value, &s);
return s.str();
}
void OpenIntoJson(const caspian::TreeNode* node, Json::Value* out, int depth) {
(*out)["idPath"] = std::string(node->id_path);
(*out)["shortNameIndex"] = node->short_name_index;
// TODO: Put correct information.
(*out)["type"] = "D";
(*out)["size"] = node->size;
(*out)["flags"] = node->flags;
(*out)["childStats"] = Json::Value(Json::objectValue);
if (depth < 0) {
(*out)["children"] = Json::Value(); // null
} else {
(*out)["children"] = Json::Value(Json::arrayValue);
for (unsigned int i = 0; i < node->children.size(); i++) {
OpenIntoJson(node->children[i], &(*out)["children"][i], depth - 1);
}
}
}
std::string_view DirName(std::string_view path, char sep) {
auto end = path.find_last_of(sep);
if (end != std::string_view::npos) {
......@@ -65,7 +48,7 @@ std::string_view DirName(std::string_view path, char sep) {
return "";
}
void AttachToParent(caspian::TreeNode* child, caspian::TreeNode* parent) {
void AttachToParent(TreeNode* child, TreeNode* parent) {
if (child->parent != nullptr) {
std::cerr << "Child " << child->id_path << " already attached to parent "
<< child->parent->id_path << std::endl;
......@@ -77,28 +60,29 @@ void AttachToParent(caspian::TreeNode* child, caspian::TreeNode* parent) {
child->parent = parent;
// Update size information along tree.
caspian::TreeNode* node = child;
TreeNode* node = child;
while (node->parent) {
node->parent->size += child->size;
node->parent->node_stats += child->node_stats;
node = node->parent;
}
}
caspian::TreeNode* GetOrMakeParentNode(caspian::TreeNode* child_node) {
TreeNode* GetOrMakeParentNode(TreeNode* child_node) {
std::string_view parent_path;
if (child_node->id_path.empty()) {
parent_path = _NO_NAME;
parent_path = NO_NAME;
} else {
parent_path = DirName(child_node->id_path, '/');
}
caspian::TreeNode*& parent = parents[parent_path];
TreeNode*& parent = parents[parent_path];
if (parent == nullptr) {
parent = new caspian::TreeNode();
parent = new TreeNode();
parent->id_path = parent_path;
parent->short_name_index = parent_path.find_last_of('/') + 1;
// TODO: Set parent type to directory or component.
parents[parent_path] = parent;
// TODO: Container type might be a component instead of a directory.
parent->containerType = ContainerType::kDirectory;
}
if (child_node->parent != parent) {
AttachToParent(child_node, parent);
......@@ -107,36 +91,40 @@ caspian::TreeNode* GetOrMakeParentNode(caspian::TreeNode* child_node) {
}
void AddFileEntry(const std::string_view source_path,
const std::vector<const caspian::Symbol*>& symbols) {
const std::vector<const Symbol*>& symbols) {
// Creates a single file node with a child for each symbol in that file.
caspian::TreeNode* file_node = new caspian::TreeNode();
TreeNode* file_node = new TreeNode();
file_node->containerType = ContainerType::kFile;
if (source_path.empty()) {
file_node->id_path = _NO_NAME;
file_node->id_path = NO_NAME;
} else {
file_node->id_path = source_path;
}
file_node->short_name_index = source_path.find_last_of('/') + 1;
parents[file_node->id_path] = file_node;
// TODO: Initialize file type, source path, component
// Create symbol nodes.
for (const auto sym : symbols) {
caspian::TreeNode* symbol_node = new caspian::TreeNode();
TreeNode* symbol_node = new TreeNode();
symbol_node->containerType = ContainerType::kSymbol;
symbol_node->id_path = sym->full_name;
symbol_node->size = sym->size;
symbol_node->node_stats =
NodeStats(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.
caspian::TreeNode* orphan_node = file_node;
TreeNode* orphan_node = file_node;
while (orphan_node != &root) {
orphan_node = GetOrMakeParentNode(orphan_node);
}
}
void AddSymbolsAndFileNodes(caspian::SizeInfo* size_info) {
void AddSymbolsAndFileNodes(SizeInfo* size_info) {
// Group symbols by source path.
std::unordered_map<std::string_view, std::vector<const caspian::Symbol*>>
symbols;
std::unordered_map<std::string_view, std::vector<const Symbol*>> symbols;
for (auto& sym : size_info->raw_symbols) {
std::string_view key = sym.source_path;
if (key == nullptr) {
......@@ -153,8 +141,9 @@ void AddSymbolsAndFileNodes(caspian::SizeInfo* size_info) {
extern "C" {
bool LoadSizeFile(const char* compressed, size_t size) {
writer.reset(Json::StreamWriterBuilder().newStreamWriter());
caspian::ParseSizeInfo(compressed, size, &info);
ParseSizeInfo(compressed, size, &info);
// Build tree
root.containerType = ContainerType::kDirectory;
root.id_path = "/";
parents[""] = &root;
AddSymbolsAndFileNodes(&info);
......@@ -168,8 +157,8 @@ const char* Open(const char* path) {
if (node != parents.end()) {
Json::Value v;
OpenIntoJson(node->second, &v, 1);
result = ToJson(v);
node->second->WriteIntoJson(&v, 1);
result = JsonSerialize(v);
return result.c_str();
} else {
std::cerr << "Tried to open nonexistent node with path: " << path
......@@ -178,3 +167,4 @@ const char* Open(const char* path) {
}
}
} // extern "C"
} // namespace caspian
......@@ -4,6 +4,10 @@
#include "tools/binary_size/libsupersize/caspian/model.h"
#include <algorithm>
#include <iostream>
#include <string>
#include "tools/binary_size/libsupersize/caspian/file_format.h"
using namespace caspian;
......@@ -16,3 +20,111 @@ TreeNode::~TreeNode() = default;
SizeInfo::SizeInfo() = default;
SizeInfo::~SizeInfo() = default;
SectionId SizeInfo::ShortSectionName(const char* section_name) {
static std::map<const char*, SectionId> short_section_name_cache;
SectionId& ret = short_section_name_cache[section_name];
if (ret == SectionId::kNone) {
if (!strcmp(section_name, ".text")) {
ret = SectionId::kText;
} else if (!strcmp(section_name, ".dex")) {
ret = SectionId::kDex;
} else if (!strcmp(section_name, ".dex.method")) {
ret = SectionId::kDex;
} else if (!strcmp(section_name, ".other")) {
ret = SectionId::kOther;
} else if (!strcmp(section_name, ".rodata")) {
ret = SectionId::kRoData;
} else if (!strcmp(section_name, ".data")) {
ret = SectionId::kData;
} else if (!strcmp(section_name, ".data.rel.ro")) {
ret = SectionId::kDataRelRo;
} else if (!strcmp(section_name, ".bss")) {
ret = SectionId::kBss;
} else if (!strcmp(section_name, ".bss.rel.ro")) {
ret = SectionId::kBss;
} else if (!strcmp(section_name, ".pak.nontranslated")) {
ret = SectionId::kPakNontranslated;
} else if (!strcmp(section_name, ".pak.translations")) {
ret = SectionId::kPakTranslations;
} else {
std::cerr << "Attributing unrecognized section name to .other: "
<< section_name << std::endl;
ret = SectionId::kOther;
}
}
return ret;
}
void TreeNode::WriteIntoJson(Json::Value* out, int depth) {
(*out)["idPath"] = std::string(this->id_path);
(*out)["shortNameIndex"] = this->short_name_index;
// TODO: Put correct information.
std::string type;
if (containerType != ContainerType::kSymbol) {
type += static_cast<char>(containerType);
}
SectionId biggest_section = this->node_stats.ComputeBiggestSection();
type += static_cast<char>(biggest_section);
(*out)["type"] = type;
(*out)["size"] = this->size;
(*out)["flags"] = this->flags;
this->node_stats.WriteIntoJson(&(*out)["childStats"]);
if (depth < 0 && this->children.size() > 1) {
(*out)["children"] = Json::Value(); // null
} else {
(*out)["children"] = Json::Value(Json::arrayValue);
// Reorder children for output.
// TODO: Support additional compare functions.
auto compare_func = [](const TreeNode* const& l,
const TreeNode* const& r) -> bool {
return l->size > r->size;
};
std::sort(this->children.begin(), this->children.end(), compare_func);
for (unsigned int i = 0; i < this->children.size(); i++) {
this->children[i]->WriteIntoJson(&(*out)["children"][i], depth - 1);
}
}
}
NodeStats::NodeStats() = default;
NodeStats::~NodeStats() = default;
NodeStats::NodeStats(SectionId sectionId,
int32_t count,
int32_t highlight,
int32_t size) {
childStats[sectionId] = {count, highlight, size};
}
void NodeStats::WriteIntoJson(Json::Value* out) const {
(*out) = Json::Value(Json::objectValue);
for (const auto kv : this->childStats) {
const std::string sectionId = std::string(1, static_cast<char>(kv.first));
const Stat stats = kv.second;
(*out)[sectionId] = Json::Value(Json::objectValue);
(*out)[sectionId]["size"] = stats.size;
(*out)[sectionId]["count"] = stats.count;
(*out)[sectionId]["highlight"] = stats.highlight;
}
}
NodeStats& NodeStats::operator+=(const NodeStats& other) {
for (const auto& it : other.childStats) {
childStats[it.first] += it.second;
}
return *this;
}
SectionId NodeStats::ComputeBiggestSection() const {
SectionId ret = SectionId::kNone;
int32_t max = 0;
for (auto& pair : childStats) {
if (pair.second.size > max) {
ret = pair.first;
max = pair.second.size;
}
}
return ret;
}
......@@ -8,6 +8,7 @@
#include <stdlib.h>
#include <deque>
#include <map>
#include <vector>
#include "third_party/jsoncpp/source/include/json/json.h"
......@@ -16,6 +17,29 @@
namespace caspian {
enum class ContainerType : char {
kSymbol = '\0',
kDirectory = 'D',
kComponent = 'C',
kFile = 'F',
kJavaClass = 'J',
};
enum class SectionId : char {
// kNone is unused except for default-initializing in containers
kNone = '\0',
kBss = 'b',
kData = 'd',
kDataRelRo = 'R',
kDex = 'x',
kDexMethod = 'm',
kOther = 'o',
kRoData = 'r',
kText = 't',
kPakNontranslated = 'P',
kPakTranslations = 'p',
};
struct Symbol {
Symbol();
Symbol(const Symbol& other);
......@@ -24,22 +48,69 @@ struct Symbol {
int32_t size = 0;
int32_t flags = 0;
int32_t padding = 0;
const char* full_name = nullptr;
// Pointers into SizeInfo->raw_decompressed;
const char* section_name = nullptr;
const char* full_name = nullptr;
const char* object_path = nullptr;
const char* source_path = nullptr;
const char* component = nullptr;
std::vector<Symbol*>* aliases = nullptr;
};
struct SizeInfo {
SizeInfo();
~SizeInfo();
SizeInfo(const SizeInfo& other) = delete;
SizeInfo& operator=(const SizeInfo& other) = delete;
SectionId ShortSectionName(const char* section_name);
std::vector<caspian::Symbol> raw_symbols;
Json::Value metadata;
// Entries in |raw_symbols| hold pointers to this data.
std::vector<const char*> object_paths;
std::vector<const char*> source_paths;
std::vector<const char*> components;
std::vector<const char*> section_names;
std::vector<char> raw_decompressed;
// A container for each symbol group.
std::deque<std::vector<Symbol*>> alias_groups;
};
struct Stat {
int32_t count = 0;
int32_t highlight = 0;
int32_t size = 0;
void operator+=(const Stat& other) {
count += other.count;
highlight += other.highlight;
size += other.size;
}
};
struct NodeStats {
NodeStats();
~NodeStats();
NodeStats(SectionId section, int32_t count, int32_t highlight, int32_t size);
void WriteIntoJson(Json::Value* out) const;
NodeStats& operator+=(const NodeStats& other);
SectionId ComputeBiggestSection() const;
std::map<SectionId, Stat> childStats;
};
struct TreeNode {
TreeNode();
~TreeNode();
void WriteIntoJson(Json::Value* out, int depth);
std::string_view id_path;
const char* src_path = nullptr;
const char* component = nullptr;
int32_t size = 0;
NodeStats node_stats;
int32_t flags = 0;
int32_t short_name_index = 0;
......@@ -49,30 +120,12 @@ struct TreeNode {
childStats,
*/
std::deque<TreeNode*> children;
ContainerType containerType = ContainerType::kSymbol;
std::vector<TreeNode*> children;
TreeNode* parent = nullptr;
Symbol* symbol = nullptr;
};
struct SizeInfo {
SizeInfo();
~SizeInfo();
SizeInfo(const SizeInfo& other) = delete;
SizeInfo& operator=(const SizeInfo& other) = delete;
std::vector<caspian::Symbol> raw_symbols;
Json::Value metadata;
// Entries in |raw_symbols| hold pointers to this data.
std::vector<const char*> object_paths;
std::vector<const char*> source_paths;
std::vector<const char*> components;
std::vector<const char*> section_names;
std::vector<char> raw_decompressed;
// A container for each symbol group.
std::deque<std::vector<Symbol*>> alias_groups;
};
} // namespace caspian
#endif // TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_MODEL_H_
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