compact sched_waking: event decoding in trace_processor

Compact waking events are now handled by the sched_event_tracker, which populates the
raw & instants tables. Handling of the default-encoded events hasn't changed.

The emitting pid is inferred from the [pending] scheduling slices, in other
words, sched_switch events. So if you have compact sched_waking, but no
sched_switch, we skip all waking events during parsing. I think this is fine,
and just needs some extra documentation. We could instead start encoding the
emitting pid in the trace to remove this limitation, but I'm not sure who'd want
*compact* sched_waking without any sched_switch events.

Sorter: I've kept the approach of having all events in the same per-cpu queue.
We can theoretically do better with multiple queues, but that also requires
further changes to the sorter that are outside the scope of this cl.  In
practice, this approach does not give significant overheads on typical traces
(especially if there's no write_to_file, as then we sort each queue at most once
at the end).

Change-Id: I1deda1d433b696dd16a5f127604c9ab8f6f8cdc9
diff --git a/src/trace_processor/importers/ftrace/ftrace_module.h b/src/trace_processor/importers/ftrace/ftrace_module.h
index 9d8d489..fd1ace0 100644
--- a/src/trace_processor/importers/ftrace/ftrace_module.h
+++ b/src/trace_processor/importers/ftrace/ftrace_module.h
@@ -65,14 +65,7 @@
 
   ModuleResult ParseFtracePacket(uint32_t cpu,
                                  const TimestampedTracePiece& ttp) {
-    // Handle the (optional) alternative encoding format for sched_switch.
-    if (ttp.inline_event.type == InlineEvent::Type::kSchedSwitch) {
-      parser_.ParseInlineSchedSwitch(cpu, ttp.timestamp,
-                                     ttp.inline_event.sched_switch);
-      return ModuleResult::Handled();
-    }
-
-    return parser_.ParseFtraceEvent(cpu, ttp.timestamp, ttp.blob_view);
+    return parser_.ParseFtraceEvent(cpu, ttp);
   }
 
  private:
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index f325192..3ee0cf2 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -184,12 +184,32 @@
 
 PERFETTO_ALWAYS_INLINE
 util::Status FtraceParser::ParseFtraceEvent(uint32_t cpu,
-                                            int64_t ts,
-                                            const TraceBlobView& event) {
+                                            const TimestampedTracePiece& ttp) {
+  using protos::pbzero::FtraceEvent;
+  int64_t ts = ttp.timestamp;
+
+  // Handle the (optional) alternative encoding format for sched_switch.
+  if (ttp.inline_event.type == InlineEvent::Type::kSchedSwitch) {
+    const auto& event = ttp.inline_event.sched_switch;
+    context_->sched_tracker->PushSchedSwitchCompact(
+        cpu, ts, event.prev_state, static_cast<uint32_t>(event.next_pid),
+        event.next_prio, event.next_comm);
+    return util::OkStatus();
+  }
+
+  // Handle the (optional) alternative encoding format for sched_waking.
+  if (ttp.inline_event.type == InlineEvent::Type::kSchedWaking) {
+    const auto& event = ttp.inline_event.sched_waking;
+    context_->sched_tracker->PushSchedWakingCompact(
+        cpu, ts, static_cast<uint32_t>(event.pid), event.target_cpu, event.prio,
+        event.comm);
+    return util::OkStatus();
+  }
+
+  const TraceBlobView& event = ttp.blob_view;
   ProtoDecoder decoder(event.data(), event.length());
   uint64_t raw_pid = 0;
-  if (auto pid_field =
-          decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber)) {
+  if (auto pid_field = decoder.FindField(FtraceEvent::kPidFieldNumber)) {
     raw_pid = pid_field.as_uint64();
   } else {
     return util::ErrStatus("Pid field not found in ftrace packet");
@@ -197,126 +217,125 @@
   uint32_t pid = static_cast<uint32_t>(raw_pid);
 
   for (auto fld = decoder.ReadField(); fld.valid(); fld = decoder.ReadField()) {
-    bool is_metadata_field =
-        fld.id() == protos::pbzero::FtraceEvent::kPidFieldNumber ||
-        fld.id() == protos::pbzero::FtraceEvent::kTimestampFieldNumber;
+    bool is_metadata_field = fld.id() == FtraceEvent::kPidFieldNumber ||
+                             fld.id() == FtraceEvent::kTimestampFieldNumber;
     if (is_metadata_field)
       continue;
 
     ConstBytes data = fld.as_bytes();
-    if (fld.id() == protos::pbzero::FtraceEvent::kGenericFieldNumber) {
+    if (fld.id() == FtraceEvent::kGenericFieldNumber) {
       ParseGenericFtrace(ts, cpu, pid, data);
-    } else if (fld.id() !=
-               protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber) {
+    } else if (fld.id() != FtraceEvent::kSchedSwitchFieldNumber) {
+      // sched_switch parsing populates the raw table by itself
       ParseTypedFtraceToRaw(fld.id(), ts, cpu, pid, data);
     }
 
     switch (fld.id()) {
-      case protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber: {
+      case FtraceEvent::kSchedSwitchFieldNumber: {
         ParseSchedSwitch(cpu, ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSchedWakeupFieldNumber: {
+      case FtraceEvent::kSchedWakeupFieldNumber: {
         ParseSchedWakeup(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSchedWakingFieldNumber: {
+      case FtraceEvent::kSchedWakingFieldNumber: {
         ParseSchedWaking(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSchedProcessFreeFieldNumber: {
+      case FtraceEvent::kSchedProcessFreeFieldNumber: {
         ParseSchedProcessFree(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kCpuFrequencyFieldNumber: {
+      case FtraceEvent::kCpuFrequencyFieldNumber: {
         ParseCpuFreq(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kGpuFrequencyFieldNumber: {
+      case FtraceEvent::kGpuFrequencyFieldNumber: {
         ParseGpuFreq(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kCpuIdleFieldNumber: {
+      case FtraceEvent::kCpuIdleFieldNumber: {
         ParseCpuIdle(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kPrintFieldNumber: {
+      case FtraceEvent::kPrintFieldNumber: {
         ParsePrint(cpu, ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kZeroFieldNumber: {
+      case FtraceEvent::kZeroFieldNumber: {
         ParseZero(cpu, ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kRssStatFieldNumber: {
+      case FtraceEvent::kRssStatFieldNumber: {
         ParseRssStat(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kIonHeapGrowFieldNumber: {
+      case FtraceEvent::kIonHeapGrowFieldNumber: {
         ParseIonHeapGrowOrShrink(ts, pid, data, true);
         break;
       }
-      case protos::pbzero::FtraceEvent::kIonHeapShrinkFieldNumber: {
+      case FtraceEvent::kIonHeapShrinkFieldNumber: {
         ParseIonHeapGrowOrShrink(ts, pid, data, false);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSignalGenerateFieldNumber: {
+      case FtraceEvent::kSignalGenerateFieldNumber: {
         ParseSignalGenerate(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSignalDeliverFieldNumber: {
+      case FtraceEvent::kSignalDeliverFieldNumber: {
         ParseSignalDeliver(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kLowmemoryKillFieldNumber: {
+      case FtraceEvent::kLowmemoryKillFieldNumber: {
         ParseLowmemoryKill(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kOomScoreAdjUpdateFieldNumber: {
+      case FtraceEvent::kOomScoreAdjUpdateFieldNumber: {
         ParseOOMScoreAdjUpdate(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kMmEventRecordFieldNumber: {
+      case FtraceEvent::kMmEventRecordFieldNumber: {
         ParseMmEventRecord(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSysEnterFieldNumber: {
+      case FtraceEvent::kSysEnterFieldNumber: {
         ParseSysEvent(ts, pid, true, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSysExitFieldNumber: {
+      case FtraceEvent::kSysExitFieldNumber: {
         ParseSysEvent(ts, pid, false, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kTaskNewtaskFieldNumber: {
+      case FtraceEvent::kTaskNewtaskFieldNumber: {
         ParseTaskNewTask(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kTaskRenameFieldNumber: {
+      case FtraceEvent::kTaskRenameFieldNumber: {
         ParseTaskRename(data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderTransactionFieldNumber: {
+      case FtraceEvent::kBinderTransactionFieldNumber: {
         ParseBinderTransaction(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderTransactionReceivedFieldNumber: {
+      case FtraceEvent::kBinderTransactionReceivedFieldNumber: {
         ParseBinderTransactionReceived(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderTransactionAllocBufFieldNumber: {
+      case FtraceEvent::kBinderTransactionAllocBufFieldNumber: {
         ParseBinderTransactionAllocBuf(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderLockFieldNumber: {
+      case FtraceEvent::kBinderLockFieldNumber: {
         ParseBinderLock(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderUnlockFieldNumber: {
+      case FtraceEvent::kBinderUnlockFieldNumber: {
         ParseBinderUnlock(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderLockedFieldNumber: {
+      case FtraceEvent::kBinderLockedFieldNumber: {
         ParseBinderLocked(ts, pid, data);
         break;
       }
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index 0117d7c..3d7138b 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -34,17 +34,7 @@
 
   void ParseFtraceStats(protozero::ConstBytes);
 
-  void ParseInlineSchedSwitch(uint32_t cpu,
-                              int64_t ts,
-                              const InlineSchedSwitch& event) {
-    context_->sched_tracker->PushSchedSwitchCompact(
-        cpu, ts, event.prev_state, static_cast<uint32_t>(event.next_pid),
-        event.next_prio, event.next_comm);
-  }
-
-  util::Status ParseFtraceEvent(uint32_t cpu,
-                                int64_t ts,
-                                const TraceBlobView& packet);
+  util::Status ParseFtraceEvent(uint32_t cpu, const TimestampedTracePiece& ttp);
 
  private:
   void ParseGenericFtrace(int64_t timestamp,
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
index 10c6aaf..4319e2d 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
@@ -65,19 +65,66 @@
 }
 
 PERFETTO_ALWAYS_INLINE
+void FtraceTokenizer::TokenizeFtraceEvent(uint32_t cpu, TraceBlobView event) {
+  constexpr auto kTimestampFieldNumber =
+      protos::pbzero::FtraceEvent::kTimestampFieldNumber;
+  const uint8_t* data = event.data();
+  const size_t length = event.length();
+  ProtoDecoder decoder(data, length);
+  uint64_t raw_timestamp = 0;
+  bool timestamp_found = false;
+
+  // Speculate on the fact that the timestamp is often the 1st field of the
+  // event.
+  constexpr auto timestampFieldTag = MakeTagVarInt(kTimestampFieldNumber);
+  if (PERFETTO_LIKELY(length > 10 && data[0] == timestampFieldTag)) {
+    // Fastpath.
+    const uint8_t* next = ParseVarInt(data + 1, data + 11, &raw_timestamp);
+    timestamp_found = next != data + 1;
+    decoder.Reset(next);
+  } else {
+    // Slowpath.
+    if (auto ts_field = decoder.FindField(kTimestampFieldNumber)) {
+      timestamp_found = true;
+      raw_timestamp = ts_field.as_uint64();
+    }
+  }
+
+  if (PERFETTO_UNLIKELY(!timestamp_found)) {
+    PERFETTO_ELOG("Timestamp field not found in FtraceEvent");
+    context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
+    return;
+  }
+
+  int64_t timestamp = static_cast<int64_t>(raw_timestamp);
+
+  // We don't need to parse this packet, just push it to be sorted with
+  // the timestamp.
+  context_->sorter->PushFtraceEvent(cpu, timestamp, std::move(event));
+}
+
+PERFETTO_ALWAYS_INLINE
 void FtraceTokenizer::TokenizeFtraceCompactSched(uint32_t cpu,
                                                  const uint8_t* data,
                                                  size_t size) {
-  protos::pbzero::FtraceEventBundle_CompactSched::Decoder compact(data, size);
-
-  // Build the interning table for next_comm fields.
+  protos::pbzero::FtraceEventBundle::CompactSched::Decoder compact_sched(data,
+                                                                         size);
+  // Build the interning table for comm fields.
   std::vector<StringId> string_table;
   string_table.reserve(512);
-  for (auto it = compact.intern_table(); it; it++) {
+  for (auto it = compact_sched.intern_table(); it; it++) {
     StringId value = context_->storage->InternString(*it);
     string_table.push_back(value);
   }
 
+  TokenizeFtraceCompactSchedSwitch(cpu, compact_sched, string_table);
+  TokenizeFtraceCompactSchedWaking(cpu, compact_sched, string_table);
+}
+
+void FtraceTokenizer::TokenizeFtraceCompactSchedSwitch(
+    uint32_t cpu,
+    const protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact,
+    const std::vector<StringId>& string_table) {
   // Accumulator for timestamp deltas.
   int64_t timestamp_acc = 0;
 
@@ -117,43 +164,48 @@
     context_->storage->IncrementStats(stats::compact_sched_has_parse_errors);
 }
 
-PERFETTO_ALWAYS_INLINE
-void FtraceTokenizer::TokenizeFtraceEvent(uint32_t cpu, TraceBlobView event) {
-  constexpr auto kTimestampFieldNumber =
-      protos::pbzero::FtraceEvent::kTimestampFieldNumber;
-  const uint8_t* data = event.data();
-  const size_t length = event.length();
-  ProtoDecoder decoder(data, length);
-  uint64_t raw_timestamp = 0;
-  bool timestamp_found = false;
+void FtraceTokenizer::TokenizeFtraceCompactSchedWaking(
+    uint32_t cpu,
+    const protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact,
+    const std::vector<StringId>& string_table) {
+  // Accumulator for timestamp deltas.
+  int64_t timestamp_acc = 0;
 
-  // Speculate on the fact that the timestamp is often the 1st field of the
-  // event.
-  constexpr auto timestampFieldTag = MakeTagVarInt(kTimestampFieldNumber);
-  if (PERFETTO_LIKELY(length > 10 && data[0] == timestampFieldTag)) {
-    // Fastpath.
-    const uint8_t* next = ParseVarInt(data + 1, data + 11, &raw_timestamp);
-    timestamp_found = next != data + 1;
-    decoder.Reset(next);
-  } else {
-    // Slowpath.
-    if (auto ts_field = decoder.FindField(kTimestampFieldNumber)) {
-      timestamp_found = true;
-      raw_timestamp = ts_field.as_uint64();
-    }
+  // The events' fields are stored in a structure-of-arrays style, using packed
+  // repeated fields. Walk each repeated field in step to recover individual
+  // events.
+  bool parse_error = false;
+  auto timestamp_it = compact.waking_timestamp(&parse_error);
+  auto pid_it = compact.waking_pid(&parse_error);
+  auto tcpu_it = compact.waking_target_cpu(&parse_error);
+  auto prio_it = compact.waking_prio(&parse_error);
+  auto comm_it = compact.waking_comm_index(&parse_error);
+
+  for (; timestamp_it && pid_it && tcpu_it && prio_it && comm_it;
+       ++timestamp_it, ++pid_it, ++tcpu_it, ++prio_it, ++comm_it) {
+    InlineSchedWaking event{};
+
+    // delta-encoded timestamp
+    timestamp_acc += static_cast<int64_t>(*timestamp_it);
+    int64_t event_timestamp = timestamp_acc;
+
+    // index into the interned string table
+    PERFETTO_DCHECK(*comm_it < string_table.size());
+    event.comm = string_table[*comm_it];
+
+    event.pid = *pid_it;
+    event.target_cpu = *tcpu_it;
+    event.prio = *prio_it;
+
+    context_->sorter->PushInlineFtraceEvent(cpu, event_timestamp,
+                                            InlineEvent::SchedWaking(event));
   }
 
-  if (PERFETTO_UNLIKELY(!timestamp_found)) {
-    PERFETTO_ELOG("Timestamp field not found in FtraceEvent");
-    context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
-    return;
-  }
-
-  int64_t timestamp = static_cast<int64_t>(raw_timestamp);
-
-  // We don't need to parse this packet, just push it to be sorted with
-  // the timestamp.
-  context_->sorter->PushFtraceEvent(cpu, timestamp, std::move(event));
+  // Check that all packed buffers were decoded correctly, and fully.
+  bool sizes_match =
+      !timestamp_it && !pid_it && !tcpu_it && !prio_it && !comm_it;
+  if (parse_error || !sizes_match)
+    context_->storage->IncrementStats(stats::compact_sched_has_parse_errors);
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.h b/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
index 30b5a5f..8d1007f 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
@@ -17,8 +17,10 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_TOKENIZER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_TOKENIZER_H_
 
+#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
 #include "src/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -28,13 +30,21 @@
   explicit FtraceTokenizer(TraceProcessorContext* context)
       : context_(context) {}
 
-  void TokenizeFtraceBundle(TraceBlobView);
+  void TokenizeFtraceBundle(TraceBlobView bundle);
 
  private:
-  void TokenizeFtraceEvent(uint32_t cpu, TraceBlobView);
+  void TokenizeFtraceEvent(uint32_t cpu, TraceBlobView event);
   void TokenizeFtraceCompactSched(uint32_t cpu,
                                   const uint8_t* data,
                                   size_t size);
+  void TokenizeFtraceCompactSchedSwitch(
+      uint32_t cpu,
+      const protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact,
+      const std::vector<StringId>& string_table);
+  void TokenizeFtraceCompactSchedWaking(
+      uint32_t cpu,
+      const protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact,
+      const std::vector<StringId>& string_table);
 
   TraceProcessorContext* context_;
 };
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.cc b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
index 4c38e99..a4027f6 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
@@ -36,15 +36,27 @@
 
 SchedEventTracker::SchedEventTracker(TraceProcessorContext* context)
     : context_(context) {
-  auto* descriptor = GetMessageDescriptorForId(
+  // pre-parse sched_switch
+  auto* switch_descriptor = GetMessageDescriptorForId(
       protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber);
-  PERFETTO_CHECK(descriptor->max_field_id == kSchedSwitchMaxFieldId);
+  PERFETTO_CHECK(switch_descriptor->max_field_id == kSchedSwitchMaxFieldId);
 
   for (size_t i = 1; i <= kSchedSwitchMaxFieldId; i++) {
     sched_switch_field_ids_[i] =
-        context->storage->InternString(descriptor->fields[i].name);
+        context->storage->InternString(switch_descriptor->fields[i].name);
   }
-  sched_switch_id_ = context->storage->InternString(descriptor->name);
+  sched_switch_id_ = context->storage->InternString(switch_descriptor->name);
+
+  // pre-parse sched_waking
+  auto* waking_descriptor = GetMessageDescriptorForId(
+      protos::pbzero::FtraceEvent::kSchedWakingFieldNumber);
+  PERFETTO_CHECK(waking_descriptor->max_field_id == kSchedWakingMaxFieldId);
+
+  for (size_t i = 1; i <= kSchedWakingMaxFieldId; i++) {
+    sched_waking_field_ids_[i] =
+        context->storage->InternString(waking_descriptor->fields[i].name);
+  }
+  sched_waking_id_ = context->storage->InternString(waking_descriptor->name);
 }
 
 SchedEventTracker::~SchedEventTracker() = default;
@@ -130,6 +142,8 @@
   // create slices as normal, but the first per-cpu switch is effectively
   // discarded.
   if (pending_sched->last_utid == std::numeric_limits<UniqueTid>::max()) {
+    context_->storage->IncrementStats(stats::compact_sched_switch_skipped);
+
     pending_sched->last_pid = next_pid;
     pending_sched->last_utid = next_utid;
     pending_sched->last_prio = next_prio;
@@ -226,6 +240,62 @@
   slices->set_end_state(pending_slice_idx, task_state);
 }
 
+// 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,
+                                               int32_t target_cpu,
+                                               int32_t prio,
+                                               StringId comm_id) {
+  // 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",
+                  (context_->event_tracker->max_timestamp() - ts) / 1e6);
+    context_->storage->IncrementStats(stats::sched_waking_out_of_order);
+    return;
+  }
+  context_->event_tracker->UpdateMaxTimestamp(ts);
+  PERFETTO_DCHECK(cpu < base::kMaxCpus);
+
+  // 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 = &pending_sched_per_cpu_[cpu];
+  if (pending_sched->last_utid == std::numeric_limits<UniqueTid>::max()) {
+    context_->storage->IncrementStats(stats::compact_sched_waking_skipped);
+    return;
+  }
+  auto curr_utid = pending_sched->last_utid;
+
+  // Add an entry to the raw table.
+  auto rid = context_->storage->mutable_raw_events()->AddRawEvent(
+      ts, sched_waking_id_, cpu, curr_utid);
+
+  // "success" is hardcoded as always 1 by the kernel, with a TODO to remove it.
+  static constexpr int32_t kHardcodedSuccess = 1;
+
+  using SW = protos::pbzero::SchedWakingFtraceEvent;
+  auto add_raw_arg = [this](RowId row_id, int field_num, Variadic var) {
+    StringId key = sched_waking_field_ids_[static_cast<size_t>(field_num)];
+    context_->args_tracker->AddArg(row_id, key, key, var);
+  };
+  add_raw_arg(rid, SW::kCommFieldNumber, Variadic::String(comm_id));
+  add_raw_arg(rid, SW::kPidFieldNumber, Variadic::Integer(wakee_pid));
+  add_raw_arg(rid, SW::kPrioFieldNumber, Variadic::Integer(prio));
+  add_raw_arg(rid, SW::kSuccessFieldNumber,
+              Variadic::Integer(kHardcodedSuccess));
+  add_raw_arg(rid, SW::kTargetCpuFieldNumber, Variadic::Integer(target_cpu));
+
+  // Add a waking entry to the instants.
+  auto wakee_utid = context_->process_tracker->GetOrCreateThread(wakee_pid);
+  auto* instants = context_->storage->mutable_instants();
+  instants->AddInstantEvent(ts, sched_waking_id_, /*value=*/0, wakee_utid,
+                            RefType::kRefUtid);
+}
+
 void SchedEventTracker::FlushPendingEvents() {
   // TODO(lalitm): the day this method is called before end of trace, don't
   // flush the sched events as they will probably be pushed in the next round
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.h b/src/trace_processor/importers/ftrace/sched_event_tracker.h
index 46acb8f..3c100b6 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.h
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.h
@@ -59,6 +59,16 @@
                               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,
+                              int32_t target_cpu,
+                              int32_t prio,
+                              StringId comm_id);
+
   // Called at the end of trace to flush any events which are pending to the
   // storage.
   void FlushPendingEvents();
@@ -99,6 +109,10 @@
   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_;
 };
 
diff --git a/src/trace_processor/stats.h b/src/trace_processor/stats.h
index 8c4452c..583dfa2 100644
--- a/src/trace_processor/stats.h
+++ b/src/trace_processor/stats.h
@@ -121,7 +121,10 @@
   F(packages_list_has_parse_errors,           kSingle,  kError,    kTrace),    \
   F(packages_list_has_read_errors,            kSingle,  kError,    kTrace),    \
   F(compact_sched_has_parse_errors,           kSingle,  kError,    kTrace),    \
-  F(misplaced_end_event,                      kSingle,  kDataLoss, kAnalysis)
+  F(misplaced_end_event,                      kSingle,  kDataLoss, kAnalysis), \
+  F(sched_waking_out_of_order,                kSingle,  kError,    kAnalysis), \
+  F(compact_sched_switch_skipped,             kSingle,  kInfo,     kAnalysis), \
+  F(compact_sched_waking_skipped,             kSingle,  kInfo,     kAnalysis)
 // clang-format on
 
 enum Type {
diff --git a/src/trace_processor/timestamped_trace_piece.h b/src/trace_processor/timestamped_trace_piece.h
index 84e21fb..83b4932 100644
--- a/src/trace_processor/timestamped_trace_piece.h
+++ b/src/trace_processor/timestamped_trace_piece.h
@@ -44,10 +44,17 @@
   StringId next_comm;
 };
 
+struct InlineSchedWaking {
+  int32_t pid;
+  int32_t target_cpu;
+  int32_t prio;
+  StringId comm;
+};
+
 // Discriminated union of events that are cannot be easily read from the
 // mapped trace.
 struct InlineEvent {
-  enum class Type { kInvalid = 0, kSchedSwitch };
+  enum class Type { kInvalid = 0, kSchedSwitch, kSchedWaking };
 
   static InlineEvent SchedSwitch(InlineSchedSwitch content) {
     InlineEvent evt;
@@ -56,9 +63,17 @@
     return evt;
   }
 
+  static InlineEvent SchedWaking(InlineSchedWaking content) {
+    InlineEvent evt;
+    evt.type = Type::kSchedWaking;
+    evt.sched_waking = content;
+    return evt;
+  }
+
   Type type = Type::kInvalid;
   union {
     InlineSchedSwitch sched_switch;
+    InlineSchedWaking sched_waking;
   };
 };
 
@@ -135,12 +150,16 @@
                               sequence_state,
                               InlineEvent{}) {}
 
+  // TODO(rsavitski): each "empty" TraceBlobView created by this constructor
+  // still allocates ref-counting structures for the nonexistent memory.
+  // It's not a significant overhead, but consider making the class have a
+  // legitimate empty state.
   TimestampedTracePiece(int64_t ts, uint64_t idx, InlineEvent inline_evt)
       : TimestampedTracePiece(ts,
                               /*thread_ts=*/0,
                               /*thread_instructions=*/0,
                               idx,
-                              /*tbv=*/TraceBlobView(nullptr, 0, 0),
+                              TraceBlobView(nullptr, 0, 0),
                               /*value=*/nullptr,
                               /*fpv=*/nullptr,
                               /*sequence_state=*/nullptr,
diff --git a/src/traced/probes/ftrace/compact_sched.cc b/src/traced/probes/ftrace/compact_sched.cc
index 70168e9..7ca8526 100644
--- a/src/traced/probes/ftrace/compact_sched.cc
+++ b/src/traced/probes/ftrace/compact_sched.cc
@@ -152,9 +152,6 @@
 // TODO(rsavitski): could avoid looping over all events if the caller did the
 // work to remember the relevant events (translation table construction already
 // loops over them).
-// TODO(rsavitski): consider tracking the validity of the formats individually,
-// so that we can e.g. still use compact_sched on a device without
-// compact_waking.
 CompactSchedEventFormat ValidateFormatForCompactSched(
     const std::vector<Event>& events) {
   using protos::pbzero::FtraceEvent;
diff --git a/test/trace_processor/index b/test/trace_processor/index
index bf3c781..958b11e 100644
--- a/test/trace_processor/index
+++ b/test/trace_processor/index
@@ -149,3 +149,8 @@
 
 # Config
 config.textproto metadata.sql config_metadata.out
+
+# Decoding of sched_waking events from a trace with compact scheduling events.
+# Verifies the contents of raw & instants tables.
+../data/compact_sched.pb sched_waking_raw.sql sched_waking_raw_compact_sched.out
+../data/compact_sched.pb sched_waking_instants.sql sched_waking_instants_compact_sched.out
diff --git a/test/trace_processor/sched_waking_instants.sql b/test/trace_processor/sched_waking_instants.sql
new file mode 100644
index 0000000..1fe4a76
--- /dev/null
+++ b/test/trace_processor/sched_waking_instants.sql
@@ -0,0 +1 @@
+SELECT ts, instants.name, thread.name, thread.tid FROM instants JOIN thread ON instants.ref == thread.utid WHERE instants.name == "sched_waking" ORDER BY ts ASC
diff --git a/test/trace_processor/sched_waking_instants_compact_sched.out b/test/trace_processor/sched_waking_instants_compact_sched.out
new file mode 100644
index 0000000..201d48b
--- /dev/null
+++ b/test/trace_processor/sched_waking_instants_compact_sched.out
@@ -0,0 +1,82 @@
+"ts","name","name","tid"
+250978439491994,"sched_waking","rcu_sched",8
+250978441664547,"sched_waking","ksoftirqd/1",18
+250978441702515,"sched_waking","kworker/u16:18",17473
+250978444958245,"sched_waking","rcuop/0",10
+250978444974443,"sched_waking","rcuop/1",21
+250978444984234,"sched_waking","rcu_preempt",7
+250978445783141,"sched_waking","rcu_sched",8
+250978451590173,"sched_waking","rcu_preempt",7
+250978451591891,"sched_waking","rcu_sched",8
+250978451609131,"sched_waking","kworker/u16:18",17473
+250978451646579,"sched_waking","rcuos/0",11
+250978451716891,"sched_waking","rcuos/1",22
+250978452047048,"sched_waking","kworker/u16:18",17473
+250978458337725,"sched_waking","sugov:0",625
+250978458362100,"sched_waking","rcu_preempt",7
+250978458398455,"sched_waking","rcuop/0",10
+250978458419496,"sched_waking","rcuop/1",21
+250978459118038,"sched_waking","sugov:0",625
+250978465172309,"sched_waking","ksoftirqd/3",34
+250978465266789,"sched_waking","kworker/u16:18",17473
+250978593466697,"sched_waking","kworker/2:0",17438
+250978593734510,"sched_waking","kworker/u16:18",17473
+250978595521125,"sched_waking","kworker/2:3",17754
+250978595696645,"sched_waking","kworker/2:0",17438
+250978596068468,"sched_waking","kworker/2:3",17754
+250978596565656,"sched_waking","kworker/2:0",17438
+250978600598417,"sched_waking","kworker/2:0",17438
+250978600639042,"sched_waking","ksoftirqd/0",6
+250978604651907,"sched_waking","ksoftirqd/0",6
+250978604739980,"sched_waking","kworker/2:0",17438
+250978605185448,"sched_waking","ksoftirqd/2",26
+250978605260240,"sched_waking","kworker/u16:18",17473
+250978608903886,"sched_waking","kworker/2:0",17438
+250978608942220,"sched_waking","ksoftirqd/0",6
+250978612887272,"sched_waking","ksoftirqd/0",6
+250978612971283,"sched_waking","kworker/2:0",17438
+250978616876595,"sched_waking","kworker/2:0",17438
+250978616921700,"sched_waking","ksoftirqd/0",6
+250978617016804,"sched_waking","kworker/u16:18",17473
+250978620859669,"sched_waking","ksoftirqd/0",6
+250978620939148,"sched_waking","kworker/2:0",17438
+250978624836805,"sched_waking","kworker/2:0",17438
+250978624875398,"sched_waking","ksoftirqd/0",6
+250978624978003,"sched_waking","kworker/u16:18",17473
+250978628278107,"sched_waking","logd.klogd",651
+250978629289722,"sched_waking","rcuop/2",29
+250978629405763,"sched_waking","rcu_preempt",7
+250978635024462,"sched_waking","rcu_preempt",7
+250978635060191,"sched_waking","kworker/u16:18",17473
+250978635219514,"sched_waking","rcuop/2",29
+250978683441081,"sched_waking","kworker/2:0",17438
+250978683732331,"sched_waking","kworker/u16:18",17473
+250978685694259,"sched_waking","kworker/u16:18",17473
+250978740127441,"sched_waking","kworker/2:0",17438
+250978740425410,"sched_waking","kworker/u16:18",17473
+250978743210358,"sched_waking","kworker/u16:18",17473
+250978744945567,"sched_waking","logd.klogd",651
+250978746028431,"sched_waking","rcuop/2",29
+250978746173171,"sched_waking","rcu_preempt",7
+250978751622494,"sched_waking","rcu_preempt",7
+250978751661348,"sched_waking","kworker/u16:18",17473
+250978751792859,"sched_waking","rcuop/2",29
+250978751971817,"sched_waking","rcu_preempt",7
+250978758784578,"sched_waking","rcu_preempt",7
+250978758873745,"sched_waking","rcuop/2",29
+250978805144375,"sched_waking","traced_probes",17772
+250978806359896,"sched_waking","kworker/5:1",17667
+250978806428646,"sched_waking","kworker/u16:18",17473
+250978808058698,"sched_waking","rcuop/0",10
+250978808519271,"sched_waking","rcu_preempt",7
+250978815488490,"sched_waking","rcu_preempt",7
+250978815535678,"sched_waking","kworker/u16:18",17473
+250978815675782,"sched_waking","rcuop/0",10
+250978815738022,"sched_waking","rcuop/1",21
+250978860203182,"sched_waking","kworker/u16:18",17473
+250978880151205,"sched_waking","kworker/2:0",17438
+250978880432143,"sched_waking","kworker/u16:18",17473
+250978884120216,"sched_waking","traced",17771
+250978885260685,"sched_waking","rcuop/0",10
+250978885784018,"sched_waking","rcu_preempt",7
+250978885786674,"sched_waking","traced_probes",17772
diff --git a/test/trace_processor/sched_waking_raw.sql b/test/trace_processor/sched_waking_raw.sql
new file mode 100644
index 0000000..259e2fe
--- /dev/null
+++ b/test/trace_processor/sched_waking_raw.sql
@@ -0,0 +1 @@
+SELECT ts, name, cpu, key, int_value, string_value FROM raw JOIN args ON raw.arg_set_id == args.arg_set_id WHERE name == "sched_waking" ORDER BY cpu ASC, ts ASC;
diff --git a/test/trace_processor/sched_waking_raw_compact_sched.out b/test/trace_processor/sched_waking_raw_compact_sched.out
new file mode 100644
index 0000000..7067a25
--- /dev/null
+++ b/test/trace_processor/sched_waking_raw_compact_sched.out
@@ -0,0 +1,406 @@
+"ts","name","cpu","key","int_value","string_value"
+250978451591891,"sched_waking",0,"comm","[NULL]","rcu_sched"
+250978451591891,"sched_waking",0,"pid",8,"[NULL]"
+250978451591891,"sched_waking",0,"prio",120,"[NULL]"
+250978451591891,"sched_waking",0,"success",1,"[NULL]"
+250978451591891,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978451609131,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
+250978451609131,"sched_waking",0,"pid",17473,"[NULL]"
+250978451609131,"sched_waking",0,"prio",120,"[NULL]"
+250978451609131,"sched_waking",0,"success",1,"[NULL]"
+250978451609131,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978596565656,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978596565656,"sched_waking",0,"pid",17438,"[NULL]"
+250978596565656,"sched_waking",0,"prio",120,"[NULL]"
+250978596565656,"sched_waking",0,"success",1,"[NULL]"
+250978596565656,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978600598417,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978600598417,"sched_waking",0,"pid",17438,"[NULL]"
+250978600598417,"sched_waking",0,"prio",120,"[NULL]"
+250978600598417,"sched_waking",0,"success",1,"[NULL]"
+250978600598417,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978600639042,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978600639042,"sched_waking",0,"pid",6,"[NULL]"
+250978600639042,"sched_waking",0,"prio",120,"[NULL]"
+250978600639042,"sched_waking",0,"success",1,"[NULL]"
+250978600639042,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978604651907,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978604651907,"sched_waking",0,"pid",6,"[NULL]"
+250978604651907,"sched_waking",0,"prio",120,"[NULL]"
+250978604651907,"sched_waking",0,"success",1,"[NULL]"
+250978604651907,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978604739980,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978604739980,"sched_waking",0,"pid",17438,"[NULL]"
+250978604739980,"sched_waking",0,"prio",120,"[NULL]"
+250978604739980,"sched_waking",0,"success",1,"[NULL]"
+250978604739980,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978608903886,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978608903886,"sched_waking",0,"pid",17438,"[NULL]"
+250978608903886,"sched_waking",0,"prio",120,"[NULL]"
+250978608903886,"sched_waking",0,"success",1,"[NULL]"
+250978608903886,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978608942220,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978608942220,"sched_waking",0,"pid",6,"[NULL]"
+250978608942220,"sched_waking",0,"prio",120,"[NULL]"
+250978608942220,"sched_waking",0,"success",1,"[NULL]"
+250978608942220,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978612887272,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978612887272,"sched_waking",0,"pid",6,"[NULL]"
+250978612887272,"sched_waking",0,"prio",120,"[NULL]"
+250978612887272,"sched_waking",0,"success",1,"[NULL]"
+250978612887272,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978612971283,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978612971283,"sched_waking",0,"pid",17438,"[NULL]"
+250978612971283,"sched_waking",0,"prio",120,"[NULL]"
+250978612971283,"sched_waking",0,"success",1,"[NULL]"
+250978612971283,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978616876595,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978616876595,"sched_waking",0,"pid",17438,"[NULL]"
+250978616876595,"sched_waking",0,"prio",120,"[NULL]"
+250978616876595,"sched_waking",0,"success",1,"[NULL]"
+250978616876595,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978616921700,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978616921700,"sched_waking",0,"pid",6,"[NULL]"
+250978616921700,"sched_waking",0,"prio",120,"[NULL]"
+250978616921700,"sched_waking",0,"success",1,"[NULL]"
+250978616921700,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978617016804,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
+250978617016804,"sched_waking",0,"pid",17473,"[NULL]"
+250978617016804,"sched_waking",0,"prio",120,"[NULL]"
+250978617016804,"sched_waking",0,"success",1,"[NULL]"
+250978617016804,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978620859669,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978620859669,"sched_waking",0,"pid",6,"[NULL]"
+250978620859669,"sched_waking",0,"prio",120,"[NULL]"
+250978620859669,"sched_waking",0,"success",1,"[NULL]"
+250978620859669,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978620939148,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978620939148,"sched_waking",0,"pid",17438,"[NULL]"
+250978620939148,"sched_waking",0,"prio",120,"[NULL]"
+250978620939148,"sched_waking",0,"success",1,"[NULL]"
+250978620939148,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978624836805,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978624836805,"sched_waking",0,"pid",17438,"[NULL]"
+250978624836805,"sched_waking",0,"prio",120,"[NULL]"
+250978624836805,"sched_waking",0,"success",1,"[NULL]"
+250978624836805,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978624875398,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978624875398,"sched_waking",0,"pid",6,"[NULL]"
+250978624875398,"sched_waking",0,"prio",120,"[NULL]"
+250978624875398,"sched_waking",0,"success",1,"[NULL]"
+250978624875398,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978624978003,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
+250978624978003,"sched_waking",0,"pid",17473,"[NULL]"
+250978624978003,"sched_waking",0,"prio",120,"[NULL]"
+250978624978003,"sched_waking",0,"success",1,"[NULL]"
+250978624978003,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978441664547,"sched_waking",1,"comm","[NULL]","ksoftirqd/1"
+250978441664547,"sched_waking",1,"pid",18,"[NULL]"
+250978441664547,"sched_waking",1,"prio",120,"[NULL]"
+250978441664547,"sched_waking",1,"success",1,"[NULL]"
+250978441664547,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978441702515,"sched_waking",1,"comm","[NULL]","kworker/u16:18"
+250978441702515,"sched_waking",1,"pid",17473,"[NULL]"
+250978441702515,"sched_waking",1,"prio",120,"[NULL]"
+250978441702515,"sched_waking",1,"success",1,"[NULL]"
+250978441702515,"sched_waking",1,"target_cpu",2,"[NULL]"
+250978805144375,"sched_waking",1,"comm","[NULL]","traced_probes"
+250978805144375,"sched_waking",1,"pid",17772,"[NULL]"
+250978805144375,"sched_waking",1,"prio",120,"[NULL]"
+250978805144375,"sched_waking",1,"success",1,"[NULL]"
+250978805144375,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978808058698,"sched_waking",1,"comm","[NULL]","rcuop/0"
+250978808058698,"sched_waking",1,"pid",10,"[NULL]"
+250978808058698,"sched_waking",1,"prio",120,"[NULL]"
+250978808058698,"sched_waking",1,"success",1,"[NULL]"
+250978808058698,"sched_waking",1,"target_cpu",2,"[NULL]"
+250978884120216,"sched_waking",1,"comm","[NULL]","traced"
+250978884120216,"sched_waking",1,"pid",17771,"[NULL]"
+250978884120216,"sched_waking",1,"prio",120,"[NULL]"
+250978884120216,"sched_waking",1,"success",1,"[NULL]"
+250978884120216,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978885260685,"sched_waking",1,"comm","[NULL]","rcuop/0"
+250978885260685,"sched_waking",1,"pid",10,"[NULL]"
+250978885260685,"sched_waking",1,"prio",120,"[NULL]"
+250978885260685,"sched_waking",1,"success",1,"[NULL]"
+250978885260685,"sched_waking",1,"target_cpu",2,"[NULL]"
+250978885786674,"sched_waking",1,"comm","[NULL]","traced_probes"
+250978885786674,"sched_waking",1,"pid",17772,"[NULL]"
+250978885786674,"sched_waking",1,"prio",120,"[NULL]"
+250978885786674,"sched_waking",1,"success",1,"[NULL]"
+250978885786674,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978444958245,"sched_waking",2,"comm","[NULL]","rcuop/0"
+250978444958245,"sched_waking",2,"pid",10,"[NULL]"
+250978444958245,"sched_waking",2,"prio",120,"[NULL]"
+250978444958245,"sched_waking",2,"success",1,"[NULL]"
+250978444958245,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978444974443,"sched_waking",2,"comm","[NULL]","rcuop/1"
+250978444974443,"sched_waking",2,"pid",21,"[NULL]"
+250978444974443,"sched_waking",2,"prio",120,"[NULL]"
+250978444974443,"sched_waking",2,"success",1,"[NULL]"
+250978444974443,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978444984234,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978444984234,"sched_waking",2,"pid",7,"[NULL]"
+250978444984234,"sched_waking",2,"prio",120,"[NULL]"
+250978444984234,"sched_waking",2,"success",1,"[NULL]"
+250978444984234,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978451590173,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978451590173,"sched_waking",2,"pid",7,"[NULL]"
+250978451590173,"sched_waking",2,"prio",120,"[NULL]"
+250978451590173,"sched_waking",2,"success",1,"[NULL]"
+250978451590173,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978451646579,"sched_waking",2,"comm","[NULL]","rcuos/0"
+250978451646579,"sched_waking",2,"pid",11,"[NULL]"
+250978451646579,"sched_waking",2,"prio",120,"[NULL]"
+250978451646579,"sched_waking",2,"success",1,"[NULL]"
+250978451646579,"sched_waking",2,"target_cpu",4,"[NULL]"
+250978451716891,"sched_waking",2,"comm","[NULL]","rcuos/1"
+250978451716891,"sched_waking",2,"pid",22,"[NULL]"
+250978451716891,"sched_waking",2,"prio",120,"[NULL]"
+250978451716891,"sched_waking",2,"success",1,"[NULL]"
+250978451716891,"sched_waking",2,"target_cpu",1,"[NULL]"
+250978452047048,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978452047048,"sched_waking",2,"pid",17473,"[NULL]"
+250978452047048,"sched_waking",2,"prio",120,"[NULL]"
+250978452047048,"sched_waking",2,"success",1,"[NULL]"
+250978452047048,"sched_waking",2,"target_cpu",0,"[NULL]"
+250978458337725,"sched_waking",2,"comm","[NULL]","sugov:0"
+250978458337725,"sched_waking",2,"pid",625,"[NULL]"
+250978458337725,"sched_waking",2,"prio",49,"[NULL]"
+250978458337725,"sched_waking",2,"success",1,"[NULL]"
+250978458337725,"sched_waking",2,"target_cpu",3,"[NULL]"
+250978458362100,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978458362100,"sched_waking",2,"pid",7,"[NULL]"
+250978458362100,"sched_waking",2,"prio",120,"[NULL]"
+250978458362100,"sched_waking",2,"success",1,"[NULL]"
+250978458362100,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978458398455,"sched_waking",2,"comm","[NULL]","rcuop/0"
+250978458398455,"sched_waking",2,"pid",10,"[NULL]"
+250978458398455,"sched_waking",2,"prio",120,"[NULL]"
+250978458398455,"sched_waking",2,"success",1,"[NULL]"
+250978458398455,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978458419496,"sched_waking",2,"comm","[NULL]","rcuop/1"
+250978458419496,"sched_waking",2,"pid",21,"[NULL]"
+250978458419496,"sched_waking",2,"prio",120,"[NULL]"
+250978458419496,"sched_waking",2,"success",1,"[NULL]"
+250978458419496,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978459118038,"sched_waking",2,"comm","[NULL]","sugov:0"
+250978459118038,"sched_waking",2,"pid",625,"[NULL]"
+250978459118038,"sched_waking",2,"prio",49,"[NULL]"
+250978459118038,"sched_waking",2,"success",1,"[NULL]"
+250978459118038,"sched_waking",2,"target_cpu",3,"[NULL]"
+250978593466697,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978593466697,"sched_waking",2,"pid",17438,"[NULL]"
+250978593466697,"sched_waking",2,"prio",120,"[NULL]"
+250978593466697,"sched_waking",2,"success",1,"[NULL]"
+250978593466697,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978593734510,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978593734510,"sched_waking",2,"pid",17473,"[NULL]"
+250978593734510,"sched_waking",2,"prio",120,"[NULL]"
+250978593734510,"sched_waking",2,"success",1,"[NULL]"
+250978593734510,"sched_waking",2,"target_cpu",3,"[NULL]"
+250978595521125,"sched_waking",2,"comm","[NULL]","kworker/2:3"
+250978595521125,"sched_waking",2,"pid",17754,"[NULL]"
+250978595521125,"sched_waking",2,"prio",120,"[NULL]"
+250978595521125,"sched_waking",2,"success",1,"[NULL]"
+250978595521125,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978595696645,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978595696645,"sched_waking",2,"pid",17438,"[NULL]"
+250978595696645,"sched_waking",2,"prio",120,"[NULL]"
+250978595696645,"sched_waking",2,"success",1,"[NULL]"
+250978595696645,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978596068468,"sched_waking",2,"comm","[NULL]","kworker/2:3"
+250978596068468,"sched_waking",2,"pid",17754,"[NULL]"
+250978596068468,"sched_waking",2,"prio",120,"[NULL]"
+250978596068468,"sched_waking",2,"success",1,"[NULL]"
+250978596068468,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978605185448,"sched_waking",2,"comm","[NULL]","ksoftirqd/2"
+250978605185448,"sched_waking",2,"pid",26,"[NULL]"
+250978605185448,"sched_waking",2,"prio",120,"[NULL]"
+250978605185448,"sched_waking",2,"success",1,"[NULL]"
+250978605185448,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978605260240,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978605260240,"sched_waking",2,"pid",17473,"[NULL]"
+250978605260240,"sched_waking",2,"prio",120,"[NULL]"
+250978605260240,"sched_waking",2,"success",1,"[NULL]"
+250978605260240,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978628278107,"sched_waking",2,"comm","[NULL]","logd.klogd"
+250978628278107,"sched_waking",2,"pid",651,"[NULL]"
+250978628278107,"sched_waking",2,"prio",130,"[NULL]"
+250978628278107,"sched_waking",2,"success",1,"[NULL]"
+250978628278107,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978629289722,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978629289722,"sched_waking",2,"pid",29,"[NULL]"
+250978629289722,"sched_waking",2,"prio",120,"[NULL]"
+250978629289722,"sched_waking",2,"success",1,"[NULL]"
+250978629289722,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978629405763,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978629405763,"sched_waking",2,"pid",7,"[NULL]"
+250978629405763,"sched_waking",2,"prio",120,"[NULL]"
+250978629405763,"sched_waking",2,"success",1,"[NULL]"
+250978629405763,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978635024462,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978635024462,"sched_waking",2,"pid",7,"[NULL]"
+250978635024462,"sched_waking",2,"prio",120,"[NULL]"
+250978635024462,"sched_waking",2,"success",1,"[NULL]"
+250978635024462,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978635060191,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978635060191,"sched_waking",2,"pid",17473,"[NULL]"
+250978635060191,"sched_waking",2,"prio",120,"[NULL]"
+250978635060191,"sched_waking",2,"success",1,"[NULL]"
+250978635060191,"sched_waking",2,"target_cpu",0,"[NULL]"
+250978635219514,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978635219514,"sched_waking",2,"pid",29,"[NULL]"
+250978635219514,"sched_waking",2,"prio",120,"[NULL]"
+250978635219514,"sched_waking",2,"success",1,"[NULL]"
+250978635219514,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978683441081,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978683441081,"sched_waking",2,"pid",17438,"[NULL]"
+250978683441081,"sched_waking",2,"prio",120,"[NULL]"
+250978683441081,"sched_waking",2,"success",1,"[NULL]"
+250978683441081,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978683732331,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978683732331,"sched_waking",2,"pid",17473,"[NULL]"
+250978683732331,"sched_waking",2,"prio",120,"[NULL]"
+250978683732331,"sched_waking",2,"success",1,"[NULL]"
+250978683732331,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978685694259,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978685694259,"sched_waking",2,"pid",17473,"[NULL]"
+250978685694259,"sched_waking",2,"prio",120,"[NULL]"
+250978685694259,"sched_waking",2,"success",1,"[NULL]"
+250978685694259,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978740127441,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978740127441,"sched_waking",2,"pid",17438,"[NULL]"
+250978740127441,"sched_waking",2,"prio",120,"[NULL]"
+250978740127441,"sched_waking",2,"success",1,"[NULL]"
+250978740127441,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978740425410,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978740425410,"sched_waking",2,"pid",17473,"[NULL]"
+250978740425410,"sched_waking",2,"prio",120,"[NULL]"
+250978740425410,"sched_waking",2,"success",1,"[NULL]"
+250978740425410,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978743210358,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978743210358,"sched_waking",2,"pid",17473,"[NULL]"
+250978743210358,"sched_waking",2,"prio",120,"[NULL]"
+250978743210358,"sched_waking",2,"success",1,"[NULL]"
+250978743210358,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978744945567,"sched_waking",2,"comm","[NULL]","logd.klogd"
+250978744945567,"sched_waking",2,"pid",651,"[NULL]"
+250978744945567,"sched_waking",2,"prio",130,"[NULL]"
+250978744945567,"sched_waking",2,"success",1,"[NULL]"
+250978744945567,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978746028431,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978746028431,"sched_waking",2,"pid",29,"[NULL]"
+250978746028431,"sched_waking",2,"prio",120,"[NULL]"
+250978746028431,"sched_waking",2,"success",1,"[NULL]"
+250978746028431,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978746173171,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978746173171,"sched_waking",2,"pid",7,"[NULL]"
+250978746173171,"sched_waking",2,"prio",120,"[NULL]"
+250978746173171,"sched_waking",2,"success",1,"[NULL]"
+250978746173171,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751622494,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978751622494,"sched_waking",2,"pid",7,"[NULL]"
+250978751622494,"sched_waking",2,"prio",120,"[NULL]"
+250978751622494,"sched_waking",2,"success",1,"[NULL]"
+250978751622494,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751661348,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978751661348,"sched_waking",2,"pid",17473,"[NULL]"
+250978751661348,"sched_waking",2,"prio",120,"[NULL]"
+250978751661348,"sched_waking",2,"success",1,"[NULL]"
+250978751661348,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751792859,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978751792859,"sched_waking",2,"pid",29,"[NULL]"
+250978751792859,"sched_waking",2,"prio",120,"[NULL]"
+250978751792859,"sched_waking",2,"success",1,"[NULL]"
+250978751792859,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751971817,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978751971817,"sched_waking",2,"pid",7,"[NULL]"
+250978751971817,"sched_waking",2,"prio",120,"[NULL]"
+250978751971817,"sched_waking",2,"success",1,"[NULL]"
+250978751971817,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978758784578,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978758784578,"sched_waking",2,"pid",7,"[NULL]"
+250978758784578,"sched_waking",2,"prio",120,"[NULL]"
+250978758784578,"sched_waking",2,"success",1,"[NULL]"
+250978758784578,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978758873745,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978758873745,"sched_waking",2,"pid",29,"[NULL]"
+250978758873745,"sched_waking",2,"prio",120,"[NULL]"
+250978758873745,"sched_waking",2,"success",1,"[NULL]"
+250978758873745,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978808519271,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978808519271,"sched_waking",2,"pid",7,"[NULL]"
+250978808519271,"sched_waking",2,"prio",120,"[NULL]"
+250978808519271,"sched_waking",2,"success",1,"[NULL]"
+250978808519271,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815488490,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978815488490,"sched_waking",2,"pid",7,"[NULL]"
+250978815488490,"sched_waking",2,"prio",120,"[NULL]"
+250978815488490,"sched_waking",2,"success",1,"[NULL]"
+250978815488490,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815535678,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978815535678,"sched_waking",2,"pid",17473,"[NULL]"
+250978815535678,"sched_waking",2,"prio",120,"[NULL]"
+250978815535678,"sched_waking",2,"success",1,"[NULL]"
+250978815535678,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815675782,"sched_waking",2,"comm","[NULL]","rcuop/0"
+250978815675782,"sched_waking",2,"pid",10,"[NULL]"
+250978815675782,"sched_waking",2,"prio",120,"[NULL]"
+250978815675782,"sched_waking",2,"success",1,"[NULL]"
+250978815675782,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815738022,"sched_waking",2,"comm","[NULL]","rcuop/1"
+250978815738022,"sched_waking",2,"pid",21,"[NULL]"
+250978815738022,"sched_waking",2,"prio",120,"[NULL]"
+250978815738022,"sched_waking",2,"success",1,"[NULL]"
+250978815738022,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978860203182,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978860203182,"sched_waking",2,"pid",17473,"[NULL]"
+250978860203182,"sched_waking",2,"prio",120,"[NULL]"
+250978860203182,"sched_waking",2,"success",1,"[NULL]"
+250978860203182,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978880151205,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978880151205,"sched_waking",2,"pid",17438,"[NULL]"
+250978880151205,"sched_waking",2,"prio",120,"[NULL]"
+250978880151205,"sched_waking",2,"success",1,"[NULL]"
+250978880151205,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978880432143,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978880432143,"sched_waking",2,"pid",17473,"[NULL]"
+250978880432143,"sched_waking",2,"prio",120,"[NULL]"
+250978880432143,"sched_waking",2,"success",1,"[NULL]"
+250978880432143,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978885784018,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978885784018,"sched_waking",2,"pid",7,"[NULL]"
+250978885784018,"sched_waking",2,"prio",120,"[NULL]"
+250978885784018,"sched_waking",2,"success",1,"[NULL]"
+250978885784018,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978465172309,"sched_waking",3,"comm","[NULL]","ksoftirqd/3"
+250978465172309,"sched_waking",3,"pid",34,"[NULL]"
+250978465172309,"sched_waking",3,"prio",120,"[NULL]"
+250978465172309,"sched_waking",3,"success",1,"[NULL]"
+250978465172309,"sched_waking",3,"target_cpu",3,"[NULL]"
+250978465266789,"sched_waking",3,"comm","[NULL]","kworker/u16:18"
+250978465266789,"sched_waking",3,"pid",17473,"[NULL]"
+250978465266789,"sched_waking",3,"prio",120,"[NULL]"
+250978465266789,"sched_waking",3,"success",1,"[NULL]"
+250978465266789,"sched_waking",3,"target_cpu",0,"[NULL]"
+250978439491994,"sched_waking",4,"comm","[NULL]","rcu_sched"
+250978439491994,"sched_waking",4,"pid",8,"[NULL]"
+250978439491994,"sched_waking",4,"prio",120,"[NULL]"
+250978439491994,"sched_waking",4,"success",1,"[NULL]"
+250978439491994,"sched_waking",4,"target_cpu",1,"[NULL]"
+250978445783141,"sched_waking",5,"comm","[NULL]","rcu_sched"
+250978445783141,"sched_waking",5,"pid",8,"[NULL]"
+250978445783141,"sched_waking",5,"prio",120,"[NULL]"
+250978445783141,"sched_waking",5,"success",1,"[NULL]"
+250978445783141,"sched_waking",5,"target_cpu",5,"[NULL]"
+250978806359896,"sched_waking",5,"comm","[NULL]","kworker/5:1"
+250978806359896,"sched_waking",5,"pid",17667,"[NULL]"
+250978806359896,"sched_waking",5,"prio",120,"[NULL]"
+250978806359896,"sched_waking",5,"success",1,"[NULL]"
+250978806359896,"sched_waking",5,"target_cpu",5,"[NULL]"
+250978806428646,"sched_waking",5,"comm","[NULL]","kworker/u16:18"
+250978806428646,"sched_waking",5,"pid",17473,"[NULL]"
+250978806428646,"sched_waking",5,"prio",120,"[NULL]"
+250978806428646,"sched_waking",5,"success",1,"[NULL]"
+250978806428646,"sched_waking",5,"target_cpu",2,"[NULL]"
diff --git a/tools/compact_reencode/main.cc b/tools/compact_reencode/main.cc
index 982be0e..ae4a2c4 100644
--- a/tools/compact_reencode/main.cc
+++ b/tools/compact_reencode/main.cc
@@ -30,8 +30,14 @@
 
 // Re-encodes the given trace, converting sched events to their compact
 // representation.
-// Note: doesn't do bundle splitting/merging, the original trace must already
-// have multi-page bundles for the re-encoding to be realistic.
+//
+// Notes:
+// * doesn't do bundle splitting/merging, the original trace must already
+//   have multi-page bundles for the re-encoding to be realistic.
+// * when importing the resulting trace into trace_processor, a few leading
+//   switch/wakeup events can be skipped (since there's not enough info to
+//   reconstruct the full events at that point), and this might change the
+//   trace_bounds.
 
 namespace perfetto {
 namespace compact_reencode {
@@ -91,11 +97,20 @@
     return static_cast<uint32_t>(new_idx);
   };
 
+  // sched_waking pieces
+  protozero::PackedVarInt waking_timestamp;
+  protozero::PackedVarInt waking_pid;
+  protozero::PackedVarInt waking_target_cpu;
+  protozero::PackedVarInt waking_prio;
+  protozero::PackedVarInt waking_comm_index;
+
+  uint64_t last_waking_timestamp = 0;
+
   for (auto event_it = bundle.event(); event_it; ++event_it) {
     protos::pbzero::FtraceEvent::Decoder event(*event_it);
-    if (!event.has_sched_switch()) {
+    if (!event.has_sched_switch() && !event.has_sched_waking()) {
       CopyField(bundle_out, event_it.field());
-    } else {
+    } else if (event.has_sched_switch()) {
       switch_timestamp.Append(event.timestamp() - last_switch_timestamp);
       last_switch_timestamp = event.timestamp();
 
@@ -108,8 +123,22 @@
       switch_next_pid.Append(sswitch.next_pid());
       switch_next_prio.Append(sswitch.next_prio());
       switch_prev_state.Append(sswitch.prev_state());
+    } else {
+      waking_timestamp.Append(event.timestamp() - last_waking_timestamp);
+      last_waking_timestamp = event.timestamp();
+
+      protos::pbzero::SchedWakingFtraceEvent::Decoder swaking(
+          event.sched_waking());
+
+      auto iid = intern(swaking.comm().ToStdString());
+      waking_comm_index.Append(iid);
+
+      waking_pid.Append(swaking.pid());
+      waking_target_cpu.Append(swaking.target_cpu());
+      waking_prio.Append(swaking.prio());
     }
   }
+
   auto* compact_sched = bundle_out->set_compact_sched();
 
   for (const auto& s : string_table)
@@ -120,6 +149,12 @@
   compact_sched->set_switch_next_pid(switch_next_pid);
   compact_sched->set_switch_next_prio(switch_next_prio);
   compact_sched->set_switch_prev_state(switch_prev_state);
+
+  compact_sched->set_waking_timestamp(waking_timestamp);
+  compact_sched->set_waking_pid(waking_pid);
+  compact_sched->set_waking_target_cpu(waking_target_cpu);
+  compact_sched->set_waking_prio(waking_prio);
+  compact_sched->set_waking_comm_index(waking_comm_index);
 }
 
 std::string ReEncode(const std::string& raw) {
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 1f454bb..a8cd83e 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -146,8 +146,8 @@
     # Example traces for regression tests.
     (
         'buildtools/test_data.zip',
-        'https://storage.googleapis.com/perfetto/test-data-20191025-102200.zip',
-        '25a61211f82cd73c6ce5b67da9d73f046de7b3f9',
+        'https://storage.googleapis.com/perfetto/test-data-20191107-164334.zip',
+        '499f11fbc2b04ef7742662a26b85ef03141e24bd',
         'all',
     ),