Merge "Add parser for proguard maps."
diff --git a/Android.bp b/Android.bp
index 3cfd830..469fec7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -832,6 +832,11 @@
name: "perfetto_include_perfetto_ext_tracing_ipc_ipc",
}
+// GN: //include/perfetto/profiling:deobfuscator
+filegroup {
+ name: "perfetto_include_perfetto_profiling_deobfuscator",
+}
+
// GN: //include/perfetto/profiling:normalize
filegroup {
name: "perfetto_include_perfetto_profiling_normalize",
@@ -4272,6 +4277,14 @@
],
}
+// GN: //src/profiling:deobfuscator
+filegroup {
+ name: "perfetto_src_profiling_deobfuscator",
+ srcs: [
+ "src/profiling/deobfuscator.cc",
+ ],
+}
+
// GN: //src/profiling/memory:client
filegroup {
name: "perfetto_src_profiling_memory_client",
@@ -4368,6 +4381,14 @@
],
}
+// GN: //src/profiling:unittests
+filegroup {
+ name: "perfetto_src_profiling_unittests",
+ srcs: [
+ "src/profiling/deobfuscator_unittest.cc",
+ ],
+}
+
// GN: //src/protozero/protoc_plugin:cppgen_plugin
cc_binary_host {
name: "perfetto_src_protozero_protoc_plugin_cppgen_plugin",
@@ -5376,6 +5397,7 @@
":perfetto_include_perfetto_ext_traced_traced",
":perfetto_include_perfetto_ext_tracing_core_core",
":perfetto_include_perfetto_ext_tracing_ipc_ipc",
+ ":perfetto_include_perfetto_profiling_deobfuscator",
":perfetto_include_perfetto_profiling_normalize",
":perfetto_include_perfetto_protozero_protozero",
":perfetto_include_perfetto_trace_processor_basic_types",
@@ -5460,6 +5482,7 @@
":perfetto_src_perfetto_cmd_protos_gen",
":perfetto_src_perfetto_cmd_trigger_producer",
":perfetto_src_perfetto_cmd_unittests",
+ ":perfetto_src_profiling_deobfuscator",
":perfetto_src_profiling_memory_client",
":perfetto_src_profiling_memory_daemon",
":perfetto_src_profiling_memory_proc_utils",
@@ -5468,6 +5491,7 @@
":perfetto_src_profiling_memory_scoped_spinlock",
":perfetto_src_profiling_memory_unittests",
":perfetto_src_profiling_memory_wire_protocol",
+ ":perfetto_src_profiling_unittests",
":perfetto_src_protozero_protozero",
":perfetto_src_protozero_testing_messages_cpp_gen",
":perfetto_src_protozero_testing_messages_lite_gen",
diff --git a/gn/perfetto_unittests.gni b/gn/perfetto_unittests.gni
index bdcf82f..b8e033f 100644
--- a/gn/perfetto_unittests.gni
+++ b/gn/perfetto_unittests.gni
@@ -20,6 +20,7 @@
"src/base:unittests",
"src/protozero:unittests",
"src/tracing:unittests",
+ "src/profiling:unittests",
]
if (enable_perfetto_tools && current_toolchain == host_toolchain) {
diff --git a/include/perfetto/profiling/BUILD.gn b/include/perfetto/profiling/BUILD.gn
index 23e19c9..03f8350 100644
--- a/include/perfetto/profiling/BUILD.gn
+++ b/include/perfetto/profiling/BUILD.gn
@@ -27,3 +27,9 @@
"normalize.h",
]
}
+
+source_set("deobfuscator") {
+ sources = [
+ "deobfuscator.h",
+ ]
+}
diff --git a/include/perfetto/profiling/deobfuscator.h b/include/perfetto/profiling/deobfuscator.h
new file mode 100644
index 0000000..a6f467a
--- /dev/null
+++ b/include/perfetto/profiling/deobfuscator.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PROFILING_DEOBFUSCATOR_H_
+#define INCLUDE_PERFETTO_PROFILING_DEOBFUSCATOR_H_
+
+#include <map>
+#include <string>
+
+namespace perfetto {
+namespace profiling {
+
+struct ObfuscatedClass {
+ ObfuscatedClass(std::string d) : deobfuscated_name(std::move(d)) {}
+ ObfuscatedClass(std::string d, std::map<std::string, std::string> f)
+ : deobfuscated_name(std::move(d)), deobfuscated_fields(std::move(f)) {}
+
+ std::string deobfuscated_name;
+ std::map<std::string, std::string> deobfuscated_fields;
+};
+
+class ProguardParser {
+ public:
+ // A return value of false means this line failed to parse. This leaves the
+ // parser in an undefined state and it should no longer be used.
+ bool AddLine(std::string line);
+
+ std::map<std::string, ObfuscatedClass> ConsumeMapping() {
+ return std::move(mapping_);
+ }
+
+ private:
+ std::map<std::string, ObfuscatedClass> mapping_;
+ ObfuscatedClass* current_class_ = nullptr;
+};
+
+} // namespace profiling
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_PROFILING_DEOBFUSCATOR_H_
diff --git a/src/profiling/BUILD.gn b/src/profiling/BUILD.gn
new file mode 100644
index 0000000..3ddfe8d
--- /dev/null
+++ b/src/profiling/BUILD.gn
@@ -0,0 +1,43 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../gn/perfetto.gni")
+import("../../gn/test.gni")
+
+source_set("deobfuscator") {
+ sources = [
+ "deobfuscator.cc",
+ ]
+ deps = [
+ "../..//include/perfetto/ext/base:base",
+ "../../gn:default_deps",
+ ]
+ public_deps = [
+ "../../../include/perfetto/profiling:deobfuscator",
+ ]
+}
+
+perfetto_unittest_source_set("unittests") {
+ testonly = true
+ deps = [
+ ":deobfuscator",
+ "../../gn:default_deps",
+ "../../gn:gtest_and_gmock",
+ "../base",
+ "../base:test_support",
+ ]
+ sources = [
+ "deobfuscator_unittest.cc",
+ ]
+}
diff --git a/src/profiling/deobfuscator.cc b/src/profiling/deobfuscator.cc
new file mode 100644
index 0000000..8306ded
--- /dev/null
+++ b/src/profiling/deobfuscator.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/profiling/deobfuscator.h"
+#include "perfetto/ext/base/string_splitter.h"
+
+#include "perfetto/ext/base/optional.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+base::Optional<std::pair<std::string, std::string>> ParseClass(
+ std::string line) {
+ base::StringSplitter ss(std::move(line), ' ');
+
+ if (!ss.Next()) {
+ PERFETTO_ELOG("Missing deobfuscated name.");
+ return base::nullopt;
+ }
+ std::string deobfuscated_name(ss.cur_token(), ss.cur_token_size());
+
+ if (!ss.Next() || ss.cur_token_size() != 2 ||
+ strncmp("->", ss.cur_token(), 2) != 0) {
+ PERFETTO_ELOG("Missing ->");
+ return base::nullopt;
+ }
+
+ if (!ss.Next()) {
+ PERFETTO_ELOG("Missing obfuscated name.");
+ return base::nullopt;
+ }
+ std::string obfuscated_name(ss.cur_token(), ss.cur_token_size());
+ if (obfuscated_name.size() == 0) {
+ PERFETTO_ELOG("Empty obfuscated name.");
+ return base::nullopt;
+ }
+ if (obfuscated_name.back() != ':') {
+ PERFETTO_ELOG("Expected colon.");
+ return base::nullopt;
+ }
+
+ obfuscated_name.resize(obfuscated_name.size() - 1);
+ if (ss.Next()) {
+ PERFETTO_ELOG("Unexpected data.");
+ return base::nullopt;
+ }
+ return std::make_pair(std::move(obfuscated_name),
+ std::move(deobfuscated_name));
+}
+
+base::Optional<std::pair<std::string, std::string>> ParseMember(
+ std::string line) {
+ base::StringSplitter ss(std::move(line), ' ');
+
+ if (!ss.Next()) {
+ PERFETTO_ELOG("Missing type name.");
+ return base::nullopt;
+ }
+ std::string type_name(ss.cur_token(), ss.cur_token_size());
+
+ if (!ss.Next()) {
+ PERFETTO_ELOG("Missing deobfuscated name.");
+ return base::nullopt;
+ }
+ std::string deobfuscated_name(ss.cur_token(), ss.cur_token_size());
+
+ if (!ss.Next() || ss.cur_token_size() != 2 ||
+ strncmp("->", ss.cur_token(), 2) != 0) {
+ PERFETTO_ELOG("Missing ->");
+ return base::nullopt;
+ }
+
+ if (!ss.Next()) {
+ PERFETTO_ELOG("Missing obfuscated name.");
+ return base::nullopt;
+ }
+ std::string obfuscated_name(ss.cur_token(), ss.cur_token_size());
+
+ if (ss.Next()) {
+ PERFETTO_ELOG("Unexpected data.");
+ return base::nullopt;
+ }
+ return std::make_pair(std::move(obfuscated_name),
+ std::move(deobfuscated_name));
+}
+
+} // namespace
+
+bool ProguardParser::AddLine(std::string line) {
+ if (line.length() == 0)
+ return true;
+ bool is_member = line[0] == ' ';
+ if (is_member && !current_class_) {
+ PERFETTO_ELOG("Failed to parse proguard map. Saw member before class.");
+ return false;
+ }
+ if (!is_member) {
+ std::string obfuscated_name;
+ std::string deobfuscated_name;
+ auto opt_pair = ParseClass(std::move(line));
+ if (!opt_pair)
+ return false;
+ std::tie(obfuscated_name, deobfuscated_name) = *opt_pair;
+ auto p = mapping_.emplace(std::move(obfuscated_name),
+ std::move(deobfuscated_name));
+ if (!p.second)
+ return false;
+ current_class_ = &p.first->second;
+ } else {
+ // TODO(fmayer): Teach this to properly parse methods.
+ std::string obfuscated_name;
+ std::string deobfuscated_name;
+ auto opt_pair = ParseMember(std::move(line));
+ if (!opt_pair)
+ return false;
+ std::tie(obfuscated_name, deobfuscated_name) = *opt_pair;
+ auto p = current_class_->deobfuscated_fields.emplace(
+ std::move(obfuscated_name), std::move(deobfuscated_name));
+ if (!p.second)
+ return false;
+ }
+ return true;
+}
+
+} // namespace profiling
+} // namespace perfetto
diff --git a/src/profiling/deobfuscator_unittest.cc b/src/profiling/deobfuscator_unittest.cc
new file mode 100644
index 0000000..0b7833e
--- /dev/null
+++ b/src/profiling/deobfuscator_unittest.cc
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/profiling/deobfuscator.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace profiling {
+
+bool operator==(const ObfuscatedClass& a, const ObfuscatedClass& b);
+bool operator==(const ObfuscatedClass& a, const ObfuscatedClass& b) {
+ return a.deobfuscated_name == b.deobfuscated_name &&
+ a.deobfuscated_fields == b.deobfuscated_fields;
+}
+
+namespace {
+
+using ::testing::ElementsAre;
+
+TEST(ProguardParserTest, ReadClass) {
+ ProguardParser p;
+ ASSERT_TRUE(p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
+ ASSERT_THAT(p.ConsumeMapping(),
+ ElementsAre(std::pair<std::string, ObfuscatedClass>(
+ "android.arch.a.a.a",
+ "android.arch.core.executor.ArchTaskExecutor")));
+}
+
+TEST(ProguardParserTest, MissingColon) {
+ ProguardParser p;
+ ASSERT_FALSE(p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a"));
+}
+
+TEST(ProguardParserTest, UnexpectedMember) {
+ ProguardParser p;
+ ASSERT_FALSE(
+ p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b"));
+}
+
+TEST(ProguardParserTest, Member) {
+ ProguardParser p;
+ ASSERT_TRUE(p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
+ ASSERT_TRUE(
+ p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b"));
+ std::map<std::string, std::string> deobfuscated_fields{{"b", "mDelegate"}};
+ ASSERT_THAT(
+ p.ConsumeMapping(),
+ ElementsAre(std::pair<std::string, ObfuscatedClass>(
+ "android.arch.a.a.a", {"android.arch.core.executor.ArchTaskExecutor",
+ std::move(deobfuscated_fields)})));
+}
+
+TEST(ProguardParserTest, Method) {
+ ProguardParser p;
+ ASSERT_TRUE(p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
+ ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b"));
+}
+
+TEST(ProguardParserTest, DuplicateClass) {
+ ProguardParser p;
+ ASSERT_TRUE(p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
+ ASSERT_FALSE(p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
+}
+
+TEST(ProguardParserTest, DuplicateField) {
+ ProguardParser p;
+ ASSERT_TRUE(p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
+ ASSERT_TRUE(
+ p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b"));
+ ASSERT_FALSE(
+ p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b"));
+}
+
+} // namespace
+} // namespace profiling
+} // namespace perfetto