Merge "Add kernel wakelocks data source support to trace processor, stdlib and UI." into main
diff --git a/Android.bp b/Android.bp
index 6b7e76b..d9e8854 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12854,6 +12854,7 @@
     srcs: [
         "src/trace_processor/importers/proto/additional_modules.cc",
         "src/trace_processor/importers/proto/android_camera_event_module.cc",
+        "src/trace_processor/importers/proto/android_kernel_wakelocks_module.cc",
         "src/trace_processor/importers/proto/android_probes_module.cc",
         "src/trace_processor/importers/proto/android_probes_parser.cc",
         "src/trace_processor/importers/proto/android_probes_tracker.cc",
@@ -13660,6 +13661,7 @@
         "src/trace_processor/perfetto_sql/stdlib/android/io.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/job_scheduler.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/job_scheduler_states.sql",
+        "src/trace_processor/perfetto_sql/stdlib/android/kernel_wakelocks.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/memory/dmabuf.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/memory/heap_graph/class_summary_tree.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/memory/heap_graph/class_tree.sql",
diff --git a/BUILD b/BUILD
index 51f7ee6..5c19464 100644
--- a/BUILD
+++ b/BUILD
@@ -2254,6 +2254,8 @@
         "src/trace_processor/importers/proto/additional_modules.h",
         "src/trace_processor/importers/proto/android_camera_event_module.cc",
         "src/trace_processor/importers/proto/android_camera_event_module.h",
+        "src/trace_processor/importers/proto/android_kernel_wakelocks_module.cc",
+        "src/trace_processor/importers/proto/android_kernel_wakelocks_module.h",
         "src/trace_processor/importers/proto/android_probes_module.cc",
         "src/trace_processor/importers/proto/android_probes_module.h",
         "src/trace_processor/importers/proto/android_probes_parser.cc",
@@ -3109,6 +3111,7 @@
         "src/trace_processor/perfetto_sql/stdlib/android/io.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/job_scheduler.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/job_scheduler_states.sql",
+        "src/trace_processor/perfetto_sql/stdlib/android/kernel_wakelocks.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/network_packets.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql",
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index a5b9181..82cf565 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -115,6 +115,8 @@
     "additional_modules.h",
     "android_camera_event_module.cc",
     "android_camera_event_module.h",
+    "android_kernel_wakelocks_module.cc",
+    "android_kernel_wakelocks_module.h",
     "android_probes_module.cc",
     "android_probes_module.h",
     "android_probes_parser.cc",
diff --git a/src/trace_processor/importers/proto/additional_modules.cc b/src/trace_processor/importers/proto/additional_modules.cc
index 296de58..a900b1c 100644
--- a/src/trace_processor/importers/proto/additional_modules.cc
+++ b/src/trace_processor/importers/proto/additional_modules.cc
@@ -21,6 +21,7 @@
 #include "src/trace_processor/importers/etw/etw_module_impl.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module_impl.h"
 #include "src/trace_processor/importers/proto/android_camera_event_module.h"
+#include "src/trace_processor/importers/proto/android_kernel_wakelocks_module.h"
 #include "src/trace_processor/importers/proto/android_probes_module.h"
 #include "src/trace_processor/importers/proto/content_analyzer.h"
 #include "src/trace_processor/importers/proto/graphics_event_module.h"
@@ -44,6 +45,7 @@
   context->descriptor_pool_->AddFromFileDescriptorSet(kTraceDescriptor.data(),
                                                       kTraceDescriptor.size());
 
+  context->modules.emplace_back(new AndroidKernelWakelocksModule(context));
   context->modules.emplace_back(new AndroidProbesModule(context));
   context->modules.emplace_back(new NetworkTraceModule(context));
   context->modules.emplace_back(new GraphicsEventModule(context));
diff --git a/src/trace_processor/importers/proto/android_kernel_wakelocks_module.cc b/src/trace_processor/importers/proto/android_kernel_wakelocks_module.cc
new file mode 100644
index 0000000..36ba07e
--- /dev/null
+++ b/src/trace_processor/importers/proto/android_kernel_wakelocks_module.cc
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2025 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/android_kernel_wakelocks_module.h"
+
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/common/tracks.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+#include "protos/perfetto/trace/android/kernel_wakelock_data.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto::trace_processor {
+
+using perfetto::protos::pbzero::TracePacket;
+
+struct KernelWakelockMetadata {
+  std::string name;
+  protos::pbzero::KernelWakelockData_Wakelock_Type type;
+};
+
+struct KernelWakelockLastValue {
+  uint64_t value;
+  protos::pbzero::KernelWakelockData_Wakelock_Type type;
+};
+
+AndroidKernelWakelocksModule::AndroidKernelWakelocksModule(
+    TraceProcessorContext* context)
+    : context_(context),
+      kernel_name_id_(context->storage->InternString("kernel")),
+      native_name_id_(context->storage->InternString("native")),
+      unknown_name_id_(context->storage->InternString("unknown")) {
+  RegisterForField(TracePacket::kKernelWakelockDataFieldNumber, context);
+}
+
+AndroidKernelWakelocksModule::~AndroidKernelWakelocksModule() = default;
+
+void AndroidKernelWakelocksModule::OnIncrementalStateCleared(
+    uint32_t /* packet_sequence_id */) {
+  wakelocks_.Clear();
+  wakelock_last_values_.Clear();
+}
+
+void AndroidKernelWakelocksModule::ParseTracePacketData(
+    const TracePacket::Decoder& decoder,
+    int64_t ts,
+    const TracePacketData&,
+    uint32_t field_id) {
+  if (field_id != TracePacket::kKernelWakelockDataFieldNumber) {
+    return;
+  }
+
+  std::unordered_set<std::string> names_with_value_this_packet;
+
+  protos::pbzero::KernelWakelockData::Decoder evt(
+      decoder.kernel_wakelock_data());
+  for (auto it = evt.wakelock(); it; ++it) {
+    protos::pbzero::KernelWakelockData_Wakelock::Decoder wakelock(*it);
+    std::string name = wakelock.wakelock_name().ToStdString();
+    auto [info, inserted] =
+        wakelocks_.Insert(wakelock.wakelock_id(), KernelWakelockMetadata{});
+    if (!inserted) {
+      context_->storage->IncrementStats(stats::kernel_wakelock_reused_id);
+      continue;
+    }
+    info->name = name;
+    info->type = static_cast<protos::pbzero::KernelWakelockData_Wakelock_Type>(
+        wakelock.wakelock_type());
+  }
+
+  bool parse_error = false;
+  auto time_it = evt.time_held_millis(&parse_error);
+  for (auto it = evt.wakelock_id(&parse_error); it && time_it;
+       ++it, ++time_it) {
+    auto* data = wakelocks_.Find(*it);
+    if (!data) {
+      context_->storage->IncrementStats(stats::kernel_wakelock_unknown_id);
+      continue;
+    }
+
+    const auto& name = data->name;
+    names_with_value_this_packet.insert(name);
+
+    uint64_t delta = *time_it;
+    auto [last_value, inserted] =
+        wakelock_last_values_.Insert(name, KernelWakelockLastValue{});
+    last_value->value += delta;
+    last_value->type = data->type;
+    UpdateCounter(ts, name, data->type, last_value->value);
+  }
+
+  // Anything we knew about but didn't see in this packet must not have
+  // incremented.
+  for (auto it = wakelock_last_values_.GetIterator(); it; ++it) {
+    if (names_with_value_this_packet.count(it.key())) {
+      continue;
+    }
+    UpdateCounter(ts, it.key(), it.value().type, it.value().value);
+  }
+}
+
+void AndroidKernelWakelocksModule::UpdateCounter(
+    int64_t ts,
+    const std::string& name,
+    protos::pbzero::KernelWakelockData_Wakelock_Type type,
+    uint64_t value) {
+  static constexpr auto kBlueprint = tracks::CounterBlueprint(
+      "android_kernel_wakelock", tracks::StaticUnitBlueprint("ms"),
+      tracks::DimensionBlueprints(
+          tracks::StringDimensionBlueprint("wakelock_name"),
+          tracks::StringDimensionBlueprint("wakelock_type")),
+      tracks::DynamicNameBlueprint());
+  StringId type_id;
+  switch (type) {
+    case protos::pbzero::KernelWakelockData_Wakelock_Type::WAKELOCK_TYPE_KERNEL:
+      type_id = kernel_name_id_;
+      break;
+    case protos::pbzero::KernelWakelockData_Wakelock_Type::WAKELOCK_TYPE_NATIVE:
+      type_id = native_name_id_;
+      break;
+    case protos::pbzero::KernelWakelockData_Wakelock_Type::
+        WAKELOCK_TYPE_UNKNOWN:
+      type_id = unknown_name_id_;
+      break;
+  }
+
+  StringId name_id = context_->storage->InternString(name);
+  TrackId track = context_->track_tracker->InternTrack(
+      kBlueprint,
+      tracks::Dimensions(context_->storage->GetString(name_id),
+                         context_->storage->GetString(type_id)),
+      tracks::DynamicName(name_id));
+  context_->event_tracker->PushCounter(ts, 1e6 * double(value), track);
+}
+
+}  // namespace perfetto::trace_processor
diff --git a/src/trace_processor/importers/proto/android_kernel_wakelocks_module.h b/src/trace_processor/importers/proto/android_kernel_wakelocks_module.h
new file mode 100644
index 0000000..7f8bf73
--- /dev/null
+++ b/src/trace_processor/importers/proto/android_kernel_wakelocks_module.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2025 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_ANDROID_KERNEL_WAKELOCKS_MODULE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_KERNEL_WAKELOCKS_MODULE_H_
+
+#include "protos/perfetto/trace/android/kernel_wakelock_data.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+struct KernelWakelockMetadata;
+struct KernelWakelockLastValue;
+
+class AndroidKernelWakelocksModule : public ProtoImporterModule {
+ public:
+  explicit AndroidKernelWakelocksModule(TraceProcessorContext* context);
+
+  ~AndroidKernelWakelocksModule() override;
+
+  void OnIncrementalStateCleared(uint32_t /* packet_sequence_id */) override;
+
+  void ParseTracePacketData(const protos::pbzero::TracePacket::Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
+
+ private:
+  void UpdateCounter(int64_t ts,
+                     const std::string& name,
+                     protos::pbzero::KernelWakelockData_Wakelock_Type type,
+                     uint64_t value);
+
+  base::FlatHashMap<uint32_t, KernelWakelockMetadata> wakelocks_;
+  base::FlatHashMap<std::string, KernelWakelockLastValue> wakelock_last_values_;
+  TraceProcessorContext* context_;
+
+  const StringId kernel_name_id_;
+  const StringId native_name_id_;
+  const StringId unknown_name_id_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_KERNEL_WAKELOCKS_MODULE_H_
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
index 4f0208c..585cf51 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
@@ -43,6 +43,7 @@
     "io.sql",
     "job_scheduler.sql",
     "job_scheduler_states.sql",
+    "kernel_wakelocks.sql",
     "monitor_contention.sql",
     "network_packets.sql",
     "oom_adjuster.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/kernel_wakelocks.sql b/src/trace_processor/perfetto_sql/stdlib/android/kernel_wakelocks.sql
new file mode 100644
index 0000000..aa78dda
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/kernel_wakelocks.sql
@@ -0,0 +1,68 @@
+-- Copyright 2025 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+include perfetto module android.suspend;
+
+-- Table of kernel (or native) wakelocks with held duration.
+--
+-- Subtracts suspended time from each period to calculate the
+-- fraction of awake time for which the wakelock was held.
+CREATE PERFETTO TABLE android_kernel_wakelocks(
+  -- Timestamp.
+  ts TIMESTAMP,
+  -- Duration.
+  dur DURATION,
+  -- Kernel or native wakelock name.
+  name STRING,
+  -- 'kernel' or 'native'.
+  type STRING,
+  -- Time the wakelock was held.
+  held_dur DURATION,
+  -- Fraction of awake (not suspended) time the wakelock was held.
+  held_ratio DOUBLE) AS
+with raw as (
+  select
+    -- Move back by one period here since our ts represents wakelock counts up
+    -- to that point, but counters project forward from their ts.
+    lag(ts) over (partition by track_id order by ts) as ts,
+    ts - lag(ts) over (partition by track_id order by ts) as dur,
+    name,
+    extract_arg(dimension_arg_set_id, 'wakelock_type') as type,
+    value - lag(value) over (partition by track_id order by ts) as held_dur
+  from track t join counter c on t.id = c.track_id
+  where t.type = 'android_kernel_wakelock'
+),
+suspended as (
+  select
+    ts,
+    dur,
+    name,
+    type,
+    ifnull((select sum(dur) from android_suspend_state s
+        where power_state = 'suspended'
+          and s.ts > raw.ts - dur
+          and s.ts < raw.ts), 0) as suspended_dur,
+    held_dur
+  from raw
+  where dur is not null
+)
+select
+  ts,
+  dur,
+  name,
+  type,
+  cast_int!(held_dur) as held_dur,
+  min(held_dur / (dur - suspended_dur), 1.0) as held_ratio
+from suspended;
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 1639997..ea03e42 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -99,6 +99,10 @@
   F(interned_data_tokenizer_errors,       kSingle,  kInfo,     kAnalysis, ""), \
   F(invalid_clock_snapshots,              kSingle,  kError,    kAnalysis, ""), \
   F(invalid_cpu_times,                    kSingle,  kError,    kAnalysis, ""), \
+  F(kernel_wakelock_reused_id,            kSingle,  kError,    kAnalysis,      \
+       "Duplicated interning ID seen. Should never happen."),                  \
+  F(kernel_wakelock_unknown_id,           kSingle,  kError,    kAnalysis,      \
+       "Interning ID not found. Should never happen."),                        \
   F(meminfo_unknown_keys,                 kSingle,  kError,    kAnalysis, ""), \
   F(mismatched_sched_switch_tids,         kSingle,  kError,    kAnalysis, ""), \
   F(mm_unknown_type,                      kSingle,  kError,    kAnalysis, ""), \
diff --git a/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts b/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts
index c3efb68..d8a5d03 100644
--- a/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts
@@ -423,8 +423,8 @@
   from step2
   where severity != 'NONE'`;
 
-const KERNEL_WAKELOCKS = `
-  create or replace perfetto table kernel_wakelocks as
+const KERNEL_WAKELOCKS_STATSD = `
+  create or replace perfetto table kernel_wakelocks_statsd as
   with kernel_wakelock_args as (
     select
       arg_set_id,
@@ -486,9 +486,9 @@
     min(100.0 * wakelock_dur / (ts_end - ts - suspended_dur), 100) as value
   from step3`;
 
-const KERNEL_WAKELOCKS_SUMMARY = `
+const KERNEL_WAKELOCKS_STATSD_SUMMARY = `
   select wakelock_name, max(value) as max_value
-  from kernel_wakelocks
+  from kernel_wakelocks_statsd
   where wakelock_name not in ('PowerManager.SuspendLockout', 'PowerManagerService.Display')
   group by 1
   having max_value > 1
@@ -1573,24 +1573,49 @@
     }
   }
 
-  async addKernelWakelocks(ctx: Trace, features: Set<string>): Promise<void> {
+  async addKernelWakelocks(ctx: Trace): Promise<void> {
+    const e = ctx.engine;
+    await e.query(`INCLUDE PERFETTO MODULE android.kernel_wakelocks;`);
+    const result = await e.query(
+      `SELECT DISTINCT name, type FROM android_kernel_wakelocks`,
+    );
+    const it = result.iter({name: 'str', type: 'str'});
+    for (; it.valid(); it.next()) {
+      await this.addCounterTrack(
+        ctx,
+        it.name,
+        `SELECT ts, dur, held_ratio * 100 AS value
+         FROM android_kernel_wakelocks
+         WHERE name = "${it.name}"`,
+        'Kernel Wakelock Summary',
+        {yRangeSharingKey: 'kernel_wakelock', unit: '%'},
+      );
+    }
+  }
+
+  async addKernelWakelocksStatsd(
+    ctx: Trace,
+    features: Set<string>,
+  ): Promise<void> {
     if (!features.has('atom.kernel_wakelock')) {
       return;
     }
-    const groupName = 'Kernel Wakelock Summary';
+    const groupName = 'Kernel Wakelock Summary (statsd)';
 
     const e = ctx.engine;
     await e.query(`INCLUDE PERFETTO MODULE android.suspend;`);
-    await e.query(KERNEL_WAKELOCKS);
-    const result = await e.query(KERNEL_WAKELOCKS_SUMMARY);
+    await e.query(KERNEL_WAKELOCKS_STATSD);
+    const result = await e.query(KERNEL_WAKELOCKS_STATSD_SUMMARY);
     const it = result.iter({wakelock_name: 'str'});
     for (; it.valid(); it.next()) {
       await this.addCounterTrack(
         ctx,
-        it.wakelock_name,
-        `select ts, dur, value from kernel_wakelocks where wakelock_name = "${it.wakelock_name}"`,
+        `${it.wakelock_name} (statsd)`,
+        `select ts, dur, value
+         from kernel_wakelocks_statsd
+         where wakelock_name = "${it.wakelock_name}"`,
         groupName,
-        {yRangeSharingKey: 'kernel_wakelock', unit: '%'},
+        {yRangeSharingKey: 'kernel_wakelock_statsd', unit: '%'},
       );
     }
   }
@@ -1906,7 +1931,8 @@
     await this.addAtomCounters(ctx);
     await this.addAtomSlices(ctx);
     await this.addModemDetail(ctx, features);
-    await this.addKernelWakelocks(ctx, features);
+    await this.addKernelWakelocks(ctx);
+    await this.addKernelWakelocksStatsd(ctx, features);
     await this.addWakeups(ctx, features);
     await this.addDeviceState(ctx, features);
     await this.addHighCpu(ctx, features);