blob: 36ba07e5db5c612318f921909def9b349ec39768 [file]
/*
* 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