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

SuperSize: Caspian: Display formatted symbol names

Since deriving symbol full_name and template_name is too costly to do
for all dex and native symbols, we derive lazily and cache.

Bug: 1011921
Change-Id: Ic7e530d1fde8202566c7e5872550d7315a3ad772
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1913079
Commit-Queue: Jasper Chapman-Black <jaspercb@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714998}
parent bf9cc894
...@@ -161,7 +161,9 @@ size_t FindReturnValueSpace(std::string_view name, size_t paren_idx) { ...@@ -161,7 +161,9 @@ size_t FindReturnValueSpace(std::string_view name, size_t paren_idx) {
return space_idx; return space_idx;
} }
std::string StripTemplateArgs(std::string name) { std::string StripTemplateArgs(std::string_view name_view) {
// TODO(jaspercb): Could pass in |owned_strings| to avoid this allocation.
std::string name(name_view);
size_t last_right_idx = std::string::npos; size_t last_right_idx = std::string::npos;
while (true) { while (true) {
last_right_idx = name.substr(0, last_right_idx).rfind('>'); last_right_idx = name.substr(0, last_right_idx).rfind('>');
...@@ -293,38 +295,48 @@ size_t FindParameterListParen(std::string_view name) { ...@@ -293,38 +295,48 @@ size_t FindParameterListParen(std::string_view name) {
} }
} }
std::tuple<std::string, std::string, std::string> ParseCpp( std::tuple<std::string_view, std::string_view, std::string_view> ParseCpp(
const std::string& input_name) { std::string_view full_name,
std::string name = input_name; std::deque<std::string>* owned_strings) {
size_t left_paren_idx = FindParameterListParen(input_name); std::string name;
std::string full_name = input_name; std::string_view name_view;
size_t left_paren_idx = FindParameterListParen(full_name);
if (left_paren_idx != std::string::npos && left_paren_idx > 0) { if (left_paren_idx != std::string::npos && left_paren_idx > 0) {
size_t right_paren_idx = name.rfind(')'); size_t right_paren_idx = full_name.rfind(')');
if (right_paren_idx <= left_paren_idx) { if (right_paren_idx <= left_paren_idx) {
std::cerr << "ParseCpp() received bad symbol: " << name << std::endl; std::cerr << "ParseCpp() received bad symbol: " << full_name << std::endl;
exit(1); exit(1);
} }
size_t space_idx = FindReturnValueSpace(name, left_paren_idx); size_t space_idx = FindReturnValueSpace(full_name, left_paren_idx);
std::string name_no_params = std::string name_no_params =
std::string(Slice(name, space_idx + 1, left_paren_idx)); std::string(Slice(full_name, space_idx + 1, left_paren_idx));
// Special case for top-level lambdas. // Special case for top-level lambdas.
if (EndsWith(name_no_params, "}::_FUN")) { if (EndsWith(name_no_params, "}::_FUN")) {
// Don't use |name_no_params| in here since prior _idx will be off if // Don't use |name_no_params| in here since prior _idx will be off if
// there was a return value. // there was a return value.
name = NormalizeTopLevelGccLambda(name, left_paren_idx); owned_strings->push_back(
return ParseCpp(name); NormalizeTopLevelGccLambda(full_name, left_paren_idx));
return ParseCpp(owned_strings->back(), owned_strings);
} else if (EndsWith(name_no_params, "::__invoke") && } else if (EndsWith(name_no_params, "::__invoke") &&
name_no_params.find('$') != std::string::npos) { name_no_params.find('$') != std::string::npos) {
name = NormalizeTopLevelClangLambda(name, left_paren_idx); owned_strings->push_back(
return ParseCpp(name); NormalizeTopLevelClangLambda(full_name, left_paren_idx));
return ParseCpp(owned_strings->back(), owned_strings);
} }
full_name = name.substr(space_idx + 1); name = name_no_params + std::string(full_name.substr(right_paren_idx + 1));
name = name_no_params + name.substr(right_paren_idx + 1); name_view = name;
full_name = full_name.substr(space_idx + 1);
} else {
name_view = full_name;
} }
std::string template_name = name; owned_strings->emplace_back(name_view);
name = StripTemplateArgs(name); std::string_view template_name = owned_strings->back();
return std::make_tuple(full_name, template_name, name);
owned_strings->push_back(StripTemplateArgs(name_view));
std::string_view returned_name = owned_strings->back();
return std::make_tuple(full_name, template_name, returned_name);
} }
} // namespace caspian } // namespace caspian
...@@ -31,8 +31,9 @@ std::tuple<std::string_view, std::string_view, std::string_view> ParseJava( ...@@ -31,8 +31,9 @@ std::tuple<std::string_view, std::string_view, std::string_view> ParseJava(
// * name without return type (symbol.full_name), // * name without return type (symbol.full_name),
// * full_name without params (symbol.template_name), // * full_name without params (symbol.template_name),
// * full_name without params and template args (symbol.name) // * full_name without params and template args (symbol.name)
std::tuple<std::string, std::string, std::string> ParseCpp( std::tuple<std::string_view, std::string_view, std::string_view> ParseCpp(
const std::string& name); std::string_view name,
std::deque<std::string>* owned_strings);
// Returns the last index of |target_char| that is not within ()s nor <>s. // Returns the last index of |target_char| that is not within ()s nor <>s.
size_t FindLastCharOutsideOfBrackets(std::string_view name, size_t FindLastCharOutsideOfBrackets(std::string_view name,
...@@ -53,7 +54,7 @@ std::string NormalizeTopLevelClangLambda(std::string_view name, ...@@ -53,7 +54,7 @@ std::string NormalizeTopLevelClangLambda(std::string_view name,
size_t left_paren_idx); size_t left_paren_idx);
// Strips the contents of <>, leaving empty <>s to denote that it's a template. // Strips the contents of <>, leaving empty <>s to denote that it's a template.
std::string StripTemplateArgs(std::string name); std::string StripTemplateArgs(std::string_view name);
} // namespace caspian } // namespace caspian
#endif // TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_FUNCTION_SIGNATURE_H_ #endif // TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_FUNCTION_SIGNATURE_H_
...@@ -91,7 +91,7 @@ TEST(AnalyzeTest, NormalizeTopLevelClangLambda) { ...@@ -91,7 +91,7 @@ TEST(AnalyzeTest, NormalizeTopLevelClangLambda) {
} }
TEST(AnalyzeTest, ParseJavaFunctionSignature) { TEST(AnalyzeTest, ParseJavaFunctionSignature) {
::std::deque<std::string> owned_strings; std::deque<std::string> owned_strings;
// Java method with no args // Java method with no args
auto do_test = [&owned_strings](std::string sig, std::string exp_full_name, auto do_test = [&owned_strings](std::string sig, std::string exp_full_name,
std::string exp_template_name, std::string exp_template_name,
...@@ -127,9 +127,11 @@ TEST(AnalyzeTest, ParseJavaFunctionSignature) { ...@@ -127,9 +127,11 @@ TEST(AnalyzeTest, ParseJavaFunctionSignature) {
} }
TEST(AnalyzeTest, ParseFunctionSignature) { TEST(AnalyzeTest, ParseFunctionSignature) {
auto check = [](std::string ret_part, std::string name_part, std::deque<std::string> owned_strings;
std::string params_part, std::string after_part = "", auto check = [&owned_strings](std::string ret_part, std::string name_part,
std::string name_without_templates = "") { std::string params_part,
std::string after_part = "",
std::string name_without_templates = "") {
if (name_without_templates.empty()) { if (name_without_templates.empty()) {
name_without_templates = name_part; name_without_templates = name_part;
// Heuristic to drop templates: std::vector<int> -> std::vector<> // Heuristic to drop templates: std::vector<int> -> std::vector<>
...@@ -137,7 +139,7 @@ TEST(AnalyzeTest, ParseFunctionSignature) { ...@@ -137,7 +139,7 @@ TEST(AnalyzeTest, ParseFunctionSignature) {
name_without_templates += after_part; name_without_templates += after_part;
} }
std::string signature = name_part + params_part + after_part; std::string signature = name_part + params_part + after_part;
auto result = caspian::ParseCpp(signature); auto result = caspian::ParseCpp(signature, &owned_strings);
EXPECT_EQ(name_without_templates, std::get<2>(result)); EXPECT_EQ(name_without_templates, std::get<2>(result));
EXPECT_EQ(name_part + after_part, std::get<1>(result)); EXPECT_EQ(name_part + after_part, std::get<1>(result));
EXPECT_EQ(name_part + params_part + after_part, std::get<0>(result)); EXPECT_EQ(name_part + params_part + after_part, std::get<0>(result));
...@@ -145,7 +147,7 @@ TEST(AnalyzeTest, ParseFunctionSignature) { ...@@ -145,7 +147,7 @@ TEST(AnalyzeTest, ParseFunctionSignature) {
if (!ret_part.empty()) { if (!ret_part.empty()) {
// Parse should be unchanged when we prepend |ret_part| // Parse should be unchanged when we prepend |ret_part|
signature = ret_part + name_part + params_part + after_part; signature = ret_part + name_part + params_part + after_part;
result = caspian::ParseCpp(signature); result = caspian::ParseCpp(signature, &owned_strings);
EXPECT_EQ(name_without_templates, std::get<2>(result)); EXPECT_EQ(name_without_templates, std::get<2>(result));
EXPECT_EQ(name_part + after_part, std::get<1>(result)); EXPECT_EQ(name_part + after_part, std::get<1>(result));
EXPECT_EQ(name_part + params_part + after_part, std::get<0>(result)); EXPECT_EQ(name_part + params_part + after_part, std::get<0>(result));
...@@ -214,7 +216,7 @@ TEST(AnalyzeTest, ParseFunctionSignature) { ...@@ -214,7 +216,7 @@ TEST(AnalyzeTest, ParseFunctionSignature) {
// See function_signature_test.py for full comment // See function_signature_test.py for full comment
std::string sig = std::string sig =
"(anonymous namespace)::Foo::Baz() const::GLSLFP::onData(Foo, Bar)"; "(anonymous namespace)::Foo::Baz() const::GLSLFP::onData(Foo, Bar)";
auto ret = caspian::ParseCpp(sig); auto ret = caspian::ParseCpp(sig, &owned_strings);
EXPECT_EQ("(anonymous namespace)::Foo::Baz", std::get<2>(ret)); EXPECT_EQ("(anonymous namespace)::Foo::Baz", std::get<2>(ret));
EXPECT_EQ("(anonymous namespace)::Foo::Baz", std::get<1>(ret)); EXPECT_EQ("(anonymous namespace)::Foo::Baz", std::get<1>(ret));
EXPECT_EQ(sig, std::get<0>(ret)); EXPECT_EQ(sig, std::get<0>(ret));
...@@ -222,13 +224,13 @@ TEST(AnalyzeTest, ParseFunctionSignature) { ...@@ -222,13 +224,13 @@ TEST(AnalyzeTest, ParseFunctionSignature) {
// Top-level lambda. // Top-level lambda.
// Note: Inline lambdas do not seem to be broken into their own symbols. // Note: Inline lambdas do not seem to be broken into their own symbols.
sig = "cc::{lambda(cc::PaintOp*)#63}::_FUN(cc::PaintOp*)"; sig = "cc::{lambda(cc::PaintOp*)#63}::_FUN(cc::PaintOp*)";
ret = caspian::ParseCpp(sig); ret = caspian::ParseCpp(sig, &owned_strings);
EXPECT_EQ("cc::$lambda#63", std::get<2>(ret)); EXPECT_EQ("cc::$lambda#63", std::get<2>(ret));
EXPECT_EQ("cc::$lambda#63", std::get<1>(ret)); EXPECT_EQ("cc::$lambda#63", std::get<1>(ret));
EXPECT_EQ("cc::$lambda#63(cc::PaintOp*)", std::get<0>(ret)); EXPECT_EQ("cc::$lambda#63(cc::PaintOp*)", std::get<0>(ret));
sig = "cc::$_63::__invoke(cc::PaintOp*)"; sig = "cc::$_63::__invoke(cc::PaintOp*)";
ret = caspian::ParseCpp(sig); ret = caspian::ParseCpp(sig, &owned_strings);
EXPECT_EQ("cc::$lambda#63", std::get<2>(ret)); EXPECT_EQ("cc::$lambda#63", std::get<2>(ret));
EXPECT_EQ("cc::$lambda#63", std::get<1>(ret)); EXPECT_EQ("cc::$lambda#63", std::get<1>(ret));
EXPECT_EQ("cc::$lambda#63(cc::PaintOp*)", std::get<0>(ret)); EXPECT_EQ("cc::$lambda#63(cc::PaintOp*)", std::get<0>(ret));
......
...@@ -5,13 +5,16 @@ ...@@ -5,13 +5,16 @@
#include "tools/binary_size/libsupersize/caspian/model.h" #include "tools/binary_size/libsupersize/caspian/model.h"
#include <algorithm> #include <algorithm>
#include <deque>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <list> #include <list>
#include <string> #include <string>
#include <tuple>
#include <unordered_map> #include <unordered_map>
#include "tools/binary_size/libsupersize/caspian/file_format.h" #include "tools/binary_size/libsupersize/caspian/file_format.h"
#include "tools/binary_size/libsupersize/caspian/function_signature.h"
namespace { namespace {
struct SymbolMatchIndex { struct SymbolMatchIndex {
...@@ -82,6 +85,48 @@ Symbol Symbol::DiffSymbolFrom(const Symbol* before_sym, ...@@ -82,6 +85,48 @@ Symbol Symbol::DiffSymbolFrom(const Symbol* before_sym,
return ret; return ret;
} }
void Symbol::DeriveNames() const {
if (name.data() != nullptr) {
return;
}
if (IsPak()) {
// full_name: "about_ui_resources.grdp: IDR_ABOUT_UI_CREDITS_HTML".
size_t space_idx = full_name.rfind(' ');
template_name = full_name.substr(space_idx + 1);
name = template_name;
} else if ((!full_name.empty() && full_name[0] == '*') || IsOverhead() ||
IsOther()) {
template_name = full_name;
name = full_name;
} else if (IsDex()) {
std::tuple<std::string_view, std::string_view, std::string_view>
parsed_names = ParseJava(full_name, &size_info->owned_strings);
template_name = std::get<1>(parsed_names);
name = std::get<2>(parsed_names);
} else if (IsStringLiteral()) {
template_name = full_name;
name = full_name;
} else if (IsNative()) {
std::tuple<std::string_view, std::string_view, std::string_view>
parsed_names = ParseCpp(full_name, &size_info->owned_strings);
template_name = std::get<1>(parsed_names);
name = std::get<2>(parsed_names);
} else {
template_name = full_name;
name = full_name;
}
}
std::string_view Symbol::TemplateName() const {
DeriveNames();
return template_name;
}
std::string_view Symbol::Name() const {
DeriveNames();
return name;
}
TreeNode::TreeNode() = default; TreeNode::TreeNode() = default;
TreeNode::~TreeNode() { TreeNode::~TreeNode() {
// TODO(jaspercb): Could use custom allocator to delete all nodes in one go. // TODO(jaspercb): Could use custom allocator to delete all nodes in one go.
...@@ -201,13 +246,13 @@ DiffSizeInfo::DiffSizeInfo(SizeInfo* before, SizeInfo* after) ...@@ -201,13 +246,13 @@ DiffSizeInfo::DiffSizeInfo(SizeInfo* before, SizeInfo* after)
// (hopefully <2k objects) needs to consider the derived full_name. Should // (hopefully <2k objects) needs to consider the derived full_name. Should
// do this lazily for efficiency - name derivation is costly. // do this lazily for efficiency - name derivation is costly.
std::vector<const caspian::Symbol*> unmatched_before; std::vector<const Symbol*> unmatched_before;
for (const caspian::Symbol& sym : before->raw_symbols) { for (const Symbol& sym : before->raw_symbols) {
unmatched_before.push_back(&sym); unmatched_before.push_back(&sym);
} }
std::vector<const caspian::Symbol*> unmatched_after; std::vector<const Symbol*> unmatched_after;
for (const caspian::Symbol& sym : after->raw_symbols) { for (const Symbol& sym : after->raw_symbols) {
unmatched_after.push_back(&sym); unmatched_after.push_back(&sym);
} }
...@@ -219,18 +264,27 @@ DiffSizeInfo::DiffSizeInfo(SizeInfo* before, SizeInfo* after) ...@@ -219,18 +264,27 @@ DiffSizeInfo::DiffSizeInfo(SizeInfo* before, SizeInfo* after)
} }
// Add removals or deletions for any unmatched symbols. // Add removals or deletions for any unmatched symbols.
for (const caspian::Symbol* after_sym : unmatched_after) { for (const Symbol* after_sym : unmatched_after) {
raw_symbols.push_back(Symbol::DiffSymbolFrom(nullptr, after_sym)); raw_symbols.push_back(Symbol::DiffSymbolFrom(nullptr, after_sym));
} }
for (const caspian::Symbol* before_sym : unmatched_before) { for (const Symbol* before_sym : unmatched_before) {
raw_symbols.push_back(Symbol::DiffSymbolFrom(before_sym, nullptr)); raw_symbols.push_back(Symbol::DiffSymbolFrom(before_sym, nullptr));
} }
for (Symbol& sym : raw_symbols) {
sym.size_info = this;
}
} }
DiffSizeInfo::~DiffSizeInfo() {} DiffSizeInfo::~DiffSizeInfo() {}
void TreeNode::WriteIntoJson(Json::Value* out, int depth) { void TreeNode::WriteIntoJson(Json::Value* out, int depth) {
(*out)["idPath"] = std::string(this->id_path); if (symbol) {
(*out)["idPath"] = std::string(symbol->IsDex() ? symbol->full_name
: symbol->TemplateName());
} else {
(*out)["idPath"] = std::string(this->id_path);
}
(*out)["shortNameIndex"] = this->short_name_index; (*out)["shortNameIndex"] = this->short_name_index;
std::string type; std::string type;
if (container_type != ContainerType::kSymbol) { if (container_type != ContainerType::kSymbol) {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <deque> #include <deque>
#include <map> #include <map>
#include <string>
#include <string_view> #include <string_view>
#include <vector> #include <vector>
...@@ -41,6 +42,8 @@ enum class SectionId : char { ...@@ -41,6 +42,8 @@ enum class SectionId : char {
kPakTranslations = 'p', kPakTranslations = 'p',
}; };
struct BaseSizeInfo;
struct Symbol { struct Symbol {
Symbol(); Symbol();
Symbol(const Symbol& other); Symbol(const Symbol& other);
...@@ -68,10 +71,17 @@ struct Symbol { ...@@ -68,10 +71,17 @@ struct Symbol {
sectionId == SectionId::kText || sectionId == SectionId::kRoData); sectionId == SectionId::kText || sectionId == SectionId::kRoData);
} }
bool IsStringLiteral() const { return full_name.substr(0, 1) == "\""; }
int32_t SizeWithoutPadding() const { return size - padding; } int32_t SizeWithoutPadding() const { return size - padding; }
int32_t EndAddress() const { return address + SizeWithoutPadding(); } int32_t EndAddress() const { return address + SizeWithoutPadding(); }
// Derived from |full_name|. Generated lazily and cached.
void DeriveNames() const;
std::string_view TemplateName() const;
std::string_view Name() const;
int32_t address = 0; int32_t address = 0;
int32_t size = 0; int32_t size = 0;
int32_t flags = 0; int32_t flags = 0;
...@@ -85,6 +95,15 @@ struct Symbol { ...@@ -85,6 +95,15 @@ struct Symbol {
const char* source_path = nullptr; const char* source_path = nullptr;
const char* component = nullptr; const char* component = nullptr;
std::vector<Symbol*>* aliases = nullptr; std::vector<Symbol*>* aliases = nullptr;
// The SizeInfo the symbol was constructed from. Primarily used for
// allocating commonly-reused strings in a context where they won't outlive
// the symbol.
BaseSizeInfo* size_info = nullptr;
private:
mutable std::string_view template_name;
mutable std::string_view name;
}; };
std::ostream& operator<<(std::ostream& os, const Symbol& sym); std::ostream& operator<<(std::ostream& os, const Symbol& sym);
...@@ -94,6 +113,7 @@ struct BaseSizeInfo { ...@@ -94,6 +113,7 @@ struct BaseSizeInfo {
~BaseSizeInfo(); ~BaseSizeInfo();
std::vector<caspian::Symbol> raw_symbols; std::vector<caspian::Symbol> raw_symbols;
Json::Value metadata; Json::Value metadata;
std::deque<std::string> owned_strings;
}; };
struct SizeInfo : BaseSizeInfo { struct SizeInfo : BaseSizeInfo {
...@@ -158,17 +178,11 @@ struct TreeNode { ...@@ -158,17 +178,11 @@ struct TreeNode {
int32_t flags = 0; int32_t flags = 0;
int32_t short_name_index = 0; int32_t short_name_index = 0;
/*
type,
numAliases,
childStats,
*/
ContainerType container_type = ContainerType::kSymbol; ContainerType container_type = ContainerType::kSymbol;
std::vector<TreeNode*> children; std::vector<TreeNode*> children;
TreeNode* parent = nullptr; TreeNode* parent = nullptr;
Symbol* symbol = nullptr; const Symbol* symbol = nullptr;
}; };
} // namespace caspian } // namespace caspian
......
...@@ -125,6 +125,7 @@ void TreeBuilder::AddFileEntry(const std::string_view source_path, ...@@ -125,6 +125,7 @@ void TreeBuilder::AddFileEntry(const std::string_view source_path,
symbol_node->id_path = sym->full_name; symbol_node->id_path = sym->full_name;
symbol_node->size = sym->pss; symbol_node->size = sym->pss;
symbol_node->node_stats = NodeStats(sym->sectionId, 1, symbol_node->size); symbol_node->node_stats = NodeStats(sym->sectionId, 1, symbol_node->size);
symbol_node->symbol = sym;
AttachToParent(symbol_node, file_node); AttachToParent(symbol_node, file_node);
} }
......
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