trace_processor: perf sample counter tracking and distinguishing between multiple data sources
Primary additions:
* perf_session_id, which distinguishes individual profiling data sources.
* perf_counter_track, which both names the timebase for a
per-{cpu, perf_session_id} sampling stream, and tracks the sampled
counts themselves (now inserted into |counter| table).
The is_timebase column is added prematurely, but should become relevant
once we start supporting multi-counter sampling.
See go/perf-counters-tp for overall plan.
Will send a difftest in a separate patch once we agree that this
implementation makes sense.
Bug: 180437026
Change-Id: Ie23024ea4c6a9bdc1eb3c5abdb098705f3a21d58
diff --git a/Android.bp b/Android.bp
index 4c19204..a89d624 100644
--- a/Android.bp
+++ b/Android.bp
@@ -7819,6 +7819,7 @@
"src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc",
"src/trace_processor/importers/proto/metadata_tracker.cc",
"src/trace_processor/importers/proto/packet_sequence_state.cc",
+ "src/trace_processor/importers/proto/perf_sample_tracker.cc",
"src/trace_processor/importers/proto/profile_module.cc",
"src/trace_processor/importers/proto/profile_packet_utils.cc",
"src/trace_processor/importers/proto/profiler_util.cc",
@@ -7899,6 +7900,7 @@
"src/trace_processor/importers/proto/async_track_set_tracker_unittest.cc",
"src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc",
"src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc",
+ "src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc",
"src/trace_processor/importers/proto/proto_trace_parser_unittest.cc",
"src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc",
"src/trace_processor/importers/systrace/systrace_parser_unittest.cc",
diff --git a/BUILD b/BUILD
index 5309d1c..c70775b 100644
--- a/BUILD
+++ b/BUILD
@@ -1264,6 +1264,8 @@
"src/trace_processor/importers/proto/metadata_tracker.h",
"src/trace_processor/importers/proto/packet_sequence_state.cc",
"src/trace_processor/importers/proto/packet_sequence_state.h",
+ "src/trace_processor/importers/proto/perf_sample_tracker.cc",
+ "src/trace_processor/importers/proto/perf_sample_tracker.h",
"src/trace_processor/importers/proto/profile_module.cc",
"src/trace_processor/importers/proto/profile_module.h",
"src/trace_processor/importers/proto/profile_packet_utils.cc",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index ecac01e..ae240b4 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -100,6 +100,8 @@
"importers/proto/metadata_tracker.h",
"importers/proto/packet_sequence_state.cc",
"importers/proto/packet_sequence_state.h",
+ "importers/proto/perf_sample_tracker.cc",
+ "importers/proto/perf_sample_tracker.h",
"importers/proto/profile_module.cc",
"importers/proto/profile_module.h",
"importers/proto/profile_packet_utils.cc",
@@ -395,6 +397,7 @@
"importers/proto/async_track_set_tracker_unittest.cc",
"importers/proto/heap_graph_tracker_unittest.cc",
"importers/proto/heap_profile_tracker_unittest.cc",
+ "importers/proto/perf_sample_tracker_unittest.cc",
"importers/proto/proto_trace_parser_unittest.cc",
"importers/syscalls/syscall_tracker_unittest.cc",
"importers/systrace/systrace_parser_unittest.cc",
@@ -404,7 +407,9 @@
":storage_full",
"../../gn:default_deps",
"../../gn:gtest_and_gmock",
+ "../../protos/perfetto/common:cpp",
"../../protos/perfetto/common:zero",
+ "../../protos/perfetto/trace:cpp",
"../../protos/perfetto/trace:minimal_zero",
"../../protos/perfetto/trace:zero",
"../../protos/perfetto/trace/android:zero",
@@ -412,6 +417,7 @@
"../../protos/perfetto/trace/ftrace:zero",
"../../protos/perfetto/trace/gpu:zero",
"../../protos/perfetto/trace/interned_data:zero",
+ "../../protos/perfetto/trace/profiling:cpp",
"../../protos/perfetto/trace/ps:zero",
"../../protos/perfetto/trace/sys_stats:zero",
"../../protos/perfetto/trace/track_event:zero",
diff --git a/src/trace_processor/importers/common/track_tracker.cc b/src/trace_processor/importers/common/track_tracker.cc
index a43bcac..2682f90 100644
--- a/src/trace_processor/importers/common/track_tracker.cc
+++ b/src/trace_processor/importers/common/track_tracker.cc
@@ -306,5 +306,16 @@
return context_->storage->mutable_gpu_counter_track_table()->Insert(row).id;
}
+TrackId TrackTracker::CreatePerfCounterTrack(StringId name,
+ uint32_t perf_session_id,
+ uint32_t cpu,
+ bool is_timebase) {
+ tables::PerfCounterTrackTable::Row row(name);
+ row.perf_session_id = perf_session_id;
+ row.cpu = cpu;
+ row.is_timebase = is_timebase;
+ return context_->storage->mutable_perf_counter_track_table()->Insert(row).id;
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/importers/common/track_tracker.h b/src/trace_processor/importers/common/track_tracker.h
index 502389a..6f4236d 100644
--- a/src/trace_processor/importers/common/track_tracker.h
+++ b/src/trace_processor/importers/common/track_tracker.h
@@ -101,6 +101,13 @@
StringId description = StringId::Null(),
StringId unit = StringId::Null());
+ // Creaates a counter track for values within perf samples.
+ // The tracks themselves are managed by PerfSampleTracker.
+ TrackId CreatePerfCounterTrack(StringId name,
+ uint32_t perf_session_id,
+ uint32_t cpu,
+ bool is_timebase);
+
private:
struct GpuTrackTuple {
StringId track_name;
@@ -133,7 +140,6 @@
std::map<GpuTrackTuple, TrackId> gpu_tracks_;
std::map<ChromeTrackTuple, TrackId> chrome_tracks_;
std::map<UniquePid, TrackId> chrome_process_instant_tracks_;
- std::map<UniquePid, TrackId> perf_stack_tracks_;
std::map<StringId, TrackId> global_counter_tracks_by_name_;
std::map<std::pair<StringId, uint32_t>, TrackId> cpu_counter_tracks_;
diff --git a/src/trace_processor/importers/proto/perf_sample_tracker.cc b/src/trace_processor/importers/proto/perf_sample_tracker.cc
new file mode 100644
index 0000000..d14d2aa
--- /dev/null
+++ b/src/trace_processor/importers/proto/perf_sample_tracker.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 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/proto/perf_sample_tracker.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+#include "protos/perfetto/common/perf_events.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
+#include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+// Follow perf tool naming convention.
+const char* StringifyCounter(int32_t counter) {
+ using protos::pbzero::PerfEvents;
+ switch (counter) {
+ case (PerfEvents::SW_CPU_CLOCK):
+ return "cpu-clock";
+ case (PerfEvents::SW_PAGE_FAULTS):
+ return "page-faults";
+ case (PerfEvents::HW_CPU_CYCLES):
+ return "cpu-cycles";
+ case (PerfEvents::HW_INSTRUCTIONS):
+ return "instructions";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+StringId InternTimebaseCounterName(
+ protos::pbzero::TracePacketDefaults::Decoder* defaults,
+ TraceProcessorContext* context) {
+ using namespace protos::pbzero;
+ PerfSampleDefaults::Decoder perf_defaults(defaults->perf_sample_defaults());
+ PerfEvents::Timebase::Decoder timebase(perf_defaults.timebase());
+
+ if (timebase.counter() != PerfEvents::UNKNOWN_COUNTER) {
+ return context->storage->InternString(StringifyCounter(timebase.counter()));
+ }
+ PerfEvents::Tracepoint::Decoder tracepoint(timebase.tracepoint());
+ return context->storage->InternString(tracepoint.name());
+}
+} // namespace
+
+PerfSampleTracker::SamplingStreamInfo PerfSampleTracker::GetSamplingStreamInfo(
+ uint32_t seq_id,
+ uint32_t cpu,
+ protos::pbzero::TracePacketDefaults::Decoder* nullable_defaults) {
+ auto seq_it = seq_state_.find(seq_id);
+ if (seq_it == seq_state_.end()) {
+ seq_it = seq_state_.emplace(seq_id, next_perf_session_id_++).first;
+ }
+ SequenceState* seq_state = &seq_it->second;
+ uint32_t session_id = seq_state->perf_session_id;
+
+ auto cpu_it = seq_state->per_cpu.find(cpu);
+ if (cpu_it != seq_state->per_cpu.end())
+ return {seq_state->perf_session_id, cpu_it->second.timebase_track_id};
+
+ // No defaults means legacy producer implementation, assume default timebase
+ // of per-cpu timer. Always the case for Android R builds, and it isn't worth
+ // guaranteeing support for intermediate S builds in this aspect.
+ StringId name_id = kNullStringId;
+ if (!nullable_defaults || !nullable_defaults->has_perf_sample_defaults()) {
+ name_id = context_->storage->InternString(
+ StringifyCounter(protos::pbzero::PerfEvents::SW_CPU_CLOCK));
+ } else {
+ name_id = InternTimebaseCounterName(nullable_defaults, context_);
+ }
+
+ TrackId timebase_track_id = context_->track_tracker->CreatePerfCounterTrack(
+ name_id, session_id, cpu, /*is_timebase=*/true);
+
+ seq_state->per_cpu.emplace(cpu, timebase_track_id);
+
+ return {session_id, timebase_track_id};
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/perf_sample_tracker.h b/src/trace_processor/importers/proto/perf_sample_tracker.h
new file mode 100644
index 0000000..82f40d0
--- /dev/null
+++ b/src/trace_processor/importers/proto/perf_sample_tracker.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 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_PROTO_PERF_SAMPLE_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PERF_SAMPLE_TRACKER_H_
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+class TracePacketDefaults_Decoder;
+} // namespace pbzero
+} // namespace protos
+namespace trace_processor {
+class TraceProcessorContext;
+
+class PerfSampleTracker {
+ public:
+ struct SamplingStreamInfo {
+ uint32_t perf_session_id = 0;
+ TrackId timebase_track_id = kInvalidTrackId;
+
+ SamplingStreamInfo(uint32_t _perf_session_id, TrackId _timebase_track_id)
+ : perf_session_id(_perf_session_id),
+ timebase_track_id(_timebase_track_id) {}
+ };
+
+ explicit PerfSampleTracker(TraceProcessorContext* context)
+ : context_(context) {}
+
+ SamplingStreamInfo GetSamplingStreamInfo(
+ uint32_t seq_id,
+ uint32_t cpu,
+ protos::pbzero::TracePacketDefaults_Decoder* nullable_defaults);
+
+ private:
+ struct CpuSequenceState {
+ TrackId timebase_track_id = kInvalidTrackId;
+
+ CpuSequenceState(TrackId _timebase_track_id)
+ : timebase_track_id(_timebase_track_id) {}
+ };
+
+ struct SequenceState {
+ uint32_t perf_session_id = 0;
+ std::unordered_map<uint32_t, CpuSequenceState> per_cpu;
+
+ SequenceState(uint32_t _perf_session_id)
+ : perf_session_id(_perf_session_id) {}
+ };
+
+ std::unordered_map<uint32_t, SequenceState> seq_state_;
+ uint32_t next_perf_session_id_ = 0;
+
+ TraceProcessorContext* const context_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PERF_SAMPLE_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc b/src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc
new file mode 100644
index 0000000..16c65b3
--- /dev/null
+++ b/src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 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/proto/perf_sample_tracker.h"
+
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "test/gtest_and_gmock.h"
+
+#include "protos/perfetto/common/perf_events.gen.h"
+#include "protos/perfetto/trace/profiling/profile_packet.gen.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
+#include "protos/perfetto/trace/trace_packet_defaults.gen.h"
+#include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+class PerfSampleTrackerTest : public ::testing::Test {
+ public:
+ PerfSampleTrackerTest() {
+ context.storage.reset(new TraceStorage());
+ context.track_tracker.reset(new TrackTracker(&context));
+ context.perf_sample_tracker.reset(new PerfSampleTracker(&context));
+ }
+
+ protected:
+ TraceProcessorContext context;
+};
+
+TEST_F(PerfSampleTrackerTest, PerCpuCounterTracks) {
+ uint32_t seq_id = 42;
+ uint32_t cpu0 = 0;
+ uint32_t cpu1 = 1;
+
+ auto stream = context.perf_sample_tracker->GetSamplingStreamInfo(
+ seq_id, cpu0, /*nullable_defaults=*/nullptr);
+ auto stream2 = context.perf_sample_tracker->GetSamplingStreamInfo(
+ seq_id, cpu1, /*nullable_defaults=*/nullptr);
+
+ // same session, different counter tracks
+ EXPECT_EQ(stream.perf_session_id, stream2.perf_session_id);
+ EXPECT_NE(stream.timebase_track_id, stream2.timebase_track_id);
+
+ // re-querying one of the existing streams gives the same ids
+ auto stream3 = context.perf_sample_tracker->GetSamplingStreamInfo(
+ seq_id, cpu1, /*nullable_defaults=*/nullptr);
+
+ EXPECT_EQ(stream2.perf_session_id, stream3.perf_session_id);
+ EXPECT_EQ(stream2.timebase_track_id, stream3.timebase_track_id);
+}
+
+TEST_F(PerfSampleTrackerTest, TimebaseTrackName_Counter) {
+ uint32_t seq_id = 42;
+ uint32_t cpu0 = 0;
+
+ protos::gen::TracePacketDefaults defaults;
+ auto* perf_defaults = defaults.mutable_perf_sample_defaults();
+ perf_defaults->mutable_timebase()->set_frequency(100);
+ perf_defaults->mutable_timebase()->set_counter(
+ protos::gen::PerfEvents::SW_PAGE_FAULTS);
+ auto defaults_pb = defaults.SerializeAsString();
+ protos::pbzero::TracePacketDefaults::Decoder defaults_decoder(defaults_pb);
+
+ auto stream = context.perf_sample_tracker->GetSamplingStreamInfo(
+ seq_id, cpu0, &defaults_decoder);
+
+ TrackId track_id = stream.timebase_track_id;
+ const auto& track_table = context.storage->perf_counter_track_table();
+ auto row_id = track_table.id().IndexOf(track_id);
+
+ // track exists and looks sensible
+ ASSERT_TRUE(row_id.has_value());
+ EXPECT_EQ(track_table.perf_session_id()[*row_id], stream.perf_session_id);
+ EXPECT_EQ(track_table.cpu()[*row_id], cpu0);
+ EXPECT_TRUE(track_table.is_timebase()[*row_id]);
+
+ // Name derived from the timebase.
+ std::string track_name =
+ context.storage->GetString(track_table.name()[*row_id]).ToStdString();
+ ASSERT_EQ(track_name, "page-faults");
+}
+
+TEST_F(PerfSampleTrackerTest, TimebaseTrackName_Tracepoint) {
+ uint32_t seq_id = 42;
+ uint32_t cpu0 = 0;
+
+ protos::gen::TracePacketDefaults defaults;
+ auto* perf_defaults = defaults.mutable_perf_sample_defaults();
+ perf_defaults->mutable_timebase()->set_frequency(100);
+ perf_defaults->mutable_timebase()->mutable_tracepoint()->set_name(
+ "sched:sched_switch");
+ auto defaults_pb = defaults.SerializeAsString();
+ protos::pbzero::TracePacketDefaults::Decoder defaults_decoder(defaults_pb);
+
+ auto stream = context.perf_sample_tracker->GetSamplingStreamInfo(
+ seq_id, cpu0, &defaults_decoder);
+
+ TrackId track_id = stream.timebase_track_id;
+ const auto& track_table = context.storage->perf_counter_track_table();
+ auto row_id = track_table.id().IndexOf(track_id);
+
+ // track exists and looks sensible
+ ASSERT_TRUE(row_id.has_value());
+ EXPECT_EQ(track_table.perf_session_id()[*row_id], stream.perf_session_id);
+ EXPECT_EQ(track_table.cpu()[*row_id], cpu0);
+ EXPECT_TRUE(track_table.is_timebase()[*row_id]);
+
+ // Name derived from the timebase.
+ std::string track_name =
+ context.storage->GetString(track_table.name()[*row_id]).ToStdString();
+ ASSERT_EQ(track_name, "sched:sched_switch");
+}
+
+TEST_F(PerfSampleTrackerTest, UnknownCounterTreatedAsCpuClock) {
+ uint32_t seq_id = 42;
+ uint32_t cpu0 = 0;
+
+ auto stream = context.perf_sample_tracker->GetSamplingStreamInfo(
+ seq_id, cpu0, /*nullable_defaults=*/nullptr);
+
+ TrackId track_id = stream.timebase_track_id;
+ const auto& track_table = context.storage->perf_counter_track_table();
+ auto row_id = track_table.id().IndexOf(track_id);
+
+ // track exists and looks sensible
+ ASSERT_TRUE(row_id.has_value());
+ EXPECT_EQ(track_table.perf_session_id()[*row_id], stream.perf_session_id);
+ EXPECT_EQ(track_table.cpu()[*row_id], cpu0);
+ EXPECT_TRUE(track_table.is_timebase()[*row_id]);
+
+ // If the trace doesn't have a PerfSampleDefaults describing the timebase
+ // counter, we assume cpu-clock.
+ std::string track_name =
+ context.storage->GetString(track_table.name()[*row_id]).ToStdString();
+ ASSERT_EQ(track_name, "cpu-clock");
+}
+
+} // namespace
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/profile_module.cc b/src/trace_processor/importers/proto/profile_module.cc
index aefe531..5f56041 100644
--- a/src/trace_processor/importers/proto/profile_module.cc
+++ b/src/trace_processor/importers/proto/profile_module.cc
@@ -18,8 +18,10 @@
#include "perfetto/base/logging.h"
#include "src/trace_processor/importers/common/clock_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/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/perf_sample_tracker.h"
#include "src/trace_processor/importers/proto/profile_packet_utils.h"
#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
#include "src/trace_processor/storage/trace_storage.h"
@@ -29,6 +31,7 @@
#include "src/trace_processor/types/trace_processor_context.h"
#include "protos/perfetto/common/builtin_clock.pbzero.h"
+#include "protos/perfetto/common/perf_events.pbzero.h"
#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
namespace perfetto {
@@ -71,7 +74,7 @@
case TracePacket::kPerfSampleFieldNumber:
PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
ParsePerfSample(ttp.timestamp, ttp.packet_data.sequence_state.get(),
- decoder.perf_sample());
+ decoder);
return;
}
}
@@ -157,9 +160,10 @@
void ProfileModule::ParsePerfSample(
int64_t ts,
PacketSequenceStateGeneration* sequence_state,
- ConstBytes blob) {
+ const TracePacket::Decoder& decoder) {
using PerfSample = protos::pbzero::PerfSample;
- PerfSample::Decoder sample(blob.data, blob.size);
+ const auto& sample_raw = decoder.perf_sample();
+ PerfSample::Decoder sample(sample_raw.data, sample_raw.size);
// Not a sample, but an indication of data loss in the ring buffer shared with
// the kernel.
@@ -186,14 +190,30 @@
return;
}
- // Proper sample, though possibly with an incomplete stack unwind.
- SequenceStackProfileTracker& stack_tracker =
- sequence_state->state()->sequence_stack_profile_tracker();
- ProfilePacketInternLookup intern_lookup(sequence_state);
+ // Not a sample, but an event from the producer.
+ // TODO(rsavitski): parse abrupt stops, and see if we can propagate syscall
+ // failures to this field.
+ if (sample.has_producer_event()) {
+ return;
+ }
+
+ // Proper sample, populate the |perf_sample| table with everything except the
+ // recorded counter values, which go to |counter|.
+ uint32_t seq_id = decoder.trusted_packet_sequence_id();
+ PerfSampleTracker::SamplingStreamInfo sampling_stream =
+ context_->perf_sample_tracker->GetSamplingStreamInfo(
+ seq_id, sample.cpu(), sequence_state->GetTracePacketDefaults());
+
+ context_->event_tracker->PushCounter(
+ ts, static_cast<double>(sample.timebase_count()),
+ sampling_stream.timebase_track_id);
// TODO(rsavitski): empty callsite is not an error for counter-only samples.
// But consider identifying sequences which *should* have a callstack in every
// sample, as an invalid stack there is a bug.
+ SequenceStackProfileTracker& stack_tracker =
+ sequence_state->state()->sequence_stack_profile_tracker();
+ ProfilePacketInternLookup intern_lookup(sequence_state);
uint64_t callstack_iid = sample.callstack_iid();
base::Optional<CallsiteId> cs_id =
stack_tracker.FindOrInsertCallstack(callstack_iid, &intern_lookup);
@@ -216,7 +236,8 @@
ProfilePacketUtils::StringifyStackUnwindError(unwind_error));
}
tables::PerfSampleTable::Row sample_row(ts, utid, sample.cpu(), cpu_mode_id,
- cs_id, unwind_error_id);
+ cs_id, unwind_error_id,
+ sampling_stream.perf_session_id);
context_->storage->mutable_perf_sample_table()->Insert(sample_row);
}
diff --git a/src/trace_processor/importers/proto/profile_module.h b/src/trace_processor/importers/proto/profile_module.h
index 97efc59..f2fd43b 100644
--- a/src/trace_processor/importers/proto/profile_module.h
+++ b/src/trace_processor/importers/proto/profile_module.h
@@ -56,7 +56,7 @@
void ParsePerfSample(int64_t ts,
PacketSequenceStateGeneration* sequence_state,
- protozero::ConstBytes blob);
+ const protos::pbzero::TracePacket::Decoder& decoder);
TraceProcessorContext* context_;
};
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index d9f9467..fa934af 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -444,6 +444,13 @@
return &gpu_counter_group_table_;
}
+ const tables::PerfCounterTrackTable& perf_counter_track_table() const {
+ return perf_counter_track_table_;
+ }
+ tables::PerfCounterTrackTable* mutable_perf_counter_track_table() {
+ return &perf_counter_track_table_;
+ }
+
const tables::SchedSliceTable& sched_slice_table() const {
return sched_slice_table_;
}
@@ -793,6 +800,8 @@
tables::GpuCounterTrackTable gpu_counter_track_table_{&string_pool_,
&counter_track_table_};
tables::GpuCounterGroupTable gpu_counter_group_table_{&string_pool_, nullptr};
+ tables::PerfCounterTrackTable perf_counter_track_table_{
+ &string_pool_, &counter_track_table_};
// Args for all other tables.
tables::ArgTable arg_table_{&string_pool_, nullptr};
diff --git a/src/trace_processor/tables/profiler_tables.h b/src/trace_processor/tables/profiler_tables.h
index 96e675b..3758732 100644
--- a/src/trace_processor/tables/profiler_tables.h
+++ b/src/trace_processor/tables/profiler_tables.h
@@ -185,6 +185,9 @@
// encountered an error. Such samples still reference the best-effort
// result via the callsite_id (with a synthetic error frame at the point
// where unwinding stopped).
+// @param perf_session_id distinguishes samples from different profiling
+// streams (i.e. multiple data sources).
+// {@joinable perf_counter_track.perf_session_id}
// @tablegroup Callstack profilers
#define PERFETTO_TP_PERF_SAMPLE_DEF(NAME, PARENT, C) \
NAME(PerfSampleTable, "perf_sample") \
@@ -194,7 +197,8 @@
C(uint32_t, cpu) \
C(StringPool::Id, cpu_mode) \
C(base::Optional<StackProfileCallsiteTable::Id>, callsite_id) \
- C(base::Optional<StringPool::Id>, unwind_error)
+ C(base::Optional<StringPool::Id>, unwind_error) \
+ C(uint32_t, perf_session_id)
PERFETTO_TP_TABLE(PERFETTO_TP_PERF_SAMPLE_DEF);
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index ac10a6e..e8cfc1b 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -88,6 +88,7 @@
IrqCounterTrackTable::~IrqCounterTrackTable() = default;
SoftirqCounterTrackTable::~SoftirqCounterTrackTable() = default;
GpuCounterTrackTable::~GpuCounterTrackTable() = default;
+PerfCounterTrackTable::~PerfCounterTrackTable() = default;
// memory_tables.h
MemorySnapshotTable::~MemorySnapshotTable() = default;
diff --git a/src/trace_processor/tables/track_tables.h b/src/trace_processor/tables/track_tables.h
index f9012d8..afcd708 100644
--- a/src/trace_processor/tables/track_tables.h
+++ b/src/trace_processor/tables/track_tables.h
@@ -116,6 +116,23 @@
PERFETTO_TP_TABLE(PERFETTO_TP_GPU_COUNTER_TRACK_DEF);
+// Sampled counters' values for samples in the perf_sample table.
+//
+// @param perf_session_id id of a distict profiling stream.
+// {@joinable perf_sample.perf_session_id}
+// @param cpu the core the sample was taken on.
+// @is_timebase if true, this counter was the sampling timebase for this
+// perf_session_id.
+// @tablegroup Tracks
+#define PERFETTO_TP_PERF_COUNTER_TRACK_DEF(NAME, PARENT, C) \
+ NAME(PerfCounterTrackTable, "perf_counter_track") \
+ PARENT(PERFETTO_TP_COUNTER_TRACK_DEF, C) \
+ C(uint32_t, perf_session_id) \
+ C(uint32_t, cpu) \
+ C(uint32_t, is_timebase)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_PERF_COUNTER_TRACK_DEF);
+
} // namespace tables
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index e2df18f..1524bf0 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -30,6 +30,7 @@
#include "src/trace_processor/importers/proto/async_track_set_tracker.h"
#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
#include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/importers/proto/perf_sample_tracker.h"
#include "src/trace_processor/importers/proto/proto_importer_module.h"
#include "src/trace_processor/importers/proto/proto_trace_parser.h"
#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 35fb3a0..f6e90b7 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -787,6 +787,7 @@
RegisterDbTable(storage->softirq_counter_track_table());
RegisterDbTable(storage->gpu_counter_track_table());
RegisterDbTable(storage->gpu_counter_group_table());
+ RegisterDbTable(storage->perf_counter_track_table());
RegisterDbTable(storage->heap_graph_object_table());
RegisterDbTable(storage->heap_graph_reference_table());
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index 298770b..3cdddd7 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -30,6 +30,7 @@
#include "src/trace_processor/importers/proto/async_track_set_tracker.h"
#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
#include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/importers/proto/perf_sample_tracker.h"
#include "src/trace_processor/importers/proto/proto_importer_module.h"
#include "src/trace_processor/importers/proto/proto_trace_reader.h"
#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
@@ -52,6 +53,7 @@
context_.process_tracker.reset(new ProcessTracker(&context_));
context_.clock_tracker.reset(new ClockTracker(&context_));
context_.heap_profile_tracker.reset(new HeapProfileTracker(&context_));
+ context_.perf_sample_tracker.reset(new PerfSampleTracker(&context_));
context_.global_stack_profile_tracker.reset(new GlobalStackProfileTracker());
context_.metadata_tracker.reset(new MetadataTracker(&context_));
context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index eb6bf13..eb92381 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -38,6 +38,7 @@
class GlobalStackProfileTracker;
class HeapGraphTracker;
class HeapProfileTracker;
+class PerfSampleTracker;
class MetadataTracker;
class ProtoImporterModule;
class ProcessTracker;
@@ -76,6 +77,7 @@
std::unique_ptr<EventTracker> event_tracker;
std::unique_ptr<ClockTracker> clock_tracker;
std::unique_ptr<HeapProfileTracker> heap_profile_tracker;
+ std::unique_ptr<PerfSampleTracker> perf_sample_tracker;
std::unique_ptr<GlobalStackProfileTracker> global_stack_profile_tracker;
std::unique_ptr<MetadataTracker> metadata_tracker;