Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,6 @@ perfetto_cc_library(
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_json",
":src_trace_processor_util_protozero_to_text",
":src_trace_processor_util_simple_json_parser",
":src_trace_processor_util_simple_json_serializer",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_sql_bundle",
Expand Down Expand Up @@ -689,6 +688,7 @@ perfetto_cc_library(
":src_trace_processor_plugins_wattson_gen_tpu_curves",
":src_trace_processor_trace_summary_gen_cc_trace_summary_descriptor",
":src_trace_processor_util_build_id",
":src_trace_processor_util_simple_json_parser",
] + PERFETTO_CONFIG.deps.sqlite +
PERFETTO_CONFIG.deps.sqlite_ext_percentile +
PERFETTO_CONFIG.deps.zlib +
Expand Down Expand Up @@ -884,7 +884,6 @@ perfetto_cc_library(
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_json",
":src_trace_processor_util_protozero_to_text",
":src_trace_processor_util_simple_json_parser",
":src_trace_processor_util_simple_json_serializer",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_sql_bundle",
Expand Down Expand Up @@ -1015,6 +1014,7 @@ perfetto_cc_library(
":src_trace_processor_trace_summary_gen_cc_trace_summary_descriptor",
":src_trace_processor_util_build_id",
":src_trace_processor_util_deobfuscation_deobfuscator",
":src_trace_processor_util_simple_json_parser",
":src_trace_processor_util_symbolizer_symbolize_database",
":src_trace_processor_util_symbolizer_symbolizer",
":src_traceconv_gen_cc_android_extension_descriptor",
Expand Down Expand Up @@ -5708,6 +5708,7 @@ perfetto_cc_library(
":protos_perfetto_trace_track_event_zero",
":protos_perfetto_trace_translation_zero",
":protozero",
":src_trace_processor_util_simple_json_parser",
],
linkstatic = True,
)
Expand Down Expand Up @@ -6051,12 +6052,20 @@ perfetto_filegroup(
)

# GN target: //src/trace_processor/util:simple_json_parser
perfetto_filegroup(
perfetto_cc_library(
name = "src_trace_processor_util_simple_json_parser",
srcs = [
":src_trace_processor_util_json_parser",
"src/trace_processor/util/simple_json_parser.cc",
],
hdrs = [
":include_perfetto_base_base",
":include_perfetto_ext_base_base",
":include_perfetto_public_abi_base",
":include_perfetto_public_base",
"src/trace_processor/util/simple_json_parser.h",
],
linkstatic = True,
)

# GN target: //src/trace_processor/util:simple_json_serializer
Expand Down Expand Up @@ -11611,7 +11620,6 @@ perfetto_cc_library(
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_json",
":src_trace_processor_util_protozero_to_text",
":src_trace_processor_util_simple_json_parser",
":src_trace_processor_util_simple_json_serializer",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_sql_bundle",
Expand Down Expand Up @@ -11733,6 +11741,7 @@ perfetto_cc_library(
":src_trace_processor_plugins_wattson_gen_tpu_curves",
":src_trace_processor_trace_summary_gen_cc_trace_summary_descriptor",
":src_trace_processor_util_build_id",
":src_trace_processor_util_simple_json_parser",
] + PERFETTO_CONFIG.deps.sqlite +
PERFETTO_CONFIG.deps.sqlite_ext_percentile +
PERFETTO_CONFIG.deps.zlib +
Expand Down Expand Up @@ -11949,7 +11958,6 @@ perfetto_cc_binary(
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_json",
":src_trace_processor_util_protozero_to_text",
":src_trace_processor_util_simple_json_parser",
":src_trace_processor_util_simple_json_serializer",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_sql_bundle",
Expand Down Expand Up @@ -12062,6 +12070,7 @@ perfetto_cc_binary(
":src_trace_processor_trace_summary_gen_cc_trace_summary_descriptor",
":src_trace_processor_util_build_id",
":src_trace_processor_util_deobfuscation_deobfuscator",
":src_trace_processor_util_simple_json_parser",
":src_trace_processor_util_symbolizer_symbolize_database",
":src_trace_processor_util_symbolizer_symbolizer",
":src_traceconv_gen_cc_android_extension_descriptor",
Expand Down
16 changes: 16 additions & 0 deletions protos/perfetto/trace/profiling/deobfuscation.proto
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ message ObfuscatedClass {
// fields.
repeated ObfuscatedMember obfuscated_members = 3;
repeated ObfuscatedMember obfuscated_methods = 4;

// Information about a single class merged by optimization into this
// obfuscated class.
message MergedClass {
optional string name = 1;
optional int32 class_id = 2;
optional MergedClasses merged_classes = 3;
}

// Set of classes merged into this obfuscated class by optimization.
message MergedClasses {
optional string class_id_field_name = 1;
repeated MergedClass merged_classes = 2;
}

optional MergedClasses merged_classes = 5;
}

message DeobfuscationMapping {
Expand Down
4 changes: 2 additions & 2 deletions src/trace_processor/util/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,8 @@ source_set("json_parser") {
]
}

# Header-only JSON streaming parser. No external dependencies.
source_set("simple_json_parser") {
# JSON streaming parser.
perfetto_component("simple_json_parser") {
sources = [
"simple_json_parser.cc",
"simple_json_parser.h",
Expand Down
3 changes: 3 additions & 0 deletions src/trace_processor/util/deobfuscation/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ perfetto_component("deobfuscator") {
"deobfuscator.h",
]
deps = [
"..:simple_json_parser",
"../../../../gn:default_deps",
"../../../../include/perfetto/ext/base:base",
"../../../../protos/perfetto/trace:zero",
Expand All @@ -36,6 +37,8 @@ perfetto_unittest_source_set("unittests") {
":deobfuscator",
"../../../../gn:default_deps",
"../../../../gn:gtest_and_gmock",
"../../../../protos/perfetto/trace:zero",
"../../../../protos/perfetto/trace/profiling:zero",
"../../../base",
"../../../base:test_support",
]
Expand Down
163 changes: 162 additions & 1 deletion src/trace_processor/util/deobfuscation/deobfuscator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

#include "src/trace_processor/util/deobfuscation/deobfuscator.h"
#include "src/trace_processor/util/simple_json_parser.h"

#include <stdlib.h>

Expand All @@ -25,6 +26,7 @@
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/string_utils.h"

#include "perfetto/protozero/scattered_heap_buffer.h"
#include "protos/perfetto/trace/profiling/deobfuscation.pbzero.h"
Expand All @@ -35,6 +37,18 @@ namespace perfetto {
namespace profiling {
namespace {

using SimpleJsonParser = perfetto::trace_processor::json::SimpleJsonParser;
using FieldResult = perfetto::trace_processor::json::FieldResult;

// Json keys used to parse merged classes.
constexpr std::string_view kMergedClassesId =
"com.android.tools.r8.mergedClasses";
constexpr std::string_view kIdKey = "id";
constexpr std::string_view kClassNameKey = "name";
constexpr std::string_view kClassIdKey = "class_id";
constexpr std::string_view kClassIdFieldKey = "class_id_field";
constexpr std::string_view kMergedClassesKey = "merged_classes";

struct ProguardClass {
std::string obfuscated_name;
std::string deobfuscated_name;
Expand Down Expand Up @@ -78,6 +92,107 @@ std::optional<ProguardClass> ParseClass(std::string line) {
std::move(deobfuscated_name)};
}

// Forward declaration.
base::Status ParseMergedClass(SimpleJsonParser& parser,
ObfuscatedClass::MergedClass& out);

// Parses common fields for MergedClasses (class_id_field and merged_classes).
FieldResult ParseMergedClassesField(SimpleJsonParser& parser,
std::string_view key,
ObfuscatedClass::MergedClasses& mcs) {
if (key == kClassIdFieldKey) {
if (auto val = parser.GetString()) {
mcs.class_id_field_name = std::string(*val);
} else {
return base::Status("Expected class id field name.");
}
return FieldResult::Handled{};
}
if (key == kMergedClassesKey) {
if (!parser.IsArray()) {
return base::Status("Expected array for merged_classes.");
}
base::Status array_status =
parser.ForEachArrayElement([&]() -> base::Status {
ObfuscatedClass::MergedClass mc;
RETURN_IF_ERROR(ParseMergedClass(parser, mc));
mcs.merged_classes.push_back(std::move(mc));
return base::OkStatus();
});
return FieldResult(array_status);
}
return FieldResult::Skip{};
}

// Parses a single MergedClass entry from R8 mergedClasses JSON.
base::Status ParseMergedClass(SimpleJsonParser& parser,
ObfuscatedClass::MergedClass& out) {
return parser.ForEachField([&](std::string_view key) -> FieldResult {
if (key == kClassNameKey) {
if (auto val = parser.GetString()) {
out.name = std::string(*val);
} else {
return base::Status("Expected class name.");
}
return FieldResult::Handled{};
}
if (key == kClassIdKey) {
if (auto val_str = parser.GetString()) {
if (auto parsed_val = base::StringViewToInt32(*val_str)) {
out.class_id = *parsed_val;
} else {
return base::Status("Invalid class id format in string.");
}
} else if (auto val_int = parser.GetInt64();
val_int.has_value() &&
val_int.value() >= std::numeric_limits<int32_t>::min() &&
val_int.value() <= std::numeric_limits<int32_t>::max()) {
out.class_id = static_cast<int32_t>(*val_int);
} else {
return base::Status("Expected class id.");
}
return FieldResult::Handled{};
}
FieldResult res =
ParseMergedClassesField(parser, key, out.nested_merged_classes);
if (!res.handled) {
PERFETTO_DLOG("Unknown field in merged class JSON: %.*s",
static_cast<int>(key.size()), key.data());
}
return res;
});
}

// Parses R8 `com.android.tools.r8.mergedClasses` JSON comment string in
// Proguard mapping.
base::Status ParseMergedClassesComment(std::string_view json_str,
ObfuscatedClass& target_class) {
SimpleJsonParser parser(json_str);
RETURN_IF_ERROR(parser.Parse());

bool is_merged_classes_id = false;
ObfuscatedClass::MergedClasses mcs;

base::Status s =
parser.ForEachField([&](std::string_view key) -> FieldResult {
if (key == kIdKey) {
if (auto val = parser.GetString()) {
if (*val == kMergedClassesId) {
is_merged_classes_id = true;
}
}
return FieldResult::Handled{};
}
return ParseMergedClassesField(parser, key, mcs);
});
RETURN_IF_ERROR(s);

if (is_merged_classes_id) {
*target_class.mutable_merged_classes() = std::move(mcs);
}
return base::OkStatus();
}

enum class ProguardMemberType {
kField,
kMethod,
Expand Down Expand Up @@ -332,9 +447,25 @@ std::map<std::string, std::string> ObfuscatedClass::deobfuscated_methods()
// file format we are parsing.
base::Status ProguardParser::AddLine(std::string line) {
auto first_ch_pos = line.find_first_not_of(" \t");
if (first_ch_pos == std::string::npos || line[first_ch_pos] == '#')
if (first_ch_pos == std::string::npos)
return base::Status();

if (line[first_ch_pos] == '#') {
if (current_class_ == nullptr) {
return base::Status();
}
size_t json_start = line.find('{');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be other metadata with this prefix # { .
So, trying to parse this json can be costly. What about a early substring check for the kMergedClassesId?

if (json_start != std::string::npos) {
std::string_view json_sv = std::string_view(line).substr(json_start);
base::Status s = ParseMergedClassesComment(json_sv, *current_class_);
if (!s.ok()) {
PERFETTO_DLOG("Failed to parse merged classes comment: %s\non line %s",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, push a import_logs like RecordAnalysisLog instead.

s.message().c_str(), line.c_str());
}
}
return base::Status();
}

bool is_member = line[0] == ' ';
if (is_member && !current_class_) {
return base::Status(
Expand Down Expand Up @@ -405,6 +536,35 @@ bool ProguardParser::AddLines(std::string contents) {
return true;
}

static void SerializeMergedClasses(
const profiling::ObfuscatedClass::MergedClasses& src,
perfetto::protos::pbzero::ObfuscatedClass::MergedClasses* dest) {
if (!src.class_id_field_name.empty()) {
dest->set_class_id_field_name(src.class_id_field_name);
}
for (const auto& mc : src.merged_classes) {
auto* dest_mc = dest->add_merged_classes();
if (!mc.name.empty()) {
dest_mc->set_name(mc.name);
}
if (mc.class_id.has_value()) {
dest_mc->set_class_id(*mc.class_id);
}
if (!mc.nested_merged_classes.merged_classes.empty()) {
SerializeMergedClasses(mc.nested_merged_classes,
dest_mc->set_merged_classes());
}
}
}

static void SerializeTopLevelMergedClasses(
const profiling::ObfuscatedClass::MergedClasses& src,
perfetto::protos::pbzero::ObfuscatedClass* dest) {
if (!src.merged_classes.empty()) {
SerializeMergedClasses(src, dest->set_merged_classes());
}
}

void MakeDeobfuscationPackets(
const std::string& package_name,
const std::map<std::string, profiling::ObfuscatedClass>& mapping,
Expand Down Expand Up @@ -447,6 +607,7 @@ void MakeDeobfuscationPackets(
proto_member->set_source_line_end(*method.source_line_end);
}
}
SerializeTopLevelMergedClasses(cls.merged_classes(), proto_class);
}
callback(trace.SerializeAsString());
}
Expand Down
21 changes: 21 additions & 0 deletions src/trace_processor/util/deobfuscation/deobfuscator.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ struct MethodMapping {

class ObfuscatedClass {
public:
struct MergedClass;

// Represents R8 merged classes mapping information for an obfuscated class.
struct MergedClasses {
std::string class_id_field_name;
std::vector<MergedClass> merged_classes;
};

// Represents an individual class merged by R8 into an obfuscated target
// class.
struct MergedClass {
std::string name;
std::optional<int32_t> class_id;
MergedClasses nested_merged_classes;
};

explicit ObfuscatedClass(std::string d) : deobfuscated_name_(std::move(d)) {}

const std::string& deobfuscated_name() const { return deobfuscated_name_; }
Expand Down Expand Up @@ -73,10 +89,15 @@ class ObfuscatedClass {
return method_mappings_;
}

const MergedClasses& merged_classes() const { return merged_classes_; }

MergedClasses* mutable_merged_classes() { return &merged_classes_; }

private:
std::string deobfuscated_name_;
std::map<std::string, std::string> deobfuscated_fields_;
std::vector<MethodMapping> method_mappings_;
MergedClasses merged_classes_;
};

class ProguardParser {
Expand Down
Loading
Loading