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);