Reapply "Add MappingTracker"
This reverts commit e5df54a90ec655ec4c47b7d26b0de7a54c98d75f.
The reason the tests were failing is that the new code is more strict in
determining whether a build_id is a hex module id. The old code just
uses a length of 33 to determine an id is a module_id. My new code was
also checking that the build_id was a hex string. Turns out some tests
just use dummy 33 length strings.
So I removed the extra checks (see diff patchset 1 vs 2)
Bug: b/283794416
Change-Id: I6ea5653534a258c7ca25b9bd96df56a107bcddd3
diff --git a/Android.bp b/Android.bp
index a991cc1..c3b04f9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2378,6 +2378,7 @@
":perfetto_src_trace_processor_storage_storage",
":perfetto_src_trace_processor_tables_tables",
":perfetto_src_trace_processor_types_types",
+ ":perfetto_src_trace_processor_util_build_id",
":perfetto_src_trace_processor_util_bump_allocator",
":perfetto_src_trace_processor_util_descriptors",
":perfetto_src_trace_processor_util_glob",
@@ -2391,7 +2392,6 @@
":perfetto_src_trace_processor_util_protozero_to_text",
":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
- ":perfetto_src_trace_processor_util_stack_traces_util",
":perfetto_src_trace_processor_util_stdlib",
":perfetto_src_trace_processor_util_util",
":perfetto_src_trace_processor_util_zip_reader",
@@ -11110,6 +11110,7 @@
"src/trace_processor/importers/common/event_tracker.cc",
"src/trace_processor/importers/common/flow_tracker.cc",
"src/trace_processor/importers/common/global_args_tracker.cc",
+ "src/trace_processor/importers/common/mapping_tracker.cc",
"src/trace_processor/importers/common/metadata_tracker.cc",
"src/trace_processor/importers/common/process_tracker.cc",
"src/trace_processor/importers/common/sched_event_tracker.cc",
@@ -11120,6 +11121,7 @@
"src/trace_processor/importers/common/thread_state_tracker.cc",
"src/trace_processor/importers/common/trace_parser.cc",
"src/trace_processor/importers/common/track_tracker.cc",
+ "src/trace_processor/importers/common/virtual_memory_mapping.cc",
],
}
@@ -12324,6 +12326,14 @@
name: "perfetto_src_trace_processor_unittests",
}
+// GN: //src/trace_processor/util:build_id
+filegroup {
+ name: "perfetto_src_trace_processor_util_build_id",
+ srcs: [
+ "src/trace_processor/util/build_id.cc",
+ ],
+}
+
// GN: //src/trace_processor/util:bump_allocator
filegroup {
name: "perfetto_src_trace_processor_util_bump_allocator",
@@ -12424,14 +12434,6 @@
],
}
-// GN: //src/trace_processor/util:stack_traces_util
-filegroup {
- name: "perfetto_src_trace_processor_util_stack_traces_util",
- srcs: [
- "src/trace_processor/util/stack_traces_util.cc",
- ],
-}
-
// GN: //src/trace_processor/util:stdlib
filegroup {
name: "perfetto_src_trace_processor_util_stdlib",
@@ -13959,6 +13961,7 @@
":perfetto_src_trace_processor_types_types",
":perfetto_src_trace_processor_types_unittests",
":perfetto_src_trace_processor_unittests",
+ ":perfetto_src_trace_processor_util_build_id",
":perfetto_src_trace_processor_util_bump_allocator",
":perfetto_src_trace_processor_util_descriptors",
":perfetto_src_trace_processor_util_glob",
@@ -13972,7 +13975,6 @@
":perfetto_src_trace_processor_util_protozero_to_text",
":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
- ":perfetto_src_trace_processor_util_stack_traces_util",
":perfetto_src_trace_processor_util_stdlib",
":perfetto_src_trace_processor_util_unittests",
":perfetto_src_trace_processor_util_util",
@@ -14662,6 +14664,7 @@
":perfetto_src_trace_processor_storage_storage",
":perfetto_src_trace_processor_tables_tables",
":perfetto_src_trace_processor_types_types",
+ ":perfetto_src_trace_processor_util_build_id",
":perfetto_src_trace_processor_util_bump_allocator",
":perfetto_src_trace_processor_util_descriptors",
":perfetto_src_trace_processor_util_glob",
@@ -14675,7 +14678,6 @@
":perfetto_src_trace_processor_util_protozero_to_text",
":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
- ":perfetto_src_trace_processor_util_stack_traces_util",
":perfetto_src_trace_processor_util_stdlib",
":perfetto_src_trace_processor_util_util",
":perfetto_src_trace_processor_util_zip_reader",
@@ -14896,6 +14898,7 @@
":perfetto_src_trace_processor_storage_storage",
":perfetto_src_trace_processor_tables_tables",
":perfetto_src_trace_processor_types_types",
+ ":perfetto_src_trace_processor_util_build_id",
":perfetto_src_trace_processor_util_bump_allocator",
":perfetto_src_trace_processor_util_descriptors",
":perfetto_src_trace_processor_util_glob",
@@ -14909,7 +14912,6 @@
":perfetto_src_trace_processor_util_protozero_to_text",
":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
- ":perfetto_src_trace_processor_util_stack_traces_util",
":perfetto_src_trace_processor_util_stdlib",
":perfetto_src_trace_processor_util_util",
":perfetto_src_trace_processor_util_zip_reader",
diff --git a/BUILD b/BUILD
index ac30232..a2a4a15 100644
--- a/BUILD
+++ b/BUILD
@@ -266,6 +266,7 @@
":src_trace_processor_tables_tables",
":src_trace_processor_tables_tables_python",
":src_trace_processor_types_types",
+ ":src_trace_processor_util_build_id",
":src_trace_processor_util_bump_allocator",
":src_trace_processor_util_descriptors",
":src_trace_processor_util_glob",
@@ -279,7 +280,6 @@
":src_trace_processor_util_protozero_to_text",
":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
- ":src_trace_processor_util_stack_traces_util",
":src_trace_processor_util_stdlib",
":src_trace_processor_util_util",
":src_trace_processor_util_zip_reader",
@@ -1467,6 +1467,7 @@
"src/trace_processor/importers/common/clock_converter.h",
"src/trace_processor/importers/common/clock_tracker.cc",
"src/trace_processor/importers/common/clock_tracker.h",
+ "src/trace_processor/importers/common/create_mapping_params.h",
"src/trace_processor/importers/common/deobfuscation_mapping_table.cc",
"src/trace_processor/importers/common/deobfuscation_mapping_table.h",
"src/trace_processor/importers/common/event_tracker.cc",
@@ -1475,6 +1476,8 @@
"src/trace_processor/importers/common/flow_tracker.h",
"src/trace_processor/importers/common/global_args_tracker.cc",
"src/trace_processor/importers/common/global_args_tracker.h",
+ "src/trace_processor/importers/common/mapping_tracker.cc",
+ "src/trace_processor/importers/common/mapping_tracker.h",
"src/trace_processor/importers/common/metadata_tracker.cc",
"src/trace_processor/importers/common/metadata_tracker.h",
"src/trace_processor/importers/common/process_tracker.cc",
@@ -1495,6 +1498,8 @@
"src/trace_processor/importers/common/trace_parser.cc",
"src/trace_processor/importers/common/track_tracker.cc",
"src/trace_processor/importers/common/track_tracker.h",
+ "src/trace_processor/importers/common/virtual_memory_mapping.cc",
+ "src/trace_processor/importers/common/virtual_memory_mapping.h",
],
)
@@ -2699,6 +2704,15 @@
],
)
+# GN target: //src/trace_processor/util:build_id
+perfetto_filegroup(
+ name = "src_trace_processor_util_build_id",
+ srcs = [
+ "src/trace_processor/util/build_id.cc",
+ "src/trace_processor/util/build_id.h",
+ ],
+)
+
# GN target: //src/trace_processor/util:bump_allocator
perfetto_filegroup(
name = "src_trace_processor_util_bump_allocator",
@@ -2818,15 +2832,6 @@
],
)
-# GN target: //src/trace_processor/util:stack_traces_util
-perfetto_filegroup(
- name = "src_trace_processor_util_stack_traces_util",
- srcs = [
- "src/trace_processor/util/stack_traces_util.cc",
- "src/trace_processor/util/stack_traces_util.h",
- ],
-)
-
# GN target: //src/trace_processor/util:stdlib
perfetto_filegroup(
name = "src_trace_processor_util_stdlib",
@@ -5644,6 +5649,7 @@
":src_trace_processor_tables_tables",
":src_trace_processor_tables_tables_python",
":src_trace_processor_types_types",
+ ":src_trace_processor_util_build_id",
":src_trace_processor_util_bump_allocator",
":src_trace_processor_util_descriptors",
":src_trace_processor_util_glob",
@@ -5657,7 +5663,6 @@
":src_trace_processor_util_protozero_to_text",
":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
- ":src_trace_processor_util_stack_traces_util",
":src_trace_processor_util_stdlib",
":src_trace_processor_util_util",
":src_trace_processor_util_zip_reader",
@@ -5815,6 +5820,7 @@
":src_trace_processor_tables_tables",
":src_trace_processor_tables_tables_python",
":src_trace_processor_types_types",
+ ":src_trace_processor_util_build_id",
":src_trace_processor_util_bump_allocator",
":src_trace_processor_util_descriptors",
":src_trace_processor_util_glob",
@@ -5828,7 +5834,6 @@
":src_trace_processor_util_protozero_to_text",
":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
- ":src_trace_processor_util_stack_traces_util",
":src_trace_processor_util_stdlib",
":src_trace_processor_util_util",
":src_trace_processor_util_zip_reader",
@@ -5906,7 +5911,7 @@
":src_profiling_deobfuscator",
":src_profiling_symbolizer_symbolize_database",
":src_profiling_symbolizer_symbolizer",
- ":src_trace_processor_util_stack_traces_util",
+ ":src_trace_processor_util_build_id",
":src_traceconv_pprofbuilder",
":src_traceconv_utils",
],
@@ -6038,6 +6043,7 @@
":src_trace_processor_tables_tables",
":src_trace_processor_tables_tables_python",
":src_trace_processor_types_types",
+ ":src_trace_processor_util_build_id",
":src_trace_processor_util_bump_allocator",
":src_trace_processor_util_descriptors",
":src_trace_processor_util_glob",
@@ -6051,7 +6057,6 @@
":src_trace_processor_util_protozero_to_text",
":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
- ":src_trace_processor_util_stack_traces_util",
":src_trace_processor_util_stdlib",
":src_trace_processor_util_util",
":src_trace_processor_util_zip_reader",
diff --git a/src/profiling/symbolizer/BUILD.gn b/src/profiling/symbolizer/BUILD.gn
index 722d1cf..2eb2c93 100644
--- a/src/profiling/symbolizer/BUILD.gn
+++ b/src/profiling/symbolizer/BUILD.gn
@@ -49,7 +49,7 @@
"../../../include/perfetto/trace_processor:trace_processor",
"../../../protos/perfetto/trace:zero",
"../../../protos/perfetto/trace/profiling:zero",
- "../../trace_processor/util:stack_traces_util",
+ "../../trace_processor/util:build_id",
]
sources = [
"symbolize_database.cc",
diff --git a/src/profiling/symbolizer/symbolize_database.cc b/src/profiling/symbolizer/symbolize_database.cc
index 224874b..a008e1a 100644
--- a/src/profiling/symbolizer/symbolize_database.cc
+++ b/src/profiling/symbolizer/symbolize_database.cc
@@ -28,8 +28,7 @@
#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
#include "protos/perfetto/trace/trace.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
-
-#include "src/trace_processor/util/stack_traces_util.h"
+#include "src/trace_processor/util/build_id.h"
namespace perfetto {
namespace profiling {
@@ -56,32 +55,6 @@
}
};
-std::string FromHex(const char* str, size_t size) {
- if (size % 2) {
- PERFETTO_DFATAL_OR_ELOG("Failed to parse hex %s", str);
- return "";
- }
- std::string result(size / 2, '\0');
- for (size_t i = 0; i < size; i += 2) {
- char hex_byte[3];
- hex_byte[0] = str[i];
- hex_byte[1] = str[i + 1];
- hex_byte[2] = '\0';
- char* end;
- long int byte = strtol(hex_byte, &end, 16);
- if (*end != '\0') {
- PERFETTO_DFATAL_OR_ELOG("Failed to parse hex %s", str);
- return "";
- }
- result[i / 2] = static_cast<char>(byte);
- }
- return result;
-}
-
-std::string FromHex(const std::string& str) {
- return FromHex(str.c_str(), str.size());
-}
-
std::map<UnsymbolizedMapping, std::vector<uint64_t>> GetUnsymbolizedFrames(
trace_processor::TraceProcessor* tp) {
std::map<UnsymbolizedMapping, std::vector<uint64_t>> res;
@@ -89,17 +62,10 @@
while (it.Next()) {
int64_t load_bias = it.Get(3).AsLong();
PERFETTO_CHECK(load_bias >= 0);
- std::string build_id;
- // TODO(b/148109467): Remove workaround once all active Chrome versions
- // write raw bytes instead of a string as build_id.
- std::string raw_build_id = it.Get(1).AsString();
- if (!trace_processor::util::IsHexModuleId(base::StringView(raw_build_id))) {
- build_id = FromHex(raw_build_id);
- } else {
- build_id = raw_build_id;
- }
- UnsymbolizedMapping unsymbolized_mapping{it.Get(0).AsString(), build_id,
- static_cast<uint64_t>(load_bias)};
+ trace_processor::BuildId build_id =
+ trace_processor::BuildId::FromHex(it.Get(1).AsString());
+ UnsymbolizedMapping unsymbolized_mapping{
+ it.Get(0).AsString(), build_id.raw(), static_cast<uint64_t>(load_bias)};
int64_t rel_pc = it.Get(2).AsLong();
res[unsymbolized_mapping].emplace_back(rel_pc);
}
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index e81b184..e37c9c6 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -132,7 +132,6 @@
"util:descriptors",
"util:gzip",
"util:proto_to_args_parser",
- "util:stack_traces_util",
]
public_deps = [ "../../include/perfetto/trace_processor:storage" ]
}
diff --git a/src/trace_processor/importers/common/BUILD.gn b/src/trace_processor/importers/common/BUILD.gn
index 466876a..5108dca 100644
--- a/src/trace_processor/importers/common/BUILD.gn
+++ b/src/trace_processor/importers/common/BUILD.gn
@@ -28,6 +28,7 @@
"clock_converter.h",
"clock_tracker.cc",
"clock_tracker.h",
+ "create_mapping_params.h",
"deobfuscation_mapping_table.cc",
"deobfuscation_mapping_table.h",
"event_tracker.cc",
@@ -36,6 +37,8 @@
"flow_tracker.h",
"global_args_tracker.cc",
"global_args_tracker.h",
+ "mapping_tracker.cc",
+ "mapping_tracker.h",
"metadata_tracker.cc",
"metadata_tracker.h",
"process_tracker.cc",
@@ -56,6 +59,8 @@
"trace_parser.cc",
"track_tracker.cc",
"track_tracker.h",
+ "virtual_memory_mapping.cc",
+ "virtual_memory_mapping.h",
]
public_deps = [
":trace_parser_hdr",
@@ -75,8 +80,8 @@
"../../storage",
"../../tables:tables",
"../../types",
+ "../../util:build_id",
"../../util:profiler_util",
- "../../util:stack_traces_util",
"../fuchsia:fuchsia_record",
"../systrace:systrace_line",
]
diff --git a/src/trace_processor/importers/common/create_mapping_params.h b/src/trace_processor/importers/common/create_mapping_params.h
new file mode 100644
index 0000000..7aba456
--- /dev/null
+++ b/src/trace_processor/importers/common/create_mapping_params.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 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 SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_CREATE_MAPPING_PARAMS_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_CREATE_MAPPING_PARAMS_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <tuple>
+
+#include "perfetto/ext/base/hash.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/util/build_id.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+struct CreateMappingParams {
+ AddressRange memory_range;
+ // This is the offset into the file that has been mapped at
+ // memory_range.start()
+ uint64_t exact_offset = 0;
+ // This is the offset into the file where the ELF header starts. We assume
+ // all file mappings are ELF files an thus this offset is 0.
+ uint64_t start_offset = 0;
+ // This can only be read out of the actual ELF file.
+ uint64_t load_bias = 0;
+ std::string name;
+ std::optional<BuildId> build_id;
+
+ auto ToTuple() const {
+ return std::tie(memory_range, exact_offset, start_offset, load_bias, name,
+ build_id);
+ }
+
+ bool operator==(const CreateMappingParams& o) const {
+ return ToTuple() == o.ToTuple();
+ }
+
+ struct Hasher {
+ size_t operator()(const CreateMappingParams& p) const {
+ base::Hasher h;
+ h.UpdateAll(p.memory_range.start(), p.memory_range.end(), p.exact_offset,
+ p.start_offset, p.load_bias, p.name);
+ if (p.build_id) {
+ h.Update(*p.build_id);
+ }
+ return static_cast<size_t>(h.digest());
+ }
+ };
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_CREATE_MAPPING_PARAMS_H_
diff --git a/src/trace_processor/importers/common/mapping_tracker.cc b/src/trace_processor/importers/common/mapping_tracker.cc
new file mode 100644
index 0000000..13b8274
--- /dev/null
+++ b/src/trace_processor/importers/common/mapping_tracker.cc
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2024 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 "src/trace_processor/importers/common/mapping_tracker.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <utility>
+
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/build_id.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+bool IsKernelModule(base::StringView name) {
+ return !name.StartsWith("[kernel.kallsyms]");
+}
+
+} // namespace
+
+JitDelegate::~JitDelegate() = default;
+
+template <typename MappingImpl>
+MappingImpl& MappingTracker::AddMapping(std::unique_ptr<MappingImpl> mapping) {
+ auto ptr = mapping.get();
+ PERFETTO_CHECK(
+ mappings_by_id_.Insert(ptr->mapping_id(), std::move(mapping)).second);
+
+ mappings_by_name_and_build_id_[NameAndBuildId{base::StringView(ptr->name()),
+ ptr->build_id()}]
+ .push_back(ptr);
+
+ return *ptr;
+}
+
+KernelMemoryMapping& MappingTracker::CreateKernelMemoryMapping(
+ CreateMappingParams params) {
+ // TODO(carlscab): Guess build_id if not provided. Some tools like simpleperf
+ // add a mapping file_name ->build_id that we could use here
+
+ const bool is_module = IsKernelModule(base::StringView(params.name));
+
+ if (!is_module && kernel_ != nullptr) {
+ PERFETTO_CHECK(params.memory_range == kernel_->memory_range());
+ return *kernel_;
+ }
+
+ std::unique_ptr<KernelMemoryMapping> mapping(
+ new KernelMemoryMapping(context_, std::move(params)));
+
+ if (is_module) {
+ // TODO(carlscab): Overlaps not supported (for now?). Should be fine for
+ // kernel.
+ PERFETTO_CHECK(
+ kernel_modules_.Emplace(mapping->memory_range(), mapping.get()));
+ } else {
+ kernel_ = mapping.get();
+ }
+
+ return AddMapping(std::move(mapping));
+}
+
+UserMemoryMapping& MappingTracker::CreateUserMemoryMapping(
+ UniquePid upid,
+ CreateMappingParams params) {
+ // TODO(carlscab): Guess build_id if not provided. Some tools like simpleperf
+ // add a mapping file_name ->build_id that we could use here
+
+ const AddressRange mapping_range = params.memory_range;
+ std::unique_ptr<UserMemoryMapping> mapping(
+ new UserMemoryMapping(context_, upid, std::move(params)));
+ // TODO(carlscab): Overlaps not supported (for now?).
+ PERFETTO_CHECK(user_memory_[upid].Emplace(mapping_range, mapping.get()));
+
+ jit_delegates_[upid].ForOverlaps(
+ mapping_range, [&](std::pair<const AddressRange, JitDelegate*>& entry) {
+ const auto& jit_range = entry.first;
+ JitDelegate* jit_delegate = entry.second;
+ PERFETTO_CHECK(jit_range.Contains(mapping_range));
+ mapping->SetJitDelegate(jit_delegate);
+ });
+
+ return AddMapping(std::move(mapping));
+}
+
+KernelMemoryMapping* MappingTracker::FindKernelMappingForAddress(
+ uint64_t address) const {
+ if (auto it = kernel_modules_.Find(address); it != kernel_modules_.end()) {
+ return it->second;
+ }
+ if (kernel_ && kernel_->memory_range().Contains(address)) {
+ return kernel_;
+ }
+ return nullptr;
+}
+
+UserMemoryMapping* MappingTracker::FindUserMappingForAddress(
+ UniquePid upid,
+ uint64_t address) const {
+ if (auto* vm = user_memory_.Find(upid); vm) {
+ if (auto it = vm->Find(address); it != vm->end()) {
+ return it->second;
+ }
+ }
+
+ if (auto* delegates = jit_delegates_.Find(upid); delegates) {
+ if (auto it = delegates->Find(address); it != delegates->end()) {
+ return it->second->CreateMapping();
+ }
+ }
+
+ return nullptr;
+}
+
+std::vector<VirtualMemoryMapping*> MappingTracker::FindMappings(
+ base::StringView name,
+ const BuildId& build_id) const {
+ if (auto res = mappings_by_name_and_build_id_.Find({name, build_id});
+ res != nullptr) {
+ return *res;
+ }
+ return {};
+}
+
+VirtualMemoryMapping& MappingTracker::InternMemoryMapping(
+ CreateMappingParams params) {
+ if (auto* mapping = interned_mappings_.Find(params); mapping) {
+ return **mapping;
+ }
+
+ std::unique_ptr<VirtualMemoryMapping> mapping(
+ new VirtualMemoryMapping(context_, params));
+ interned_mappings_.Insert(std::move(params), mapping.get());
+ return AddMapping(std::move(mapping));
+}
+
+void MappingTracker::AddJitRange(UniquePid upid,
+ AddressRange jit_range,
+ JitDelegate* delegate) {
+ // TODO(carlscab): Deal with overlaps
+ jit_delegates_[upid].DeleteOverlapsAndEmplace(jit_range, delegate);
+ user_memory_[upid].ForOverlaps(
+ jit_range, [&](std::pair<const AddressRange, UserMemoryMapping*>& entry) {
+ PERFETTO_CHECK(jit_range.Contains(entry.first));
+ entry.second->SetJitDelegate(delegate);
+ });
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/common/mapping_tracker.h b/src/trace_processor/importers/common/mapping_tracker.h
new file mode 100644
index 0000000..95dc355
--- /dev/null
+++ b/src/trace_processor/importers/common/mapping_tracker.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2024 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 SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_MAPPING_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_MAPPING_TRACKER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <vector>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/virtual_memory_mapping.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/build_id.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Api used to forward frame interning requests for frames that fall in a
+// jitted memory region.
+// MappingTracker allows other trackers to register ranges of memory for
+// which they need to control when a new frame is created. Jitted code can
+// move in memory over time, so the same program counter might refer to
+// different functions at different point in time. MappingTracker does
+// not keep track of such moves but instead delegates the creation of jitted
+// frames to a delegate.
+class JitDelegate {
+ public:
+ virtual ~JitDelegate();
+ // Forward frame interning request.
+ // Implementations are free to intern the frame as needed.
+ // Returns frame_id, and whether a new row as created or not.
+ virtual std::pair<FrameId, bool> InternFrame(
+ VirtualMemoryMapping* mapping,
+ uint64_t rel_pc,
+ base::StringView function_name) = 0;
+
+ // Simpleperf does not emit mmap events for jitted ranges (actually for non
+ // file backed executable mappings). So have a way to generate a mapping on
+ // the fly for FindMapping requests in a jitted region with no associated
+ // mapping.
+ virtual UserMemoryMapping* CreateMapping() = 0;
+};
+
+// Keeps track of all aspects relative to memory mappings.
+// This class keeps track of 3 types of mappings: UserMemoryMapping,
+// KernelMemoryMapping and others. The others are used to represent mapping
+// where we do not have enough information to determine what type of
+// mapping (user, kernel) we are dealing with. This is usually the case with
+// data sources that do not provide enough information about the mappings.
+//
+// TODO(carlscab): Hopefully we can slowly get rid of cases where these other
+// mappings are needed. The biggest blocker right now is determining the upid.
+// we could infer this from the actual samples that use said mapping (those
+// usually have a pid attached). So we would need to have a "fake" mapping that
+// actually materializes when we see a sample with a pid.
+//
+// ATTENTION: No overlaps allowed (for now). Eventually the order in which
+// mappings are create will matter as newer mappings will delete old ones.
+// This is how tools like linux perf behave, mmap event have a timestamp
+// associated and there are no "delete events" just new mmap events that
+// overlap (to be deleted) mappings.
+class MappingTracker {
+ public:
+ explicit MappingTracker(TraceProcessorContext* context) : context_(context) {}
+
+ // Create a new kernel space mapping. Returned reference will be valid for the
+ // duration of this instance.
+ KernelMemoryMapping& CreateKernelMemoryMapping(CreateMappingParams params);
+
+ // Create a new user space mapping. Returned reference will be valid for the
+ // duration of this instance.
+ UserMemoryMapping& CreateUserMemoryMapping(UniquePid upid,
+ CreateMappingParams params);
+
+ // Create an "other" mapping. Returned reference will be valid for the
+ // duration of this instance.
+ VirtualMemoryMapping& InternMemoryMapping(CreateMappingParams params);
+
+ // Given an absolute address find the kernel mapping where this address
+ // belongs to. Returns `nullptr` if none is found.
+ KernelMemoryMapping* FindKernelMappingForAddress(uint64_t address) const;
+
+ // Given an absolute address find the user mapping where this address
+ // belongs to. Returns `nullptr` if none is found.
+ UserMemoryMapping* FindUserMappingForAddress(UniquePid upid,
+ uint64_t address) const;
+
+ std::vector<VirtualMemoryMapping*> FindMappings(
+ base::StringView name,
+ const BuildId& build_id) const;
+
+ // Marks a range of memory as containing jitted code.
+ // If the added region overlaps with other existing ranges the latter are all
+ // deleted.
+ // Jitted ranges will only be applied to UserMemoryMappings
+ void AddJitRange(UniquePid upid, AddressRange range, JitDelegate* delegate);
+
+ private:
+ template <typename MappingImpl>
+ MappingImpl& AddMapping(std::unique_ptr<MappingImpl> mapping);
+
+ TraceProcessorContext* const context_;
+ base::FlatHashMap<MappingId, std::unique_ptr<VirtualMemoryMapping>>
+ mappings_by_id_;
+
+ base::FlatHashMap<CreateMappingParams,
+ VirtualMemoryMapping*,
+ CreateMappingParams::Hasher>
+ interned_mappings_;
+
+ struct NameAndBuildId {
+ base::StringView name;
+ std::optional<BuildId> build_id;
+
+ bool operator==(const NameAndBuildId& o) const {
+ return name == o.name && build_id == o.build_id;
+ }
+
+ bool operator!=(const NameAndBuildId& o) const { return !(*this == o); }
+
+ struct Hasher {
+ size_t operator()(const NameAndBuildId& o) const {
+ base::Hasher hasher;
+ hasher.Update(o.name);
+ if (o.build_id) {
+ hasher.Update(*o.build_id);
+ }
+ return static_cast<size_t>(hasher.digest());
+ }
+ };
+ };
+ base::FlatHashMap<NameAndBuildId,
+ std::vector<VirtualMemoryMapping*>,
+ NameAndBuildId::Hasher>
+ mappings_by_name_and_build_id_;
+
+ base::FlatHashMap<UniquePid, AddressRangeMap<UserMemoryMapping*>>
+ user_memory_;
+ AddressRangeMap<KernelMemoryMapping*> kernel_modules_;
+ KernelMemoryMapping* kernel_ = nullptr;
+
+ base::FlatHashMap<UniquePid, AddressRangeMap<JitDelegate*>> jit_delegates_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_MAPPING_TRACKER_H_
diff --git a/src/trace_processor/importers/common/stack_profile_tracker.cc b/src/trace_processor/importers/common/stack_profile_tracker.cc
index ad57523..799dc29 100644
--- a/src/trace_processor/importers/common/stack_profile_tracker.cc
+++ b/src/trace_processor/importers/common/stack_profile_tracker.cc
@@ -16,35 +16,18 @@
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
-#include "perfetto/ext/base/string_utils.h"
+#include <cstddef>
+#include <cstdint>
+
#include "perfetto/ext/base/string_view.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
#include "src/trace_processor/types/trace_processor_context.h"
#include "src/trace_processor/util/profiler_util.h"
-#include "src/trace_processor/util/stack_traces_util.h"
namespace perfetto {
namespace trace_processor {
-namespace {
-std::string CleanBuildId(base::StringView build_id) {
- if (build_id.empty()) {
- return build_id.ToStdString();
- }
- // If the build_id is 33 characters long, we assume it's a Breakpad debug
- // identifier which is already in Hex and doesn't need conversion.
- // TODO(b/148109467): Remove workaround once all active Chrome versions
- // write raw bytes instead of a string as build_id.
- if (util::IsHexModuleId(build_id)) {
- return build_id.ToStdString();
- }
-
- return base::ToHex(build_id.data(), build_id.size());
-}
-
-} // namespace
-
std::vector<FrameId> StackProfileTracker::JavaFramesForName(
NameInPackage name) const {
if (const auto* frames = java_frames_for_name_.Find(name); frames) {
@@ -53,50 +36,6 @@
return {};
}
-std::vector<MappingId> StackProfileTracker::FindMappingRow(
- StringId name,
- StringId build_id) const {
- if (const auto* mappings =
- mappings_by_name_and_build_id_.Find(std::make_pair(name, build_id));
- mappings) {
- return *mappings;
- }
- return {};
-}
-
-std::vector<FrameId> StackProfileTracker::FindFrameIds(MappingId mapping_id,
- uint64_t rel_pc) const {
- if (const auto* frames =
- frame_by_mapping_and_rel_pc_.Find(std::make_pair(mapping_id, rel_pc));
- frames) {
- return *frames;
- }
- return {};
-}
-
-MappingId StackProfileTracker::InternMapping(
- const CreateMappingParams& params) {
- tables::StackProfileMappingTable::Row row;
- row.build_id = InternBuildId(params.build_id);
- row.exact_offset = static_cast<int64_t>(params.exact_offset);
- row.start_offset = static_cast<int64_t>(params.start_offset);
- row.start = static_cast<int64_t>(params.start);
- row.end = static_cast<int64_t>(params.end);
- row.load_bias = static_cast<int64_t>(params.load_bias);
- row.name = context_->storage->InternString(params.name);
-
- if (MappingId* id = mapping_unique_row_index_.Find(row); id) {
- return *id;
- }
-
- MappingId mapping_id =
- context_->storage->mutable_stack_profile_mapping_table()->Insert(row).id;
- mapping_unique_row_index_.Insert(row, mapping_id);
- mappings_by_name_and_build_id_[{row.name, row.build_id}].push_back(
- mapping_id);
- return mapping_id;
-}
-
CallsiteId StackProfileTracker::InternCallsite(
std::optional<CallsiteId> parent_callsite_id,
FrameId frame_id,
@@ -113,22 +52,12 @@
return callsite_id;
}
-FrameId StackProfileTracker::InternFrame(MappingId mapping_id,
- uint64_t rel_pc,
- base::StringView function_name) {
- tables::StackProfileFrameTable::Row row;
- row.mapping = mapping_id;
- row.rel_pc = static_cast<int64_t>(rel_pc);
- row.name = context_->storage->InternString(function_name);
-
- if (FrameId* id = frame_unique_row_index_.Find(row); id) {
- return *id;
- }
-
- FrameId frame_id =
- context_->storage->mutable_stack_profile_frame_table()->Insert(row).id;
- frame_unique_row_index_.Insert(row, frame_id);
- frame_by_mapping_and_rel_pc_[{mapping_id, rel_pc}].push_back(frame_id);
+void StackProfileTracker::OnFrameCreated(FrameId frame_id) {
+ auto frame =
+ *context_->storage->stack_profile_frame_table().FindById(frame_id);
+ const MappingId mapping_id = frame.mapping();
+ const StringId name_id = frame.name();
+ const auto function_name = context_->storage->GetString(name_id);
if (function_name.find('.') != base::StringView::npos) {
// Java frames always contain a '.'
@@ -139,21 +68,14 @@
std::optional<std::string> package =
PackageFromLocation(context_->storage.get(), mapping_name);
if (package) {
- NameInPackage nip{row.name, context_->storage->InternString(
- base::StringView(*package))};
+ NameInPackage nip{
+ name_id, context_->storage->InternString(base::StringView(*package))};
java_frames_for_name_[nip].push_back(frame_id);
} else if (mapping_name.find("/memfd:") == 0) {
- NameInPackage nip{row.name, context_->storage->InternString("memfd")};
+ NameInPackage nip{name_id, context_->storage->InternString("memfd")};
java_frames_for_name_[nip].push_back(frame_id);
}
}
-
- return frame_id;
-}
-
-StringId StackProfileTracker::InternBuildId(base::StringView build_id) {
- return context_->storage->InternString(
- base::StringView(CleanBuildId(build_id)));
}
} // namespace trace_processor
diff --git a/src/trace_processor/importers/common/stack_profile_tracker.h b/src/trace_processor/importers/common/stack_profile_tracker.h
index a1067b8..b018f74 100644
--- a/src/trace_processor/importers/common/stack_profile_tracker.h
+++ b/src/trace_processor/importers/common/stack_profile_tracker.h
@@ -20,12 +20,11 @@
#include <cstdint>
#include <optional>
#include <tuple>
-#include <utility>
#include <vector>
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/hash.h"
-#include "perfetto/ext/base/string_view.h"
+
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
@@ -52,64 +51,39 @@
class StackProfileTracker {
public:
- struct CreateMappingParams {
- base::StringView build_id;
- uint64_t exact_offset;
- uint64_t start_offset;
- uint64_t start;
- uint64_t end;
- uint64_t load_bias;
- base::StringView name;
- };
-
explicit StackProfileTracker(TraceProcessorContext* context)
: context_(context) {}
std::vector<FrameId> JavaFramesForName(NameInPackage name) const;
- std::vector<MappingId> FindMappingRow(StringId name, StringId build_id) const;
- std::vector<FrameId> FindFrameIds(MappingId mapping_id,
- uint64_t rel_pc) const;
- MappingId InternMapping(const CreateMappingParams& params);
CallsiteId InternCallsite(std::optional<CallsiteId> parent_callsite_id,
FrameId frame_id,
uint32_t depth);
- FrameId InternFrame(MappingId mapping_id,
- uint64_t rel_pc,
- base::StringView function_name);
+
+ void OnFrameCreated(FrameId frame_id);
private:
- StringId InternBuildId(base::StringView build_id);
-
TraceProcessorContext* const context_;
- base::FlatHashMap<tables::StackProfileMappingTable::Row, MappingId>
- mapping_unique_row_index_;
base::FlatHashMap<tables::StackProfileCallsiteTable::Row, CallsiteId>
callsite_unique_row_index_;
- base::FlatHashMap<tables::StackProfileFrameTable::Row, FrameId>
- frame_unique_row_index_;
- struct MappingHasher {
- size_t operator()(const std::pair<StringId, StringId>& o) const {
- return static_cast<size_t>(
- base::Hasher::Combine(o.first.raw_id(), o.second.raw_id()));
- }
- };
- base::FlatHashMap<std::pair<StringId, StringId>,
- std::vector<MappingId>,
- MappingHasher>
- mappings_by_name_and_build_id_;
+ struct FrameKey {
+ MappingId mapping_id;
+ uint64_t rel_pc;
- struct FrameHasher {
- size_t operator()(const std::pair<MappingId, uint64_t>& o) const {
- return static_cast<size_t>(
- base::Hasher::Combine(o.first.value, o.second));
+ bool operator==(const FrameKey& o) const {
+ return mapping_id == o.mapping_id && rel_pc == o.rel_pc;
}
+
+ bool operator!=(const FrameKey& o) const { return !(*this == o); }
+
+ struct Hasher {
+ size_t operator()(const FrameKey& o) const {
+ return static_cast<size_t>(
+ base::Hasher::Combine(o.mapping_id.value, o.rel_pc));
+ }
+ };
};
- base::FlatHashMap<std::pair<MappingId, uint64_t>,
- std::vector<FrameId>,
- FrameHasher>
- frame_by_mapping_and_rel_pc_;
base::FlatHashMap<NameInPackage, std::vector<FrameId>, NameInPackage::Hasher>
java_frames_for_name_;
diff --git a/src/trace_processor/importers/common/virtual_memory_mapping.cc b/src/trace_processor/importers/common/virtual_memory_mapping.cc
new file mode 100644
index 0000000..60166f59
--- /dev/null
+++ b/src/trace_processor/importers/common/virtual_memory_mapping.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2024 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 "src/trace_processor/importers/common/virtual_memory_mapping.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/profiler_tables_py.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/build_id.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+MappingId CreateMapping(TraceProcessorContext* context,
+ const CreateMappingParams& params) {
+ StringId build_id = context->storage->InternString(base::StringView(
+ params.build_id ? params.build_id->ToHex() : std::string()));
+ MappingId mapping_id =
+ context->storage->mutable_stack_profile_mapping_table()
+ ->Insert(
+ {build_id, static_cast<int64_t>(params.exact_offset),
+ static_cast<int64_t>(params.start_offset),
+ static_cast<int64_t>(params.memory_range.start()),
+ static_cast<int64_t>(params.memory_range.end()),
+ static_cast<int64_t>(params.load_bias),
+ context->storage->InternString(base::StringView(params.name))})
+ .id;
+
+ return mapping_id;
+}
+
+} // namespace
+
+VirtualMemoryMapping::VirtualMemoryMapping(TraceProcessorContext* context,
+ CreateMappingParams params)
+ : context_(context),
+ mapping_id_(CreateMapping(context, params)),
+ memory_range_(params.memory_range),
+ offset_(params.exact_offset),
+ load_bias_(params.load_bias),
+ name_(std::move(params.name)),
+ build_id_(std::move(params.build_id)) {}
+
+VirtualMemoryMapping::~VirtualMemoryMapping() = default;
+
+KernelMemoryMapping::KernelMemoryMapping(TraceProcessorContext* context,
+ CreateMappingParams params)
+ : VirtualMemoryMapping(context, std::move(params)) {}
+
+KernelMemoryMapping::~KernelMemoryMapping() = default;
+
+UserMemoryMapping::UserMemoryMapping(TraceProcessorContext* context,
+ UniquePid upid,
+ CreateMappingParams params)
+ : VirtualMemoryMapping(context, std::move(params)), upid_(upid) {}
+
+UserMemoryMapping::~UserMemoryMapping() = default;
+
+FrameId VirtualMemoryMapping::InternFrame(uint64_t rel_pc,
+ base::StringView function_name) {
+ auto [frame_id, was_inserted] =
+ jit_delegate_ ? jit_delegate_->InternFrame(this, rel_pc, function_name)
+ : InternFrameImpl(rel_pc, function_name);
+ if (was_inserted) {
+ frames_by_rel_pc_[rel_pc].push_back(frame_id);
+ context_->stack_profile_tracker->OnFrameCreated(frame_id);
+ }
+ return frame_id;
+}
+
+std::vector<FrameId> VirtualMemoryMapping::FindFrameIds(uint64_t rel_pc) const {
+ if (auto* res = frames_by_rel_pc_.Find(rel_pc); res != nullptr) {
+ return *res;
+ }
+ return {};
+}
+
+std::pair<FrameId, bool> VirtualMemoryMapping::InternFrameImpl(
+ uint64_t rel_pc,
+ base::StringView function_name) {
+ const FrameKey frame_key{rel_pc,
+ context_->storage->InternString(function_name)};
+ if (FrameId* id = interned_frames_.Find(frame_key); id) {
+ return {*id, false};
+ }
+
+ const FrameId frame_id =
+ context_->storage->mutable_stack_profile_frame_table()
+ ->Insert(
+ {frame_key.name_id, mapping_id_, static_cast<int64_t>(rel_pc)})
+ .id;
+ interned_frames_.Insert(frame_key, frame_id);
+
+ return {frame_id, true};
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/common/virtual_memory_mapping.h b/src/trace_processor/importers/common/virtual_memory_mapping.h
new file mode 100644
index 0000000..7b8ef58
--- /dev/null
+++ b/src/trace_processor/importers/common/virtual_memory_mapping.h
@@ -0,0 +1,152 @@
+
+/*
+ * Copyright (C) 2024 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 SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_VIRTUAL_MEMORY_MAPPING_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_VIRTUAL_MEMORY_MAPPING_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/create_mapping_params.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/build_id.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// TODO(carlscab): Reconsider whether jit is the best abstraction here. All we
+// really care is about mapping a `rel_pc` to a symbol (aka symbolization) and
+// whether is this is constant.
+class JitDelegate;
+
+// Represents a mapping in virtual memory.
+class VirtualMemoryMapping {
+ public:
+ virtual ~VirtualMemoryMapping();
+ // Range of virtual memory this mapping covers.
+ AddressRange memory_range() const { return memory_range_; }
+ MappingId mapping_id() const { return mapping_id_; }
+ // This name could be the path of the underlying file mapped into memory.
+ const std::string& name() const { return name_; }
+ // For file mappings, this is the offset into the file for the first byte in
+ // the mapping
+ uint64_t offset() const { return offset_; }
+ // If the mapped file is an executable or shared library this will return the
+ // load bias, if known. Returns 0 otherwise.
+ uint64_t load_bias() const { return load_bias_; }
+ // If the mapped file is an executable or shared library this will return its
+ // build id, if known.
+ const std::optional<BuildId>& build_id() const { return build_id_; }
+
+ // Whether this maps to a region that holds jitted code.
+ bool is_jitted() const { return jit_delegate_ != nullptr; }
+
+ // Converts an absolute address into a relative one.
+ uint64_t ToRelativePc(uint64_t address) const {
+ return address - memory_range_.start() + offset_ + load_bias_;
+ }
+
+ // Creates a frame for the given `rel_pc`. Note that if the mapping
+ // `is_jitted()` same `rel_pc` values can return different mappings (as jitted
+ // functions can be created and deleted over time.) So for such mappings the
+ // returned `FrameId` should not be cached.
+ FrameId InternFrame(uint64_t rel_pc, base::StringView function_name);
+
+ // Returns all frames ever created in this mapping for the given `rel_pc`.
+ std::vector<FrameId> FindFrameIds(uint64_t rel_pc) const;
+
+ protected:
+ VirtualMemoryMapping(TraceProcessorContext* context,
+ CreateMappingParams params);
+
+ private:
+ friend class MappingTracker;
+
+ std::pair<FrameId, bool> InternFrameImpl(uint64_t rel_pc,
+ base::StringView function_name);
+
+ void SetJitDelegate(JitDelegate* jit_delegate) {
+ jit_delegate_ = jit_delegate;
+ }
+
+ TraceProcessorContext* const context_;
+ const MappingId mapping_id_;
+ const AddressRange memory_range_;
+ const uint64_t offset_;
+ const uint64_t load_bias_;
+ const std::string name_;
+ std::optional<BuildId> const build_id_;
+ JitDelegate* jit_delegate_ = nullptr;
+
+ struct FrameKey {
+ uint64_t rel_pc;
+ // It doesn't seem to make too much sense to key on name, as for the same
+ // mapping and same rel_pc the name should always be the same. But who knows
+ // how producers behave.
+ StringId name_id;
+
+ bool operator==(const FrameKey& o) const {
+ return rel_pc == o.rel_pc && name_id == o.name_id;
+ }
+
+ struct Hasher {
+ size_t operator()(const FrameKey& k) const {
+ return static_cast<size_t>(
+ base::Hasher::Combine(k.rel_pc, k.name_id.raw_id()));
+ }
+ };
+ };
+ base::FlatHashMap<FrameKey, FrameId, FrameKey::Hasher> interned_frames_;
+ base::FlatHashMap<uint64_t, std::vector<FrameId>> frames_by_rel_pc_;
+};
+
+class KernelMemoryMapping : public VirtualMemoryMapping {
+ public:
+ ~KernelMemoryMapping() override;
+
+ private:
+ friend class MappingTracker;
+ KernelMemoryMapping(TraceProcessorContext* context,
+ CreateMappingParams params);
+};
+
+class UserMemoryMapping : public VirtualMemoryMapping {
+ public:
+ ~UserMemoryMapping() override;
+ UniquePid upid() const { return upid_; }
+
+ private:
+ friend class MappingTracker;
+ UserMemoryMapping(TraceProcessorContext* context,
+ UniquePid upid,
+ CreateMappingParams params);
+
+ const UniquePid upid_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_VIRTUAL_MEMORY_MAPPING_H_
diff --git a/src/trace_processor/importers/perf/BUILD.gn b/src/trace_processor/importers/perf/BUILD.gn
index 387468f..960cd27 100644
--- a/src/trace_processor/importers/perf/BUILD.gn
+++ b/src/trace_processor/importers/perf/BUILD.gn
@@ -28,6 +28,7 @@
]
deps = [
"../../../../gn:default_deps",
+ "../../../../protos/perfetto/trace/profiling:zero",
"../../importers/common",
"../../importers/common:parser_types",
"../../sorter",
@@ -47,6 +48,8 @@
":perf",
"../../../../gn:default_deps",
"../../../../gn:gtest_and_gmock",
+ "../../../../protos/perfetto/trace/profiling:zero",
"../../../base",
+ "../../importers/common",
]
}
diff --git a/src/trace_processor/importers/perf/perf_data_parser.cc b/src/trace_processor/importers/perf/perf_data_parser.cc
index 11a5a13..bb79209 100644
--- a/src/trace_processor/importers/perf/perf_data_parser.cc
+++ b/src/trace_processor/importers/perf/perf_data_parser.cc
@@ -22,6 +22,7 @@
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/perf/perf_data_reader.h"
#include "src/trace_processor/importers/perf/perf_data_tracker.h"
@@ -58,7 +59,8 @@
// First instruction pointer in the callchain should be from kernel space, so
// it shouldn't be available in mappings.
- if (tracker_->FindMapping(*sample.pid, sample.callchain.front()).ok()) {
+ if (context_->mapping_tracker->FindUserMappingForAddress(
+ *sample.pid, sample.callchain.front())) {
context_->storage->IncrementStats(stats::perf_samples_skipped);
return;
}
@@ -70,19 +72,22 @@
std::vector<FramesTable::Row> frame_rows;
for (uint32_t i = 1; i < sample.callchain.size(); i++) {
- auto mapping = tracker_->FindMapping(*sample.pid, sample.callchain[i]);
- if (!mapping.ok()) {
+ UserMemoryMapping* mapping =
+ context_->mapping_tracker->FindUserMappingForAddress(
+ *sample.pid, sample.callchain[i]);
+ if (!mapping) {
context_->storage->IncrementStats(stats::perf_samples_skipped);
return;
}
FramesTable::Row new_row;
std::string mock_name =
- base::StackString<1024>("%" PRIu64,
- sample.callchain[i] - mapping->start)
+ base::StackString<1024>(
+ "%" PRIu64, sample.callchain[i] - mapping->memory_range().start())
.ToStdString();
new_row.name = context_->storage->InternString(mock_name.c_str());
- new_row.mapping = mapping->id;
- new_row.rel_pc = static_cast<int64_t>(sample.callchain[i] - mapping->start);
+ new_row.mapping = mapping->mapping_id();
+ new_row.rel_pc =
+ static_cast<int64_t>(mapping->ToRelativePc(sample.callchain[i]));
frame_rows.push_back(new_row);
}
diff --git a/src/trace_processor/importers/perf/perf_data_tokenizer.cc b/src/trace_processor/importers/perf/perf_data_tokenizer.cc
index be1ec02..334148a 100644
--- a/src/trace_processor/importers/perf/perf_data_tokenizer.cc
+++ b/src/trace_processor/importers/perf/perf_data_tokenizer.cc
@@ -15,6 +15,7 @@
*/
#include "src/trace_processor/importers/perf/perf_data_tokenizer.h"
+
#include <cstdint>
#include <cstring>
#include <vector>
@@ -31,9 +32,29 @@
#include "src/trace_processor/storage/stats.h"
#include "src/trace_processor/util/status_macros.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
+
namespace perfetto {
namespace trace_processor {
namespace perf_importer {
+namespace {
+protos::pbzero::Profiling::CpuMode GetCpuMode(const perf_event_header& header) {
+ switch (header.misc & kPerfRecordMiscCpumodeMask) {
+ case PERF_RECORD_MISC_KERNEL:
+ return protos::pbzero::Profiling::MODE_KERNEL;
+ case PERF_RECORD_MISC_USER:
+ return protos::pbzero::Profiling::MODE_USER;
+ case PERF_RECORD_MISC_HYPERVISOR:
+ return protos::pbzero::Profiling::MODE_HYPERVISOR;
+ case PERF_RECORD_MISC_GUEST_KERNEL:
+ return protos::pbzero::Profiling::MODE_GUEST_KERNEL;
+ case PERF_RECORD_MISC_GUEST_USER:
+ return protos::pbzero::Profiling::MODE_GUEST_USER;
+ default:
+ return protos::pbzero::Profiling::MODE_UNKNOWN;
+ }
+}
+} // namespace
PerfDataTokenizer::PerfDataTokenizer(TraceProcessorContext* ctx)
: context_(ctx),
@@ -124,6 +145,7 @@
sizeof(PerfDataTracker::Mmap2Record::Numeric));
auto record = ParseMmap2Record(record_size);
RETURN_IF_ERROR(record.status());
+ record->cpu_mode = GetCpuMode(ev_header);
tracker_->PushMmap2Record(*record);
break;
}
diff --git a/src/trace_processor/importers/perf/perf_data_tracker.cc b/src/trace_processor/importers/perf/perf_data_tracker.cc
index 0c9b209..c670258 100644
--- a/src/trace_processor/importers/perf/perf_data_tracker.cc
+++ b/src/trace_processor/importers/perf/perf_data_tracker.cc
@@ -15,12 +15,53 @@
*/
#include "src/trace_processor/importers/perf/perf_data_tracker.h"
+
+#include <optional>
+
#include "perfetto/base/status.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
namespace perfetto {
namespace trace_processor {
namespace perf_importer {
+namespace {
+
+bool IsInKernel(protos::pbzero::Profiling::CpuMode cpu_mode) {
+ switch (cpu_mode) {
+ case protos::pbzero::Profiling::MODE_UNKNOWN:
+ PERFETTO_CHECK(false);
+ case protos::pbzero::Profiling::MODE_GUEST_KERNEL:
+ case protos::pbzero::Profiling::MODE_KERNEL:
+ return true;
+ case protos::pbzero::Profiling::MODE_USER:
+ case protos::pbzero::Profiling::MODE_HYPERVISOR:
+ case protos::pbzero::Profiling::MODE_GUEST_USER:
+ return false;
+ }
+ PERFETTO_CHECK(false);
+}
+
+CreateMappingParams BuildCreateMappingParams(
+ PerfDataTracker::Mmap2Record record) {
+ return {AddressRange::FromStartAndSize(record.num.addr, record.num.len),
+ record.num.pgoff,
+ // start_offset: This is the offset into the file where the ELF header
+ // starts. We assume all file mappings are ELF files an thus this
+ // offset is 0.
+ 0,
+ // load_bias: This can only be read out of the actual ELF file, which
+ // we do not have here, so we set it to 0. When symbolizing we will
+ // hopefully have the real load bias and we can compensate there for a
+ // possible mismatch.
+ 0, record.filename, std::nullopt};
+}
+} // namespace
PerfDataTracker::~PerfDataTracker() = default;
@@ -48,31 +89,15 @@
}
void PerfDataTracker::PushMmap2Record(Mmap2Record record) {
- const auto mappings =
- context_->storage->mutable_stack_profile_mapping_table();
- MappingTable::Row row;
- row.start = static_cast<int64_t>(record.num.addr);
- row.end = static_cast<int64_t>(record.num.addr + record.num.len);
- row.name = context_->storage->InternString(record.filename.c_str());
- MappingTable::Id id = mappings->Insert(row).id;
- MmapRange mmap2_range{record.num.addr, record.num.addr + record.num.len, id};
- mmap2_ranges_[record.num.pid].push_back(mmap2_range);
-}
-
-base::StatusOr<PerfDataTracker::MmapRange> PerfDataTracker::FindMapping(
- uint32_t pid,
- uint64_t ips) {
- auto vec = mmap2_ranges_.Find(pid);
- if (!vec) {
- return base::ErrStatus("Sample pid not found in mappings.");
+ if (IsInKernel(record.cpu_mode)) {
+ context_->mapping_tracker->CreateKernelMemoryMapping(
+ BuildCreateMappingParams(std::move(record)));
+ } else {
+ UniquePid upid =
+ context_->process_tracker->GetOrCreateProcess(record.num.pid);
+ context_->mapping_tracker->CreateUserMemoryMapping(
+ upid, BuildCreateMappingParams(std::move(record)));
}
-
- for (const auto& range : *vec) {
- if (ips >= range.start && ips < range.end) {
- return range;
- }
- }
- return base::ErrStatus("No mapping for callstack frame instruction pointer");
}
base::StatusOr<PerfDataTracker::PerfSample> PerfDataTracker::ParseSample(
diff --git a/src/trace_processor/importers/perf/perf_data_tracker.h b/src/trace_processor/importers/perf/perf_data_tracker.h
index 0ab99aa..11258ed 100644
--- a/src/trace_processor/importers/perf/perf_data_tracker.h
+++ b/src/trace_processor/importers/perf/perf_data_tracker.h
@@ -25,6 +25,7 @@
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/string_utils.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
#include "src/trace_processor/importers/perf/perf_data_reader.h"
#include "src/trace_processor/importers/perf/perf_event.h"
#include "src/trace_processor/storage/trace_storage.h"
@@ -76,14 +77,10 @@
uint32_t prot;
uint32_t flags;
};
+ protos::pbzero::Profiling::CpuMode cpu_mode;
Numeric num;
std::string filename;
};
- struct MmapRange {
- uint64_t start;
- uint64_t end;
- MappingTable::Id id;
- };
PerfDataTracker(const PerfDataTracker&) = delete;
PerfDataTracker& operator=(const PerfDataTracker&) = delete;
@@ -103,14 +100,11 @@
base::StatusOr<PerfSample> ParseSample(
perfetto::trace_processor::perf_importer::Reader&);
- base::StatusOr<MmapRange> FindMapping(uint32_t pid, uint64_t ips);
-
private:
const perf_event_attr* FindAttrWithId(uint64_t id) const;
TraceProcessorContext* context_;
std::vector<AttrAndIds> attrs_;
- base::FlatHashMap</*pid=*/uint32_t, std::vector<MmapRange>> mmap2_ranges_;
uint64_t common_sample_type_;
};
} // namespace perf_importer
diff --git a/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc b/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc
index 6c59be6..3cbdc80 100644
--- a/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc
+++ b/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc
@@ -22,16 +22,35 @@
#include <vector>
#include "perfetto/base/build_config.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
#include "src/trace_processor/importers/perf/perf_event.h"
#include "test/gtest_and_gmock.h"
namespace perfetto {
namespace trace_processor {
namespace perf_importer {
+namespace {
-TEST(PerfDataTrackerUnittest, ComputeCommonSampleType) {
- TraceProcessorContext context;
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context);
+class PerfDataTrackerUnittest : public testing::Test {
+ public:
+ PerfDataTrackerUnittest() {
+ context_.storage = std::make_unique<TraceStorage>();
+ context_.process_tracker = std::make_unique<ProcessTracker>(&context_);
+ context_.stack_profile_tracker =
+ std::make_unique<StackProfileTracker>(&context_);
+ context_.mapping_tracker = std::make_unique<MappingTracker>(&context_);
+ }
+
+ protected:
+ TraceProcessorContext context_;
+};
+
+TEST_F(PerfDataTrackerUnittest, ComputeCommonSampleType) {
+ PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
PerfDataTracker::AttrAndIds attr_and_ids;
attr_and_ids.attr.sample_type =
@@ -46,16 +65,15 @@
EXPECT_FALSE(tracker->common_sample_type() & PERF_SAMPLE_CALLCHAIN);
}
-TEST(PerfDataTrackerUnittest, FindMapping) {
- TraceProcessorContext context;
- context.storage = std::make_unique<TraceStorage>();
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context);
+TEST_F(PerfDataTrackerUnittest, FindMapping) {
+ PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
PerfDataTracker::Mmap2Record rec;
rec.filename = "file1";
rec.num.addr = 1000;
rec.num.len = 100;
rec.num.pid = 1;
+ rec.cpu_mode = protos::pbzero::Profiling::MODE_USER;
tracker->PushMmap2Record(rec);
rec.num.addr = 2000;
@@ -64,32 +82,33 @@
rec.num.addr = 3000;
tracker->PushMmap2Record(rec);
- auto res_status = tracker->FindMapping(1, 2050);
- EXPECT_TRUE(res_status.ok());
- EXPECT_EQ(res_status->start, 2000u);
- EXPECT_EQ(res_status->end, 2100u);
+ UserMemoryMapping* mapping =
+ context_.mapping_tracker->FindUserMappingForAddress(
+ context_.process_tracker->GetOrCreateProcess(1), 2050);
+ ASSERT_NE(mapping, nullptr);
+ EXPECT_EQ(mapping->memory_range().start(), 2000u);
+ EXPECT_EQ(mapping->memory_range().end(), 2100u);
}
-TEST(PerfDataTrackerUnittest, FindMappingFalse) {
- TraceProcessorContext context;
- context.storage = std::make_unique<TraceStorage>();
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context);
+TEST_F(PerfDataTrackerUnittest, FindMappingFalse) {
+ PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
PerfDataTracker::Mmap2Record rec;
rec.filename = "file1";
rec.num.addr = 1000;
rec.num.len = 100;
rec.num.pid = 1;
+ rec.cpu_mode = protos::pbzero::Profiling::MODE_USER;
tracker->PushMmap2Record(rec);
- auto res_status = tracker->FindMapping(2, 2050);
- EXPECT_FALSE(res_status.ok());
+ UserMemoryMapping* mapping =
+ context_.mapping_tracker->FindUserMappingForAddress(
+ context_.process_tracker->GetOrCreateProcess(2), 2050);
+ EXPECT_EQ(mapping, nullptr);
}
-TEST(PerfDataTrackerUnittest, ParseSampleTrivial) {
- TraceProcessorContext context;
- context.storage = std::make_unique<TraceStorage>();
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context);
+TEST_F(PerfDataTrackerUnittest, ParseSampleTrivial) {
+ PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
PerfDataTracker::AttrAndIds attr_and_ids;
attr_and_ids.attr.sample_type = PERF_SAMPLE_TIME;
@@ -107,10 +126,8 @@
EXPECT_EQ(parsed_sample->ts, 100u);
}
-TEST(PerfDataTrackerUnittest, ParseSampleCallchain) {
- TraceProcessorContext context;
- context.storage = std::make_unique<TraceStorage>();
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context);
+TEST_F(PerfDataTrackerUnittest, ParseSampleCallchain) {
+ PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
PerfDataTracker::AttrAndIds attr_and_ids;
attr_and_ids.attr.sample_type = PERF_SAMPLE_CALLCHAIN;
@@ -137,10 +154,8 @@
EXPECT_EQ(parsed_sample->callchain.size(), 3u);
}
-TEST(PerfDataTrackerUnittest, ParseSampleWithoutId) {
- TraceProcessorContext context;
- context.storage = std::make_unique<TraceStorage>();
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context);
+TEST_F(PerfDataTrackerUnittest, ParseSampleWithoutId) {
+ PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
PerfDataTracker::AttrAndIds attr_and_ids;
attr_and_ids.attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
@@ -177,10 +192,8 @@
EXPECT_EQ(sample.ts, parsed_sample->ts);
}
-TEST(PerfDataTrackerUnittest, ParseSampleWithId) {
- TraceProcessorContext context;
- context.storage = std::make_unique<TraceStorage>();
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context);
+TEST_F(PerfDataTrackerUnittest, ParseSampleWithId) {
+ PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
PerfDataTracker::AttrAndIds attr_and_ids;
attr_and_ids.attr.sample_type = PERF_SAMPLE_CPU | PERF_SAMPLE_TID |
@@ -222,6 +235,7 @@
EXPECT_EQ(100u, parsed_sample->ts);
}
+} // namespace
} // namespace perf_importer
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/importers/perf/perf_event.h b/src/trace_processor/importers/perf/perf_event.h
index 53f5f05..c60709f 100644
--- a/src/trace_processor/importers/perf/perf_event.h
+++ b/src/trace_processor/importers/perf/perf_event.h
@@ -223,4 +223,15 @@
PERF_SAMPLE_MAX = 1U << 25, /* non-ABI */
};
+constexpr auto kPerfRecordMiscCpumodeMask = 0x7;
+
+enum perf_record_misc {
+ PERF_RECORD_MISC_CPUMODE_UNKNOWN = 0,
+ PERF_RECORD_MISC_KERNEL = 1,
+ PERF_RECORD_MISC_USER = 2,
+ PERF_RECORD_MISC_HYPERVISOR = 3,
+ PERF_RECORD_MISC_GUEST_KERNEL = 4,
+ PERF_RECORD_MISC_GUEST_USER = 5,
+};
+
#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PERF_PERF_EVENT_H_
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index 2eaa5fe..2e542d0 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -91,9 +91,9 @@
"../../storage",
"../../tables",
"../../types",
+ "../../util:build_id",
"../../util:gzip",
"../../util:profiler_util",
- "../../util:stack_traces_util",
"../common",
"../common:parser_types",
"../ftrace:minimal",
@@ -181,7 +181,6 @@
"../../util:profiler_util",
"../../util:proto_profiler",
"../../util:proto_to_args_parser",
- "../../util:stack_traces_util",
"../common",
"../common:parser_types",
"../etw:full",
@@ -276,7 +275,6 @@
"../../types",
"../../util:descriptors",
"../../util:profiler_util",
- "../../util:stack_traces_util",
"../common",
"../ftrace:full",
]
diff --git a/src/trace_processor/importers/proto/profile_module.cc b/src/trace_processor/importers/proto/profile_module.cc
index 61596d3..b80cae7 100644
--- a/src/trace_processor/importers/proto/profile_module.cc
+++ b/src/trace_processor/importers/proto/profile_module.cc
@@ -24,6 +24,7 @@
#include "src/trace_processor/importers/common/clock_tracker.h"
#include "src/trace_processor/importers/common/deobfuscation_mapping_table.h"
#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
#include "src/trace_processor/importers/proto/packet_sequence_state.h"
@@ -36,8 +37,8 @@
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/build_id.h"
#include "src/trace_processor/util/profiler_util.h"
-#include "src/trace_processor/util/stack_traces_util.h"
#include "protos/perfetto/common/builtin_clock.pbzero.h"
#include "protos/perfetto/common/perf_events.pbzero.h"
@@ -428,19 +429,11 @@
void ProfileModule::ParseModuleSymbols(ConstBytes blob) {
protos::pbzero::ModuleSymbols::Decoder module_symbols(blob.data, blob.size);
- StringId build_id;
- // TODO(b/148109467): Remove workaround once all active Chrome versions
- // write raw bytes instead of a string as build_id.
- if (util::IsHexModuleId(module_symbols.build_id())) {
- build_id = context_->storage->InternString(module_symbols.build_id());
- } else {
- build_id = context_->storage->InternString(base::StringView(base::ToHex(
- module_symbols.build_id().data, module_symbols.build_id().size)));
- }
+ BuildId build_id = BuildId::FromRaw(module_symbols.build_id());
- auto mapping_ids = context_->stack_profile_tracker->FindMappingRow(
- context_->storage->InternString(module_symbols.path()), build_id);
- if (mapping_ids.empty()) {
+ auto mappings =
+ context_->mapping_tracker->FindMappings(module_symbols.path(), build_id);
+ if (mappings.empty()) {
context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
return;
}
@@ -467,12 +460,11 @@
continue;
}
bool frame_found = false;
- for (MappingId mapping_id : mapping_ids) {
+ for (VirtualMemoryMapping* mapping : mappings) {
context_->args_translation_table->AddNativeSymbolTranslationRule(
- mapping_id, address_symbols.address(), last_location);
+ mapping->mapping_id(), address_symbols.address(), last_location);
std::vector<FrameId> frame_ids =
- context_->stack_profile_tracker->FindFrameIds(
- mapping_id, address_symbols.address());
+ mapping->FindFrameIds(address_symbols.address());
for (const FrameId frame_id : frame_ids) {
auto* frames = context_->storage->mutable_stack_profile_frame_table();
diff --git a/src/trace_processor/importers/proto/profile_packet_sequence_state.cc b/src/trace_processor/importers/proto/profile_packet_sequence_state.cc
index 9d889ef..31841fc 100644
--- a/src/trace_processor/importers/proto/profile_packet_sequence_state.cc
+++ b/src/trace_processor/importers/proto/profile_packet_sequence_state.cc
@@ -19,6 +19,8 @@
#include "perfetto/base/flat_set.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
#include "src/trace_processor/importers/proto/packet_sequence_state.h"
@@ -28,6 +30,7 @@
#include "src/trace_processor/storage/stats.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/build_id.h"
namespace perfetto {
namespace trace_processor {
@@ -68,17 +71,16 @@
void ProfilePacketSequenceState::AddMapping(SourceMappingId id,
const SourceMapping& mapping) {
- StackProfileTracker::CreateMappingParams params;
+ CreateMappingParams params;
if (std::string* str = strings_.Find(mapping.build_id); str) {
- params.build_id = base::StringView(*str);
+ params.build_id = BuildId::FromRaw(*str);
} else {
context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
return;
}
params.exact_offset = mapping.exact_offset;
params.start_offset = mapping.start_offset;
- params.start = mapping.start;
- params.end = mapping.end;
+ params.memory_range = AddressRange(mapping.start, mapping.end);
params.load_bias = mapping.load_bias;
std::vector<base::StringView> path_components;
@@ -93,16 +95,16 @@
break;
}
}
- std::string path = ProfilePacketUtils::MakeMappingName(path_components);
- params.name = base::StringView(path);
- MappingId mapping_id = context_->stack_profile_tracker->InternMapping(params);
- mappings_.Insert(id, mapping_id);
+
+ params.name = ProfilePacketUtils::MakeMappingName(path_components);
+ mappings_.Insert(
+ id, &context_->mapping_tracker->InternMemoryMapping(std::move(params)));
}
void ProfilePacketSequenceState::AddFrame(SourceFrameId id,
const SourceFrame& frame) {
- MappingId* mapping_id = mappings_.Find(frame.mapping_id);
- if (!mapping_id) {
+ VirtualMemoryMapping** mapping = mappings_.Find(frame.mapping_id);
+ if (!mapping) {
context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
return;
}
@@ -113,9 +115,8 @@
return;
}
- FrameId frame_id = context_->stack_profile_tracker->InternFrame(
- *mapping_id, frame.rel_pc, base::StringView(*function_name));
-
+ FrameId frame_id =
+ (*mapping)->InternFrame(frame.rel_pc, base::StringView(*function_name));
frames_.Insert(id, frame_id);
}
diff --git a/src/trace_processor/importers/proto/profile_packet_sequence_state.h b/src/trace_processor/importers/proto/profile_packet_sequence_state.h
index 678aab2..99661da 100644
--- a/src/trace_processor/importers/proto/profile_packet_sequence_state.h
+++ b/src/trace_processor/importers/proto/profile_packet_sequence_state.h
@@ -23,7 +23,6 @@
#include "perfetto/ext/base/hash.h"
#include "perfetto/ext/base/string_view.h"
-#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
#include "src/trace_processor/importers/proto/stack_profile_sequence_state.h"
#include "src/trace_processor/storage/trace_storage.h"
@@ -31,6 +30,8 @@
namespace perfetto {
namespace trace_processor {
+class VirtualMemoryMapping;
+
// Keeps sequence specific state for profile packets.
class ProfilePacketSequenceState final
: public PacketSequenceStateGeneration::InternedDataTracker {
@@ -126,7 +127,7 @@
TraceProcessorContext* const context_;
base::FlatHashMap<SourceStringId, std::string> strings_;
- base::FlatHashMap<SourceMappingId, MappingId> mappings_;
+ base::FlatHashMap<SourceMappingId, VirtualMemoryMapping*> mappings_;
base::FlatHashMap<SourceFrameId, FrameId> frames_;
base::FlatHashMap<SourceCallstackId, CallsiteId> callstacks_;
diff --git a/src/trace_processor/importers/proto/profile_packet_sequence_state_unittest.cc b/src/trace_processor/importers/proto/profile_packet_sequence_state_unittest.cc
index ab947fa..c9cd6e3 100644
--- a/src/trace_processor/importers/proto/profile_packet_sequence_state_unittest.cc
+++ b/src/trace_processor/importers/proto/profile_packet_sequence_state_unittest.cc
@@ -18,8 +18,9 @@
#include <memory>
-#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
#include "src/trace_processor/types/trace_processor_context.h"
#include "test/gtest_and_gmock.h"
@@ -58,6 +59,7 @@
public:
HeapProfileTrackerDupTest() {
context.storage.reset(new TraceStorage());
+ context.mapping_tracker.reset(new MappingTracker(&context));
context.stack_profile_tracker.reset(new StackProfileTracker(&context));
packet_sequence_state.reset(new PacketSequenceState(&context));
@@ -196,6 +198,7 @@
TEST(HeapProfileTrackerTest, SourceMappingPath) {
TraceProcessorContext context;
context.storage.reset(new TraceStorage());
+ context.mapping_tracker.reset(new MappingTracker(&context));
context.stack_profile_tracker.reset(new StackProfileTracker(&context));
PacketSequenceState pss(&context);
ProfilePacketSequenceState& ppss =
@@ -229,6 +232,7 @@
TEST(HeapProfileTrackerTest, Functional) {
TraceProcessorContext context;
context.storage.reset(new TraceStorage());
+ context.mapping_tracker.reset(new MappingTracker(&context));
context.stack_profile_tracker.reset(new StackProfileTracker(&context));
PacketSequenceState pss(&context);
diff --git a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
index 11a45f5..e612709 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -25,6 +25,7 @@
#include "src/trace_processor/importers/common/clock_tracker.h"
#include "src/trace_processor/importers/common/event_tracker.h"
#include "src/trace_processor/importers/common/flow_tracker.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/common/metadata_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/common/slice_tracker.h"
@@ -252,6 +253,7 @@
context_.track_tracker.reset(new TrackTracker(&context_));
context_.global_args_tracker.reset(
new GlobalArgsTracker(context_.storage.get()));
+ context_.mapping_tracker.reset(new MappingTracker(&context_));
context_.stack_profile_tracker.reset(new StackProfileTracker(&context_));
context_.args_tracker.reset(new ArgsTracker(&context_));
context_.args_translation_table.reset(new ArgsTranslationTable(storage_));
diff --git a/src/trace_processor/importers/proto/stack_profile_sequence_state.cc b/src/trace_processor/importers/proto/stack_profile_sequence_state.cc
index e503a09..a469f92 100644
--- a/src/trace_processor/importers/proto/stack_profile_sequence_state.cc
+++ b/src/trace_processor/importers/proto/stack_profile_sequence_state.cc
@@ -23,6 +23,8 @@
#include "perfetto/ext/base/string_view.h"
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
#include "src/trace_processor/importers/proto/packet_sequence_state.h"
#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
@@ -30,6 +32,7 @@
#include "src/trace_processor/storage/stats.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/build_id.h"
namespace perfetto {
namespace trace_processor {
@@ -48,21 +51,23 @@
std::optional<MappingId> StackProfileSequenceState::FindOrInsertMapping(
uint64_t iid) {
- if (MappingId* id = cached_mappings_.Find(iid); id) {
- return *id;
+ if (VirtualMemoryMapping* mapping = FindOrInsertMappingImpl(iid); mapping) {
+ return mapping->mapping_id();
+ }
+ return std::nullopt;
+}
+
+VirtualMemoryMapping* StackProfileSequenceState::FindOrInsertMappingImpl(
+ uint64_t iid) {
+ if (auto ptr = cached_mappings_.Find(iid); ptr) {
+ return *ptr;
}
auto* decoder =
LookupInternedMessage<protos::pbzero::InternedData::kMappingsFieldNumber,
protos::pbzero::Mapping>(iid);
if (!decoder) {
context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
- return std::nullopt;
- }
-
- std::optional<base::StringView> build_id =
- LookupInternedBuildId(decoder->build_id());
- if (!build_id) {
- return std::nullopt;
+ return nullptr;
}
std::vector<base::StringView> path_components;
@@ -75,19 +80,26 @@
}
path_components.push_back(*str);
}
- std::string path = ProfilePacketUtils::MakeMappingName(path_components);
- StackProfileTracker::CreateMappingParams params;
- params.build_id = *build_id;
+ CreateMappingParams params;
+ std::optional<base::StringView> build_id =
+ LookupInternedBuildId(decoder->build_id());
+ if (!build_id) {
+ return nullptr;
+ }
+ params.build_id = BuildId::FromRaw(*build_id);
+
+ params.memory_range = AddressRange(decoder->start(), decoder->end());
params.exact_offset = decoder->exact_offset();
params.start_offset = decoder->start_offset();
- params.start = decoder->start();
- params.end = decoder->end();
params.load_bias = decoder->load_bias();
- params.name = base::StringView(path);
- MappingId mapping_id = context_->stack_profile_tracker->InternMapping(params);
- cached_mappings_.Insert(iid, mapping_id);
- return mapping_id;
+ params.name = ProfilePacketUtils::MakeMappingName(path_components);
+
+ VirtualMemoryMapping& mapping =
+ context_->mapping_tracker->InternMemoryMapping(std::move(params));
+
+ cached_mappings_.Insert(iid, &mapping);
+ return &mapping;
}
std::optional<base::StringView>
@@ -110,11 +122,6 @@
std::optional<base::StringView>
StackProfileSequenceState::LookupInternedMappingPath(uint64_t iid) {
- // This should really be an error (value not set) or at the very least return
- // a null string, but for backward compatibility use an empty string instead.
- if (iid == 0) {
- return "";
- }
auto* decoder = LookupInternedMessage<
protos::pbzero::InternedData::kMappingPathsFieldNumber,
protos::pbzero::InternedString>(iid);
@@ -158,7 +165,7 @@
cached_callstacks_.Insert(iid, *parent_callsite_id);
- return *parent_callsite_id;
+ return parent_callsite_id;
}
std::optional<FrameId> StackProfileSequenceState::FindOrInsertFrame(
@@ -174,9 +181,9 @@
return std::nullopt;
}
- std::optional<MappingId> mapping_id =
- FindOrInsertMapping(decoder->mapping_id());
- if (!mapping_id) {
+ VirtualMemoryMapping* mapping =
+ FindOrInsertMappingImpl(decoder->mapping_id());
+ if (!mapping) {
return std::nullopt;
}
@@ -190,9 +197,7 @@
function_name = *func;
}
- FrameId frame_id = context_->stack_profile_tracker->InternFrame(
- *mapping_id, decoder->rel_pc(), function_name);
-
+ FrameId frame_id = mapping->InternFrame(decoder->rel_pc(), function_name);
cached_frames_.Insert(iid, frame_id);
return frame_id;
diff --git a/src/trace_processor/importers/proto/stack_profile_sequence_state.h b/src/trace_processor/importers/proto/stack_profile_sequence_state.h
index 7a7879a..82de785 100644
--- a/src/trace_processor/importers/proto/stack_profile_sequence_state.h
+++ b/src/trace_processor/importers/proto/stack_profile_sequence_state.h
@@ -30,6 +30,7 @@
namespace trace_processor {
class TraceProcessorContext;
+class VirtualMemoryMapping;
class StackProfileSequenceState final
: public PacketSequenceStateGeneration::InternedDataTracker {
@@ -44,13 +45,15 @@
std::optional<CallsiteId> FindOrInsertCallstack(uint64_t iid);
private:
+ // Returns `nullptr`if non could be found.
+ VirtualMemoryMapping* FindOrInsertMappingImpl(uint64_t iid);
std::optional<base::StringView> LookupInternedBuildId(uint64_t iid);
std::optional<base::StringView> LookupInternedMappingPath(uint64_t iid);
std::optional<base::StringView> LookupInternedFunctionName(uint64_t iid);
std::optional<FrameId> FindOrInsertFrame(uint64_t iid);
TraceProcessorContext* const context_;
- base::FlatHashMap<uint64_t, MappingId> cached_mappings_;
+ base::FlatHashMap<uint64_t, VirtualMemoryMapping*> cached_mappings_;
base::FlatHashMap<uint64_t, CallsiteId> cached_callstacks_;
base::FlatHashMap<uint64_t, FrameId> cached_frames_;
};
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index 908fdea..2ee7b3d 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -27,6 +27,7 @@
#include "src/trace_processor/importers/common/event_tracker.h"
#include "src/trace_processor/importers/common/flow_tracker.h"
#include "src/trace_processor/importers/common/global_args_tracker.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/common/metadata_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/common/sched_event_tracker.h"
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index 1f06a0f..b0c7a15 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -26,6 +26,7 @@
#include "src/trace_processor/importers/common/clock_tracker.h"
#include "src/trace_processor/importers/common/event_tracker.h"
#include "src/trace_processor/importers/common/flow_tracker.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/common/metadata_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/common/sched_event_tracker.h"
@@ -64,6 +65,7 @@
context_.process_tracker.reset(new ProcessTracker(&context_));
context_.clock_tracker.reset(new ClockTracker(&context_));
context_.clock_converter.reset(new ClockConverter(&context_));
+ context_.mapping_tracker.reset(new MappingTracker(&context_));
context_.perf_sample_tracker.reset(new PerfSampleTracker(&context_));
context_.stack_profile_tracker.reset(new StackProfileTracker(&context_));
context_.metadata_tracker.reset(new MetadataTracker(context_.storage.get()));
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index 3b3336c..26d1ff5 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -55,6 +55,7 @@
class StackProfileTracker;
class HeapGraphTracker;
class PerfSampleTracker;
+class MappingTracker;
class MetadataTracker;
class PacketAnalyzer;
class ProtoImporterModule;
@@ -102,6 +103,7 @@
std::unique_ptr<SchedEventTracker> sched_event_tracker;
std::unique_ptr<ClockTracker> clock_tracker;
std::unique_ptr<ClockConverter> clock_converter;
+ std::unique_ptr<MappingTracker> mapping_tracker;
std::unique_ptr<PerfSampleTracker> perf_sample_tracker;
std::unique_ptr<StackProfileTracker> stack_profile_tracker;
std::unique_ptr<MetadataTracker> metadata_tracker;
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index 8420a06..13aefe5 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -61,6 +61,17 @@
}
}
+source_set("build_id") {
+ sources = [
+ "build_id.cc",
+ "build_id.h",
+ ]
+ deps = [
+ "../../../gn:default_deps",
+ "../../../include/perfetto/ext/base:base",
+ ]
+}
+
source_set("profiler_util") {
sources = [
"profiler_util.cc",
@@ -74,18 +85,6 @@
]
}
-source_set("stack_traces_util") {
- sources = [
- "stack_traces_util.cc",
- "stack_traces_util.h",
- ]
- deps = [
- "../../../gn:default_deps",
- "../../../include/perfetto/ext/base:base",
- "../../../protos/perfetto/trace/profiling:zero",
- ]
-}
-
source_set("protozero_to_text") {
sources = [
"protozero_to_text.cc",
diff --git a/src/trace_processor/util/build_id.cc b/src/trace_processor/util/build_id.cc
new file mode 100644
index 0000000..20d76dd
--- /dev/null
+++ b/src/trace_processor/util/build_id.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 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 "src/trace_processor/util/build_id.h"
+
+#include <cctype>
+#include <cstddef>
+#include <string>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+uint8_t HexToBinary(char c) {
+ switch (c) {
+ case '0':
+ return 0;
+ case '1':
+ return 1;
+ case '2':
+ return 2;
+ case '3':
+ return 3;
+ case '4':
+ return 4;
+ case '5':
+ return 5;
+ case '6':
+ return 6;
+ case '7':
+ return 7;
+ case '8':
+ return 8;
+ case '9':
+ return 9;
+ case 'a':
+ case 'A':
+ return 10;
+ case 'b':
+ case 'B':
+ return 11;
+ case 'c':
+ case 'C':
+ return 12;
+ case 'd':
+ case 'D':
+ return 13;
+ case 'e':
+ case 'E':
+ return 14;
+ case 'f':
+ case 'F':
+ return 15;
+ default:
+ PERFETTO_CHECK(false);
+ }
+}
+
+std::string HexToBinary(base::StringView hex) {
+ std::string res;
+ res.reserve((hex.size() + 1) / 2);
+ auto it = hex.begin();
+
+ if (hex.size() % 2 != 0) {
+ res.push_back(static_cast<char>(HexToBinary(*it)));
+ ++it;
+ }
+
+ while (it != hex.end()) {
+ int v = (HexToBinary(*it++) << 4);
+ v += HexToBinary(*it++);
+ res.push_back(static_cast<char>(v));
+ }
+ return res;
+}
+
+// Returns whether this string is of a hex chrome module or not to decide
+// whether to convert the module to/from hex.
+// TODO(b/148109467): Remove workaround once all active Chrome versions
+// write raw bytes instead of a string as build_id.
+bool IsHexModuleId(base::StringView module) {
+ return module.size() == 33;
+}
+
+} // namespace
+
+// static
+BuildId BuildId::FromHex(base::StringView data) {
+ if (IsHexModuleId(data)) {
+ return BuildId(data.ToStdString());
+ }
+ return BuildId(HexToBinary(data));
+}
+
+std::string BuildId::ToHex() const {
+ if (IsHexModuleId(base::StringView(raw_))) {
+ return raw_;
+ }
+ return base::ToHex(raw_.data(), raw_.size());
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/util/build_id.h b/src/trace_processor/util/build_id.h
new file mode 100644
index 0000000..64ec9ac
--- /dev/null
+++ b/src/trace_processor/util/build_id.h
@@ -0,0 +1,84 @@
+/*
+ * 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 SRC_TRACE_PROCESSOR_UTIL_BUILD_ID_H_
+#define SRC_TRACE_PROCESSOR_UTIL_BUILD_ID_H_
+
+#include <string>
+#include <utility>
+
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/ext/base/string_view.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Represents the unique identifier of an executable, shared library, or module.
+// For example for ELF files this is the id stored in the .note.gnu.build-id
+// section. Sometimes a breakpad module id is used.
+// This class abstracts away the details of where this id comes from and how it
+// is converted to a StringId which is the representation used by tables in
+// trace_processor.
+class BuildId {
+ public:
+ // Allow hashing with base::Hash.
+ static constexpr bool kHashable = true;
+ size_t size() const { return raw_.size(); }
+ const char* data() const { return raw_.data(); }
+
+ static BuildId FromHex(base::StringView data);
+
+ static BuildId FromRaw(base::StringView data) {
+ return BuildId(data.ToStdString());
+ }
+ static BuildId FromRaw(std::string data) { return BuildId(std::move(data)); }
+ static BuildId FromRaw(const uint8_t* data, size_t size) {
+ return BuildId(std::string(reinterpret_cast<const char*>(data), size));
+ }
+
+ BuildId(const BuildId&) = default;
+ BuildId(BuildId&&) = default;
+
+ BuildId& operator=(const BuildId&) = default;
+ BuildId& operator=(BuildId&&) = default;
+
+ bool operator==(const BuildId& o) const { return raw_ == o.raw_; }
+
+ bool operator!=(const BuildId& o) const { return !(*this == o); }
+
+ bool operator<(const BuildId& o) const { return raw_ < o.raw_; }
+
+ std::string ToHex() const;
+
+ const std::string& raw() const { return raw_; }
+
+ private:
+ explicit BuildId(std::string data) : raw_(std::move(data)) {}
+ std::string raw_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+template <>
+struct std::hash<perfetto::trace_processor::BuildId> {
+ std::size_t operator()(
+ const perfetto::trace_processor::BuildId& o) const noexcept {
+ return perfetto::base::Hasher::Combine(o);
+ }
+};
+
+#endif // SRC_TRACE_PROCESSOR_UTIL_BUILD_ID_H_
diff --git a/src/trace_processor/util/stack_traces_util.cc b/src/trace_processor/util/stack_traces_util.cc
deleted file mode 100644
index a255560..0000000
--- a/src/trace_processor/util/stack_traces_util.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2022 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 "src/trace_processor/util/stack_traces_util.h"
-#include "perfetto/ext/base/string_view.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace util {
-
-bool IsHexModuleId(base::StringView module) {
- return module.size() == 33;
-}
-
-} // namespace util
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/util/stack_traces_util.h b/src/trace_processor/util/stack_traces_util.h
deleted file mode 100644
index 1171786..0000000
--- a/src/trace_processor/util/stack_traces_util.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2022 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 SRC_TRACE_PROCESSOR_UTIL_STACK_TRACES_UTIL_H_
-#define SRC_TRACE_PROCESSOR_UTIL_STACK_TRACES_UTIL_H_
-
-#include "perfetto/ext/base/string_view.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace util {
-
-// Returns whether this string is of a hex chrome module or not to decide
-// whether to convert the module to/from hex.
-// TODO(b/148109467): Remove workaround once all active Chrome versions
-// write raw bytes instead of a string as build_id.
-bool IsHexModuleId(base::StringView module);
-
-} // namespace util
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_UTIL_STACK_TRACES_UTIL_H_