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;