Merge "Add unaggregated metrics for Garbage Collection" into main
diff --git a/Android.bp b/Android.bp
index cc7b4ad..a0269f3 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,14 +11110,18 @@
"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",
"src/trace_processor/importers/common/slice_tracker.cc",
"src/trace_processor/importers/common/slice_translation_table.cc",
"src/trace_processor/importers/common/stack_profile_tracker.cc",
"src/trace_processor/importers/common/system_info_tracker.cc",
+ "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",
],
}
@@ -11146,6 +11150,7 @@
"src/trace_processor/importers/common/process_tracker_unittest.cc",
"src/trace_processor/importers/common/slice_tracker_unittest.cc",
"src/trace_processor/importers/common/slice_translation_table_unittest.cc",
+ "src/trace_processor/importers/common/thread_state_tracker_unittest.cc",
],
}
@@ -11174,14 +11179,13 @@
"src/trace_processor/importers/ftrace/drm_tracker.cc",
"src/trace_processor/importers/ftrace/ftrace_module_impl.cc",
"src/trace_processor/importers/ftrace/ftrace_parser.cc",
+ "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.cc",
"src/trace_processor/importers/ftrace/ftrace_tokenizer.cc",
"src/trace_processor/importers/ftrace/gpu_work_period_tracker.cc",
"src/trace_processor/importers/ftrace/iostat_tracker.cc",
"src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc",
"src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.cc",
"src/trace_processor/importers/ftrace/rss_stat_tracker.cc",
- "src/trace_processor/importers/ftrace/sched_event_tracker.cc",
- "src/trace_processor/importers/ftrace/thread_state_tracker.cc",
"src/trace_processor/importers/ftrace/v4l2_tracker.cc",
"src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc",
"src/trace_processor/importers/ftrace/virtio_video_tracker.cc",
@@ -11201,8 +11205,7 @@
name: "perfetto_src_trace_processor_importers_ftrace_unittests",
srcs: [
"src/trace_processor/importers/ftrace/binder_tracker_unittest.cc",
- "src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc",
- "src/trace_processor/importers/ftrace/thread_state_tracker_unittest.cc",
+ "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker_unittest.cc",
],
}
@@ -12059,6 +12062,10 @@
"src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span.sql",
"src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql",
"src/trace_processor/perfetto_sql/stdlib/sched/thread_state_flattened.sql",
+ "src/trace_processor/perfetto_sql/stdlib/sched/utilization/general.sql",
+ "src/trace_processor/perfetto_sql/stdlib/sched/utilization/process.sql",
+ "src/trace_processor/perfetto_sql/stdlib/sched/utilization/system.sql",
+ "src/trace_processor/perfetto_sql/stdlib/sched/utilization/thread.sql",
"src/trace_processor/perfetto_sql/stdlib/slices/flat_slices.sql",
"src/trace_processor/perfetto_sql/stdlib/slices/slices.sql",
"src/trace_processor/perfetto_sql/stdlib/slices/with_context.sql",
@@ -12318,6 +12325,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",
@@ -12418,14 +12433,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",
@@ -13953,6 +13960,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",
@@ -13966,7 +13974,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",
@@ -14656,6 +14663,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",
@@ -14669,7 +14677,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",
@@ -14890,6 +14897,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",
@@ -14903,7 +14911,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 0fd0cb8..bd40277 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,10 +1476,15 @@
"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",
"src/trace_processor/importers/common/process_tracker.h",
+ "src/trace_processor/importers/common/sched_event_state.h",
+ "src/trace_processor/importers/common/sched_event_tracker.cc",
+ "src/trace_processor/importers/common/sched_event_tracker.h",
"src/trace_processor/importers/common/slice_tracker.cc",
"src/trace_processor/importers/common/slice_tracker.h",
"src/trace_processor/importers/common/slice_translation_table.cc",
@@ -1487,9 +1493,13 @@
"src/trace_processor/importers/common/stack_profile_tracker.h",
"src/trace_processor/importers/common/system_info_tracker.cc",
"src/trace_processor/importers/common/system_info_tracker.h",
+ "src/trace_processor/importers/common/thread_state_tracker.cc",
+ "src/trace_processor/importers/common/thread_state_tracker.h",
"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",
],
)
@@ -1541,6 +1551,8 @@
"src/trace_processor/importers/ftrace/ftrace_module_impl.h",
"src/trace_processor/importers/ftrace/ftrace_parser.cc",
"src/trace_processor/importers/ftrace/ftrace_parser.h",
+ "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.cc",
+ "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h",
"src/trace_processor/importers/ftrace/ftrace_tokenizer.cc",
"src/trace_processor/importers/ftrace/ftrace_tokenizer.h",
"src/trace_processor/importers/ftrace/gpu_work_period_tracker.cc",
@@ -1553,10 +1565,6 @@
"src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.h",
"src/trace_processor/importers/ftrace/rss_stat_tracker.cc",
"src/trace_processor/importers/ftrace/rss_stat_tracker.h",
- "src/trace_processor/importers/ftrace/sched_event_tracker.cc",
- "src/trace_processor/importers/ftrace/sched_event_tracker.h",
- "src/trace_processor/importers/ftrace/thread_state_tracker.cc",
- "src/trace_processor/importers/ftrace/thread_state_tracker.h",
"src/trace_processor/importers/ftrace/v4l2_tracker.cc",
"src/trace_processor/importers/ftrace/v4l2_tracker.h",
"src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc",
@@ -2484,6 +2492,17 @@
],
)
+# GN target: //src/trace_processor/perfetto_sql/stdlib/sched/utilization:utilization
+perfetto_filegroup(
+ name = "src_trace_processor_perfetto_sql_stdlib_sched_utilization_utilization",
+ srcs = [
+ "src/trace_processor/perfetto_sql/stdlib/sched/utilization/general.sql",
+ "src/trace_processor/perfetto_sql/stdlib/sched/utilization/process.sql",
+ "src/trace_processor/perfetto_sql/stdlib/sched/utilization/system.sql",
+ "src/trace_processor/perfetto_sql/stdlib/sched/utilization/thread.sql",
+ ],
+)
+
# GN target: //src/trace_processor/perfetto_sql/stdlib/sched:sched
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_stdlib_sched_sched",
@@ -2530,6 +2549,7 @@
":src_trace_processor_perfetto_sql_stdlib_pkvm_pkvm",
":src_trace_processor_perfetto_sql_stdlib_prelude_prelude",
":src_trace_processor_perfetto_sql_stdlib_sched_sched",
+ ":src_trace_processor_perfetto_sql_stdlib_sched_utilization_utilization",
":src_trace_processor_perfetto_sql_stdlib_slices_slices",
":src_trace_processor_perfetto_sql_stdlib_time_time",
],
@@ -2683,6 +2703,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",
@@ -2802,15 +2831,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",
@@ -5628,6 +5648,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",
@@ -5641,7 +5662,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",
@@ -5799,6 +5819,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",
@@ -5812,7 +5833,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",
@@ -5890,7 +5910,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",
],
@@ -6022,6 +6042,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",
@@ -6035,7 +6056,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/containers/BUILD.gn b/src/trace_processor/containers/BUILD.gn
index 08cddfc..59f42c9 100644
--- a/src/trace_processor/containers/BUILD.gn
+++ b/src/trace_processor/containers/BUILD.gn
@@ -66,6 +66,7 @@
":containers",
"../../../gn:benchmark",
"../../../gn:default_deps",
+ "../../base",
]
sources = [
"bit_vector_benchmark.cc",
diff --git a/src/trace_processor/containers/bit_vector.cc b/src/trace_processor/containers/bit_vector.cc
index 43a5279..c4f2fe7 100644
--- a/src/trace_processor/containers/bit_vector.cc
+++ b/src/trace_processor/containers/bit_vector.cc
@@ -16,17 +16,28 @@
#include "src/trace_processor/containers/bit_vector.h"
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <initializer_list>
#include <limits>
+#include <utility>
+#include <vector>
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/base/compiler.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/public/compiler.h"
+#include "src/trace_processor/containers/bit_vector_iterators.h"
#include "protos/perfetto/trace_processor/serialization.pbzero.h"
-#include "src/trace_processor/containers/bit_vector_iterators.h"
#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
#include <immintrin.h>
#endif
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
namespace {
// This function implements the PDEP instruction in x64 as a loop.
@@ -103,7 +114,7 @@
// Compute the address of the new last bit in the bitvector.
Address last_addr = IndexToAddress(new_size - 1);
- uint32_t old_blocks_size = static_cast<uint32_t>(counts_.size());
+ auto old_blocks_size = static_cast<uint32_t>(counts_.size());
uint32_t new_blocks_size = last_addr.block_idx + 1;
// Resize the block and count vectors to have the correct number of entries.
@@ -158,22 +169,27 @@
}
BitVector BitVector::Copy() const {
- return BitVector(words_, counts_, size_);
-}
-
-BitVector::AllBitsIterator BitVector::IterateAllBits() const {
- return AllBitsIterator(this);
+ return {words_, counts_, size_};
}
BitVector::SetBitsIterator BitVector::IterateSetBits() const {
- return SetBitsIterator(this);
+ return {this};
}
void BitVector::Not() {
- for (uint32_t i = 0; i < words_.size(); ++i) {
- BitWord(&words_[i]).Not();
+ if (size_ == 0) {
+ return;
}
+ for (uint64_t& word : words_) {
+ BitWord(&word).Not();
+ }
+
+ // Make sure to reset the last block's trailing bits to zero to preserve the
+ // invariant of BitVector.
+ Address last_addr = IndexToAddress(size_ - 1);
+ BlockFromIndex(last_addr.block_idx).ClearAfter(last_addr.block_offset);
+
for (uint32_t i = 1; i < counts_.size(); ++i) {
counts_[i] = kBitsInBlock * i - counts_[i];
}
@@ -238,7 +254,7 @@
if (PERFETTO_UNLIKELY(current == 0))
continue;
- uint8_t popcount = static_cast<uint8_t>(PERFETTO_POPCOUNT(current));
+ auto popcount = static_cast<uint8_t>(PERFETTO_POPCOUNT(current));
PERFETTO_DCHECK(popcount >= 1);
// Check if we have enough unused bits from the previous iteration - if so,
@@ -297,17 +313,27 @@
PERFETTO_DCHECK(update.CountSetBits() == CountSetBits());
}
+void BitVector::SelectBits(const BitVector& mask_bv) {
+ BitVector::Builder res(mask_bv.CountSetBits(size_));
+ for (auto it = mask_bv.IterateSetBits(); it && it.index() < size();
+ it.Next()) {
+ res.Append(IsSet(it.index()));
+ }
+ *this = std::move(res).Build();
+}
+
BitVector BitVector::FromSortedIndexVector(
const std::vector<int64_t>& indices) {
// The rest of the algorithm depends on |indices| being non empty.
if (indices.empty()) {
- return BitVector();
+ return {};
}
- // We are creating the smallest BitVector that can have all of the values from
- // |indices| set. As we assume that |indices| is sorted, the size would be the
- // last element + 1 and the last bit of the final BitVector will be set.
- uint32_t size = static_cast<uint32_t>(indices.back() + 1);
+ // We are creating the smallest BitVector that can have all of the values
+ // from |indices| set. As we assume that |indices| is sorted, the size would
+ // be the last element + 1 and the last bit of the final BitVector will be
+ // set.
+ auto size = static_cast<uint32_t>(indices.back() + 1);
uint32_t block_count = BlockCount(size);
std::vector<uint64_t> words(block_count * Block::kWords);
@@ -319,13 +345,13 @@
std::vector<uint32_t> counts(block_count);
for (uint32_t i = 1; i < counts.size(); ++i) {
- // The number of set bits in each block is the number of set bits before and
- // in the previous block.
+ // The number of set bits in each block is the number of set bits before
+ // and in the previous block.
counts[i] = counts[i - 1] +
ConstBlock(&words[Block::kWords * (i - 1)]).CountSetBits();
}
- return BitVector(words, counts, size);
+ return {words, counts, size};
}
BitVector BitVector::IntersectRange(uint32_t range_start,
@@ -335,7 +361,7 @@
uint32_t end_idx = std::min(range_end, size());
if (range_start >= end_idx)
- return BitVector();
+ return {};
Builder builder(end_idx, range_start);
uint32_t front_bits = builder.BitsUntilWordBoundaryOrFull();
@@ -361,6 +387,15 @@
return std::move(builder).Build();
}
+std::vector<uint32_t> BitVector::GetSetBitIndices() const {
+ std::vector<uint32_t> res;
+ res.reserve(CountSetBits());
+ for (auto it = IterateSetBits(); it; it.Next()) {
+ res.push_back(it.index());
+ }
+ return res;
+}
+
void BitVector::Serialize(
protos::pbzero::SerializedColumn::BitVector* msg) const {
msg->set_size(size_);
@@ -394,5 +429,4 @@
}
}
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/containers/bit_vector.h b/src/trace_processor/containers/bit_vector.h
index c7d67c5..b50165d 100644
--- a/src/trace_processor/containers/bit_vector.h
+++ b/src/trace_processor/containers/bit_vector.h
@@ -17,33 +17,27 @@
#ifndef SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_H_
#define SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_H_
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-
#include <algorithm>
-#include <array>
-#include <cstring>
-#include <optional>
+#include <cstdint>
+#include <initializer_list>
+#include <iterator>
+#include <utility>
#include <vector>
+#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
+#include "perfetto/public/compiler.h"
namespace perfetto {
-
-namespace protos {
-namespace pbzero {
+namespace protos::pbzero {
class SerializedColumn_BitVector;
class SerializedColumn_BitVector_Decoder;
-} // namespace pbzero
-} // namespace protos
+} // namespace protos::pbzero
namespace trace_processor {
-
namespace internal {
class BaseIterator;
-class AllBitsIterator;
class SetBitsIterator;
} // namespace internal
@@ -52,9 +46,6 @@
// for each bool.
class BitVector {
public:
- using AllBitsIterator = internal::AllBitsIterator;
- using SetBitsIterator = internal::SetBitsIterator;
-
static constexpr uint32_t kBitsInWord = 64;
// Builder class which allows efficiently creating a BitVector by appending
@@ -95,7 +86,7 @@
// Creates a BitVector from this Builder.
BitVector Build() && {
if (size_ == 0)
- return BitVector();
+ return {};
std::vector<uint32_t> counts(BlockCount(size_));
PERFETTO_CHECK(skipped_blocks_ <= counts.size());
@@ -103,13 +94,13 @@
counts[i] = counts[i - 1] +
ConstBlock(&words_[Block::kWords * (i - 1)]).CountSetBits();
}
- return BitVector(std::move(words_), std::move(counts), size_);
+ return {std::move(words_), std::move(counts), size_};
}
// Returns the number of bits which are in complete words which can be
// appended to this builder before having to fallback to |Append| due to
// being close to the end.
- uint32_t BitsInCompleteWordsUntilFull() {
+ uint32_t BitsInCompleteWordsUntilFull() const {
uint32_t next_word = WordCount(global_bit_offset_);
uint32_t end_word = WordFloor(size_);
uint32_t complete_words = next_word < end_word ? end_word - next_word : 0;
@@ -120,7 +111,7 @@
// hitting a word boundary (and thus able to use |AppendWord|) or until the
// BitVector is full (i.e. no more Appends should happen), whichever would
// happen first.
- uint32_t BitsUntilWordBoundaryOrFull() {
+ uint32_t BitsUntilWordBoundaryOrFull() const {
if (global_bit_offset_ == 0 && size_ < BitWord::kBits) {
return size_;
}
@@ -132,7 +123,7 @@
// Returns the number of bits which should be appended using |Append| before
// hitting a word boundary (and thus able to use |AppendWord|) or until the
// BitVector is full (i.e. no more Appends should happen).
- uint32_t BitsUntilFull() { return size_ - global_bit_offset_; }
+ uint32_t BitsUntilFull() const { return size_ - global_bit_offset_; }
private:
std::vector<uint64_t> words_;
@@ -144,11 +135,14 @@
// Creates an empty BitVector.
BitVector();
- explicit BitVector(std::initializer_list<bool> init);
+ BitVector(std::initializer_list<bool> init);
// Creates a BitVector of |count| size filled with |value|.
explicit BitVector(uint32_t count, bool value = false);
+ BitVector(const BitVector&) = delete;
+ BitVector& operator=(const BitVector&) = delete;
+
// Enable moving BitVectors as they have no unmovable state.
BitVector(BitVector&&) noexcept = default;
BitVector& operator=(BitVector&&) = default;
@@ -238,7 +232,7 @@
if (PERFETTO_LIKELY(!old_value)) {
BlockFromIndex(addr.block_idx).Set(addr.block_offset);
- uint32_t size = static_cast<uint32_t>(counts_.size());
+ auto size = static_cast<uint32_t>(counts_.size());
for (uint32_t i = addr.block_idx + 1; i < size; ++i) {
counts_[i]++;
}
@@ -259,7 +253,7 @@
if (PERFETTO_LIKELY(old_value)) {
BlockFromIndex(addr.block_idx).Clear(addr.block_offset);
- uint32_t size = static_cast<uint32_t>(counts_.size());
+ auto size = static_cast<uint32_t>(counts_.size());
for (uint32_t i = addr.block_idx + 1; i < size; ++i) {
counts_[i]--;
}
@@ -381,24 +375,19 @@
// other: 0 1 1 0
// This will change this to the following:
// this: 0 1 0 0 1 0 0
- // TODO(lalitm): investigate whether we should just change this to And.
void UpdateSetBits(const BitVector& other);
- // Iterate all the bits in the BitVector.
+ // For each set bit position in |other|, Selects the value of each bit in
+ // |this| and stores them contiguously in |this|.
//
- // Usage:
- // for (auto it = bv.IterateAllBits(); it; it.Next()) {
- // ...
- // }
- AllBitsIterator IterateAllBits() const;
-
- // Iterate all the set bits in the BitVector.
+ // Precondition: |this.size()| <= |other.size()|.
//
- // Usage:
- // for (auto it = bv.IterateSetBits(); it; it.Next()) {
- // ...
- // }
- SetBitsIterator IterateSetBits() const;
+ // For example suppose the following:
+ // this: 1 1 0 0 1 0 1
+ // other: 0 1 0 1 0 1 0 0 1 0
+ // |this| will change this to the following:
+ // this: 1 0 0
+ void SelectBits(const BitVector& other);
// Returns the approximate cost (in bytes) of storing a BitVector with size
// |n|. This can be used to make decisions about whether using a BitVector is
@@ -411,6 +400,10 @@
return BlockCount(n) * Block::kBits + BlockCount(n) * sizeof(uint32_t);
}
+ // Returns a vector<uint32_t> containing the indices of all the set bits
+ // in the BitVector.
+ std::vector<uint32_t> GetSetBitIndices() const;
+
// Serialize internals of BitVector to proto.
void Serialize(protos::pbzero::SerializedColumn_BitVector* msg) const;
@@ -419,8 +412,8 @@
const protos::pbzero::SerializedColumn_BitVector_Decoder& bv_msg);
private:
+ using SetBitsIterator = internal::SetBitsIterator;
friend class internal::BaseIterator;
- friend class internal::AllBitsIterator;
friend class internal::SetBitsIterator;
// Represents the offset of a bit within a block.
@@ -636,9 +629,6 @@
// On x86 architectures we generally target for trace processor, the
// size of a cache line is 64 bytes (or 512 bits). For this reason,
// we make the size of the block contain 8 atoms as 8 * 64 == 512.
- //
- // TODO(lalitm): investigate whether we should tune this value for
- // WASM and ARM.
class Block {
public:
// See class documentation for how these constants are chosen.
@@ -812,9 +802,6 @@
std::vector<uint32_t> counts,
uint32_t size);
- BitVector(const BitVector&) = delete;
- BitVector& operator=(const BitVector&) = delete;
-
// Returns the number of 8 elements blocks in the BitVector.
uint32_t BlockCount() {
return static_cast<uint32_t>(words_.size()) / Block::kWords;
@@ -876,6 +863,14 @@
}
}
+ // Iterate all the set bits in the BitVector.
+ //
+ // Usage:
+ // for (auto it = bv.IterateSetBits(); it; it.Next()) {
+ // ...
+ // }
+ SetBitsIterator IterateSetBits() const;
+
// Returns the index of the word which would store |idx|.
static constexpr uint32_t WordFloor(uint32_t idx) {
return idx / BitWord::kBits;
diff --git a/src/trace_processor/containers/bit_vector_benchmark.cc b/src/trace_processor/containers/bit_vector_benchmark.cc
index 55d3307..de4e85d 100644
--- a/src/trace_processor/containers/bit_vector_benchmark.cc
+++ b/src/trace_processor/containers/bit_vector_benchmark.cc
@@ -12,13 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <cstdint>
#include <limits>
#include <random>
+#include <vector>
#include <benchmark/benchmark.h>
+#include "perfetto/base/logging.h"
#include "src/trace_processor/containers/bit_vector.h"
-#include "src/trace_processor/containers/bit_vector_iterators.h"
namespace {
@@ -48,7 +50,7 @@
}
}
-void UpdateSetBitsArgs(benchmark::internal::Benchmark* b) {
+void UpdateSetBitsSelectBitsArgs(benchmark::internal::Benchmark* b) {
if (IsBenchmarkFunctionalOnly()) {
b->Args({64, 50, 50});
} else {
@@ -199,6 +201,29 @@
}
BENCHMARK(BM_BitVectorCountSetBits)->Apply(BitVectorArgs);
+static void BM_BitVectorGetSetBitIndices(benchmark::State& state) {
+ static constexpr uint32_t kRandomSeed = 42;
+ std::minstd_rand0 rnd_engine(kRandomSeed);
+
+ auto size = static_cast<uint32_t>(state.range(0));
+ auto set_percentage = static_cast<uint32_t>(state.range(1));
+
+ BitVector bv;
+ for (uint32_t i = 0; i < size; ++i) {
+ bool value = rnd_engine() % 100 < set_percentage;
+ if (value) {
+ bv.AppendTrue();
+ } else {
+ bv.AppendFalse();
+ }
+ }
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(bv.GetSetBitIndices());
+ }
+}
+BENCHMARK(BM_BitVectorGetSetBitIndices)->Apply(BitVectorArgs);
+
static void BM_BitVectorResize(benchmark::State& state) {
static constexpr uint32_t kRandomSeed = 42;
std::minstd_rand0 rnd_engine(kRandomSeed);
@@ -265,20 +290,50 @@
picker_set_bit_count, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
}
-BENCHMARK(BM_BitVectorUpdateSetBits)->Apply(UpdateSetBitsArgs);
+BENCHMARK(BM_BitVectorUpdateSetBits)->Apply(UpdateSetBitsSelectBitsArgs);
-static void BM_BitVectorSetBitsIterator(benchmark::State& state) {
- uint32_t size = static_cast<uint32_t>(state.range(0));
- uint32_t set_percentage = static_cast<uint32_t>(state.range(1));
+static void BM_BitVectorSelectBits(benchmark::State& state) {
+ static constexpr uint32_t kRandomSeed = 42;
+ std::minstd_rand0 rnd_engine(kRandomSeed);
- BitVector bv = BvWithSizeAndSetPercentage(size, set_percentage);
- for (auto _ : state) {
- for (auto it = bv.IterateSetBits(); it; it.Next()) {
- benchmark::DoNotOptimize(it.index());
+ auto size = static_cast<uint32_t>(state.range(0));
+ auto set_percentage = static_cast<uint32_t>(state.range(1));
+ auto mask_set_percentage = static_cast<uint32_t>(state.range(2));
+
+ BitVector bv;
+ BitVector mask;
+ for (uint32_t i = 0; i < size; ++i) {
+ bool value = rnd_engine() % 100 < set_percentage;
+ if (value) {
+ bv.AppendTrue();
+ } else {
+ bv.AppendFalse();
+ }
+ bool mask_value = rnd_engine() % 100 < mask_set_percentage;
+ if (mask_value) {
+ mask.AppendTrue();
+ } else {
+ mask.AppendFalse();
}
}
+
+ uint32_t set_bit_count = bv.CountSetBits();
+ uint32_t mask_set_bit_count = mask.CountSetBits();
+
+ for (auto _ : state) {
+ BitVector copy = bv.Copy();
+ copy.SelectBits(mask);
+ benchmark::DoNotOptimize(copy);
+ }
+
+ state.counters["s/set bit"] = benchmark::Counter(
+ set_bit_count, benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
+ state.counters["s/mask bit"] = benchmark::Counter(
+ mask_set_bit_count, benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
}
-BENCHMARK(BM_BitVectorSetBitsIterator)->Apply(BitVectorArgs);
+BENCHMARK(BM_BitVectorSelectBits)->Apply(UpdateSetBitsSelectBitsArgs);
static void BM_BitVectorFromIndexVector(benchmark::State& state) {
std::vector<int64_t> indices;
diff --git a/src/trace_processor/containers/bit_vector_iterators.cc b/src/trace_processor/containers/bit_vector_iterators.cc
index 076a129..271f0fb 100644
--- a/src/trace_processor/containers/bit_vector_iterators.cc
+++ b/src/trace_processor/containers/bit_vector_iterators.cc
@@ -16,49 +16,12 @@
#include "src/trace_processor/containers/bit_vector_iterators.h"
-namespace perfetto {
-namespace trace_processor {
-namespace internal {
+namespace perfetto::trace_processor::internal {
BaseIterator::BaseIterator(BitVector* bv)
: size_(bv->size()), bv_(bv), block_(bv_->words_.data()) {}
-BaseIterator::~BaseIterator() {
- if (size_ > 0) {
- uint32_t block_idx = bv_->IndexToAddress(index_).block_idx;
- uint32_t last_block_idx = bv_->BlockCount() - 1;
-
- // If |index_| == |size_| and the last index was on a block boundary, we
- // can end up one block past the end of the bitvector. Take the
- // min of the block index and the last block
- OnBlockChange(std::min(block_idx, last_block_idx), last_block_idx);
- }
-}
-
-void BaseIterator::OnBlockChange(uint32_t old_block_idx,
- uint32_t new_block_idx) {
- if (set_bit_count_diff_ != 0) {
- // If the count of set bits has changed, go through all the counts between
- // the old and new blocks and modify them.
- // We only need to go to new_block and not to the end of the bitvector as
- // the blocks after new_block will either be updated in a future call to
- // OnBlockChange or in the destructor.
- for (uint32_t i = old_block_idx + 1; i <= new_block_idx; ++i) {
- int32_t new_count =
- static_cast<int32_t>(bv_->counts_[i]) + set_bit_count_diff_;
- PERFETTO_DCHECK(new_count >= 0);
-
- bv_->counts_[i] = static_cast<uint32_t>(new_count);
- }
- }
-
- // Reset the changed flag and cache the new block.
- is_block_changed_ = false;
- block_ = bv_->BlockFromIndex(new_block_idx);
-}
-
-AllBitsIterator::AllBitsIterator(const BitVector* bv)
- : BaseIterator(const_cast<BitVector*>(bv)) {}
+BaseIterator::~BaseIterator() = default;
SetBitsIterator::SetBitsIterator(const BitVector* bv)
: BaseIterator(const_cast<BitVector*>(bv)) {
@@ -121,6 +84,4 @@
PERFETTO_DCHECK(set_bit_count_until_i == set_bit_count_);
}
-} // namespace internal
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor::internal
diff --git a/src/trace_processor/containers/bit_vector_iterators.h b/src/trace_processor/containers/bit_vector_iterators.h
index e4ee878..32cd85b 100644
--- a/src/trace_processor/containers/bit_vector_iterators.h
+++ b/src/trace_processor/containers/bit_vector_iterators.h
@@ -17,6 +17,8 @@
#ifndef SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_
#define SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_
+#include <array>
+
#include "src/trace_processor/containers/bit_vector.h"
namespace perfetto {
@@ -30,32 +32,15 @@
// block.
class BaseIterator {
public:
- BaseIterator(BitVector* bv);
+ explicit BaseIterator(BitVector* bv);
~BaseIterator();
+ BaseIterator(const BaseIterator&) = delete;
+ BaseIterator& operator=(const BaseIterator&) = delete;
+
BaseIterator(BaseIterator&&) noexcept = default;
BaseIterator& operator=(BaseIterator&&) = default;
- // Sets the current bit the iterator points to.
- void Set() {
- if (!IsSet()) {
- block_.Set(block_offset());
-
- is_block_changed_ = true;
- ++set_bit_count_diff_;
- }
- }
-
- // Clears the current bit the iterator points to.
- void Clear() {
- if (IsSet()) {
- block_.Clear(block_offset());
-
- is_block_changed_ = true;
- --set_bit_count_diff_;
- }
- }
-
// Returns whether the current bit the iterator points to is set.
bool IsSet() { return BitVector::ConstBlock(block_).IsSet(block_offset()); }
@@ -87,22 +72,14 @@
if (PERFETTO_LIKELY(old_block == new_block))
return;
- // Slow path: we have to change block so this will involve flushing the old
- // block and counts (if necessary).
- OnBlockChange(old_block, new_block);
+ block_ = bv_->BlockFromIndex(new_block);
}
- // Handles flushing count changes and caches a new block.
- void OnBlockChange(uint32_t old_block, uint32_t new_block);
-
uint32_t size() const { return size_; }
const BitVector& bv() const { return *bv_; }
private:
- BaseIterator(const BaseIterator&) = delete;
- BaseIterator& operator=(const BaseIterator&) = delete;
-
BitVector::BlockOffset block_offset() const {
uint16_t bit_idx_inside_block = index_ % BitVector::Block::kBits;
@@ -115,35 +92,10 @@
uint32_t index_ = 0;
uint32_t size_ = 0;
- bool is_block_changed_ = false;
- int32_t set_bit_count_diff_ = 0;
-
BitVector* bv_;
BitVector::Block block_{bv_->words_.data()};
};
-// Iterator over all the bits in a bitvector.
-class AllBitsIterator : public BaseIterator {
- public:
- AllBitsIterator(const BitVector*);
-
- // Increments the iterator to point to the next bit.
- void Next() { SetIndex(index() + 1); }
-
- // Increments the iterator to skip the next |n| bits and point to the
- // following one.
- // Precondition: n >= 1 & index() + n <= size().
- void Skip(uint32_t n) {
- PERFETTO_DCHECK(n >= 1);
- PERFETTO_DCHECK(index() + n <= size());
-
- SetIndex(index() + n);
- }
-
- // Returns whether the iterator is valid.
- operator bool() const { return index() < size(); }
-};
-
// Iterator over all the set bits in a bitvector.
//
// This iterator works by first finding a batch of indices of set bits.
diff --git a/src/trace_processor/containers/bit_vector_unittest.cc b/src/trace_processor/containers/bit_vector_unittest.cc
index ee7452f..57fc0d0 100644
--- a/src/trace_processor/containers/bit_vector_unittest.cc
+++ b/src/trace_processor/containers/bit_vector_unittest.cc
@@ -17,16 +17,17 @@
#include "src/trace_processor/containers/bit_vector.h"
#include <bitset>
+#include <cstdint>
#include <limits>
#include <random>
+#include <utility>
+#include <vector>
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "protos/perfetto/trace_processor/serialization.pbzero.h"
-#include "src/trace_processor/containers/bit_vector_iterators.h"
#include "test/gtest_and_gmock.h"
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
namespace {
TEST(BitVectorUnittest, CreateAllTrue) {
@@ -309,150 +310,101 @@
}
}
-TEST(BitVectorUnittest, IterateAllBitsConst) {
- BitVector bv;
- for (uint32_t i = 0; i < 12345; ++i) {
- if (i % 7 == 0 || i % 13 == 0) {
- bv.AppendTrue();
- } else {
- bv.AppendFalse();
- }
- }
+TEST(BitVectorUnittest, SelectBitsSimple) {
+ BitVector bv = {true, false, true, false, true, true, true};
+ BitVector mask = {true, false, true, true, false, false, true};
+ bv.SelectBits(mask);
- uint32_t i = 0;
- for (auto it = bv.IterateAllBits(); it; it.Next(), ++i) {
- ASSERT_EQ(it.IsSet(), i % 7 == 0 || i % 13 == 0);
- ASSERT_EQ(it.index(), i);
- }
+ ASSERT_EQ(bv.size(), 4u);
+ ASSERT_EQ(bv.IsSet(0), true);
+ ASSERT_EQ(bv.IsSet(1), true);
+ ASSERT_EQ(bv.IsSet(2), false);
+ ASSERT_EQ(bv.IsSet(3), true);
+ ASSERT_EQ(bv.CountSetBits(), 3u);
}
-TEST(BitVectorUnittest, IterateAllBitsSet) {
- BitVector bv;
- for (uint32_t i = 0; i < 12345; ++i) {
- if (i % 7 == 0 || i % 13 == 0) {
- bv.AppendTrue();
- } else {
- bv.AppendFalse();
- }
- }
+TEST(BitVectorUnittest, SelectBitsSmallerMain) {
+ BitVector bv = {true, false, true, false};
+ BitVector mask = {true, false, true, true, false, false, true};
+ bv.SelectBits(mask);
- // Unset every 15th bit.
- for (auto it = bv.IterateAllBits(); it; it.Next()) {
- if (it.index() % 15 == 0) {
- it.Set();
- }
- }
-
- // Go through the iterator manually and check it has updated
- // to not have every 15th bit set.
- uint32_t count = 0;
- for (uint32_t i = 0; i < 12345; ++i) {
- bool is_set = i % 15 == 0 || i % 7 == 0 || i % 13 == 0;
-
- ASSERT_EQ(bv.IsSet(i), is_set);
- ASSERT_EQ(bv.CountSetBits(i), count);
-
- if (is_set) {
- ASSERT_EQ(bv.IndexOfNthSet(count++), i);
- }
- }
+ ASSERT_EQ(bv.size(), 3u);
+ ASSERT_EQ(bv.IsSet(0), true);
+ ASSERT_EQ(bv.IsSet(1), true);
+ ASSERT_EQ(bv.IsSet(2), false);
+ ASSERT_EQ(bv.CountSetBits(), 2u);
}
-TEST(BitVectorUnittest, IterateAllBitsClear) {
- BitVector bv;
- for (uint32_t i = 0; i < 12345; ++i) {
- if (i % 7 == 0 || i % 13 == 0) {
- bv.AppendTrue();
- } else {
- bv.AppendFalse();
- }
+TEST(BitVectorUnittest, SelectBitsLarge) {
+ BitVector bv = BitVector::RangeForTesting(
+ 0, 813, [](uint32_t idx) { return idx % 7 == 0; });
+ BitVector mask = BitVector::RangeForTesting(
+ 0, 813, [](uint32_t idx) { return idx % 3 == 0; });
+ bv.SelectBits(mask);
+
+ BitVector expected = BitVector::RangeForTesting(
+ 0, 271u, [](uint32_t idx) { return (idx * 3) % 7 == 0; });
+
+ ASSERT_EQ(bv.size(), 271u);
+ for (uint32_t i = 0; i < expected.size(); ++i) {
+ ASSERT_EQ(expected.IsSet(i), bv.IsSet(i)) << "Index " << i;
+ ASSERT_EQ(expected.CountSetBits(i), bv.CountSetBits(i)) << "Index " << i;
}
-
- // Unset every 15th bit.
- for (auto it = bv.IterateAllBits(); it; it.Next()) {
- if (it.index() % 15 == 0) {
- it.Clear();
- }
- }
-
- // Go through the iterator manually and check it has updated
- // to not have every 15th bit set.
- uint32_t count = 0;
- for (uint32_t i = 0; i < 12345; ++i) {
- bool is_set = i % 15 != 0 && (i % 7 == 0 || i % 13 == 0);
-
- ASSERT_EQ(bv.IsSet(i), is_set);
- ASSERT_EQ(bv.CountSetBits(i), count);
-
- if (is_set) {
- ASSERT_EQ(bv.IndexOfNthSet(count++), i);
- }
- }
+ ASSERT_EQ(expected.CountSetBits(), bv.CountSetBits());
}
-TEST(BitVectorUnittest, IterateSetBitsConst) {
- BitVector bv;
- std::vector<uint32_t> set_indices;
- for (uint32_t i = 0; i < 12345; ++i) {
- if (i % 7 == 0 || i % 13 == 0) {
- bv.AppendTrue();
- set_indices.emplace_back(i);
- } else {
- bv.AppendFalse();
- }
- }
+TEST(BitVectorUnittest, SelectBitsLargeSmallerMain) {
+ BitVector bv = BitVector::RangeForTesting(
+ 0, 279, [](uint32_t idx) { return idx % 7 == 0; });
+ BitVector mask = BitVector::RangeForTesting(
+ 0, 813, [](uint32_t idx) { return idx % 3 == 0; });
+ bv.SelectBits(mask);
- uint32_t i = 0;
- for (auto it = bv.IterateSetBits(); it; it.Next(), ++i) {
- ASSERT_EQ(it.IsSet(), true);
- ASSERT_EQ(it.index(), set_indices[i]);
+ BitVector expected = BitVector::RangeForTesting(
+ 0, 93, [](uint32_t idx) { return (idx * 3) % 7 == 0; });
+
+ ASSERT_EQ(bv.size(), 93u);
+ for (uint32_t i = 0; i < expected.size(); ++i) {
+ ASSERT_EQ(expected.IsSet(i), bv.IsSet(i)) << "Index " << i;
+ ASSERT_EQ(expected.CountSetBits(i), bv.CountSetBits(i)) << "Index " << i;
}
- ASSERT_EQ(i, set_indices.size());
+ ASSERT_EQ(expected.CountSetBits(), bv.CountSetBits());
}
-TEST(BitVectorUnittest, IterateSetBitsClear) {
- BitVector bv;
- for (uint32_t i = 0; i < 12345; ++i) {
- if (i % 7 == 0 || i % 13 == 0) {
- bv.AppendTrue();
- } else {
- bv.AppendFalse();
- }
+TEST(BitVectorUnittest, SelectBitsDense) {
+ BitVector bv =
+ BitVector::RangeForTesting(0, 279, [](uint32_t) { return true; });
+ BitVector mask =
+ BitVector::RangeForTesting(0, 279, [](uint32_t idx) { return idx < 80; });
+ bv.SelectBits(mask);
+
+ BitVector expected =
+ BitVector::RangeForTesting(0, 80, [](uint32_t) { return true; });
+
+ ASSERT_EQ(bv.size(), 80u);
+ for (uint32_t i = 0; i < expected.size(); ++i) {
+ ASSERT_EQ(expected.IsSet(i), bv.IsSet(i)) << "Index " << i;
+ ASSERT_EQ(expected.CountSetBits(i), bv.CountSetBits(i)) << "Index " << i;
}
-
- for (auto it = bv.IterateSetBits(); it; it.Next()) {
- if (it.index() % 15 == 0) {
- it.Clear();
- }
- }
-
- // Go through the iterator manually and check it has updated
- // to not have every 15th bit set.
- uint32_t count = 0;
- for (uint32_t i = 0; i < 12345; ++i) {
- bool is_set = i % 15 != 0 && (i % 7 == 0 || i % 13 == 0);
-
- ASSERT_EQ(bv.IsSet(i), is_set);
- ASSERT_EQ(bv.CountSetBits(i), count);
-
- if (is_set) {
- ASSERT_EQ(bv.IndexOfNthSet(count++), i);
- }
- }
+ ASSERT_EQ(expected.CountSetBits(), bv.CountSetBits());
}
-TEST(BitVectorUnittest, IterateSetBitsStartsCorrectly) {
- BitVector bv;
- bv.AppendFalse();
- bv.AppendTrue();
+TEST(BitVectorUnittest, SelectBitsEnd) {
+ BitVector bv = BitVector::RangeForTesting(
+ 0, 279, [](uint32_t idx) { return idx % 7 == 0; });
+ BitVector mask = BitVector::RangeForTesting(
+ 0, 813, [](uint32_t idx) { return idx % 3 == 0; });
+ bv.SelectBits(mask);
- auto it = bv.IterateSetBits();
- ASSERT_TRUE(it);
- ASSERT_EQ(it.index(), 1u);
- ASSERT_TRUE(it.IsSet());
+ BitVector expected = BitVector::RangeForTesting(
+ 0, 93, [](uint32_t idx) { return (idx * 3) % 7 == 0; });
- it.Next();
- ASSERT_FALSE(it);
+ ASSERT_EQ(bv.size(), 93u);
+ for (uint32_t i = 0; i < expected.size(); ++i) {
+ ASSERT_EQ(expected.IsSet(i), bv.IsSet(i)) << "Index " << i;
+ ASSERT_EQ(expected.CountSetBits(i), bv.CountSetBits(i)) << "Index " << i;
+ }
+ ASSERT_EQ(expected.CountSetBits(), bv.CountSetBits());
}
TEST(BitVectorUnittest, IntersectRange) {
@@ -515,6 +467,18 @@
ASSERT_EQ(intersected.CountSetBits(), 217u);
}
+TEST(BitVectorUnittest, IntersectRangeAppendFalse) {
+ BitVector bv(70u, true);
+ BitVector out = bv.IntersectRange(10, 12u);
+ out.Resize(70u);
+
+ ASSERT_TRUE(out.IsSet(10u));
+ ASSERT_TRUE(out.IsSet(11u));
+ ASSERT_FALSE(out.IsSet(12u));
+ ASSERT_FALSE(out.IsSet(60u));
+ ASSERT_FALSE(out.IsSet(69u));
+}
+
TEST(BitVectorUnittest, Range) {
BitVector bv =
BitVector::RangeForTesting(1, 9, [](uint32_t t) { return t % 3 == 0; });
@@ -698,6 +662,14 @@
EXPECT_EQ(bv.CountSetBits(), 820u);
}
+TEST(BitVectorUnittest, NotAppendAfter) {
+ BitVector bv(30);
+ bv.Not();
+ bv.AppendFalse();
+
+ ASSERT_FALSE(bv.IsSet(30));
+}
+
TEST(BitVectorUnittest, Or) {
BitVector bv{1, 1, 0, 0};
BitVector bv_second{1, 0, 1, 0};
@@ -740,31 +712,11 @@
if (res)
int_vec.emplace_back(i);
}
+}
- auto all_it = bv.IterateAllBits();
- for (uint32_t i = 0; i < kCount; ++i) {
- uint32_t count = static_cast<uint32_t>(std::count(
- bool_vec.begin(), bool_vec.begin() + static_cast<int32_t>(i), true));
- ASSERT_EQ(bv.IsSet(i), bool_vec[i]);
- ASSERT_EQ(bv.CountSetBits(i), count);
-
- ASSERT_TRUE(all_it);
- ASSERT_EQ(all_it.IsSet(), bool_vec[i]);
- ASSERT_EQ(all_it.index(), i);
- all_it.Next();
- }
- ASSERT_FALSE(all_it);
-
- auto set_it = bv.IterateSetBits();
- for (uint32_t i = 0; i < int_vec.size(); ++i) {
- ASSERT_EQ(bv.IndexOfNthSet(i), int_vec[i]);
-
- ASSERT_TRUE(set_it);
- ASSERT_EQ(set_it.IsSet(), true);
- ASSERT_EQ(set_it.index(), int_vec[i]);
- set_it.Next();
- }
- ASSERT_FALSE(set_it);
+TEST(BitVectorUnittest, GetSetBitIndices) {
+ BitVector bv = {true, false, true, false, true, true, false, false};
+ ASSERT_THAT(bv.GetSetBitIndices(), testing::ElementsAre(0u, 2u, 4u, 5u));
}
TEST(BitVectorUnittest, SerializeSimple) {
@@ -800,5 +752,4 @@
}
} // namespace
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/containers/row_map.cc b/src/trace_processor/containers/row_map.cc
index f7925ae..3ae6ee7 100644
--- a/src/trace_processor/containers/row_map.cc
+++ b/src/trace_processor/containers/row_map.cc
@@ -15,8 +15,16 @@
*/
#include "src/trace_processor/containers/row_map.h"
-#include <unordered_set>
+#include <algorithm>
+#include <cstdint>
+#include <unordered_set>
+#include <utility>
+#include <variant>
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map_algorithms.h"
namespace perfetto {
@@ -69,20 +77,16 @@
RowMap Select(const BitVector& bv, Range selector) {
PERFETTO_DCHECK(selector.end <= bv.CountSetBits());
-
+ if (selector.empty()) {
+ return {};
+ }
// If we're simply selecting every element in the bitvector, just
// return a copy of the BitVector without iterating.
- BitVector ret = bv.Copy();
if (selector.start == 0 && selector.end == bv.CountSetBits()) {
- return RowMap(std::move(ret));
+ return RowMap(bv.Copy());
}
-
- for (auto it = ret.IterateSetBits(); it; it.Next()) {
- auto set_idx = it.ordinal();
- if (set_idx < selector.start || set_idx >= selector.end)
- it.Clear();
- }
- return RowMap(std::move(ret));
+ return RowMap(bv.IntersectRange(bv.IndexOfNthSet(selector.start),
+ bv.IndexOfNthSet(selector.end - 1) + 1));
}
RowMap Select(const BitVector& bv, const BitVector& selector) {
@@ -230,26 +234,26 @@
RowMap::RowMap(IndexVector vec) : data_(vec) {}
RowMap RowMap::Copy() const {
- if (auto* range = std::get_if<Range>(&data_)) {
+ if (const auto* range = std::get_if<Range>(&data_)) {
return RowMap(*range);
}
- if (auto* bv = std::get_if<BitVector>(&data_)) {
+ if (const auto* bv = std::get_if<BitVector>(&data_)) {
return RowMap(bv->Copy());
}
- if (auto* vec = std::get_if<IndexVector>(&data_)) {
+ if (const auto* vec = std::get_if<IndexVector>(&data_)) {
return RowMap(*vec);
}
NoVariantMatched();
}
OutputIndex RowMap::Max() const {
- if (auto* range = std::get_if<Range>(&data_)) {
+ if (const auto* range = std::get_if<Range>(&data_)) {
return range->end;
}
- if (auto* bv = std::get_if<BitVector>(&data_)) {
+ if (const auto* bv = std::get_if<BitVector>(&data_)) {
return bv->size();
}
- if (auto* vec = std::get_if<IndexVector>(&data_)) {
+ if (const auto* vec = std::get_if<IndexVector>(&data_)) {
return vec->empty() ? 0 : *std::max_element(vec->begin(), vec->end()) + 1;
}
NoVariantMatched();
@@ -272,14 +276,15 @@
}
RowMap::Iterator::Iterator(const RowMap* rm) : rm_(rm) {
- if (auto* range = std::get_if<Range>(&rm_->data_)) {
+ if (const auto* range = std::get_if<Range>(&rm_->data_)) {
ordinal_ = range->start;
return;
}
- if (auto* bv = std::get_if<BitVector>(&rm_->data_)) {
- set_bits_it_.reset(new BitVector::SetBitsIterator(bv->IterateSetBits()));
+ if (const auto* bv = std::get_if<BitVector>(&rm_->data_)) {
+ results_ = bv->GetSetBitIndices();
return;
}
}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/containers/row_map.h b/src/trace_processor/containers/row_map.h
index b8f1d3d..88fe7a1 100644
--- a/src/trace_processor/containers/row_map.h
+++ b/src/trace_processor/containers/row_map.h
@@ -17,17 +17,18 @@
#ifndef SRC_TRACE_PROCESSOR_CONTAINERS_ROW_MAP_H_
#define SRC_TRACE_PROCESSOR_CONTAINERS_ROW_MAP_H_
-#include <stdint.h>
-
-#include <memory>
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
#include <numeric>
#include <optional>
+#include <utility>
#include <variant>
#include <vector>
+#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
#include "src/trace_processor/containers/bit_vector.h"
-#include "src/trace_processor/containers/bit_vector_iterators.h"
namespace perfetto {
namespace trace_processor {
@@ -82,16 +83,16 @@
struct Range {
Range(OutputIndex start_index, OutputIndex end_index)
- : start(start_index), end(end_index) {}
+ : start(start_index), end(end_index) {
+ PERFETTO_DCHECK(start_index <= end_index);
+ }
Range() : start(0), end(0) {}
- OutputIndex start = 0; // This is an inclusive index.
- OutputIndex end = 0; // This is an exclusive index.
+ OutputIndex start; // This is an inclusive index.
+ OutputIndex end; // This is an exclusive index.
- uint32_t size() const {
- PERFETTO_DCHECK(end >= start);
- return end - start;
- }
+ bool empty() const { return size() == 0; }
+ uint32_t size() const { return end - start; }
inline bool Contains(uint32_t val) const {
return val >= start && val < end;
}
@@ -106,29 +107,24 @@
public:
explicit Iterator(const RowMap* rm);
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+
Iterator(Iterator&&) noexcept = default;
Iterator& operator=(Iterator&&) = default;
// Forwards the iterator to the next row of the RowMap.
- void Next() {
- if (std::get_if<Range>(&rm_->data_)) {
- ++ordinal_;
- } else if (std::get_if<BitVector>(&rm_->data_)) {
- set_bits_it_->Next();
- } else if (std::get_if<IndexVector>(&rm_->data_)) {
- ++ordinal_;
- }
- }
+ void Next() { ++ordinal_; }
// Returns if the iterator is still valid.
- operator bool() const {
- if (auto* range = std::get_if<Range>(&rm_->data_)) {
+ explicit operator bool() const {
+ if (const auto* range = std::get_if<Range>(&rm_->data_)) {
return ordinal_ < range->end;
}
if (std::get_if<BitVector>(&rm_->data_)) {
- return bool(*set_bits_it_);
+ return ordinal_ < results_.size();
}
- if (auto* vec = std::get_if<IndexVector>(&rm_->data_)) {
+ if (const auto* vec = std::get_if<IndexVector>(&rm_->data_)) {
return ordinal_ < vec->size();
}
PERFETTO_FATAL("Didn't match any variant type.");
@@ -140,9 +136,9 @@
return ordinal_;
}
if (std::get_if<BitVector>(&rm_->data_)) {
- return set_bits_it_->index();
+ return results_[ordinal_];
}
- if (auto* vec = std::get_if<IndexVector>(&rm_->data_)) {
+ if (const auto* vec = std::get_if<IndexVector>(&rm_->data_)) {
return (*vec)[ordinal_];
}
PERFETTO_FATAL("Didn't match any variant type.");
@@ -150,26 +146,21 @@
// Returns the row of the index the iterator points to.
InputRow row() const {
- if (auto* range = std::get_if<Range>(&rm_->data_)) {
+ if (const auto* range = std::get_if<Range>(&rm_->data_)) {
return ordinal_ - range->start;
}
- if (std::get_if<BitVector>(&rm_->data_)) {
- return set_bits_it_->ordinal();
- }
- if (std::get_if<IndexVector>(&rm_->data_)) {
+ if (std::get_if<BitVector>(&rm_->data_) ||
+ std::get_if<IndexVector>(&rm_->data_)) {
return ordinal_;
}
PERFETTO_FATAL("Didn't match any variant type.");
}
private:
- Iterator(const Iterator&) = delete;
- Iterator& operator=(const Iterator&) = delete;
-
// Ordinal will not be used for BitVector based RowMap.
uint32_t ordinal_ = 0;
- // Not nullptr for BitVector based RowMap.
- std::unique_ptr<BitVector::SetBitsIterator> set_bits_it_;
+ // Not empty for BitVector based RowMap.
+ std::vector<uint32_t> results_;
const RowMap* rm_ = nullptr;
};
@@ -209,13 +200,13 @@
// Returns the size of the RowMap; that is the number of indices in the
// RowMap.
uint32_t size() const {
- if (auto* range = std::get_if<Range>(&data_)) {
+ if (const auto* range = std::get_if<Range>(&data_)) {
return range->size();
}
- if (auto* bv = std::get_if<BitVector>(&data_)) {
+ if (const auto* bv = std::get_if<BitVector>(&data_)) {
return bv->CountSetBits();
}
- if (auto* vec = std::get_if<IndexVector>(&data_)) {
+ if (const auto* vec = std::get_if<IndexVector>(&data_)) {
return static_cast<uint32_t>(vec->size());
}
NoVariantMatched();
@@ -226,13 +217,13 @@
// Returns the index at the given |row|.
OutputIndex Get(InputRow row) const {
- if (auto* range = std::get_if<Range>(&data_)) {
+ if (const auto* range = std::get_if<Range>(&data_)) {
return GetRange(*range, row);
}
- if (auto* bv = std::get_if<BitVector>(&data_)) {
+ if (const auto* bv = std::get_if<BitVector>(&data_)) {
return GetBitVector(*bv, row);
}
- if (auto* vec = std::get_if<IndexVector>(&data_)) {
+ if (const auto* vec = std::get_if<IndexVector>(&data_)) {
return GetIndexVector(*vec, row);
}
NoVariantMatched();
@@ -240,19 +231,15 @@
// Returns the vector of all indices in the RowMap.
std::vector<OutputIndex> GetAllIndices() const {
- if (auto* range = std::get_if<Range>(&data_)) {
+ if (const auto* range = std::get_if<Range>(&data_)) {
std::vector<uint32_t> res(range->size());
std::iota(res.begin(), res.end(), range->start);
return res;
}
- if (auto* bv = std::get_if<BitVector>(&data_)) {
- std::vector<uint32_t> res;
- for (auto it = bv->IterateSetBits(); it; it.Next()) {
- res.push_back(it.index());
- }
- return res;
+ if (const auto* bv = std::get_if<BitVector>(&data_)) {
+ return bv->GetSetBitIndices();
}
- if (auto* vec = std::get_if<IndexVector>(&data_)) {
+ if (const auto* vec = std::get_if<IndexVector>(&data_)) {
return *vec;
}
NoVariantMatched();
@@ -263,13 +250,13 @@
// Returns whether the RowMap contains the given index.
bool Contains(OutputIndex index) const {
- if (auto* range = std::get_if<Range>(&data_)) {
+ if (const auto* range = std::get_if<Range>(&data_)) {
return index >= range->start && index < range->end;
}
- if (auto* bv = std::get_if<BitVector>(&data_)) {
+ if (const auto* bv = std::get_if<BitVector>(&data_)) {
return index < bv->size() && bv->IsSet(index);
}
- if (auto* vec = std::get_if<IndexVector>(&data_)) {
+ if (const auto* vec = std::get_if<IndexVector>(&data_)) {
return std::find(vec->begin(), vec->end(), index) != vec->end();
}
NoVariantMatched();
@@ -277,17 +264,17 @@
// Returns the first row of the given |index| in the RowMap.
std::optional<InputRow> RowOf(OutputIndex index) const {
- if (auto* range = std::get_if<Range>(&data_)) {
+ if (const auto* range = std::get_if<Range>(&data_)) {
if (index < range->start || index >= range->end)
return std::nullopt;
return index - range->start;
}
- if (auto* bv = std::get_if<BitVector>(&data_)) {
+ if (const auto* bv = std::get_if<BitVector>(&data_)) {
return index < bv->size() && bv->IsSet(index)
? std::make_optional(bv->CountSetBits(index))
: std::nullopt;
}
- if (auto* vec = std::get_if<IndexVector>(&data_)) {
+ if (const auto* vec = std::get_if<IndexVector>(&data_)) {
auto it = std::find(vec->begin(), vec->end(), index);
return it != vec->end() ? std::make_optional(static_cast<InputRow>(
std::distance(vec->begin(), it)))
@@ -362,7 +349,7 @@
// If the selector is empty, just return an empty RowMap.
if (size == 0u)
- return RowMap();
+ return {};
// If the selector is just picking a single row, just return that row
// without any additional overhead.
@@ -398,69 +385,16 @@
// Clears this RowMap by resetting it to a newly constructed state.
void Clear() { *this = RowMap(); }
- template <typename Comparator = bool(uint32_t, uint32_t)>
- void StableSort(IndexVector* out, Comparator c) const {
- if (auto* range = std::get_if<Range>(&data_)) {
- std::stable_sort(out->begin(), out->end(),
- [range, c](uint32_t a, uint32_t b) {
- return c(GetRange(*range, a), GetRange(*range, b));
- });
- return;
- }
- if (auto* bv = std::get_if<BitVector>(&data_)) {
- std::stable_sort(out->begin(), out->end(),
- [&bv, c](uint32_t a, uint32_t b) {
- return c(GetBitVector(*bv, a), GetBitVector(*bv, b));
- });
- return;
- }
- if (auto* vec = std::get_if<IndexVector>(&data_)) {
- std::stable_sort(
- out->begin(), out->end(), [vec, c](uint32_t a, uint32_t b) {
- return c(GetIndexVector(*vec, a), GetIndexVector(*vec, b));
- });
- return;
- }
- NoVariantMatched();
- }
-
- // Filters the indices in |out| by keeping those which meet |p|.
- template <typename Predicate = bool(OutputIndex)>
- void Filter(Predicate p) {
- if (auto* range = std::get_if<Range>(&data_)) {
- data_ = FilterRange(p, *range);
- return;
- }
- if (auto* bv = std::get_if<BitVector>(&data_)) {
- for (auto it = bv->IterateSetBits(); it; it.Next()) {
- if (!p(it.index()))
- it.Clear();
- }
- return;
- }
- if (auto* vec = std::get_if<IndexVector>(&data_)) {
- auto ret = std::remove_if(vec->begin(), vec->end(),
- [p](uint32_t i) { return !p(i); });
- vec->erase(ret, vec->end());
- return;
- }
- NoVariantMatched();
- }
-
// Converts this RowMap to an index vector in the most efficient way
// possible.
- std::vector<uint32_t> TakeAsIndexVector() const&& {
- if (auto* range = std::get_if<Range>(&data_)) {
+ std::vector<uint32_t> TakeAsIndexVector() && {
+ if (const auto* range = std::get_if<Range>(&data_)) {
std::vector<uint32_t> rm(range->size());
std::iota(rm.begin(), rm.end(), range->start);
return rm;
}
- if (auto* bv = std::get_if<BitVector>(&data_)) {
- std::vector<uint32_t> rm(bv->CountSetBits());
- for (auto it = bv->IterateSetBits(); it; it.Next()) {
- rm[it.ordinal()] = it.index();
- }
- return rm;
+ if (const auto* bv = std::get_if<BitVector>(&data_)) {
+ return bv->GetSetBitIndices();
}
if (auto* vec = std::get_if<IndexVector>(&data_)) {
return std::move(*vec);
@@ -530,7 +464,7 @@
bv.Set(row);
}
- PERFETTO_NORETURN void NoVariantMatched() const {
+ PERFETTO_NORETURN static void NoVariantMatched() {
PERFETTO_FATAL("Didn't match any variant type.");
}
diff --git a/src/trace_processor/containers/row_map_algorithms.h b/src/trace_processor/containers/row_map_algorithms.h
index 6b79141..5398fa6 100644
--- a/src/trace_processor/containers/row_map_algorithms.h
+++ b/src/trace_processor/containers/row_map_algorithms.h
@@ -51,11 +51,7 @@
inline std::vector<uint32_t> SelectBvWithIvByConvertToIv(
const BitVector& bv,
const std::vector<uint32_t>& selector) {
- std::vector<uint32_t> bv_conv(bv.CountSetBits());
- for (auto it = bv.IterateSetBits(); it; it.Next()) {
- bv_conv[it.ordinal()] = it.index();
- }
- return SelectIvWithIv(bv_conv, selector);
+ return SelectIvWithIv(bv.GetSetBitIndices(), selector);
}
// Returns a vector containing elements from |bv| by selecting indices from
diff --git a/src/trace_processor/db/column/data_layer.h b/src/trace_processor/db/column/data_layer.h
index 8f34845..f43999c 100644
--- a/src/trace_processor/db/column/data_layer.h
+++ b/src/trace_processor/db/column/data_layer.h
@@ -130,9 +130,9 @@
// to positions in the storage.
//
// Notes for implementors:
- // * Implementations should ensure that the return value *only* includes
- // positions in |range| as callers will expect this to be true and can
- // optimize based on this.
+ // * Implementations should ensure that the return value is empty or *only*
+ // includes positions in |range|. Callers are free to assume this and can
+ // optimize based on it.
// * Implementations should ensure that, if they return a BitVector, it is
// precisely of size |range.end|.
PERFETTO_ALWAYS_INLINE RangeOrBitVector Search(FilterOp op,
diff --git a/src/trace_processor/db/column/dense_null_overlay.cc b/src/trace_processor/db/column/dense_null_overlay.cc
index d9a7fb1..970eb0f 100644
--- a/src/trace_processor/db/column/dense_null_overlay.cc
+++ b/src/trace_processor/db/column/dense_null_overlay.cc
@@ -109,8 +109,8 @@
// |non_null_| which matches the range. Then, resize to |in.end| as this
// is mandated by the API contract of |Storage::Search|.
Range inner_range = std::move(inner_res).TakeIfRange();
- PERFETTO_DCHECK(inner_range.end <= in.end);
- PERFETTO_DCHECK(inner_range.start >= in.start);
+ PERFETTO_DCHECK(inner_range.empty() || inner_range.end <= in.end);
+ PERFETTO_DCHECK(inner_range.empty() || inner_range.start >= in.start);
res = non_null_->IntersectRange(inner_range.start, inner_range.end);
res.Resize(in.end, false);
} else {
diff --git a/src/trace_processor/db/column/id_storage.cc b/src/trace_processor/db/column/id_storage.cc
index 6fdb800..df662af 100644
--- a/src/trace_processor/db/column/id_storage.cc
+++ b/src/trace_processor/db/column/id_storage.cc
@@ -327,13 +327,13 @@
case FilterOp::kEq:
return {val, val + (range.start <= val && val < range.end)};
case FilterOp::kLe:
- return {range.start, std::min(val + 1, range.end)};
+ return {range.start, std::clamp(val + 1, range.start, range.end)};
case FilterOp::kLt:
- return {range.start, std::min(val, range.end)};
+ return {range.start, std::clamp(val, range.start, range.end)};
case FilterOp::kGe:
- return {std::max(val, range.start), range.end};
+ return {std::clamp(val, range.start, range.end), range.end};
case FilterOp::kGt:
- return {std::max(val + 1, range.start), range.end};
+ return {std::clamp(val + 1, range.start, range.end), range.end};
case FilterOp::kIsNotNull:
case FilterOp::kNe:
case FilterOp::kIsNull:
diff --git a/src/trace_processor/db/column/null_overlay.cc b/src/trace_processor/db/column/null_overlay.cc
index 7984656..8634eb6 100644
--- a/src/trace_processor/db/column/null_overlay.cc
+++ b/src/trace_processor/db/column/null_overlay.cc
@@ -48,7 +48,7 @@
BitVector res;
if (storage_result.IsRange()) {
Range range = std::move(storage_result).TakeIfRange();
- if (range.size() > 0) {
+ if (!range.empty()) {
res = non_null.IntersectRange(non_null.IndexOfNthSet(range.start),
non_null.IndexOfNthSet(range.end - 1) + 1);
diff --git a/src/trace_processor/db/column/numeric_storage.cc b/src/trace_processor/db/column/numeric_storage.cc
index 529e93d..2c16edd 100644
--- a/src/trace_processor/db/column/numeric_storage.cc
+++ b/src/trace_processor/db/column/numeric_storage.cc
@@ -22,11 +22,11 @@
#include <cstdint>
#include <functional>
#include <limits>
-#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <variant>
+#include <vector>
#include "perfetto/base/logging.h"
#include "perfetto/public/compiler.h"
diff --git a/src/trace_processor/db/column/range_overlay.cc b/src/trace_processor/db/column/range_overlay.cc
index 0b8275b..338c989 100644
--- a/src/trace_processor/db/column/range_overlay.cc
+++ b/src/trace_processor/db/column/range_overlay.cc
@@ -65,6 +65,9 @@
auto inner_res = inner_->SearchValidated(op, sql_val, inner_search_range);
if (inner_res.IsRange()) {
Range inner_res_range = std::move(inner_res).TakeIfRange();
+ if (inner_res_range.empty()) {
+ return RangeOrBitVector(Range());
+ }
return RangeOrBitVector(Range(inner_res_range.start - range_->start,
inner_res_range.end - range_->start));
}
diff --git a/src/trace_processor/db/column/selector_overlay.cc b/src/trace_processor/db/column/selector_overlay.cc
index ff1e463..9ebbb71 100644
--- a/src/trace_processor/db/column/selector_overlay.cc
+++ b/src/trace_processor/db/column/selector_overlay.cc
@@ -65,6 +65,9 @@
inner_->SearchValidated(op, sql_val, Range(start_idx, end_idx));
if (storage_result.IsRange()) {
Range storage_range = std::move(storage_result).TakeIfRange();
+ if (storage_range.empty()) {
+ return RangeOrBitVector(Range());
+ }
uint32_t out_start = selector_->CountSetBits(storage_range.start);
uint32_t out_end = selector_->CountSetBits(storage_range.end);
return RangeOrBitVector(Range(out_start, out_end));
@@ -72,15 +75,12 @@
BitVector storage_bitvector = std::move(storage_result).TakeIfBitVector();
PERFETTO_DCHECK(storage_bitvector.size() <= selector_->size());
-
- // TODO(b/283763282): implement ParallelExtractBits to optimize this
- // operation.
- BitVector::Builder res(in.end);
- for (auto it = selector_->IterateSetBits();
- it && it.index() < storage_bitvector.size(); it.Next()) {
- res.Append(storage_bitvector.IsSet(it.index()));
+ storage_bitvector.SelectBits(*selector_);
+ if (storage_bitvector.size() == 0) {
+ return RangeOrBitVector(std::move(storage_bitvector));
}
- return RangeOrBitVector(std::move(res).Build());
+ PERFETTO_DCHECK(storage_bitvector.size() == in.end);
+ return RangeOrBitVector(std::move(storage_bitvector));
}
RangeOrBitVector SelectorOverlay::ChainImpl::IndexSearchValidated(
diff --git a/src/trace_processor/db/column_storage_overlay.h b/src/trace_processor/db/column_storage_overlay.h
index c13c095..3e68687 100644
--- a/src/trace_processor/db/column_storage_overlay.h
+++ b/src/trace_processor/db/column_storage_overlay.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <optional>
+#include <utility>
#include <vector>
#include "src/trace_processor/containers/bit_vector.h"
@@ -41,7 +42,7 @@
// Allows efficient iteration over the rows of a ColumnStorageOverlay.
class Iterator {
public:
- Iterator(RowMap::Iterator it) : it_(std::move(it)) {}
+ explicit Iterator(RowMap::Iterator it) : it_(std::move(it)) {}
Iterator(Iterator&&) noexcept = default;
Iterator& operator=(Iterator&&) = default;
@@ -50,7 +51,7 @@
void Next() { return it_.Next(); }
// Returns if the iterator is still valid.
- operator bool() const { return it_; }
+ explicit operator bool() const { return bool(it_); }
// Returns the index pointed to by this iterator.
OutputIndex index() const { return it_.index(); }
diff --git a/src/trace_processor/db/query_executor_benchmark.cc b/src/trace_processor/db/query_executor_benchmark.cc
index dd1e8a9..8502e04 100644
--- a/src/trace_processor/db/query_executor_benchmark.cc
+++ b/src/trace_processor/db/query_executor_benchmark.cc
@@ -103,22 +103,27 @@
return base::SplitString(table_csv, "\n");
}
+StringPool::Id StripAndIntern(StringPool& pool, const std::string& data) {
+ std::string res = base::StripSuffix(base::StripPrefix(data, "\""), "\"");
+ return pool.InternString(base::StringView(res));
+}
+
SliceTable::Row GetSliceTableRow(const std::string& string_row,
StringPool& pool) {
std::vector<std::string> row_vec = SplitCSVLine(string_row);
SliceTable::Row row;
- PERFETTO_CHECK(row_vec.size() >= 12);
+ PERFETTO_CHECK(row_vec.size() >= 14);
row.ts = *base::StringToInt64(row_vec[2]);
row.dur = *base::StringToInt64(row_vec[3]);
row.track_id = ThreadTrackTable::Id(*base::StringToUInt32(row_vec[4]));
- row.category = pool.InternString(base::StringView(row_vec[5]));
- row.name = pool.InternString(base::StringView(row_vec[6]));
+ row.category = StripAndIntern(pool, row_vec[5]);
+ row.name = StripAndIntern(pool, row_vec[6]);
row.depth = *base::StringToUInt32(row_vec[7]);
row.stack_id = *base::StringToInt32(row_vec[8]);
row.parent_stack_id = *base::StringToInt32(row_vec[9]);
- row.parent_id = base::StringToUInt32(row_vec[11]).has_value()
+ row.parent_id = base::StringToUInt32(row_vec[10]).has_value()
? std::make_optional<SliceTable::Id>(
- *base::StringToUInt32(row_vec[11]))
+ *base::StringToUInt32(row_vec[10]))
: std::nullopt;
row.arg_set_id = *base::StringToUInt32(row_vec[11]);
row.thread_ts = base::StringToInt64(row_vec[12]);
@@ -236,6 +241,10 @@
benchmark::Counter(static_cast<double>(table.table_.row_count()),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
+ state.counters["s/out"] = benchmark::Counter(
+ static_cast<double>(table.table_.QueryToRowMap(c, {}).size()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
}
void BenchmarkSliceTableSort(benchmark::State& state,
@@ -261,6 +270,10 @@
benchmark::Counter(static_cast<double>(table.table_.row_count()),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
+ state.counters["s/out"] = benchmark::Counter(
+ static_cast<double>(table.table_.QueryToRowMap({c}, {}).size()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
}
void BenchmarkFtraceEventTableFilter(benchmark::State& state,
@@ -273,6 +286,10 @@
benchmark::Counter(static_cast<double>(table.table_.row_count()),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
+ state.counters["s/out"] = benchmark::Counter(
+ static_cast<double>(table.table_.QueryToRowMap({c}, {}).size()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
}
void BenchmarkFtraceEventTableSort(benchmark::State& state,
@@ -289,7 +306,7 @@
void BM_QESliceTableTrackIdEq(benchmark::State& state) {
SliceTableForBenchmark table(state);
- BenchmarkSliceTableFilter(state, table, {table.table_.track_id().eq(100)});
+ BenchmarkSliceTableFilter(state, table, {table.table_.track_id().eq(1213)});
}
BENCHMARK(BM_QESliceTableTrackIdEq);
@@ -304,29 +321,33 @@
void BM_QESliceTableParentIdEq(benchmark::State& state) {
SliceTableForBenchmark table(state);
- BenchmarkSliceTableFilter(state, table, {table.table_.parent_id().eq(88)});
+ BenchmarkSliceTableFilter(state, table, {table.table_.parent_id().eq(26711)});
}
BENCHMARK(BM_QESliceTableParentIdEq);
void BM_QESliceTableNameEq(benchmark::State& state) {
SliceTableForBenchmark table(state);
- BenchmarkSliceTableFilter(state, table, {table.table_.name().eq("cheese")});
+ BenchmarkSliceTableFilter(
+ state, table,
+ {table.table_.name().eq("MarkFromReadBarrierWithMeasurements")});
}
BENCHMARK(BM_QESliceTableNameEq);
void BM_QESliceTableNameGlobNoStars(benchmark::State& state) {
SliceTableForBenchmark table(state);
- BenchmarkSliceTableFilter(state, table, {table.table_.name().glob("cheese")});
+ BenchmarkSliceTableFilter(
+ state, table,
+ {table.table_.name().glob("MarkFromReadBarrierWithMeasurements")});
}
BENCHMARK(BM_QESliceTableNameGlobNoStars);
void BM_QESliceTableNameGlob(benchmark::State& state) {
SliceTableForBenchmark table(state);
- BenchmarkSliceTableFilter(state, table,
- {table.table_.name().glob("chee*se")});
+ BenchmarkSliceTableFilter(
+ state, table, {table.table_.name().glob("HIDL::IMapper::unlock::*")});
}
BENCHMARK(BM_QESliceTableNameGlob);
@@ -341,7 +362,9 @@
void BM_QESliceTableSorted(benchmark::State& state) {
SliceTableForBenchmark table(state);
- BenchmarkSliceTableFilter(state, table, {table.table_.ts().gt(1000)});
+ BenchmarkSliceTableFilter(state, table,
+ {table.table_.ts().gt(1738923505854),
+ table.table_.ts().lt(1738950140556)});
}
BENCHMARK(BM_QESliceTableSorted);
@@ -349,7 +372,7 @@
void BM_QEFilterWithSparseSelector(benchmark::State& state) {
ExpectedFrameTimelineTableForBenchmark table(state);
BenchmarkExpectedFrameTableFilter(state, table,
- table.table_.track_id().eq(88));
+ table.table_.track_id().eq(1445));
}
BENCHMARK(BM_QEFilterWithSparseSelector);
@@ -379,8 +402,8 @@
SliceTableForBenchmark table(state);
BenchmarkSliceTableFilter(
state, table,
- {table.table_.ts().ge(1740530419866), table.table_.ts().le(1740530474097),
- table.table_.track_id().eq(100)});
+ {table.table_.ts().ge(1738923505854), table.table_.ts().le(1738950140556),
+ table.table_.track_id().eq(1422)});
}
BENCHMARK(BM_QESliceTableTsAndTrackId);
@@ -388,7 +411,8 @@
void BM_QEFilterOneElement(benchmark::State& state) {
SliceTableForBenchmark table(state);
BenchmarkSliceTableFilter(
- state, table, {table.table_.id().eq(10), table.table_.dur().eq(100)});
+ state, table,
+ {table.table_.id().eq(11732), table.table_.track_id().eq(1422)});
}
BENCHMARK(BM_QEFilterOneElement);
@@ -407,6 +431,10 @@
static_cast<double>(slice_sorted_with_duration.row_count()),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
+ state.counters["s/out"] = benchmark::Counter(
+ static_cast<double>(table.table_.QueryToRowMap({c}, {}).size()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
}
BENCHMARK(BM_QEFilterWithArrangement);
@@ -422,6 +450,10 @@
benchmark::Counter(static_cast<double>(table.table_.row_count()),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
+ state.counters["s/out"] = benchmark::Counter(
+ static_cast<double>(table.table_.QueryToRowMap({c}, {}).size()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
}
BENCHMARK(BM_QEDenseNullFilter);
@@ -436,6 +468,10 @@
benchmark::Counter(static_cast<double>(table.table_.row_count()),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
+ state.counters["s/out"] = benchmark::Counter(
+ static_cast<double>(table.table_.QueryToRowMap({c}, {}).size()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
}
BENCHMARK(BM_QEDenseNullFilterIsNull);
@@ -471,10 +507,32 @@
static_cast<double>(slice_sorted_with_duration.row_count()),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
+ state.counters["s/out"] = benchmark::Counter(
+ static_cast<double>(table.table_.QueryToRowMap({c}, {}).size()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
}
BENCHMARK(BM_QEFilterOrderedArrangement);
+void BM_QESliceFilterIndexSearchOneElement(benchmark::State& state) {
+ SliceTableForBenchmark table(state);
+ BenchmarkSliceTableFilter(
+ state, table,
+ {table.table_.track_id().eq(1422), table.table_.id().eq(11732)});
+}
+
+BENCHMARK(BM_QESliceFilterIndexSearchOneElement);
+
+void BM_QESliceFilterIndexSearch(benchmark::State& state) {
+ SliceTableForBenchmark table(state);
+ BenchmarkSliceTableFilter(state, table,
+ {table.table_.track_id().eq(1422),
+ table.table_.name().eq("notifyFramePending")});
+}
+
+BENCHMARK(BM_QESliceFilterIndexSearch);
+
void BM_QESliceSortNumericAsc(benchmark::State& state) {
SliceTableForBenchmark table(state);
BenchmarkSliceTableSort(state, table, {table.table_.track_id().ascending()});
diff --git a/src/trace_processor/db/table.h b/src/trace_processor/db/table.h
index eab9f92..7d53106 100644
--- a/src/trace_processor/db/table.h
+++ b/src/trace_processor/db/table.h
@@ -23,7 +23,6 @@
#include <utility>
#include <vector>
-#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
#include "perfetto/trace_processor/basic_types.h"
#include "perfetto/trace_processor/ref_counted.h"
@@ -33,7 +32,6 @@
#include "src/trace_processor/db/column/data_layer.h"
#include "src/trace_processor/db/column/types.h"
#include "src/trace_processor/db/column_storage_overlay.h"
-#include "src/trace_processor/db/query_executor.h"
namespace perfetto::trace_processor {
@@ -77,7 +75,7 @@
}
// Returns whether the row the iterator is pointing at is valid.
- explicit operator bool() const { return its_[0]; }
+ explicit operator bool() const { return bool(its_[0]); }
// Returns the value at the current row for column |col_idx|.
SqlValue Get(uint32_t col_idx) const {
diff --git a/src/trace_processor/importers/common/BUILD.gn b/src/trace_processor/importers/common/BUILD.gn
index 823edd8..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,10 +37,15 @@
"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",
"process_tracker.h",
+ "sched_event_state.h",
+ "sched_event_tracker.cc",
+ "sched_event_tracker.h",
"slice_tracker.cc",
"slice_tracker.h",
"slice_translation_table.cc",
@@ -48,9 +54,13 @@
"stack_profile_tracker.h",
"system_info_tracker.cc",
"system_info_tracker.h",
+ "thread_state_tracker.cc",
+ "thread_state_tracker.h",
"trace_parser.cc",
"track_tracker.cc",
"track_tracker.h",
+ "virtual_memory_mapping.cc",
+ "virtual_memory_mapping.h",
]
public_deps = [
":trace_parser_hdr",
@@ -70,8 +80,8 @@
"../../storage",
"../../tables:tables",
"../../types",
+ "../../util:build_id",
"../../util:profiler_util",
- "../../util:stack_traces_util",
"../fuchsia:fuchsia_record",
"../systrace:systrace_line",
]
@@ -105,6 +115,7 @@
"process_tracker_unittest.cc",
"slice_tracker_unittest.cc",
"slice_translation_table_unittest.cc",
+ "thread_state_tracker_unittest.cc",
]
testonly = true
deps = [
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/sched_event_state.h b/src/trace_processor/importers/common/sched_event_state.h
new file mode 100644
index 0000000..4ebc6ce
--- /dev/null
+++ b/src/trace_processor/importers/common/sched_event_state.h
@@ -0,0 +1,69 @@
+/*
+ * 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_SCHED_EVENT_STATE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SCHED_EVENT_STATE_H_
+
+#include <iosfwd>
+
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/version_number.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Responsible for keeping the state of pending sched events.
+class SchedEventState {
+ public:
+ // Information retained from the preceding sched_switch seen on a given cpu.
+ struct PendingSchedInfo {
+ // The pending scheduling slice that the next event will complete.
+ uint32_t pending_slice_storage_idx = std::numeric_limits<uint32_t>::max();
+
+ // pid/utid/prio corresponding to the last sched_switch seen on this cpu
+ // (its "next_*" fields). There is some duplication with respect to the
+ // slices storage, but we don't always have a slice when decoding events in
+ // the compact format.
+ uint32_t last_pid = std::numeric_limits<uint32_t>::max();
+ UniqueTid last_utid = std::numeric_limits<UniqueTid>::max();
+ int32_t last_prio = std::numeric_limits<int32_t>::max();
+ };
+
+ SchedEventState() {
+ // Pre-allocate space for 128 CPUs, which should be enough for most hosts.
+ // It's OK if this number is too small, the vector will be grown on-demand.
+ pending_sched_per_cpu_.reserve(128);
+ }
+ SchedEventState(const SchedEventState&) = delete;
+ ~SchedEventState() = default;
+
+ // Get the sched info for the given CPU, resizing the vector if necessary.
+ PendingSchedInfo* GetPendingSchedInfoForCpu(uint32_t cpu) {
+ if (PERFETTO_UNLIKELY(cpu >= pending_sched_per_cpu_.size())) {
+ pending_sched_per_cpu_.resize(cpu + 1);
+ }
+ return &pending_sched_per_cpu_[cpu];
+ }
+
+ private:
+ // Information retained from the preceding sched_switch seen on a given cpu.
+ std::vector<PendingSchedInfo> pending_sched_per_cpu_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SCHED_EVENT_STATE_H_
diff --git a/src/trace_processor/util/stack_traces_util.cc b/src/trace_processor/importers/common/sched_event_tracker.cc
similarity index 71%
rename from src/trace_processor/util/stack_traces_util.cc
rename to src/trace_processor/importers/common/sched_event_tracker.cc
index a255560..e541d58 100644
--- a/src/trace_processor/util/stack_traces_util.cc
+++ b/src/trace_processor/importers/common/sched_event_tracker.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,17 +14,12 @@
* limitations under the License.
*/
-#include "src/trace_processor/util/stack_traces_util.h"
-#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/common/sched_event_tracker.h"
namespace perfetto {
namespace trace_processor {
-namespace util {
-bool IsHexModuleId(base::StringView module) {
- return module.size() == 33;
-}
+SchedEventTracker::~SchedEventTracker() = default;
-} // namespace util
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/importers/common/sched_event_tracker.h b/src/trace_processor/importers/common/sched_event_tracker.h
new file mode 100644
index 0000000..68a2926
--- /dev/null
+++ b/src/trace_processor/importers/common/sched_event_tracker.h
@@ -0,0 +1,109 @@
+/*
+ * 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_SCHED_EVENT_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SCHED_EVENT_TRACKER_H_
+
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/utils.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/system_info_tracker.h"
+#include "src/trace_processor/importers/common/thread_state_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/destructible.h"
+#include "src/trace_processor/types/task_state.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Tracks sched events and stores them into the storage as sched slices.
+class SchedEventTracker : public Destructible {
+ public:
+ PERFETTO_ALWAYS_INLINE
+ SchedEventTracker(TraceProcessorContext* context) : context_(context) {}
+ SchedEventTracker(const SchedEventTracker&) = delete;
+ ~SchedEventTracker() override;
+
+ PERFETTO_ALWAYS_INLINE
+ uint32_t AddStartSlice(uint32_t cpu,
+ int64_t ts,
+ UniqueTid next_utid,
+ int32_t next_prio) {
+ // Open a new scheduling slice, corresponding to the task that was
+ // just switched to. Set the duration to -1, to indicate that the event is
+ // not finished. Duration will be updated later after event finish.
+ auto* sched = context_->storage->mutable_sched_slice_table();
+ auto row_and_id = sched->Insert(
+ {ts, /* duration */ -1, cpu, next_utid, kNullStringId, next_prio});
+ SchedId sched_id = row_and_id.id;
+ return *sched->id().IndexOf(sched_id);
+ }
+
+ PERFETTO_ALWAYS_INLINE
+ bool UpdateEventTrackerTimestamp(int64_t ts,
+ const char* event_name,
+ size_t stats) {
+ // At this stage all events should be globally timestamp ordered.
+ if (ts < context_->event_tracker->max_timestamp()) {
+ PERFETTO_ELOG(
+ "%s event out of order by %.4f ms, skipping", event_name,
+ static_cast<double>(context_->event_tracker->max_timestamp() - ts) /
+ 1e6);
+ context_->storage->IncrementStats(stats);
+ return false;
+ }
+ context_->event_tracker->UpdateMaxTimestamp(ts);
+ return true;
+ }
+
+ PERFETTO_ALWAYS_INLINE
+ void ClosePendingSlice(uint32_t pending_slice_idx,
+ int64_t ts,
+ StringId prev_state) {
+ auto* slices = context_->storage->mutable_sched_slice_table();
+
+ int64_t duration = ts - slices->ts()[pending_slice_idx];
+ slices->mutable_dur()->Set(pending_slice_idx, duration);
+
+ // We store the state as a uint16 as we only consider values up to 2048
+ // when unpacking the information inside; this allows savings of 48 bits
+ // per slice.
+ slices->mutable_end_state()->Set(pending_slice_idx, prev_state);
+ }
+
+ PERFETTO_ALWAYS_INLINE
+ StringId TaskStateToStringId(int64_t task_state_int) {
+ using ftrace_utils::TaskState;
+
+ std::optional<VersionNumber> kernel_version =
+ SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
+ TaskState task_state = TaskState::FromRawPrevState(
+ static_cast<uint16_t>(task_state_int), kernel_version);
+ return task_state.is_valid()
+ ? context_->storage->InternString(task_state.ToString().data())
+ : kNullStringId;
+ }
+
+ private:
+ TraceProcessorContext* const context_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SCHED_EVENT_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/ftrace/thread_state_tracker.cc b/src/trace_processor/importers/common/thread_state_tracker.cc
similarity index 98%
rename from src/trace_processor/importers/ftrace/thread_state_tracker.cc
rename to src/trace_processor/importers/common/thread_state_tracker.cc
index 7eda59e..a2daecd 100644
--- a/src/trace_processor/importers/ftrace/thread_state_tracker.cc
+++ b/src/trace_processor/importers/common/thread_state_tracker.cc
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "src/trace_processor/importers/ftrace/thread_state_tracker.h"
+
+#include "src/trace_processor/importers/common/thread_state_tracker.h"
#include <optional>
namespace perfetto {
diff --git a/src/trace_processor/importers/ftrace/thread_state_tracker.h b/src/trace_processor/importers/common/thread_state_tracker.h
similarity index 95%
rename from src/trace_processor/importers/ftrace/thread_state_tracker.h
rename to src/trace_processor/importers/common/thread_state_tracker.h
index 839ad31..2b7206b 100644
--- a/src/trace_processor/importers/ftrace/thread_state_tracker.h
+++ b/src/trace_processor/importers/common/thread_state_tracker.h
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_THREAD_STATE_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_THREAD_STATE_TRACKER_H_
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_THREAD_STATE_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_THREAD_STATE_TRACKER_H_
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/destructible.h"
@@ -104,4 +105,4 @@
} // namespace trace_processor
} // namespace perfetto
-#endif // SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_THREAD_STATE_TRACKER_H_
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_THREAD_STATE_TRACKER_H_
diff --git a/src/trace_processor/importers/ftrace/thread_state_tracker_unittest.cc b/src/trace_processor/importers/common/thread_state_tracker_unittest.cc
similarity index 98%
rename from src/trace_processor/importers/ftrace/thread_state_tracker_unittest.cc
rename to src/trace_processor/importers/common/thread_state_tracker_unittest.cc
index 1b0e1a3..ea6794a 100644
--- a/src/trace_processor/importers/ftrace/thread_state_tracker_unittest.cc
+++ b/src/trace_processor/importers/common/thread_state_tracker_unittest.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "src/trace_processor/importers/ftrace/thread_state_tracker.h"
+#include "src/trace_processor/importers/common/thread_state_tracker.h"
#include <algorithm>
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/ftrace/BUILD.gn b/src/trace_processor/importers/ftrace/BUILD.gn
index c618978..3a533d1 100644
--- a/src/trace_processor/importers/ftrace/BUILD.gn
+++ b/src/trace_processor/importers/ftrace/BUILD.gn
@@ -37,6 +37,8 @@
"ftrace_module_impl.h",
"ftrace_parser.cc",
"ftrace_parser.h",
+ "ftrace_sched_event_tracker.cc",
+ "ftrace_sched_event_tracker.h",
"ftrace_tokenizer.cc",
"ftrace_tokenizer.h",
"gpu_work_period_tracker.cc",
@@ -49,10 +51,6 @@
"pkvm_hyp_cpu_tracker.h",
"rss_stat_tracker.cc",
"rss_stat_tracker.h",
- "sched_event_tracker.cc",
- "sched_event_tracker.h",
- "thread_state_tracker.cc",
- "thread_state_tracker.h",
"v4l2_tracker.cc",
"v4l2_tracker.h",
"virtio_gpu_tracker.cc",
@@ -99,8 +97,7 @@
testonly = true
sources = [
"binder_tracker_unittest.cc",
- "sched_event_tracker_unittest.cc",
- "thread_state_tracker_unittest.cc",
+ "ftrace_sched_event_tracker_unittest.cc",
]
deps = [
"../../../../gn:default_deps",
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 951c03d..6948fc3 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -26,9 +26,9 @@
#include "src/trace_processor/importers/common/metadata_tracker.h"
#include "src/trace_processor/importers/common/parser_types.h"
#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/thread_state_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
#include "src/trace_processor/importers/ftrace/binder_tracker.h"
-#include "src/trace_processor/importers/ftrace/thread_state_tracker.h"
#include "src/trace_processor/importers/ftrace/v4l2_tracker.h"
#include "src/trace_processor/importers/ftrace/virtio_video_tracker.h"
#include "src/trace_processor/importers/i2c/i2c_tracker.h"
@@ -1120,10 +1120,11 @@
}
using protos::pbzero::FtraceEvent;
- SchedEventTracker* sched_tracker = SchedEventTracker::GetOrCreate(context_);
- sched_tracker->PushSchedSwitchCompact(cpu, ts, data.prev_state,
- static_cast<uint32_t>(data.next_pid),
- data.next_prio, data.next_comm);
+ FtraceSchedEventTracker* ftrace_sched_tracker =
+ FtraceSchedEventTracker::GetOrCreate(context_);
+ ftrace_sched_tracker->PushSchedSwitchCompact(
+ cpu, ts, data.prev_state, static_cast<uint32_t>(data.next_pid),
+ data.next_prio, data.next_comm);
return util::OkStatus();
}
@@ -1138,8 +1139,9 @@
return util::OkStatus();
}
using protos::pbzero::FtraceEvent;
- SchedEventTracker* sched_tracker = SchedEventTracker::GetOrCreate(context_);
- sched_tracker->PushSchedWakingCompact(
+ FtraceSchedEventTracker* ftrace_sched_tracker =
+ FtraceSchedEventTracker::GetOrCreate(context_);
+ ftrace_sched_tracker->PushSchedWakingCompact(
cpu, ts, static_cast<uint32_t>(data.pid), data.target_cpu, data.prio,
data.comm, data.common_flags);
return util::OkStatus();
@@ -1321,7 +1323,7 @@
protos::pbzero::SchedSwitchFtraceEvent::Decoder ss(blob.data, blob.size);
uint32_t prev_pid = static_cast<uint32_t>(ss.prev_pid());
uint32_t next_pid = static_cast<uint32_t>(ss.next_pid());
- SchedEventTracker::GetOrCreate(context_)->PushSchedSwitch(
+ FtraceSchedEventTracker::GetOrCreate(context_)->PushSchedSwitch(
cpu, timestamp, prev_pid, ss.prev_comm(), ss.prev_prio(), ss.prev_state(),
next_pid, ss.next_comm(), ss.next_prio());
}
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index f3234bd..b3764f1 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -26,12 +26,12 @@
#include "src/trace_processor/importers/common/trace_parser.h"
#include "src/trace_processor/importers/ftrace/drm_tracker.h"
#include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
+#include "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h"
#include "src/trace_processor/importers/ftrace/gpu_work_period_tracker.h"
#include "src/trace_processor/importers/ftrace/iostat_tracker.h"
#include "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h"
#include "src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.h"
#include "src/trace_processor/importers/ftrace/rss_stat_tracker.h"
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
#include "src/trace_processor/importers/ftrace/virtio_gpu_tracker.h"
#include "src/trace_processor/types/trace_processor_context.h"
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.cc b/src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.cc
similarity index 67%
rename from src/trace_processor/importers/ftrace/sched_event_tracker.cc
rename to src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.cc
index 09459c4..36bf028 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
+#include "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h"
#include <math.h>
@@ -22,9 +22,11 @@
#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/common/event_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/sched_event_tracker.h"
+#include "src/trace_processor/importers/common/sched_event_state.h"
#include "src/trace_processor/importers/common/system_info_tracker.h"
+#include "src/trace_processor/importers/common/thread_state_tracker.h"
#include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
-#include "src/trace_processor/importers/ftrace/thread_state_tracker.h"
#include "src/trace_processor/storage/stats.h"
#include "src/trace_processor/types/task_state.h"
#include "src/trace_processor/types/trace_processor_context.h"
@@ -36,9 +38,8 @@
namespace perfetto {
namespace trace_processor {
-SchedEventTracker::SchedEventTracker(TraceProcessorContext* context)
- : waker_utid_id_(context->storage->InternString("waker_utid")),
- context_(context) {
+FtraceSchedEventTracker::FtraceSchedEventTracker(TraceProcessorContext* context)
+ : context_(context) {
// pre-parse sched_switch
auto* switch_descriptor = GetMessageDescriptorForId(
protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber);
@@ -60,15 +61,11 @@
context->storage->InternString(waking_descriptor->fields[i].name);
}
sched_waking_id_ = context->storage->InternString(waking_descriptor->name);
-
- // Pre-allocate space for 128 CPUs, which should be enough for most hosts.
- // It's OK if this number is too small, the vector will be grown on-demand.
- pending_sched_per_cpu_.reserve(128);
}
-SchedEventTracker::~SchedEventTracker() = default;
+FtraceSchedEventTracker::~FtraceSchedEventTracker() = default;
-void SchedEventTracker::PushSchedSwitch(uint32_t cpu,
+void FtraceSchedEventTracker::PushSchedSwitch(uint32_t cpu,
int64_t ts,
uint32_t prev_pid,
base::StringView prev_comm,
@@ -77,16 +74,10 @@
uint32_t next_pid,
base::StringView next_comm,
int32_t next_prio) {
- // At this stage all events should be globally timestamp ordered.
- if (ts < context_->event_tracker->max_timestamp()) {
- PERFETTO_ELOG(
- "sched_switch event out of order by %.4f ms, skipping",
- static_cast<double>(context_->event_tracker->max_timestamp() - ts) /
- 1e6);
- context_->storage->IncrementStats(stats::sched_switch_out_of_order);
+ if (!context_->sched_event_tracker->UpdateEventTrackerTimestamp(ts,
+ "sched_switch",stats::sched_switch_out_of_order)) {
return;
}
- context_->event_tracker->UpdateMaxTimestamp(ts);
StringId next_comm_id = context_->storage->InternString(next_comm);
UniqueTid next_utid = context_->process_tracker->UpdateThreadName(
@@ -94,16 +85,18 @@
// First use this data to close the previous slice.
bool prev_pid_match_prev_next_pid = false;
- auto* pending_sched = PendingSchedByCPU(cpu);
+ auto* pending_sched = sched_event_state_.GetPendingSchedInfoForCpu(cpu);
uint32_t pending_slice_idx = pending_sched->pending_slice_storage_idx;
- StringId prev_state_string_id = TaskStateToStringId(prev_state);
+ StringId prev_state_string_id = context_->sched_event_tracker
+ ->TaskStateToStringId(prev_state);
if (prev_state_string_id == kNullStringId) {
context_->storage->IncrementStats(stats::task_state_invalid);
}
if (pending_slice_idx < std::numeric_limits<uint32_t>::max()) {
prev_pid_match_prev_next_pid = prev_pid == pending_sched->last_pid;
if (PERFETTO_LIKELY(prev_pid_match_prev_next_pid)) {
- ClosePendingSlice(pending_slice_idx, ts, prev_state_string_id);
+ context_->sched_event_tracker->ClosePendingSlice(pending_slice_idx, ts,
+ prev_state_string_id);
} else {
// If the pids are not consistent, make a note of this.
context_->storage->IncrementStats(stats::mismatched_sched_switch_tids);
@@ -117,9 +110,11 @@
UniqueTid prev_utid = context_->process_tracker->UpdateThreadName(
prev_pid, prev_comm_id, ThreadNamePriority::kFtrace);
- auto new_slice_idx = AddRawEventAndStartSlice(
- cpu, ts, prev_utid, prev_pid, prev_comm_id, prev_prio, prev_state,
- next_utid, next_pid, next_comm_id, next_prio);
+ AddRawSchedSwitchEvent(cpu, ts, prev_utid, prev_pid, prev_comm_id, prev_prio,
+ prev_state, next_pid, next_comm_id, next_prio);
+
+ auto new_slice_idx = context_->sched_event_tracker
+ ->AddStartSlice(cpu, ts, next_utid, next_prio);
// Finally, update the info for the next sched switch on this CPU.
pending_sched->pending_slice_storage_idx = new_slice_idx;
@@ -132,27 +127,21 @@
ts, cpu, prev_utid, prev_state_string_id, next_utid);
}
-void SchedEventTracker::PushSchedSwitchCompact(uint32_t cpu,
- int64_t ts,
- int64_t prev_state,
- uint32_t next_pid,
- int32_t next_prio,
- StringId next_comm_id) {
- // At this stage all events should be globally timestamp ordered.
- if (ts < context_->event_tracker->max_timestamp()) {
- PERFETTO_ELOG(
- "sched_switch event out of order by %.4f ms, skipping",
- static_cast<double>(context_->event_tracker->max_timestamp() - ts) /
- 1e6);
- context_->storage->IncrementStats(stats::sched_switch_out_of_order);
+void FtraceSchedEventTracker::PushSchedSwitchCompact(uint32_t cpu,
+ int64_t ts,
+ int64_t prev_state,
+ uint32_t next_pid,
+ int32_t next_prio,
+ StringId next_comm_id) {
+ if (!context_->sched_event_tracker->UpdateEventTrackerTimestamp(ts,
+ "sched_switch", stats::sched_switch_out_of_order)) {
return;
}
- context_->event_tracker->UpdateMaxTimestamp(ts);
UniqueTid next_utid = context_->process_tracker->UpdateThreadName(
next_pid, next_comm_id, ThreadNamePriority::kFtrace);
- auto* pending_sched = PendingSchedByCPU(cpu);
+ auto* pending_sched = sched_event_state_.GetPendingSchedInfoForCpu(cpu);
// If we're processing the first compact event for this cpu, don't start a
// slice since we're missing the "prev_*" fields. The successive events will
@@ -172,12 +161,14 @@
// Close the pending slice if any (we won't have one when processing the first
// two compact events for a given cpu).
uint32_t pending_slice_idx = pending_sched->pending_slice_storage_idx;
- StringId prev_state_string_id = TaskStateToStringId(prev_state);
+ StringId prev_state_string_id = context_->sched_event_tracker
+ ->TaskStateToStringId(prev_state);
if (prev_state_string_id == kNullStringId) {
context_->storage->IncrementStats(stats::task_state_invalid);
}
if (pending_slice_idx < std::numeric_limits<uint32_t>::max())
- ClosePendingSlice(pending_slice_idx, ts, prev_state_string_id);
+ context_->sched_event_tracker->ClosePendingSlice(pending_slice_idx, ts,
+ prev_state_string_id);
// Use the previous event's values to infer this event's "prev_*" fields.
// There are edge cases, but this assumption should still produce sensible
@@ -192,9 +183,10 @@
context_->storage->thread_table().name()[prev_utid].value_or(
kNullStringId);
- auto new_slice_idx = AddRawEventAndStartSlice(
- cpu, ts, prev_utid, prev_pid, prev_comm_id, prev_prio, prev_state,
- next_utid, next_pid, next_comm_id, next_prio);
+ AddRawSchedSwitchEvent(cpu, ts, prev_utid, prev_pid, prev_comm_id, prev_prio,
+ prev_state, next_pid, next_comm_id, next_prio);
+ auto new_slice_idx = context_->sched_event_tracker
+ ->AddStartSlice(cpu, ts, next_utid, next_prio);
// Finally, update the info for the next sched switch on this CPU.
pending_sched->pending_slice_storage_idx = new_slice_idx;
@@ -209,30 +201,24 @@
// Processes a sched_waking that was decoded from a compact representation,
// adding to the raw and instants tables.
-void SchedEventTracker::PushSchedWakingCompact(uint32_t cpu,
- int64_t ts,
- uint32_t wakee_pid,
- uint16_t target_cpu,
- uint16_t prio,
- StringId comm_id,
- uint16_t common_flags) {
- // At this stage all events should be globally timestamp ordered.
- if (ts < context_->event_tracker->max_timestamp()) {
- PERFETTO_ELOG(
- "sched_waking event out of order by %.4f ms, skipping",
- static_cast<double>(context_->event_tracker->max_timestamp() - ts) /
- 1e6);
- context_->storage->IncrementStats(stats::sched_waking_out_of_order);
+void FtraceSchedEventTracker::PushSchedWakingCompact(uint32_t cpu,
+ int64_t ts,
+ uint32_t wakee_pid,
+ uint16_t target_cpu,
+ uint16_t prio,
+ StringId comm_id,
+ uint16_t common_flags) {
+ if (!context_->sched_event_tracker->UpdateEventTrackerTimestamp(ts,
+ "sched_waking", stats::sched_waking_out_of_order)) {
return;
}
- context_->event_tracker->UpdateMaxTimestamp(ts);
// We infer the task that emitted the event (i.e. common_pid) from the
// scheduling slices. Drop the event if we haven't seen any sched_switch
// events for this cpu yet.
// Note that if sched_switch wasn't enabled, we will have to skip all
// compact waking events.
- auto* pending_sched = PendingSchedByCPU(cpu);
+ auto* pending_sched = sched_event_state_.GetPendingSchedInfoForCpu(cpu);
if (pending_sched->last_utid == std::numeric_limits<UniqueTid>::max()) {
context_->storage->IncrementStats(stats::compact_sched_waking_skipped);
return;
@@ -269,14 +255,13 @@
}
PERFETTO_ALWAYS_INLINE
-uint32_t SchedEventTracker::AddRawEventAndStartSlice(uint32_t cpu,
+void FtraceSchedEventTracker::AddRawSchedSwitchEvent(uint32_t cpu,
int64_t ts,
UniqueTid prev_utid,
uint32_t prev_pid,
StringId prev_comm_id,
int32_t prev_prio,
int64_t prev_state,
- UniqueTid next_utid,
uint32_t next_pid,
StringId next_comm_id,
int32_t next_prio) {
@@ -305,42 +290,6 @@
add_raw_arg(SS::kNextPidFieldNumber, Variadic::Integer(next_pid));
add_raw_arg(SS::kNextPrioFieldNumber, Variadic::Integer(next_prio));
}
-
- // Open a new scheduling slice, corresponding to the task that was
- // just switched to. Set the duration to -1, to indicate that the event is not
- // finished. Duration will be updated later after event finish.
- auto* sched = context_->storage->mutable_sched_slice_table();
- auto row_and_id = sched->Insert(
- {ts, /* duration */ -1, cpu, next_utid, kNullStringId, next_prio});
- SchedId sched_id = row_and_id.id;
- return *sched->id().IndexOf(sched_id);
-}
-
-StringId SchedEventTracker::TaskStateToStringId(int64_t task_state_int) {
- using ftrace_utils::TaskState;
-
- std::optional<VersionNumber> kernel_version =
- SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
- TaskState task_state = TaskState::FromRawPrevState(
- static_cast<uint16_t>(task_state_int), kernel_version);
- return task_state.is_valid()
- ? context_->storage->InternString(task_state.ToString().data())
- : kNullStringId;
-}
-
-PERFETTO_ALWAYS_INLINE
-void SchedEventTracker::ClosePendingSlice(uint32_t pending_slice_idx,
- int64_t ts,
- StringId prev_state) {
- auto* slices = context_->storage->mutable_sched_slice_table();
-
- int64_t duration = ts - slices->ts()[pending_slice_idx];
- slices->mutable_dur()->Set(pending_slice_idx, duration);
-
- // We store the state as a uint16 as we only consider values up to 2048
- // when unpacking the information inside; this allows savings of 48 bits
- // per slice.
- slices->mutable_end_state()->Set(pending_slice_idx, prev_state);
}
} // namespace trace_processor
diff --git a/src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h b/src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h
new file mode 100644
index 0000000..bc90532
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h
@@ -0,0 +1,115 @@
+/*
+ * 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_IMPORTERS_FTRACE_FTRACE_SCHED_EVENT_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_SCHED_EVENT_TRACKER_H_
+
+#include <array>
+#include <limits>
+
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/utils.h"
+#include "src/trace_processor/importers/common/sched_event_tracker.h"
+#include "src/trace_processor/importers/common/sched_event_state.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/destructible.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class EventTracker;
+
+// Tracks sched events and stores them into the storage as sched slices.
+class FtraceSchedEventTracker : public Destructible {
+ public:
+ explicit FtraceSchedEventTracker(TraceProcessorContext*);
+ ~FtraceSchedEventTracker() override;
+
+ FtraceSchedEventTracker(
+ const FtraceSchedEventTracker& ftrace_sched_event_tracker) = delete;
+ FtraceSchedEventTracker& operator=(
+ const FtraceSchedEventTracker& ftrace_sched_event_tracker) = delete;
+
+ static FtraceSchedEventTracker* GetOrCreate(TraceProcessorContext* context) {
+ if (!context->ftrace_sched_tracker) {
+ context->ftrace_sched_tracker.reset(new FtraceSchedEventTracker(context));
+ }
+ return static_cast<FtraceSchedEventTracker*>(
+ context->ftrace_sched_tracker.get());
+ }
+
+ // This method is called when a sched_switch event is seen in the trace.
+ // Virtual for testing.
+ virtual void PushSchedSwitch(uint32_t cpu,
+ int64_t timestamp,
+ uint32_t prev_pid,
+ base::StringView prev_comm,
+ int32_t prev_prio,
+ int64_t prev_state,
+ uint32_t next_pid,
+ base::StringView next_comm,
+ int32_t next_prio);
+
+ void AddRawSchedSwitchEvent(uint32_t cpu,
+ int64_t ts,
+ UniqueTid prev_utid,
+ uint32_t prev_pid,
+ StringId prev_comm_id,
+ int32_t prev_prio,
+ int64_t prev_state,
+ uint32_t next_pid,
+ StringId next_comm_id,
+ int32_t next_prio);
+
+ // This method is called when parsing a sched_switch encoded in the compact
+ // format.
+ void PushSchedSwitchCompact(uint32_t cpu,
+ int64_t ts,
+ int64_t prev_state,
+ uint32_t next_pid,
+ int32_t next_prio,
+ StringId next_comm_id);
+
+ // This method is called when parsing a sched_waking encoded in the compact
+ // format. Note that the default encoding is handled by
+ // |EventTracker::PushInstant|.
+ void PushSchedWakingCompact(uint32_t cpu,
+ int64_t ts,
+ uint32_t wakee_pid,
+ uint16_t target_cpu,
+ uint16_t prio,
+ StringId comm_id,
+ uint16_t common_flags);
+
+ private:
+ static constexpr uint8_t kSchedSwitchMaxFieldId = 7;
+ std::array<StringId, kSchedSwitchMaxFieldId + 1> sched_switch_field_ids_;
+ StringId sched_switch_id_;
+
+ static constexpr uint8_t kSchedWakingMaxFieldId = 5;
+ std::array<StringId, kSchedWakingMaxFieldId + 1> sched_waking_field_ids_;
+ StringId sched_waking_id_;
+
+ TraceProcessorContext* const context_;
+
+ SchedEventState sched_event_state_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_SCHED_EVENT_TRACKER_H_
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc b/src/trace_processor/importers/ftrace/ftrace_sched_event_tracker_unittest.cc
similarity index 93%
rename from src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc
rename to src/trace_processor/importers/ftrace/ftrace_sched_event_tracker_unittest.cc
index 5d275d6..a085078 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_sched_event_tracker_unittest.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
+#include "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h"
#include "perfetto/base/logging.h"
#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/sched_event_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "test/gtest_and_gmock.h"
@@ -39,12 +40,13 @@
context.args_tracker.reset(new ArgsTracker(&context));
context.event_tracker.reset(new EventTracker(&context));
context.process_tracker.reset(new ProcessTracker(&context));
- sched_tracker = SchedEventTracker::GetOrCreate(&context);
+ context.sched_event_tracker.reset(new SchedEventTracker(&context));
+ sched_tracker = FtraceSchedEventTracker::GetOrCreate(&context);
}
protected:
TraceProcessorContext context;
- SchedEventTracker* sched_tracker;
+ FtraceSchedEventTracker* sched_tracker;
};
TEST_F(SchedEventTrackerTest, InsertSecondSched) {
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.h b/src/trace_processor/importers/ftrace/sched_event_tracker.h
deleted file mode 100644
index 745b7c3..0000000
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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_IMPORTERS_FTRACE_SCHED_EVENT_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_SCHED_EVENT_TRACKER_H_
-
-#include <array>
-#include <limits>
-
-#include "perfetto/ext/base/string_view.h"
-#include "perfetto/ext/base/utils.h"
-#include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/types/destructible.h"
-#include "src/trace_processor/types/trace_processor_context.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-class EventTracker;
-
-// Tracks sched events and stores them into the storage as sched slices.
-class SchedEventTracker : public Destructible {
- public:
- // Declared public for testing only.
- explicit SchedEventTracker(TraceProcessorContext*);
- SchedEventTracker(const SchedEventTracker&) = delete;
- SchedEventTracker& operator=(const SchedEventTracker&) = delete;
- ~SchedEventTracker() override;
- static SchedEventTracker* GetOrCreate(TraceProcessorContext* context) {
- if (!context->sched_tracker) {
- context->sched_tracker.reset(new SchedEventTracker(context));
- }
- return static_cast<SchedEventTracker*>(context->sched_tracker.get());
- }
-
- // This method is called when a sched_switch event is seen in the trace.
- // Virtual for testing.
- virtual void PushSchedSwitch(uint32_t cpu,
- int64_t timestamp,
- uint32_t prev_pid,
- base::StringView prev_comm,
- int32_t prev_prio,
- int64_t prev_state,
- uint32_t next_pid,
- base::StringView next_comm,
- int32_t next_prio);
-
- // This method is called when parsing a sched_switch encoded in the compact
- // format.
- void PushSchedSwitchCompact(uint32_t cpu,
- int64_t ts,
- int64_t prev_state,
- uint32_t next_pid,
- int32_t next_prio,
- StringId next_comm_id);
-
- // This method is called when parsing a sched_waking encoded in the compact
- // format. Note that the default encoding is handled by
- // |EventTracker::PushInstant|.
- void PushSchedWakingCompact(uint32_t cpu,
- int64_t ts,
- uint32_t wakee_pid,
- uint16_t target_cpu,
- uint16_t prio,
- StringId comm_id,
- uint16_t common_flags);
-
- private:
- // Information retained from the preceding sched_switch seen on a given cpu.
- struct PendingSchedInfo {
- // The pending scheduling slice that the next event will complete.
- uint32_t pending_slice_storage_idx = std::numeric_limits<uint32_t>::max();
-
- // pid/utid/prio corresponding to the last sched_switch seen on this cpu
- // (its "next_*" fields). There is some duplication with respect to the
- // slices storage, but we don't always have a slice when decoding events in
- // the compact format.
- uint32_t last_pid = std::numeric_limits<uint32_t>::max();
- UniqueTid last_utid = std::numeric_limits<UniqueTid>::max();
- int32_t last_prio = std::numeric_limits<int32_t>::max();
- };
-
- uint32_t AddRawEventAndStartSlice(uint32_t cpu,
- int64_t ts,
- UniqueTid prev_utid,
- uint32_t prev_pid,
- StringId prev_comm_id,
- int32_t prev_prio,
- int64_t prev_state,
- UniqueTid next_utid,
- uint32_t next_pid,
- StringId next_comm_id,
- int32_t next_prio);
-
- StringId TaskStateToStringId(int64_t task_state);
-
- void ClosePendingSlice(uint32_t slice_idx, int64_t ts, StringId prev_state);
-
- // Information retained from the preceding sched_switch seen on a given cpu.
- std::vector<PendingSchedInfo> pending_sched_per_cpu_;
-
- // Get the sched info for the given CPU, resizing the vector if necessary.
- PendingSchedInfo* PendingSchedByCPU(uint32_t cpu) {
- if (PERFETTO_UNLIKELY(cpu >= pending_sched_per_cpu_.size())) {
- pending_sched_per_cpu_.resize(cpu + 1);
- }
- return &pending_sched_per_cpu_[cpu];
- }
-
- static constexpr uint8_t kSchedSwitchMaxFieldId = 7;
- std::array<StringId, kSchedSwitchMaxFieldId + 1> sched_switch_field_ids_;
- StringId sched_switch_id_;
-
- static constexpr uint8_t kSchedWakingMaxFieldId = 5;
- std::array<StringId, kSchedWakingMaxFieldId + 1> sched_waking_field_ids_;
- StringId sched_waking_id_;
-
- StringId waker_utid_id_;
-
- TraceProcessorContext* const context_;
-};
-
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_SCHED_EVENT_TRACKER_H_
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc b/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
index d644742..9f90bed 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
@@ -31,7 +31,7 @@
#include "src/trace_processor/importers/common/slice_tracker.h"
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
+#include "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h"
#include "src/trace_processor/importers/proto/additional_modules.h"
#include "src/trace_processor/importers/proto/default_modules.h"
#include "src/trace_processor/importers/proto/proto_trace_parser.h"
@@ -91,10 +91,10 @@
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::UnorderedElementsAreArray;
-class MockSchedEventTracker : public SchedEventTracker {
+class MockSchedEventTracker : public FtraceSchedEventTracker {
public:
explicit MockSchedEventTracker(TraceProcessorContext* context)
- : SchedEventTracker(context) {}
+ : FtraceSchedEventTracker(context) {}
MOCK_METHOD(void,
PushSchedSwitch,
@@ -244,7 +244,7 @@
event_ = new MockEventTracker(&context_);
context_.event_tracker.reset(event_);
sched_ = new MockSchedEventTracker(&context_);
- context_.sched_tracker.reset(sched_);
+ context_.ftrace_sched_tracker.reset(sched_);
process_ = new NiceMock<MockProcessTracker>(&context_);
context_.process_tracker.reset(process_);
slice_ = new NiceMock<MockSliceTracker>(&context_);
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 148bf60..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,12 +25,13 @@
#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"
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
+#include "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h"
#include "src/trace_processor/importers/proto/additional_modules.h"
#include "src/trace_processor/importers/proto/default_modules.h"
#include "src/trace_processor/importers/proto/proto_trace_parser.h"
@@ -106,10 +107,10 @@
}
} // namespace
-class MockSchedEventTracker : public SchedEventTracker {
+class MockSchedEventTracker : public FtraceSchedEventTracker {
public:
explicit MockSchedEventTracker(TraceProcessorContext* context)
- : SchedEventTracker(context) {}
+ : FtraceSchedEventTracker(context) {}
MOCK_METHOD(void,
PushSchedSwitch,
@@ -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_));
@@ -260,7 +262,7 @@
event_ = new MockEventTracker(&context_);
context_.event_tracker.reset(event_);
sched_ = new MockSchedEventTracker(&context_);
- context_.sched_tracker.reset(sched_);
+ context_.ftrace_sched_tracker.reset(sched_);
process_ = new NiceMock<MockProcessTracker>(&context_);
context_.process_tracker.reset(process_);
slice_ = new NiceMock<MockSliceTracker>(&context_);
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/importers/systrace/systrace_line_parser.cc b/src/trace_processor/importers/systrace/systrace_line_parser.cc
index ccf711e..7bfb844 100644
--- a/src/trace_processor/importers/systrace/systrace_line_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_line_parser.cc
@@ -23,10 +23,10 @@
#include "src/trace_processor/importers/common/event_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/thread_state_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
#include "src/trace_processor/importers/ftrace/binder_tracker.h"
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
-#include "src/trace_processor/importers/ftrace/thread_state_tracker.h"
+#include "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h"
#include "src/trace_processor/importers/systrace/systrace_parser.h"
#include "src/trace_processor/types/task_state.h"
@@ -107,7 +107,7 @@
return util::Status("Could not parse sched_switch");
}
- SchedEventTracker::GetOrCreate(context_)->PushSchedSwitch(
+ FtraceSchedEventTracker::GetOrCreate(context_)->PushSchedSwitch(
line.cpu, line.ts, prev_pid.value(), prev_comm, prev_prio.value(),
prev_state, next_pid.value(), next_comm, next_prio.value());
} else if (line.event_name == "tracing_mark_write" ||
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/ancestor.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/ancestor.cc
index 7680b24..f102b1f 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/ancestor.cc
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/ancestor.cc
@@ -70,6 +70,10 @@
// Update the loop variable by looking up the next parent_id.
maybe_parent_id = ref.parent_id();
}
+ // We traverse the tree in reverse id order. To ensure we meet the
+ // requirements of the extension vectors being sorted, ensure that we reverse
+ // the row numbers to be in id order.
+ std::reverse(row_numbers_accumulator.begin(), row_numbers_accumulator.end());
return base::OkStatus();
}
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
index fa5ba77..642b822 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
@@ -15,6 +15,7 @@
import("../../../../../gn/perfetto_sql.gni")
perfetto_sql_source_set("sched") {
+ deps = [ "utilization" ]
sources = [
"states.sql",
"thread_executing_span.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/utilization/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/sched/utilization/BUILD.gn
new file mode 100644
index 0000000..d4be2cc
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/utilization/BUILD.gn
@@ -0,0 +1,24 @@
+# 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.
+
+import("../../../../../../gn/perfetto_sql.gni")
+
+perfetto_sql_source_set("utilization") {
+ sources = [
+ "general.sql",
+ "process.sql",
+ "system.sql",
+ "thread.sql",
+ ]
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/utilization/general.sql b/src/trace_processor/perfetto_sql/stdlib/sched/utilization/general.sql
new file mode 100644
index 0000000..629e5a3
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/utilization/general.sql
@@ -0,0 +1,75 @@
+--
+-- Copyright 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
+--
+-- https://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.
+
+-- Returns the timestamp of the start of the partition that contains the |ts|.
+CREATE PERFETTO FUNCTION _partition_start(ts INT, size INT) RETURNS INT AS
+-- Division of two ints would result in floor(ts/size).
+SELECT ($ts/$size)*$size;
+
+-- Returns the number of partitions required to cover all of the trace
+-- timestamps.
+CREATE PERFETTO FUNCTION _partition_count(size INT) RETURNS INT AS
+SELECT
+ (_partition_start(TRACE_END(), $size) -
+ _partition_start(TRACE_START(), $size))/$size + 1;
+
+-- Returns a table of partitions with first partition containing the
+-- TRACE_START() and last one containing TRACE_END().
+CREATE PERFETTO FUNCTION _partitions(size INT)
+RETURNS TABLE (ts INT, ts_end INT) AS
+WITH no_ends AS (
+SELECT
+ _partition_start(TRACE_START(), $size) + (id * $size) AS ts
+-- We are using the sched table for source of ids. If the table is too small
+-- for specified size, the results would be invalid none the less.
+FROM sched
+LIMIT _partition_count($size))
+SELECT ts, ts + $size AS ts_end FROM no_ends;
+
+-- Partitions any |intervals| table with partitions defined in the |partitions|
+-- table.
+CREATE PERFETTO MACRO _interval_partitions(
+ -- Requires |ts| and |ts_end| columns.
+ partitions TableOrSubquery,
+ -- Requires |ts| and |ts_end| column.
+ intervals TableOrSubquery
+) RETURNS TableOrSubquery AS (
+SELECT
+ p.ts AS partition_ts,
+ IIF(i.ts_end < p.ts_end, i.ts_end, p.ts_end) AS ts_end,
+ IIF(i.ts < p.ts, p.ts, i.ts) AS ts
+FROM $intervals i
+JOIN $partitions p
+ON (p.ts <= i.ts AND i.ts < p.ts_end));
+
+-- Returns a table of utilization per given period.
+-- Utilization is calculated as sum of average utilization of each CPU in each
+-- period, which is defined as a multiply of |interval|. For this reason
+-- first and last period might have lower then real utilization.
+CREATE PERFETTO MACRO _sched_avg_utilization_per_period(
+ -- Length of the period on which utilization should be averaged.
+ interval Expr,
+ -- Either sched table or its filtered down version.
+ sched_table TableOrSubquery
+)
+-- The returned table has the schema (ts UINT32, utilization DOUBLE,
+-- unnormalized_utilization DOUBLE).
+RETURNS TableOrSubquery AS (
+SELECT
+ partition_ts AS ts,
+ SUM(ts_end - ts)/(cast_double!($interval) * (SELECT MAX(cpu) + 1 FROM sched)) AS utilization,
+ SUM(ts_end - ts)/cast_double!($interval) AS unnormalized_utilization
+FROM _interval_partitions!(_partitions($interval), $sched_table)
+GROUP BY 1);
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/utilization/process.sql b/src/trace_processor/perfetto_sql/stdlib/sched/utilization/process.sql
new file mode 100644
index 0000000..8437cb4
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/utilization/process.sql
@@ -0,0 +1,72 @@
+--
+-- Copyright 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
+--
+-- https://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 MODULE sched.utilization.general;
+INCLUDE PERFETTO MODULE time.conversion;
+
+-- Returns a table of process utilization per given period.
+-- Utilization is calculated as sum of average utilization of each CPU in each
+-- period, which is defined as a multiply of |interval|. For this reason
+-- first and last period might have lower then real utilization.
+CREATE PERFETTO FUNCTION sched_process_utilization_per_period(
+ -- Length of the period on which utilization should be averaged.
+ interval INT,
+ -- Upid of the process.
+ upid INT
+)
+RETURNS TABLE(
+ -- Timestamp of start of a second.
+ ts INT,
+ -- Sum of average utilization over period.
+ -- Note: as the data is normalized, the values will be in the
+ -- [0, 1] range.
+ utilization DOUBLE,
+ -- Sum of average utilization over all CPUs over period.
+ -- Note: as the data is unnormalized, the values will be in the
+ -- [0, cpu_count] range.
+ unnormalized_utilization DOUBLE
+) AS
+WITH sched_for_upid AS (
+ SELECT
+ ts,
+ ts_end,
+ utid
+ FROM sched
+ JOIN thread USING (utid)
+ JOIN process USING (upid)
+ WHERE upid = $upid AND utid != 0)
+SELECT * FROM _sched_avg_utilization_per_period!($interval, sched_for_upid);
+
+-- Returns a table of process utilization per second.
+-- Utilization is calculated as sum of average utilization of each CPU in each
+-- period, which is defined as a multiply of |interval|. For this reason
+-- first and last period might have lower then real utilization.
+CREATE PERFETTO FUNCTION sched_process_utilization_per_second(
+ -- Upid of the process.
+ upid INT
+)
+RETURNS TABLE (
+ -- Timestamp of start of a second.
+ ts INT,
+ -- Sum of average utilization over period.
+ -- Note: as the data is normalized, the values will be in the
+ -- [0, 1] range.
+ utilization DOUBLE,
+ -- Sum of average utilization over all CPUs over period.
+ -- Note: as the data is unnormalized, the values will be in the
+ -- [0, cpu_count] range.
+ unnormalized_utilization DOUBLE
+) AS
+SELECT * FROM sched_process_utilization_per_period(time_from_s(1), $upid);
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/utilization/system.sql b/src/trace_processor/perfetto_sql/stdlib/sched/utilization/system.sql
new file mode 100644
index 0000000..7dd671d
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/utilization/system.sql
@@ -0,0 +1,67 @@
+--
+-- Copyright 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
+--
+-- https://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 MODULE sched.utilization.general;
+INCLUDE PERFETTO MODULE time.conversion;
+
+-- The purpose of this module is to provide high level aggregates of system
+-- utilization, akin to /proc/stat results.
+
+-- Returns a table of system utilization per given period.
+-- Utilization is calculated as sum of average utilization of each CPU in each
+-- period, which is defined as a multiply of |interval|. For this reason
+-- first and last period might have lower then real utilization.
+CREATE PERFETTO FUNCTION sched_utilization_per_period(
+ -- Length of the period on which utilization should be averaged.
+ interval INT)
+RETURNS TABLE (
+ -- Timestamp of start of a second.
+ ts INT,
+ -- Sum of average utilization over period.
+ -- Note: as the data is normalized, the values will be in the
+ -- [0, 1] range.
+ utilization DOUBLE,
+ -- Sum of average utilization over all CPUs over period.
+ -- Note: as the data is unnormalized, the values will be in the
+ -- [0, cpu_count] range.
+ unnormalized_utilization DOUBLE
+) AS
+SELECT *
+FROM _sched_avg_utilization_per_period!(
+ $interval,
+ (SELECT * FROM sched WHERE utid != 0)
+);
+
+-- Table with system utilization per second.
+-- Utilization is calculated by sum of average utilization of each CPU every
+-- second. For this reason first and last second might have lower then real
+-- utilization.
+CREATE PERFETTO TABLE sched_utilization_per_second(
+ -- Timestamp of start of a second.
+ ts INT,
+ -- Sum of average utilization over period.
+ -- Note: as the data is normalized, the values will be in the
+ -- [0, 1] range.
+ utilization DOUBLE,
+ -- Sum of average utilization over all CPUs over period.
+ -- Note: as the data is unnormalized, the values will be in the
+ -- [0, cpu_count] range.
+ unnormalized_utilization DOUBLE
+) AS
+SELECT
+ ts,
+ utilization,
+ unnormalized_utilization
+FROM sched_utilization_per_period(time_from_s(1));
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/utilization/thread.sql b/src/trace_processor/perfetto_sql/stdlib/sched/utilization/thread.sql
new file mode 100644
index 0000000..ae1d3f6
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/utilization/thread.sql
@@ -0,0 +1,70 @@
+--
+-- Copyright 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
+--
+-- https://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 MODULE sched.utilization.general;
+INCLUDE PERFETTO MODULE time.conversion;
+
+-- Returns a table of thread utilization per given period.
+-- Utilization is calculated as sum of average utilization of each CPU in each
+-- period, which is defined as a multiply of |interval|. For this reason
+-- first and last period might have lower then real utilization.
+CREATE PERFETTO FUNCTION sched_thread_utilization_per_period(
+ -- Length of the period on which utilization should be averaged.
+ interval INT,
+ -- Utid of the thread.
+ utid INT
+)
+RETURNS TABLE(
+ -- Timestamp of start of a second.
+ ts INT,
+ -- Sum of average utilization over period.
+ -- Note: as the data is normalized, the values will be in the
+ -- [0, 1] range.
+ utilization DOUBLE,
+ -- Sum of average utilization over all CPUs over period.
+ -- Note: as the data is unnormalized, the values will be in the
+ -- [0, cpu_count] range.
+ unnormalized_utilization DOUBLE
+) AS
+WITH sched_for_utid AS (
+ SELECT
+ ts,
+ ts_end,
+ utid
+ FROM sched
+ WHERE utid = $utid
+) SELECT * FROM _sched_avg_utilization_per_period!($interval, sched_for_utid);
+
+-- Returns a table of thread utilization per second.
+-- Utilization is calculated as sum of average utilization of each CPU in each
+-- period, which is defined as a multiply of |interval|. For this reason
+-- first and last period might have lower then real utilization.
+CREATE PERFETTO FUNCTION sched_thread_utilization_per_second(
+ -- Utid of the thread.
+ utid INT
+)
+RETURNS TABLE (
+ -- Timestamp of start of a second.
+ ts INT,
+ -- Sum of average utilization over period.
+ -- Note: as the data is normalized, the values will be in the
+ -- [0, 1] range.
+ utilization DOUBLE,
+ -- Sum of average utilization over all CPUs over period.
+ -- Note: as the data is unnormalized, the values will be in the
+ -- [0, cpu_count] range.
+ unnormalized_utilization DOUBLE
+) AS
+SELECT * FROM sched_thread_utilization_per_period(time_from_s(1), $utid);
\ No newline at end of file
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index c7cea38..2ee7b3d 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -27,8 +27,10 @@
#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"
#include "src/trace_processor/importers/common/slice_tracker.h"
#include "src/trace_processor/importers/common/slice_translation_table.h"
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 9ad4635..692b105 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -1672,7 +1672,7 @@
RETURN_IF_ERROR(ExportTraceToDatabase(options.sqlite_file_path));
}
- if (options.enable_httpd || options.enable_stdiod) {
+ if (options.enable_httpd) {
#if PERFETTO_HAS_SIGNAL_H()
if (options.metatrace_path.empty()) {
// Restore the default signal handler to allow the user to terminate
@@ -1688,18 +1688,16 @@
}
#endif
- if (options.enable_httpd) {
#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
- RunHttpRPCServer(std::move(tp), options.port_number);
- PERFETTO_FATAL("Should never return");
+ RunHttpRPCServer(std::move(tp), options.port_number);
+ PERFETTO_FATAL("Should never return");
#else
- PERFETTO_FATAL("HTTP not available");
+ PERFETTO_FATAL("HTTP not available");
#endif
- }
+ }
- if (options.enable_stdiod) {
- return RunStdioRpcServer(std::move(tp));
- }
+ if (options.enable_stdiod) {
+ return RunStdioRpcServer(std::move(tp));
}
if (options.launch_shell) {
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index 05b88c0..b0c7a15 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -26,8 +26,10 @@
#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"
#include "src/trace_processor/importers/common/slice_tracker.h"
#include "src/trace_processor/importers/common/slice_translation_table.h"
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
@@ -59,9 +61,11 @@
new SliceTranslationTable(context_.storage.get()));
context_.flow_tracker.reset(new FlowTracker(&context_));
context_.event_tracker.reset(new EventTracker(&context_));
+ context_.sched_event_tracker.reset(new SchedEventTracker(&context_));
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 ad4e148..26d1ff5 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -55,11 +55,13 @@
class StackProfileTracker;
class HeapGraphTracker;
class PerfSampleTracker;
+class MappingTracker;
class MetadataTracker;
class PacketAnalyzer;
class ProtoImporterModule;
class TrackEventModule;
class ProcessTracker;
+class SchedEventTracker;
class SliceTracker;
class SliceTranslationTable;
class FlowTracker;
@@ -98,8 +100,10 @@
std::unique_ptr<FlowTracker> flow_tracker;
std::unique_ptr<ProcessTracker> process_tracker;
std::unique_ptr<EventTracker> event_tracker;
+ 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;
@@ -112,7 +116,6 @@
std::unique_ptr<Destructible> android_probes_tracker; // AndroidProbesTracker
std::unique_ptr<Destructible> binder_tracker; // BinderTracker
std::unique_ptr<Destructible> heap_graph_tracker; // HeapGraphTracker
- std::unique_ptr<Destructible> sched_tracker; // SchedEventTracker
std::unique_ptr<Destructible> syscall_tracker; // SyscallTracker
std::unique_ptr<Destructible> system_info_tracker; // SystemInfoTracker
std::unique_ptr<Destructible> v4l2_tracker; // V4l2Tracker
@@ -125,6 +128,8 @@
std::unique_ptr<Destructible>
shell_transitions_tracker; // ShellTransitionsTracker
std::unique_ptr<Destructible> v8_tracker; // V8Tracker
+ std::unique_ptr<Destructible>
+ ftrace_sched_tracker; // FtraceSchedEventTracker
// These fields are trace readers which will be called by |forwarding_parser|
// once the format of the trace is discovered. They are placed here as they
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..889291d
--- /dev/null
+++ b/src/trace_processor/util/build_id.cc
@@ -0,0 +1,128 @@
+/*
+ * 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;
+}
+
+bool IsHex(base::StringView module) {
+ for (char c : module) {
+ if (!std::isxdigit(c)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// 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 && IsHex(module);
+}
+
+} // 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.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_
diff --git a/src/trace_redaction/find_package_uid.cc b/src/trace_redaction/find_package_uid.cc
index d069fe2..0c46ff5 100644
--- a/src/trace_redaction/find_package_uid.cc
+++ b/src/trace_redaction/find_package_uid.cc
@@ -17,7 +17,9 @@
#include "src/trace_redaction/find_package_uid.h"
#include "perfetto/ext/base/status_or.h"
-#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_redaction/trace_redaction_framework.h"
+
#include "protos/perfetto/trace/android/packages_list.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
diff --git a/src/trace_redaction/find_package_uid.h b/src/trace_redaction/find_package_uid.h
index 4ae34d4..d43b2af 100644
--- a/src/trace_redaction/find_package_uid.h
+++ b/src/trace_redaction/find_package_uid.h
@@ -18,9 +18,10 @@
#define SRC_TRACE_REDACTION_FIND_PACKAGE_UID_H_
#include "perfetto/ext/base/status_or.h"
-#include "protos/perfetto/trace/trace_packet.pbzero.h"
#include "src/trace_redaction/trace_redaction_framework.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
namespace perfetto::trace_redaction {
// Writes the uid for the package matching `Context.package_name`. Returns
diff --git a/src/trace_redaction/find_package_uid_unittest.cc b/src/trace_redaction/find_package_uid_unittest.cc
index 44f0a45..5f390ab 100644
--- a/src/trace_redaction/find_package_uid_unittest.cc
+++ b/src/trace_redaction/find_package_uid_unittest.cc
@@ -15,18 +15,18 @@
* limitations under the License.
*/
-#include "find_package_uid.h"
+#include "src/trace_redaction/find_package_uid.h"
#include <cstdint>
#include <string>
-#include "protos/perfetto/trace/android/packages_list.gen.h"
-#include "protos/perfetto/trace/ps/process_tree.gen.h"
-#include "protos/perfetto/trace/ps/process_tree.pbzero.h"
-#include "protos/perfetto/trace/trace_packet.gen.h"
#include "src/base/test/status_matchers.h"
#include "test/gtest_and_gmock.h"
+#include "protos/perfetto/trace/android/packages_list.gen.h"
+#include "protos/perfetto/trace/ps/process_tree.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
+
namespace perfetto::trace_redaction {
namespace {
diff --git a/src/trace_redaction/prune_package_list.cc b/src/trace_redaction/prune_package_list.cc
index 52c1394..83d2355 100644
--- a/src/trace_redaction/prune_package_list.cc
+++ b/src/trace_redaction/prune_package_list.cc
@@ -16,7 +16,12 @@
#include "src/trace_redaction/prune_package_list.h"
+#include <string>
+
+#include "perfetto/base/status.h"
+
#include "protos/perfetto/trace/android/packages_list.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
namespace perfetto::trace_redaction {
diff --git a/src/trace_redaction/prune_package_list.h b/src/trace_redaction/prune_package_list.h
index cf1298f..24d9ec2 100644
--- a/src/trace_redaction/prune_package_list.h
+++ b/src/trace_redaction/prune_package_list.h
@@ -19,6 +19,7 @@
#include <string>
+#include "perfetto/base/status.h"
#include "src/trace_redaction/trace_redaction_framework.h"
namespace perfetto::trace_redaction {
diff --git a/src/trace_redaction/prune_package_list_unittest.cc b/src/trace_redaction/prune_package_list_unittest.cc
index 6a9af6c..0f78cba 100644
--- a/src/trace_redaction/prune_package_list_unittest.cc
+++ b/src/trace_redaction/prune_package_list_unittest.cc
@@ -16,13 +16,15 @@
*/
#include <cstdint>
+#include <memory>
#include <string>
+#include "src/trace_redaction/prune_package_list.h"
+#include "test/gtest_and_gmock.h"
+
#include "protos/perfetto/trace/android/packages_list.gen.h"
#include "protos/perfetto/trace/ps/process_tree.gen.h"
#include "protos/perfetto/trace/trace_packet.gen.h"
-#include "src/trace_redaction/prune_package_list.h"
-#include "test/gtest_and_gmock.h"
namespace perfetto::trace_redaction {
diff --git a/src/trace_redaction/scrub_trace_packet.cc b/src/trace_redaction/scrub_trace_packet.cc
index e4f03cf..e56acf9 100644
--- a/src/trace_redaction/scrub_trace_packet.cc
+++ b/src/trace_redaction/scrub_trace_packet.cc
@@ -14,8 +14,12 @@
* limitations under the License.
*/
+#include <string>
+
#include "src/trace_redaction/scrub_trace_packet.h"
+#include "perfetto/base/status.h"
+
namespace perfetto::trace_redaction {
// The TracePacket message has a simple structure. At its core its one sub
// message (e.g. ProcessTree) and some additional context (e.g. timestamp).
diff --git a/src/trace_redaction/scrub_trace_packet.h b/src/trace_redaction/scrub_trace_packet.h
index 06710d3..fbf89ca 100644
--- a/src/trace_redaction/scrub_trace_packet.h
+++ b/src/trace_redaction/scrub_trace_packet.h
@@ -19,8 +19,6 @@
#include "src/trace_redaction/trace_redaction_framework.h"
-#include "protos/perfetto/trace/trace_packet.pbzero.h"
-
namespace perfetto::trace_redaction {
// Drops whole trace packets based on an allow-list (e.g. retain ProcessTree
diff --git a/src/trace_redaction/scrub_trace_packet_unittest.cc b/src/trace_redaction/scrub_trace_packet_unittest.cc
index 82b99d4..f203165 100644
--- a/src/trace_redaction/scrub_trace_packet_unittest.cc
+++ b/src/trace_redaction/scrub_trace_packet_unittest.cc
@@ -21,6 +21,7 @@
#include "test/gtest_and_gmock.h"
#include "protos/perfetto/trace/ps/process_tree.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
namespace perfetto::trace_redaction {
diff --git a/src/trace_redaction/trace_redaction_framework.h b/src/trace_redaction/trace_redaction_framework.h
index b481961..76b8594 100644
--- a/src/trace_redaction/trace_redaction_framework.h
+++ b/src/trace_redaction/trace_redaction_framework.h
@@ -20,12 +20,11 @@
#include <cstdint>
#include <optional>
#include <string>
-#include <vector>
#include "perfetto/base/flat_set.h"
+#include "perfetto/base/status.h"
#include "perfetto/ext/base/status_or.h"
-#include "protos/perfetto/trace/trace_packet.gen.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
namespace perfetto::trace_redaction {
diff --git a/src/trace_redaction/trace_redactor.h b/src/trace_redaction/trace_redactor.h
index 99a8832..82ec371 100644
--- a/src/trace_redaction/trace_redactor.h
+++ b/src/trace_redaction/trace_redactor.h
@@ -17,8 +17,12 @@
#ifndef SRC_TRACE_REDACTION_TRACE_REDACTOR_H_
#define SRC_TRACE_REDACTION_TRACE_REDACTOR_H_
+#include <memory>
+#include <string>
#include <string_view>
+#include <vector>
+#include "perfetto/base/status.h"
#include "perfetto/trace_processor/trace_blob_view.h"
#include "src/trace_redaction/trace_redaction_framework.h"
diff --git a/src/trace_redaction/trace_redactor_integrationtest.cc b/src/trace_redaction/trace_redactor_integrationtest.cc
index 8e10095..8f14744 100644
--- a/src/trace_redaction/trace_redactor_integrationtest.cc
+++ b/src/trace_redaction/trace_redactor_integrationtest.cc
@@ -14,11 +14,19 @@
* limitations under the License.
*/
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "perfetto/base/status.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/temp_file.h"
#include "src/base/test/utils.h"
#include "src/trace_redaction/find_package_uid.h"
#include "src/trace_redaction/prune_package_list.h"
+#include "src/trace_redaction/trace_redaction_framework.h"
#include "src/trace_redaction/trace_redactor.h"
#include "test/gtest_and_gmock.h"
@@ -36,9 +44,6 @@
constexpr std::string_view kTracePath =
"test/data/trace-redaction-general.pftrace";
-constexpr std::string_view kPackageName =
- "com.Unity.com.unity.multiplayer.samples.coop";
-
constexpr uint64_t kPackageUid = 10252;
class TraceRedactorIntegrationTest : public testing::Test {
@@ -85,7 +90,7 @@
redaction.transformers()->emplace_back(new PrunePackageList());
Context context;
- context.package_name = kPackageName;
+ context.package_name = "com.Unity.com.unity.multiplayer.samples.coop";
auto result = redaction.Redact(src_trace(), dest_trace(), &context);
@@ -97,27 +102,73 @@
Trace::Decoder redacted_trace(redacted_buffer);
std::vector<protozero::ConstBytes> infos = GetPackageInfos(redacted_trace);
+ ASSERT_TRUE(context.package_uid.has_value());
+ ASSERT_EQ(NormalizeUid(context.package_uid.value()),
+ NormalizeUid(kPackageUid));
+
// It is possible for two packages_list to appear in the trace. The
// find_package_uid will stop after the first one is found. Package uids are
// appear as n * 1,000,000 where n is some integer. It is also possible for
// two packages_list to contain copies of each other - for example
// "com.Unity.com.unity.multiplayer.samples.coop" appears in both
// packages_list.
- ASSERT_GE(infos.size(), 1u);
+ ASSERT_EQ(infos.size(), 2u);
- for (const auto& info_buffer : infos) {
- PackageInfo::Decoder info(info_buffer);
+ std::array<PackageInfo::Decoder, 2> decoders = {
+ PackageInfo::Decoder(infos[0]), PackageInfo::Decoder(infos[1])};
+ for (auto& decoder : decoders) {
+ ASSERT_TRUE(decoder.has_name());
+ ASSERT_EQ(decoder.name().ToStdString(),
+ "com.Unity.com.unity.multiplayer.samples.coop");
+
+ ASSERT_TRUE(decoder.has_uid());
+ ASSERT_EQ(NormalizeUid(decoder.uid()), NormalizeUid(kPackageUid));
+ }
+}
+
+// It is possible for multiple packages to share a uid. The names will appears
+// across multiple package lists. The only time the package name appears is in
+// the package list, so there is no way to differentiate these packages (only
+// the uid is used later), so each entry should remain.
+TEST_F(TraceRedactorIntegrationTest, RetainsAllInstancesOfUid) {
+ TraceRedactor redaction;
+ redaction.collectors()->emplace_back(new FindPackageUid());
+ redaction.transformers()->emplace_back(new PrunePackageList());
+
+ Context context;
+ context.package_name = "com.google.android.networkstack.tethering";
+
+ auto result = redaction.Redact(src_trace(), dest_trace(), &context);
+ ASSERT_TRUE(result.ok()) << result.message();
+
+ std::string redacted_buffer;
+ ASSERT_TRUE(base::ReadFile(dest_trace(), &redacted_buffer));
+
+ Trace::Decoder redacted_trace(redacted_buffer);
+ std::vector<protozero::ConstBytes> infos = GetPackageInfos(redacted_trace);
+
+ ASSERT_EQ(infos.size(), 8u);
+
+ std::array<std::string, 8> package_names;
+
+ for (size_t i = 0; i < infos.size(); ++i) {
+ PackageInfo::Decoder info(infos[i]);
ASSERT_TRUE(info.has_name());
- ASSERT_EQ(info.name().ToStdString(), kPackageName);
-
- ASSERT_TRUE(info.has_uid());
- ASSERT_EQ(NormalizeUid(info.uid()), NormalizeUid(kPackageUid));
+ package_names[i] = info.name().ToStdString();
}
- ASSERT_TRUE(context.package_uid.has_value());
- ASSERT_EQ(NormalizeUid(context.package_uid.value()),
- NormalizeUid(kPackageUid));
+ std::sort(package_names.begin(), package_names.end());
+ ASSERT_EQ(package_names[0], "com.google.android.cellbroadcastservice");
+ ASSERT_EQ(package_names[1], "com.google.android.cellbroadcastservice");
+ ASSERT_EQ(package_names[2], "com.google.android.networkstack");
+ ASSERT_EQ(package_names[3], "com.google.android.networkstack");
+ ASSERT_EQ(package_names[4],
+ "com.google.android.networkstack.permissionconfig");
+ ASSERT_EQ(package_names[5],
+ "com.google.android.networkstack.permissionconfig");
+ ASSERT_EQ(package_names[6], "com.google.android.networkstack.tethering");
+ ASSERT_EQ(package_names[7], "com.google.android.networkstack.tethering");
}
} // namespace
diff --git a/test/trace_processor/diff_tests/stdlib/sched/tests.py b/test/trace_processor/diff_tests/stdlib/sched/tests.py
index ee96746..443d049 100644
--- a/test/trace_processor/diff_tests/stdlib/sched/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/sched/tests.py
@@ -45,6 +45,7 @@
trace=Path('../../common/synth_1.py'),
query="""
INCLUDE PERFETTO MODULE sched.thread_level_parallelism;
+
SELECT * FROM sched_active_cpu_count;
""",
out=Csv("""
@@ -58,3 +59,105 @@
250,2
390,2
"""))
+
+ def test_sched_utilization_per_second(self):
+ return DiffTestBlueprint(
+ trace=DataPath('example_android_trace_30s.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE sched.utilization.system;
+
+ SELECT * FROM sched_utilization_per_second;
+ """,
+ out=Csv("""
+ "ts","utilization","unnormalized_utilization"
+ 70000000000,0.004545,0.036362
+ 71000000000,0.022596,0.180764
+ 72000000000,0.163393,1.307146
+ 73000000000,0.452122,3.616972
+ 74000000000,0.525557,4.204453
+ 75000000000,0.388632,3.109057
+ 76000000000,0.425447,3.403579
+ 77000000000,0.201112,1.608896
+ 78000000000,0.280247,2.241977
+ 79000000000,0.345228,2.761827
+ 80000000000,0.303258,2.426064
+ 81000000000,0.487522,3.900172
+ 82000000000,0.080542,0.644336
+ 83000000000,0.362450,2.899601
+ 84000000000,0.076438,0.611501
+ 85000000000,0.110689,0.885514
+ 86000000000,0.681488,5.451901
+ 87000000000,0.808331,6.466652
+ 88000000000,0.941768,7.534142
+ 89000000000,0.480556,3.844446
+ 90000000000,0.453268,3.626142
+ 91000000000,0.280310,2.242478
+ 92000000000,0.006381,0.051049
+ 93000000000,0.030991,0.247932
+ 94000000000,0.031981,0.255845
+ 95000000000,0.027931,0.223446
+ 96000000000,0.063066,0.504529
+ 97000000000,0.023847,0.190773
+ 98000000000,0.011291,0.090328
+ 99000000000,0.024065,0.192518
+ 100000000000,0.001964,0.015711
+ """))
+
+ def test_sched_process_utilization_per_second(self):
+ return DiffTestBlueprint(
+ trace=DataPath('example_android_trace_30s.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE sched.utilization.process;
+
+ SELECT *
+ FROM sched_process_utilization_per_second(10);
+ """,
+ out=Csv("""
+ "ts","utilization","unnormalized_utilization"
+ 72000000000,0.000187,0.001495
+ 73000000000,0.000182,0.001460
+ 77000000000,0.000072,0.000579
+ 78000000000,0.000275,0.002204
+ 82000000000,0.000300,0.002404
+ 83000000000,0.000004,0.000034
+ 87000000000,0.000133,0.001065
+ 88000000000,0.000052,0.000416
+ 89000000000,0.000212,0.001697
+ 92000000000,0.000207,0.001658
+ 97000000000,0.000353,0.002823
+ """))
+
+ def test_sched_thread_utilization_per_second(self):
+ return DiffTestBlueprint(
+ trace=DataPath('example_android_trace_30s.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE sched.utilization.thread;
+
+ SELECT *
+ FROM sched_thread_utilization_per_second(10);
+ """,
+ out=Csv("""
+ "ts","utilization","unnormalized_utilization"
+ 70000000000,0.000024,0.000195
+ 72000000000,0.000025,0.000200
+ 73000000000,0.000053,0.000420
+ 74000000000,0.000044,0.000352
+ 75000000000,0.000058,0.000461
+ 76000000000,0.000075,0.000603
+ 77000000000,0.000051,0.000407
+ 78000000000,0.000047,0.000374
+ 79000000000,0.000049,0.000396
+ 80000000000,0.000084,0.000673
+ 81000000000,0.000041,0.000329
+ 82000000000,0.000048,0.000383
+ 83000000000,0.000040,0.000323
+ 84000000000,0.000018,0.000145
+ 85000000000,0.000053,0.000421
+ 86000000000,0.000121,0.000972
+ 87000000000,0.000049,0.000392
+ 88000000000,0.000036,0.000285
+ 89000000000,0.000033,0.000266
+ 90000000000,0.000050,0.000401
+ 91000000000,0.000025,0.000201
+ 92000000000,0.000009,0.000071
+ """))
diff --git a/tools/open_trace_in_ui b/tools/open_trace_in_ui
index 0bf8ea7..289b44a 100755
--- a/tools/open_trace_in_ui
+++ b/tools/open_trace_in_ui
@@ -41,14 +41,14 @@
def do_GET(self):
if self.path != '/' + self.server.expected_fname:
- self.send_error(404, "File not found")
+ self.send_error(404, 'File not found')
return
self.server.fname_get_completed = True
super().do_GET()
def do_POST(self):
- self.send_error(404, "File not found")
+ self.send_error(404, 'File not found')
def prt(msg, colors=ANSI.END):
@@ -77,23 +77,31 @@
def main():
- examples = '\n'.join(
- [ANSI.BOLD + 'Usage:' + ANSI.END, ' -i path/trace_file_name [-n]'])
+ examples = '\n'.join([
+ ANSI.BOLD + 'Examples:' + ANSI.END,
+ ' tools/open_trace_in_ui trace.pftrace',
+ ])
parser = argparse.ArgumentParser(
epilog=examples, formatter_class=argparse.RawTextHelpFormatter)
- help = 'Input trace filename'
- parser.add_argument('-i', '--trace', help=help)
+ parser.add_argument('positional_trace', metavar='trace', nargs='?')
parser.add_argument(
'-n', '--no-open-browser', action='store_true', default=False)
parser.add_argument('--origin', default='https://ui.perfetto.dev')
+ parser.add_argument(
+ '-i', '--trace', help='input filename (overrides positional argument)')
args = parser.parse_args()
- trace_file = args.trace
open_browser = not args.no_open_browser
+ trace_file = None
+ if args.positional_trace is not None:
+ trace_file = args.positional_trace
+ if args.trace is not None:
+ trace_file = args.trace
+
if trace_file is None:
- prt('Please specify trace file name with -i/--trace argument', ANSI.RED)
+ prt('Please specify trace file name', ANSI.RED)
sys.exit(1)
elif not os.path.exists(trace_file):
prt('%s not found ' % trace_file, ANSI.RED)
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index 7ce3c1b..b1943e4 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-@use "sass:math";
-
@import "widgets/theme";
@import "typefaces";
@import "fonts";
@@ -45,17 +43,6 @@
content: $content;
}
-@mixin track_shell_title() {
- font-size: 14px;
- max-height: 30px;
- overflow: hidden;
- text-align: left;
- overflow-wrap: break-word;
- font-family: "Roboto Condensed", sans-serif;
- font-weight: 300;
- line-break: anywhere;
-}
-
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
@@ -159,18 +146,6 @@
.page {
grid-area: page;
- position: relative;
- display: flex;
- flex-direction: column;
- overflow: hidden;
-}
-
-.split-panel {
- flex: 1;
- display: flex;
- flex-flow: row;
- position: relative;
- overflow: hidden;
}
.alerts {
@@ -377,169 +352,6 @@
}
}
-.track-content.pf-track-content-error {
- // Necessary trig because we have a 45deg stripes
- $pattern-density: 1px * math.sqrt(2);
- $pattern-col: #ddd;
-
- // box-shadow: inset 0 0 0 5px red;
- background: repeating-linear-gradient(
- -45deg,
- $pattern-col,
- $pattern-col $pattern-density,
- white $pattern-density,
- white $pattern-density * 2
- );
-}
-
-.track {
- display: grid;
- grid-template-columns: auto 1fr;
- grid-template-rows: 1fr 0;
-
- &::after {
- display: block;
- content: "";
- grid-column: 1 / span 2;
- border-top: 1px solid var(--track-border-color);
- margin-top: -1px;
- z-index: 2;
- }
-
- .track-shell {
- @include transition();
- padding-left: 10px;
- display: grid;
- cursor: grab;
- grid-template-areas: "title buttons";
- grid-template-columns: 1fr auto;
- align-items: center;
- width: var(--track-shell-width);
- background: #fff;
- border-right: 1px solid #c7d0db;
- overflow: hidden;
-
- &.drag {
- background-color: #eee;
- box-shadow: 0 4px 12px -4px #999 inset;
- }
- &.drop-before {
- box-shadow: 0 4px 2px -1px hsl(213, 40%, 50%) inset;
- }
- &.drop-after {
- box-shadow: 0 -4px 2px -1px hsl(213, 40%, 50%) inset;
- }
-
- &.selected {
- background-color: #ebeef9;
- }
-
- &.alternating-thread-track {
- background: hsl(214, 22%, 95%);
- }
-
- .chip {
- background-color: #bed6ff;
- border-radius: $pf-border-radius;
- font-size: smaller;
- padding: 0 0.1rem;
- margin-left: 1ch;
- }
-
- h1 {
- grid-area: title;
- color: hsl(213, 22%, 30%);
- @include track_shell_title();
- }
- .track-buttons {
- grid-area: buttons;
- display: flex;
- height: 100%;
- align-items: center;
- }
- .track-button {
- @include transition();
- color: rgb(60, 86, 136);
- cursor: pointer;
- width: 22px;
- font-size: 18px;
- opacity: 0;
- }
-
- .track-button.show {
- opacity: 1;
- }
- .track-button.full-height {
- display: flex;
- height: 100%;
- align-items: center;
- justify-content: center;
-
- &:hover {
- background-color: #ebeef9;
- }
- }
-
- &:hover .track-button {
- opacity: 1;
- }
- &.flash {
- background-color: #ffe263;
- }
- }
-}
-
-.pinned-panel-container {
- max-height: 50%;
- box-shadow: 1px 3px 15px rgba(23, 32, 44, 0.3);
- z-index: 1;
- flex-grow: 0;
- flex-shrink: 0;
- overflow: hidden;
- overflow-y: auto;
-}
-
-.scrolling-panel-container {
- overflow-x: hidden;
- overflow-y: auto;
- flex: 1 1 auto;
- will-change: transform; // Force layer creation.
-}
-
-.details-panel-container {
- box-shadow: #0000003b 0px 0px 3px 1px;
- overflow: auto;
-}
-
-.header-panel-container {
- overflow: visible;
- box-shadow: 1px 3px 15px rgba(23, 32, 44, 0.3);
- z-index: 2;
-}
-
-.pan-and-zoom-content {
- flex: 1;
- position: relative;
- display: flex;
- flex-flow: column nowrap;
-}
-
-.overview-timeline {
- height: 70px;
-}
-
-.time-axis-panel {
- height: 22px;
-}
-
-.tickbar {
- height: 5px;
-}
-
-.notes-panel {
- height: 20px;
-}
-
.x-scrollable {
overflow-x: auto;
}
@@ -594,15 +406,6 @@
margin: auto 0 auto 1rem;
}
-.debug-panel-border {
- position: absolute;
- top: 0;
- height: 100%;
- width: 100%;
- border: 1px solid rgba(69, 187, 73, 0.5);
- pointer-events: none;
-}
-
.perf-stats {
--stroke-color: hsl(217, 39%, 94%);
position: fixed;
@@ -649,117 +452,6 @@
}
}
-.track-group-panel {
- --collapsed-transparent: hsla(190, 49%, 97%, 0);
- --expanded-transparent: hsl(215, 22%, 19%, 0);
- display: grid;
- grid-template-columns: auto 1fr;
- grid-template-rows: 1fr;
- transition: background-color 0.4s, color 0.4s;
- height: 40px;
- &::after {
- display: block;
- content: "";
- grid-column: 1 / span 2;
- border-top: 1px solid var(--track-border-color);
- margin-top: -1px;
- }
- &[collapsed="true"] {
- background-color: var(--collapsed-transparent);
- .shell {
- border-right: 1px solid #c7d0db;
- background-color: var(--collapsed-background);
- }
- .track-button {
- color: rgb(60, 86, 136);
- }
- }
- &[collapsed="false"] {
- background-color: var(--expanded-transparent);
- color: white;
- font-weight: bold;
- .shell.flash {
- color: #121212;
- }
- .track-button {
- color: white;
- }
- span.chip {
- color: #121212;
- }
- }
- .shell {
- padding: 4px 4px;
- display: grid;
- grid-template-areas: "fold-button title buttons check";
- grid-template-columns: 28px 1fr auto 20px;
- align-items: center;
- line-height: 1;
- width: var(--track-shell-width);
- min-height: 40px;
- transition: background-color 0.4s;
-
- .track-title {
- user-select: text;
- }
-
- .track-subtitle {
- font-size: 0.6rem;
- font-weight: normal;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- // Maximum width according to grid-template-columns value for .shell
- width: calc(var(--track-shell-width) - 56px);
- }
-
- .chip {
- background-color: #bed6ff;
- border-radius: 3px;
- font-size: smaller;
- padding: 0 0.1rem;
- margin-left: 1ch;
- }
-
- .title-wrapper {
- grid-area: title;
- overflow: hidden;
- }
- h1 {
- @include track_shell_title();
- }
- .fold-button {
- grid-area: fold-button;
- }
- .track-button {
- font-size: 20px;
- }
- &:hover {
- cursor: pointer;
- .fold-button {
- color: hsl(45, 100%, 48%);
- }
- }
- &.flash {
- background-color: #ffe263;
- }
- &.selected {
- background-color: #ebeef9;
- }
- }
- .track-content {
- display: grid;
- span {
- @include track_shell_title();
- align-self: center;
- }
- }
-}
-
-.time-selection-panel {
- height: 10px;
-}
-
.cookie-consent {
position: absolute;
z-index: 10;
diff --git a/ui/src/assets/panel_container.scss b/ui/src/assets/panel_container.scss
index 9790dd0..6163353 100644
--- a/ui/src/assets/panel_container.scss
+++ b/ui/src/assets/panel_container.scss
@@ -12,42 +12,39 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-.panel-container {
- position: relative;
- // display: grid;
- // grid-template-columns: 1fr;
- // grid-template-rows: 1fr;
- // grid-template-areas: "space";
-}
-
-// In the scrolling case, since the canvas is overdrawn and continuously
-// repositioned, we need the canvas to be in a div with overflow hidden and
-// height equaling the total height of the content to prevent scrolling
-// height from growing.
-.scroll-limiter {
- position: absolute;
- top: 0;
- left: 0;
- overflow: hidden;
- height: 100%;
-}
-
-canvas.main-canvas {
- z-index: -1;
-}
-
-.panels {
- position: relative;
+.pf-panel-container {
+ // We need to drag over this element for various reasons, so just disable
+ // selection over the entire thing.
+ // TODO(stevegolton): If we enable this, we can get scrolling while dragging,
+ // so we might want to enable this here and disable selection in titles
+ // instead.
user-select: none;
-}
-.panel {
- position: relative; // Otherwise canvas covers panel dom.
+ .pf-panels {
+ // Make this a positioned element so .pf-scroll-limiter is positioned
+ // relative to this element.
+ position: relative;
- &.sticky {
- position: sticky;
- z-index: 3;
- top: 0;
- background-color: hsl(215, 22%, 19%);
+ // In the scrolling case, since the canvas is overdrawn and continuously
+ // repositioned, we need the canvas to be in a div with overflow hidden and
+ // height equalling the total height of the content to prevent scrolling
+ // height from growing.
+ .pf-scroll-limiter {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ overflow: hidden;
+
+ // Make this overlay invisible to pointer events.
+ pointer-events: none;
+ }
+
+ .pf-panel {
+ &.pf-sticky {
+ position: sticky;
+ top: 0;
+ }
+ }
}
}
diff --git a/ui/src/assets/perfetto.scss b/ui/src/assets/perfetto.scss
index 0cf20b8..8bb1cdc 100644
--- a/ui/src/assets/perfetto.scss
+++ b/ui/src/assets/perfetto.scss
@@ -15,6 +15,8 @@
@import "typefaces";
@import "common";
@import "panel_container";
+@import "viewer_page";
+@import "track_panel";
@import "home_page";
@import "query_page";
@import "metrics_page";
diff --git a/ui/src/assets/track_panel.scss b/ui/src/assets/track_panel.scss
new file mode 100644
index 0000000..0091fba
--- /dev/null
+++ b/ui/src/assets/track_panel.scss
@@ -0,0 +1,235 @@
+// 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.
+
+@use "sass:math";
+
+@mixin track_shell_title() {
+ font-size: 14px;
+ max-height: 30px;
+ overflow: hidden;
+ text-align: left;
+ overflow-wrap: break-word;
+ font-family: "Roboto Condensed", sans-serif;
+ font-weight: 300;
+ line-break: anywhere;
+}
+
+.track-content.pf-track-content-error {
+ // Necessary trig because we have a 45deg stripes
+ $pattern-density: 1px * math.sqrt(2);
+ $pattern-col: #ddd;
+
+ // box-shadow: inset 0 0 0 5px red;
+ background: repeating-linear-gradient(
+ -45deg,
+ $pattern-col,
+ $pattern-col $pattern-density,
+ white $pattern-density,
+ white $pattern-density * 2
+ );
+}
+
+.track {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ grid-template-rows: 1fr 0;
+
+ &::after {
+ display: block;
+ content: "";
+ grid-column: 1 / span 2;
+ border-top: 1px solid var(--track-border-color);
+ margin-top: -1px;
+ z-index: 2;
+ }
+
+ .track-shell {
+ @include transition();
+ padding-left: 10px;
+ display: grid;
+ cursor: grab;
+ grid-template-areas: "title buttons";
+ grid-template-columns: 1fr auto;
+ align-items: center;
+ width: var(--track-shell-width);
+ border-right: 1px solid #c7d0db;
+ overflow: hidden;
+
+ &.drag {
+ background-color: #eee;
+ box-shadow: 0 4px 12px -4px #999 inset;
+ }
+ &.drop-before {
+ box-shadow: 0 4px 2px -1px hsl(213, 40%, 50%) inset;
+ }
+ &.drop-after {
+ box-shadow: 0 -4px 2px -1px hsl(213, 40%, 50%) inset;
+ }
+
+ &.selected {
+ background-color: #ebeef9;
+ }
+
+ .chip {
+ background-color: #bed6ff;
+ border-radius: $pf-border-radius;
+ font-size: smaller;
+ padding: 0 0.1rem;
+ margin-left: 1ch;
+ }
+
+ h1 {
+ grid-area: title;
+ color: hsl(213, 22%, 30%);
+ @include track_shell_title();
+ }
+ .track-buttons {
+ grid-area: buttons;
+ display: flex;
+ height: 100%;
+ align-items: center;
+ }
+ .track-button {
+ @include transition();
+ color: rgb(60, 86, 136);
+ cursor: pointer;
+ width: 22px;
+ font-size: 18px;
+ opacity: 0;
+ }
+
+ .track-button.show {
+ opacity: 1;
+ }
+ .track-button.full-height {
+ display: flex;
+ height: 100%;
+ align-items: center;
+ justify-content: center;
+
+ &:hover {
+ background-color: #ebeef9;
+ }
+ }
+
+ &:hover .track-button {
+ opacity: 1;
+ }
+ &.flash {
+ background-color: #ffe263;
+ }
+ }
+}
+
+.track-group-panel {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ grid-template-rows: 1fr;
+ height: 40px;
+ &::after {
+ display: block;
+ content: "";
+ grid-column: 1 / span 2;
+ border-top: 1px solid var(--track-border-color);
+ margin-top: -1px;
+ }
+ &[collapsed="true"] {
+ background-color: var(--collapsed-background);
+ .shell {
+ border-right: 1px solid #c7d0db;
+ }
+ .track-button {
+ color: rgb(60, 86, 136);
+ }
+ }
+ &[collapsed="false"] {
+ background-color: var(--expanded-background);
+ color: white;
+ font-weight: bold;
+ .shell.flash {
+ color: #121212;
+ }
+ .track-button {
+ color: white;
+ }
+ span.chip {
+ color: #121212;
+ }
+ }
+ .shell {
+ padding: 4px 4px;
+ display: grid;
+ grid-template-areas: "fold-button title buttons check";
+ grid-template-columns: 28px 1fr auto 20px;
+ align-items: center;
+ line-height: 1;
+ width: var(--track-shell-width);
+ min-height: 40px;
+
+ .track-title {
+ user-select: text;
+ }
+
+ .track-subtitle {
+ font-size: 0.6rem;
+ font-weight: normal;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ // Maximum width according to grid-template-columns value for .shell
+ width: calc(var(--track-shell-width) - 56px);
+ }
+
+ .chip {
+ background-color: #bed6ff;
+ border-radius: 3px;
+ font-size: smaller;
+ padding: 0 0.1rem;
+ margin-left: 1ch;
+ }
+
+ .title-wrapper {
+ grid-area: title;
+ overflow: hidden;
+ }
+ h1 {
+ @include track_shell_title();
+ }
+ .fold-button {
+ grid-area: fold-button;
+ }
+ .track-button {
+ font-size: 20px;
+ }
+ &:hover {
+ cursor: pointer;
+ .fold-button {
+ color: hsl(45, 100%, 48%);
+ }
+ }
+ &.flash {
+ background-color: #ffe263;
+ }
+ &.selected {
+ background-color: #ebeef9;
+ }
+ }
+ .track-content {
+ display: grid;
+ span {
+ @include track_shell_title();
+ align-self: center;
+ }
+ }
+}
diff --git a/ui/src/assets/viewer_page.scss b/ui/src/assets/viewer_page.scss
new file mode 100644
index 0000000..3fdd737
--- /dev/null
+++ b/ui/src/assets/viewer_page.scss
@@ -0,0 +1,76 @@
+// 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.
+
+.viewer-page {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+
+ .pinned-panel-container {
+ max-height: 50%;
+ box-shadow: 1px 3px 15px rgba(23, 32, 44, 0.3);
+ z-index: 1;
+ flex-grow: 0;
+ flex-shrink: 0;
+ overflow: hidden;
+ overflow-y: auto;
+ }
+
+ .scrolling-panel-container {
+ overflow-x: hidden;
+ overflow-y: auto;
+ flex: 1 1 auto;
+ will-change: transform; // Force layer creation.
+ }
+
+ .details-panel-container {
+ box-shadow: #0000003b 0px 0px 3px 1px;
+ overflow: auto;
+ }
+
+ .header-panel-container {
+ overflow: visible;
+ box-shadow: 1px 3px 15px rgba(23, 32, 44, 0.3);
+ z-index: 2;
+ }
+
+ .pan-and-zoom-content {
+ flex: 1;
+ position: relative;
+ display: flex;
+ flex-flow: column nowrap;
+ overflow: hidden;
+ }
+
+ .overview-timeline {
+ height: 70px;
+ }
+
+ .time-axis-panel {
+ height: 22px;
+ }
+
+ .tickbar {
+ height: 5px;
+ }
+
+ .notes-panel {
+ height: 20px;
+ }
+
+ .time-selection-panel {
+ height: 10px;
+ }
+}
diff --git a/ui/src/base/classnames.ts b/ui/src/base/classnames.ts
index b2f6007..d69a244 100644
--- a/ui/src/base/classnames.ts
+++ b/ui/src/base/classnames.ts
@@ -18,8 +18,14 @@
type ArgType = string|false|undefined|ArgType[];
// Join class names together into valid HTML class attributes
-// Falsey elements are ignored
+// Falsy elements are ignored
// Nested arrays are flattened
-export function classNames(...args: ArgType[]): string {
- return args.flat().filter((x) => x).join(' ');
+// If all elements are falsy, returns undefined
+export function classNames(...args: ArgType[]): string|undefined {
+ const filtered = args.flat().filter((x) => x);
+ if (filtered.length === 0) {
+ return undefined;
+ } else {
+ return filtered.join(' ');
+ }
}
diff --git a/ui/src/base/dom_utils.ts b/ui/src/base/dom_utils.ts
index 5caf9d9..1c20a34 100644
--- a/ui/src/base/dom_utils.ts
+++ b/ui/src/base/dom_utils.ts
@@ -31,7 +31,7 @@
// Throws if the element is not an HTMLElement.
export function toHTMLElement(el: Element): HTMLElement {
if (!(el instanceof HTMLElement)) {
- throw new Error('Element is not an HTLMElement');
+ throw new Error('Element is not an HTMLElement');
}
return el as HTMLElement;
}
diff --git a/ui/src/common/canvas_utils.ts b/ui/src/common/canvas_utils.ts
index e0111f9..3d94ee4 100644
--- a/ui/src/common/canvas_utils.ts
+++ b/ui/src/common/canvas_utils.ts
@@ -175,3 +175,14 @@
ctx.fillText(text2, x + paddingPx, y + paddingPx + yOffsetPx);
}
}
+
+export function canvasClip(
+ ctx: CanvasRenderingContext2D,
+ x: number,
+ y: number,
+ w: number,
+ h: number): void {
+ ctx.beginPath();
+ ctx.rect(x, y, w, h);
+ ctx.clip();
+}
diff --git a/ui/src/common/registry.ts b/ui/src/common/registry.ts
index f20ad5c..4edd5a5 100644
--- a/ui/src/common/registry.ts
+++ b/ui/src/common/registry.ts
@@ -61,6 +61,10 @@
return registrant;
}
+ tryGet(kind: string): T|undefined {
+ return this.registry.get(kind);
+ }
+
// Support iteration: for (const foo of fooRegistry.values()) { ... }
* values() {
yield* this.registry.values();
diff --git a/ui/src/common/track_cache.ts b/ui/src/common/track_cache.ts
index c531bf2..613ba3d 100644
--- a/ui/src/common/track_cache.ts
+++ b/ui/src/common/track_cache.ts
@@ -98,7 +98,7 @@
// Look up track into for a given track's URI.
// Returns |undefined| if no track can be found.
resolveTrackInfo(uri: string): TrackDescriptor|undefined {
- return this.trackRegistry.get(uri);
+ return this.trackRegistry.tryGet(uri);
}
// Creates a new track using |uri| and |params| or retrieves a cached track if
diff --git a/ui/src/controller/cpu_profile_controller.ts b/ui/src/controller/cpu_profile_controller.ts
index a251442..081812a 100644
--- a/ui/src/controller/cpu_profile_controller.ts
+++ b/ui/src/controller/cpu_profile_controller.ts
@@ -116,7 +116,7 @@
WHERE symbol.symbol_set_id = spf.symbol_set_id
LIMIT 1
),
- COALESCE(spf.deobfuscated_name, spf.name)
+ COALESCE(spf.deobfuscated_name, spf.name, "")
) AS name,
spm.name AS mapping
FROM cpu_profile_stack_sample AS samples
diff --git a/ui/src/frontend/aggregation_tab.ts b/ui/src/frontend/aggregation_tab.ts
index 30dd743..4c96c21 100644
--- a/ui/src/frontend/aggregation_tab.ts
+++ b/ui/src/frontend/aggregation_tab.ts
@@ -65,15 +65,6 @@
}
}
- // Add this after all aggregation panels, to make it appear after 'Slices'
- if (globals.selectedFlows.length > 0) {
- views.push({
- key: 'selected_flows',
- name: 'Flow Events',
- content: m(FlowEventsAreaSelectedPanel),
- });
- }
-
const pivotTableState = globals.state.nonSerializableState.pivotTable;
if (pivotTableState.selectionArea !== undefined) {
views.push({
@@ -86,6 +77,15 @@
});
}
+ // Add this after all aggregation panels, to make it appear after 'Slices'
+ if (globals.selectedFlows.length > 0) {
+ views.push({
+ key: 'selected_flows',
+ name: 'Flow Events',
+ content: m(FlowEventsAreaSelectedPanel),
+ });
+ }
+
return views;
}
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index 5efacf7..1690267 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -67,7 +67,7 @@
constructor(readonly key: string) {}
- get mithril(): m.Children {
+ render(): m.Children {
const allCollapsed = Object.values(globals.state.trackGroups)
.every((group) => group.collapsed);
diff --git a/ui/src/frontend/overview_timeline_panel.ts b/ui/src/frontend/overview_timeline_panel.ts
index 0accdb8..b1cc984 100644
--- a/ui/src/frontend/overview_timeline_panel.ts
+++ b/ui/src/frontend/overview_timeline_panel.ts
@@ -94,7 +94,7 @@
.removeEventListener('mousemove', this.boundOnMouseMove);
}
- get mithril(): m.Children {
+ render(): m.Children {
return m('.overview-timeline', {
oncreate: (vnode) => this.oncreate(vnode),
onupdate: (vnode) => this.onupdate(vnode),
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index 9b19db0..52ecf86 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -41,6 +41,7 @@
} from './flow_events_renderer';
import {globals} from './globals';
import {PanelSize} from './panel';
+import {canvasClip} from '../common/canvas_utils';
// If the panel container scrolls, the backing canvas height is
// SCROLLING_CANVAS_OVERDRAW_FACTOR * parent container height.
@@ -48,7 +49,7 @@
export interface Panel {
kind: 'panel';
- mithril: m.Children;
+ render(): m.Children;
selectable: boolean;
key: string;
trackKey?: string;
@@ -67,7 +68,7 @@
export type PanelOrGroup = Panel|PanelGroup;
-export interface Attrs {
+export interface PanelContainerAttrs {
panels: PanelOrGroup[];
doesScroll: boolean;
kind: 'TRACKS'|'OVERVIEW';
@@ -83,7 +84,7 @@
y: number;
}
-export class PanelContainer implements m.ClassComponent<Attrs>,
+export class PanelContainer implements m.ClassComponent<PanelContainerAttrs>,
PerfStatsSource {
// These values are updated with proper values in oncreate.
private parentWidth = 0;
@@ -108,7 +109,7 @@
// Attrs received in the most recent mithril redraw. We receive a new vnode
// with new attrs on every redraw, and we cache it here so that resize
// listeners and canvas redraw callbacks can access it.
- private attrs: Attrs;
+ private attrs: PanelContainerAttrs;
private ctx?: CanvasRenderingContext2D;
@@ -116,6 +117,7 @@
private readonly SCROLL_LIMITER_REF = 'scroll-limiter';
private readonly PANELS_REF = 'panels';
+ private readonly OVERLAY_CANVAS_REF = 'canvas';
get canvasOverdrawFactor() {
return this.attrs.doesScroll ? SCROLLING_CANVAS_OVERDRAW_FACTOR : 1;
@@ -188,12 +190,12 @@
globals.timeline.selectArea(area.start, area.end, tracks);
}
- constructor(vnode: m.CVnode<Attrs>) {
+ constructor(vnode: m.CVnode<PanelContainerAttrs>) {
this.attrs = vnode.attrs;
this.flowEventsRenderer = new FlowEventsRenderer();
this.trash = new Trash();
- const onRedraw = () => this.redrawCanvas();
+ const onRedraw = () => this.renderCanvas();
raf.addRedrawCallback(onRedraw);
this.trash.addCallback(() => {
raf.removeRedrawCallback(onRedraw);
@@ -205,9 +207,9 @@
});
}
- oncreate({dom}: m.CVnodeDOM<Attrs>) {
+ oncreate({dom}: m.CVnodeDOM<PanelContainerAttrs>) {
// Save the canvas context in the state.
- const canvas = dom.querySelector('.main-canvas') as HTMLCanvasElement;
+ const canvas = findRef(dom, this.OVERLAY_CANVAS_REF) as HTMLCanvasElement;
const ctx = canvas.getContext('2d');
if (!ctx) {
throw Error('Cannot create canvas context');
@@ -226,7 +228,7 @@
if (parentSizeChanged) {
this.updateCanvasDimensions();
this.repositionCanvas();
- this.redrawCanvas();
+ this.renderCanvas();
}
}));
@@ -252,12 +254,7 @@
renderPanel(node: Panel, key: string, extraClass = ''): m.Vnode {
assertFalse(this.panelByKey.has(key));
this.panelByKey.set(key, node);
- const mithril = node.mithril;
-
- return m(`.panel${extraClass}`, {key, 'data-key': key},
- perfDebug() ?
- [mithril, m('.debug-panel-border')] :
- mithril);
+ return m(`.pf-panel${extraClass}`, {key, 'data-key': key}, node.render());
}
// Render a tree of panels into one vnode. Argument `path` is used to build
@@ -269,30 +266,30 @@
'div',
{key: path},
this.renderPanel(
- node.header, `${path}-header`, node.collapsed ? '' : '.sticky'),
+ node.header, `${path}-header`, node.collapsed ? '' : '.pf-sticky'),
...node.childTracks.map(
(child, index) => this.renderTree(child, `${path}-${index}`)));
}
return this.renderPanel(node, assertExists(node.key));
}
- view({attrs}: m.CVnode<Attrs>) {
+ view({attrs}: m.CVnode<PanelContainerAttrs>) {
this.attrs = attrs;
this.panelByKey.clear();
const children = attrs.panels.map(
(panel, index) => this.renderTree(panel, `track-tree-${index}`));
- return m('.panel-container', {className: attrs.className},
- m('.panels', {ref: this.PANELS_REF},
- m('.scroll-limiter', {ref: this.SCROLL_LIMITER_REF},
- m('canvas.main-canvas'),
+ return m('.pf-panel-container', {className: attrs.className},
+ m('.pf-panels', {ref: this.PANELS_REF},
+ m('.pf-scroll-limiter', {ref: this.SCROLL_LIMITER_REF},
+ m('canvas.pf-overlay-canvas', {ref: this.OVERLAY_CANVAS_REF}),
),
children,
),
);
}
- onupdate({dom}: m.CVnodeDOM<Attrs>) {
+ onupdate({dom}: m.CVnodeDOM<PanelContainerAttrs>) {
const totalPanelHeightChanged = this.readPanelHeightsFromDom(dom);
const parentSizeChanged = this.readParentSizeFromDom(dom);
const canvasSizeShouldChange =
@@ -304,7 +301,7 @@
globals.timeline.updateLocalLimits(
0, this.parentWidth - TRACK_SHELL_WIDTH);
}
- this.redrawCanvas();
+ this.renderCanvas();
}
}
@@ -364,7 +361,7 @@
this.panelContainerTop = domRect.y;
this.panelContainerHeight = domRect.height;
- dom.querySelectorAll('.panel').forEach((panelElement) => {
+ dom.querySelectorAll('.pf-panel').forEach((panelElement) => {
const key = assertExists(panelElement.getAttribute('data-key'));
const panel = assertExists(this.panelByKey.get(key));
@@ -389,7 +386,7 @@
return yEnd > 0 && yStart < this.canvasHeight;
}
- private redrawCanvas() {
+ private renderCanvas() {
const redrawStart = debugNow();
if (!this.ctx) return;
this.ctx.clearRect(0, 0, this.parentWidth, this.canvasHeight);
@@ -480,6 +477,12 @@
const canvasYStart =
Math.floor(this.scrollTop - this.getCanvasOverdrawHeightPerSide());
this.ctx.translate(TRACK_SHELL_WIDTH, -canvasYStart);
+
+ // Clip off any drawing happening outside the bounds of the timeline area
+ canvasClip(
+ this.ctx,
+ 0, 0, this.parentWidth - TRACK_SHELL_WIDTH, this.totalPanelHeight);
+
this.ctx.strokeRect(
startX,
selectedTracksMaxY,
@@ -499,6 +502,16 @@
}
renderStats.addValue(renderTime);
+ // Draw a green box around the whole panel
+ ctx.strokeStyle = 'rgba(69, 187, 73, 0.5)';
+ const lineWidth = 1;
+ ctx.lineWidth = lineWidth;
+ ctx.strokeRect(
+ lineWidth/2,
+ lineWidth/2,
+ size.width - lineWidth,
+ size.height - lineWidth);
+
const statW = 300;
ctx.fillStyle = 'hsl(97, 100%, 96%)';
ctx.fillRect(size.width - statW, size.height - 20, statW, 20);
diff --git a/ui/src/frontend/tickmark_panel.ts b/ui/src/frontend/tickmark_panel.ts
index 7600d05..2ed6249 100644
--- a/ui/src/frontend/tickmark_panel.ts
+++ b/ui/src/frontend/tickmark_panel.ts
@@ -35,7 +35,7 @@
constructor(readonly key: string) {}
- get mithril(): m.Children {
+ render(): m.Children {
return m('.tickbar');
}
diff --git a/ui/src/frontend/time_axis_panel.ts b/ui/src/frontend/time_axis_panel.ts
index 43e7244..ec918b8 100644
--- a/ui/src/frontend/time_axis_panel.ts
+++ b/ui/src/frontend/time_axis_panel.ts
@@ -40,7 +40,7 @@
constructor(readonly key: string) {}
- get mithril() {
+ render(): m.Children {
return m('.time-axis-panel');
}
diff --git a/ui/src/frontend/time_selection_panel.ts b/ui/src/frontend/time_selection_panel.ts
index 67358b3..ee7838e 100644
--- a/ui/src/frontend/time_selection_panel.ts
+++ b/ui/src/frontend/time_selection_panel.ts
@@ -138,7 +138,7 @@
constructor(readonly key: string) {}
- get mithril(): m.Children {
+ render(): m.Children {
return m('.time-selection-panel');
}
diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts
index 58071ce..3a83c81 100644
--- a/ui/src/frontend/track_group_panel.ts
+++ b/ui/src/frontend/track_group_panel.ts
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {hex} from 'color-convert';
import m from 'mithril';
import {Icons} from '../base/semantic_icons';
@@ -24,18 +23,14 @@
import {TrackTags} from '../public';
import {
- COLLAPSED_BACKGROUND,
- EXPANDED_BACKGROUND,
TRACK_SHELL_WIDTH,
} from './css_constants';
import {globals} from './globals';
import {drawGridLines} from './gridline_helper';
import {PanelSize} from './panel';
import {Panel} from './panel_container';
-import {CrashButton, renderChips, TrackContent} from './track_panel';
-import {
- drawVerticalLineAtTime,
-} from './vertical_line_helper';
+import {CrashButton, renderChips, renderHoveredCursorVertical, renderHoveredNoteVertical, renderNoteVerticals, renderWakeupVertical, TrackContent} from './track_panel';
+import {canvasClip} from '../common/canvas_utils';
interface Attrs {
trackGroupId: string;
@@ -58,7 +53,7 @@
this.key = attrs.key;
}
- get mithril(): m.Children {
+ render(): m.Children {
const {
trackGroupId,
title,
@@ -187,86 +182,37 @@
trackFSM: track,
} = this.attrs;
- ctx.fillStyle = collapsed ? COLLAPSED_BACKGROUND : EXPANDED_BACKGROUND;
- ctx.fillRect(0, 0, size.width, size.height);
-
if (!collapsed) return;
- this.highlightIfTrackSelected(ctx, size);
-
+ ctx.save();
+ canvasClip(
+ ctx, TRACK_SHELL_WIDTH, 0, size.width - TRACK_SHELL_WIDTH, size.height);
drawGridLines(
ctx,
size.width,
size.height);
- ctx.save();
- ctx.translate(TRACK_SHELL_WIDTH, 0);
if (track) {
+ ctx.save();
+ ctx.translate(TRACK_SHELL_WIDTH, 0);
const trackSize = {...size, width: size.width - TRACK_SHELL_WIDTH};
if (!track.getError()) {
track.update();
track.track.render(ctx, trackSize);
}
+ ctx.restore();
}
- ctx.restore();
this.highlightIfTrackSelected(ctx, size);
const {visibleTimeScale} = globals.timeline;
// Draw vertical line when hovering on the notes panel.
- if (globals.state.hoveredNoteTimestamp !== -1n) {
- drawVerticalLineAtTime(
- ctx,
- visibleTimeScale,
- globals.state.hoveredNoteTimestamp,
- size.height,
- `#aaa`);
- }
- if (globals.state.hoverCursorTimestamp !== -1n) {
- drawVerticalLineAtTime(
- ctx,
- visibleTimeScale,
- globals.state.hoverCursorTimestamp,
- size.height,
- `#344596`);
- }
+ renderHoveredNoteVertical(ctx, visibleTimeScale, size);
+ renderHoveredCursorVertical(ctx, visibleTimeScale, size);
+ renderWakeupVertical(ctx, visibleTimeScale, size);
+ renderNoteVerticals(ctx, visibleTimeScale, size);
- if (globals.state.currentSelection !== null) {
- if (globals.state.currentSelection.kind === 'SLICE' &&
- globals.sliceDetails.wakeupTs !== undefined) {
- drawVerticalLineAtTime(
- ctx,
- visibleTimeScale,
- globals.sliceDetails.wakeupTs,
- size.height,
- `black`);
- }
- }
- // All marked areas should have semi-transparent vertical lines
- // marking the start and end.
- for (const note of Object.values(globals.state.notes)) {
- if (note.noteType === 'AREA') {
- const transparentNoteColor =
- 'rgba(' + hex.rgb(note.color.substr(1)).toString() + ', 0.65)';
- drawVerticalLineAtTime(
- ctx,
- visibleTimeScale,
- globals.state.areas[note.areaId].start,
- size.height,
- transparentNoteColor,
- 1);
- drawVerticalLineAtTime(
- ctx,
- visibleTimeScale,
- globals.state.areas[note.areaId].end,
- size.height,
- transparentNoteColor,
- 1);
- } else if (note.noteType === 'DEFAULT') {
- drawVerticalLineAtTime(
- ctx, visibleTimeScale, note.timestamp, size.height, note.color);
- }
- }
+ ctx.restore();
}
}
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 697e45a..bb0b664 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -36,6 +36,8 @@
import {classNames} from '../base/classnames';
import {Button} from '../widgets/button';
import {Popup} from '../widgets/popup';
+import {canvasClip} from '../common/canvas_utils';
+import {TimeScale} from './time_scale';
function getTitleSize(title: string): string|undefined {
const length = title.length;
@@ -133,7 +135,7 @@
view({attrs}: m.CVnode<TrackShellAttrs>) {
// The shell should be highlighted if the current search result is inside
// this track.
- let highlightClass = '';
+ let highlightClass = undefined;
const searchIndex = globals.state.searchIndex;
if (searchIndex !== -1) {
const trackKey = globals.currentSearchResults.trackKeys[searchIndex];
@@ -142,12 +144,14 @@
}
}
- const dragClass = this.dragging ? `drag` : '';
- const dropClass = this.dropping ? `drop-${this.dropping}` : '';
return m(
`.track-shell[draggable=true]`,
{
- class: `${highlightClass} ${dragClass} ${dropClass}`,
+ className: classNames(
+ highlightClass,
+ this.dragging && 'drag',
+ this.dropping && `drop-${this.dropping}`,
+ ),
ondragstart: (e: DragEvent) => this.ondragstart(e, attrs.trackKey),
ondragend: this.ondragend.bind(this),
ondragover: this.ondragover.bind(this),
@@ -421,7 +425,7 @@
return this.attrs.trackKey;
}
- get mithril(): m.Children {
+ render(): m.Children {
const attrs = this.attrs;
if (attrs.trackFSM) {
@@ -472,6 +476,8 @@
renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
ctx.save();
+ canvasClip(
+ ctx, TRACK_SHELL_WIDTH, 0, size.width - TRACK_SHELL_WIDTH, size.height);
drawGridLines(
ctx,
@@ -480,6 +486,7 @@
const track = this.attrs.trackFSM;
+ ctx.save();
ctx.translate(TRACK_SHELL_WIDTH, 0);
if (track !== undefined) {
const trackSize = {...size, width: size.width - TRACK_SHELL_WIDTH};
@@ -496,59 +503,12 @@
const {visibleTimeScale} = globals.timeline;
// Draw vertical line when hovering on the notes panel.
- if (globals.state.hoveredNoteTimestamp !== -1n) {
- drawVerticalLineAtTime(
- ctx,
- visibleTimeScale,
- globals.state.hoveredNoteTimestamp,
- size.height,
- `#aaa`);
- }
- if (globals.state.hoverCursorTimestamp !== -1n) {
- drawVerticalLineAtTime(
- ctx,
- visibleTimeScale,
- globals.state.hoverCursorTimestamp,
- size.height,
- `#344596`);
- }
+ renderHoveredNoteVertical(ctx, visibleTimeScale, size);
+ renderHoveredCursorVertical(ctx, visibleTimeScale, size);
+ renderWakeupVertical(ctx, visibleTimeScale, size);
+ renderNoteVerticals(ctx, visibleTimeScale, size);
- if (globals.state.currentSelection !== null) {
- if (globals.state.currentSelection.kind === 'SLICE' &&
- globals.sliceDetails.wakeupTs !== undefined) {
- drawVerticalLineAtTime(
- ctx,
- visibleTimeScale,
- globals.sliceDetails.wakeupTs,
- size.height,
- `black`);
- }
- }
- // All marked areas should have semi-transparent vertical lines
- // marking the start and end.
- for (const note of Object.values(globals.state.notes)) {
- if (note.noteType === 'AREA') {
- const transparentNoteColor =
- 'rgba(' + hex.rgb(note.color.substr(1)).toString() + ', 0.65)';
- drawVerticalLineAtTime(
- ctx,
- visibleTimeScale,
- globals.state.areas[note.areaId].start,
- size.height,
- transparentNoteColor,
- 1);
- drawVerticalLineAtTime(
- ctx,
- visibleTimeScale,
- globals.state.areas[note.areaId].end,
- size.height,
- transparentNoteColor,
- 1);
- } else if (note.noteType === 'DEFAULT') {
- drawVerticalLineAtTime(
- ctx, visibleTimeScale, note.timestamp, size.height, note.color);
- }
- }
+ ctx.restore();
}
getSliceRect(tStart: time, tDur: time, depth: number): SliceRect|undefined {
@@ -558,3 +518,71 @@
return this.attrs.trackFSM.track.getSliceRect?.(tStart, tDur, depth);
}
}
+
+export function renderHoveredCursorVertical(
+ ctx: CanvasRenderingContext2D, visibleTimeScale: TimeScale, size: PanelSize) {
+ if (globals.state.hoverCursorTimestamp !== -1n) {
+ drawVerticalLineAtTime(
+ ctx,
+ visibleTimeScale,
+ globals.state.hoverCursorTimestamp,
+ size.height,
+ `#344596`);
+ }
+}
+
+export function renderHoveredNoteVertical(
+ ctx: CanvasRenderingContext2D, visibleTimeScale: TimeScale, size: PanelSize) {
+ if (globals.state.hoveredNoteTimestamp !== -1n) {
+ drawVerticalLineAtTime(
+ ctx,
+ visibleTimeScale,
+ globals.state.hoveredNoteTimestamp,
+ size.height,
+ `#aaa`);
+ }
+}
+
+export function renderWakeupVertical(
+ ctx: CanvasRenderingContext2D, visibleTimeScale: TimeScale, size: PanelSize) {
+ if (globals.state.currentSelection !== null) {
+ if (globals.state.currentSelection.kind === 'SLICE' &&
+ globals.sliceDetails.wakeupTs !== undefined) {
+ drawVerticalLineAtTime(
+ ctx,
+ visibleTimeScale,
+ globals.sliceDetails.wakeupTs,
+ size.height,
+ `black`);
+ }
+ }
+}
+
+export function renderNoteVerticals(
+ ctx: CanvasRenderingContext2D, visibleTimeScale: TimeScale, size: PanelSize) {
+ // All marked areas should have semi-transparent vertical lines
+ // marking the start and end.
+ for (const note of Object.values(globals.state.notes)) {
+ if (note.noteType === 'AREA') {
+ const transparentNoteColor = 'rgba(' + hex.rgb(note.color.substr(1)).toString() + ', 0.65)';
+ drawVerticalLineAtTime(
+ ctx,
+ visibleTimeScale,
+ globals.state.areas[note.areaId].start,
+ size.height,
+ transparentNoteColor,
+ 1);
+ drawVerticalLineAtTime(
+ ctx,
+ visibleTimeScale,
+ globals.state.areas[note.areaId].end,
+ size.height,
+ transparentNoteColor,
+ 1);
+ } else if (note.noteType === 'DEFAULT') {
+ drawVerticalLineAtTime(
+ ctx, visibleTimeScale, note.timestamp, size.height, note.color);
+ }
+ }
+}
+
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 973fdaf..91f7490 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -14,7 +14,7 @@
import m from 'mithril';
-import {getScrollbarWidth} from '../base/dom_utils';
+import {findRef, getScrollbarWidth, toHTMLElement} from '../base/dom_utils';
import {clamp} from '../base/math_utils';
import {Time} from '../base/time';
import {Actions} from '../common/actions';
@@ -39,6 +39,7 @@
import {DISMISSED_PANNING_HINT_KEY} from './topbar';
import {TrackGroupPanel} from './track_group_panel';
import {TrackPanel} from './track_panel';
+import {assertExists} from '../base/logging';
const OVERVIEW_PANEL_FLAG = featureFlags.register({
id: 'overviewVisible',
@@ -88,6 +89,8 @@
private notesPanel = new NotesPanel('notes');
private tickmarkPanel = new TickmarkPanel('searchTickmarks');
+ private readonly PAN_ZOOM_CONTENT_REF = 'pan-and-zoom-content';
+
oncreate(vnode: m.CVnodeDOM) {
const timeline = globals.timeline;
const updateDimensions = () => {
@@ -107,8 +110,8 @@
// Once ResizeObservers are out, we can stop accessing the window here.
window.addEventListener('resize', this.onResize);
- const panZoomEl =
- vnode.dom.querySelector('.pan-and-zoom-content') as HTMLElement;
+ const panZoomElRaw = findRef(vnode.dom, this.PAN_ZOOM_CONTENT_REF);
+ const panZoomEl = toHTMLElement(assertExists(panZoomElRaw));
this.zoomContent = new PanAndZoomHandler({
element: panZoomEl,
@@ -279,53 +282,52 @@
}
const result = m(
- '.page',
- m('.split-panel',
- m('.pan-and-zoom-content',
- {
- onclick: () => {
- // We don't want to deselect when panning/drag selecting.
- if (this.keepCurrentSelection) {
- this.keepCurrentSelection = false;
- return;
- }
- globals.makeSelection(Actions.deselect({}));
- },
+ '.page.viewer-page',
+ m('.pan-and-zoom-content',
+ {
+ ref: this.PAN_ZOOM_CONTENT_REF,
+ onclick: () => {
+ // We don't want to deselect when panning/drag selecting.
+ if (this.keepCurrentSelection) {
+ this.keepCurrentSelection = false;
+ return;
+ }
+ globals.makeSelection(Actions.deselect({}));
},
- m(PanelContainer, {
- className: 'header-panel-container',
- doesScroll: false,
- panels: [
- ...overviewPanel,
- this.timeAxisPanel,
- this.timeSelectionPanel,
- this.notesPanel,
- this.tickmarkPanel,
- ],
- kind: 'OVERVIEW',
+ },
+ m(PanelContainer, {
+ className: 'header-panel-container',
+ doesScroll: false,
+ panels: [
+ ...overviewPanel,
+ this.timeAxisPanel,
+ this.timeSelectionPanel,
+ this.notesPanel,
+ this.tickmarkPanel,
+ ],
+ kind: 'OVERVIEW',
+ }),
+ m(PanelContainer, {
+ className: 'pinned-panel-container',
+ doesScroll: true,
+ panels: globals.state.pinnedTracks.map((key) => {
+ const trackBundle = this.resolveTrack(key);
+ return new TrackPanel({
+ trackKey: key,
+ title: trackBundle.title,
+ tags: trackBundle.tags,
+ trackFSM: trackBundle.trackFSM,
+ revealOnCreate: true,
+ });
}),
- m(PanelContainer, {
- className: 'pinned-panel-container',
- doesScroll: true,
- panels: globals.state.pinnedTracks.map((key) => {
- const trackBundle = this.resolveTrack(key);
- return new TrackPanel({
- trackKey: key,
- title: trackBundle.title,
- tags: trackBundle.tags,
- trackFSM: trackBundle.trackFSM,
- revealOnCreate: true,
- });
- }),
- kind: 'TRACKS',
- }),
- m(PanelContainer, {
- className: 'scrolling-panel-container',
- doesScroll: true,
- panels: scrollingPanels,
- kind: 'TRACKS',
- }),
- ),
+ kind: 'TRACKS',
+ }),
+ m(PanelContainer, {
+ className: 'scrolling-panel-container',
+ doesScroll: true,
+ panels: scrollingPanels,
+ kind: 'TRACKS',
+ }),
),
this.renderTabPanel());
diff --git a/ui/src/test/ui_integrationtest.ts b/ui/src/test/ui_integrationtest.ts
index d876ab8..861a798 100644
--- a/ui/src/test/ui_integrationtest.ts
+++ b/ui/src/test/ui_integrationtest.ts
@@ -86,7 +86,7 @@
});
test('expand_camera', async () => {
- await page.click('.main-canvas');
+ await page.click('.pf-overlay-canvas');
await page.click('h1[title="com.google.android.GoogleCamera 5506"]');
await page.evaluate(() => {
document.querySelector('.scrolling-panel-container')!.scrollTo(0, 400);
@@ -114,7 +114,7 @@
test('expand_browser_proc', async () => {
const page = await getPage();
- await page.click('.main-canvas');
+ await page.click('.pf-overlay-canvas');
await page.click('h1[title="Browser 12685"]');
await waitForPerfettoIdle(page);
});