blob: 6520597a76f59d23db34aca6c01e582894f4e131 [file]
/*
* 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
*
* 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 "ProtoLogConfig.h"
#include <config/android/protolog_config.pzc.h>
#include <log/log.h>
#include <perfetto/public/abi/pb_decoder_abi.h>
#include <perfetto/public/protos/config/data_source_config.pzc.h>
#include "ProtoLogTypes.h"
namespace android {
namespace protolog {
ProtoLogLevel ParseProtoLogLevel(uint64_t logLevel) {
switch (logLevel) {
case perfetto_protos_PROTOLOG_LEVEL_UNDEFINED:
return ProtoLogLevel::UNDEFINED;
case perfetto_protos_PROTOLOG_LEVEL_VERBOSE:
return ProtoLogLevel::VERBOSE;
case perfetto_protos_PROTOLOG_LEVEL_DEBUG:
return ProtoLogLevel::DEBUG;
case perfetto_protos_PROTOLOG_LEVEL_INFO:
return ProtoLogLevel::INFO;
case perfetto_protos_PROTOLOG_LEVEL_WARN:
return ProtoLogLevel::WARN;
case perfetto_protos_PROTOLOG_LEVEL_ERROR:
return ProtoLogLevel::ERROR;
case perfetto_protos_PROTOLOG_LEVEL_WTF:
return ProtoLogLevel::WTF;
default:
return ProtoLogLevel::UNDEFINED;
}
}
bool DeserializeProtoLogGroup(struct PerfettoPbDecoder* decoder, std::string* out_name,
ProtoLogGroupConfig* out_group) {
struct PerfettoPbDecoderField field;
while ((field = PerfettoPbDecoderParseField(decoder)).status == PERFETTO_PB_DECODER_OK) {
switch (field.id) {
case perfetto_protos_ProtoLogGroup_group_name_field_number:
if (field.wire_type != PERFETTO_PB_WIRE_TYPE_DELIMITED) return false;
out_name->assign((const char*)field.value.delimited.start,
field.value.delimited.len);
break;
case perfetto_protos_ProtoLogGroup_log_from_field_number:
if (field.wire_type != PERFETTO_PB_WIRE_TYPE_VARINT) return false;
out_group->log_from = ParseProtoLogLevel(field.value.integer64);
break;
case perfetto_protos_ProtoLogGroup_collect_stacktrace_field_number:
if (field.wire_type != PERFETTO_PB_WIRE_TYPE_VARINT) return false;
out_group->collect_stacktrace = (field.value.integer64 != 0);
break;
}
}
return field.status == PERFETTO_PB_DECODER_DONE;
}
bool ParseProtoLogConfig(struct PerfettoPbDecoder* decoder, ProtoLogConfig* out_config) {
TracingMode tracingMode = TracingMode::DEFAULT;
ProtoLogLevel defaultLevel = ProtoLogLevel::UNDEFINED;
struct PerfettoPbDecoderField field;
while ((field = PerfettoPbDecoderParseField(decoder)).status == PERFETTO_PB_DECODER_OK) {
switch (field.id) {
case perfetto_protos_ProtoLogConfig_group_overrides_field_number: {
if (field.wire_type != PERFETTO_PB_WIRE_TYPE_DELIMITED) return false;
struct PerfettoPbDecoder group_overrides_decoder = {
field.value.delimited.start,
field.value.delimited.start + field.value.delimited.len,
};
std::string group_name;
ProtoLogGroupConfig group_cfg;
if (!DeserializeProtoLogGroup(&group_overrides_decoder, &group_name, &group_cfg))
return false;
if (!group_name.empty()) {
out_config->group_configs[group_name] = group_cfg;
}
break;
}
case perfetto_protos_ProtoLogConfig_tracing_mode_field_number:
if (field.wire_type != PERFETTO_PB_WIRE_TYPE_VARINT) return false;
tracingMode = (enum TracingMode)field.value.integer64;
out_config->tracing_mode = tracingMode;
break;
case perfetto_protos_ProtoLogConfig_default_log_from_level_field_number:
if (field.wire_type != PERFETTO_PB_WIRE_TYPE_VARINT) return false;
defaultLevel = ParseProtoLogLevel(field.value.integer64);
break;
}
if (tracingMode == TracingMode::ENABLE_ALL && defaultLevel == ProtoLogLevel::UNDEFINED) {
defaultLevel = ProtoLogLevel::VERBOSE;
}
out_config->default_log_from_level = defaultLevel;
for (auto& [group_name, group_cfg] : out_config->group_configs) {
if (group_cfg.log_from == ProtoLogLevel::UNDEFINED) {
group_cfg.log_from = defaultLevel;
}
}
}
return field.status == PERFETTO_PB_DECODER_DONE;
}
ProtoLogGroupConfig ProtoLogConfig::getConfigForGroup(const std::string_view group_name) const {
auto it = group_configs.find(group_name);
if (it != group_configs.end()) {
return it->second;
}
ProtoLogGroupConfig default_cfg;
if (tracing_mode == TracingMode::ENABLE_ALL) {
default_cfg.log_from = default_log_from_level;
}
return default_cfg;
}
bool ProtoLogConfig::parse(const void* ds_config, size_t ds_config_size) {
if (!ds_config || ds_config_size == 0) {
ALOGI("No config provided, using defaults.");
return true;
}
this->group_configs.clear();
struct PerfettoPbDecoder config_decoder = {
(const uint8_t*)ds_config,
(const uint8_t*)ds_config + ds_config_size,
};
struct PerfettoPbDecoderField field;
while ((field = PerfettoPbDecoderParseField(&config_decoder)).status ==
PERFETTO_PB_DECODER_OK) {
switch (field.id) {
case perfetto_protos_DataSourceConfig_protolog_config_field_number: {
struct PerfettoPbDecoder protolog_config_decoder = {
field.value.delimited.start,
field.value.delimited.start + field.value.delimited.len,
};
ParseProtoLogConfig(&protolog_config_decoder, this);
break;
}
default: {
ALOGD("Skipping unknown field in DataSourceConfig %d", field.id);
}
}
}
return field.status == PERFETTO_PB_DECODER_DONE;
}
} // namespace protolog
} // namespace android