blob: 201f8811d8aa34cfce73aea711b648a3e856b3b9 [file]
/*
* Copyright (C) 2022 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/util/proto_profiler.h"
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/protozero/field.h"
#include "perfetto/protozero/proto_decoder.h"
#include "perfetto/protozero/proto_utils.h"
#include "src/trace_processor/util/descriptors.h"
#include "protos/perfetto/common/descriptor.pbzero.h"
namespace perfetto::trace_processor::util {
namespace {
using ::perfetto::protos::pbzero::FieldDescriptorProto;
using ::protozero::proto_utils::ProtoWireType;
// Takes a type full name, and returns only the final part.
// For example, .perfetto.protos.TracePacket -> TracePacket
std::string GetFieldTypeName(const std::string& full_type_name) {
auto pos = full_type_name.rfind('.');
if (pos == std::string::npos) {
return full_type_name;
}
return full_type_name.substr(pos + 1);
}
std::string GetLeafTypeName(uint32_t type_id) {
std::string raw_name = FieldDescriptorProto::Type_Name(
static_cast<FieldDescriptorProto::Type>(type_id));
return base::StripPrefix(base::ToLower(raw_name), "type_");
}
} // namespace
SizeProfileComputer::Field::Field(uint32_t field_idx_in,
std::string field_name,
std::string type_name)
: field_idx(field_idx_in),
field_name_(field_idx == 0
? ""
: (field_name.empty() ? "#unknown" : "#" + field_name)),
type_name_(std::move(type_name)) {}
SizeProfileComputer::SizeProfileComputer(DescriptorPool* pool,
const std::string& message_type)
: pool_(pool) {
auto message_idx = pool_->FindDescriptorIdx(message_type);
if (!message_idx) {
PERFETTO_ELOG("Cannot find descriptor for type %s", message_type.c_str());
return;
}
root_message_idx_ = *message_idx;
}
void SizeProfileComputer::Reset(const uint8_t* ptr, size_t size) {
state_stack_.clear();
field_path_.fields.clear();
protozero::ProtoDecoder decoder(ptr, size);
const ProtoDescriptor* descriptor = &pool_->descriptors()[root_message_idx_];
state_stack_.push_back(State{descriptor, decoder, size, 0});
field_path_.fields.emplace_back(0, /* field_name= */ "",
GetFieldTypeName(descriptor->full_name()));
}
std::optional<size_t> SizeProfileComputer::GetNext() {
std::optional<size_t> result;
if (state_stack_.empty())
return result;
if (field_path_.fields.size() > state_stack_.size()) {
// The leaf path needs to be popped to continue iterating on the current
// proto.
field_path_.fields.pop_back();
PERFETTO_DCHECK(field_path_.fields.size() == state_stack_.size());
}
State& state = state_stack_.back();
for (;;) {
if (state.decoder.bytes_left() == 0) {
break;
}
protozero::Field field = state.decoder.ReadField();
if (!field.valid()) {
PERFETTO_ELOG("Field not valid (can mean field id >1000)");
break;
}
ProtoWireType type = field.type();
size_t field_size = GetFieldSize(field);
state.overhead -= field_size;
const FieldDescriptor* field_descriptor =
state.descriptor->FindFieldByTag(field.id());
if (!field_descriptor) {
state.unknown += field_size;
continue;
}
bool is_message_type =
field_descriptor->type() == FieldDescriptorProto::TYPE_MESSAGE;
if (type == ProtoWireType::kLengthDelimited && is_message_type) {
auto message_idx =
pool_->FindDescriptorIdx(field_descriptor->resolved_type_name());
if (!message_idx) {
PERFETTO_ELOG("Cannot find descriptor for type %s",
field_descriptor->resolved_type_name().c_str());
return result;
}
protozero::ProtoDecoder decoder(field.data(), field.size());
const ProtoDescriptor* descriptor = &pool_->descriptors()[*message_idx];
field_path_.fields.emplace_back(
field.id(), field_descriptor->name(),
GetFieldTypeName(descriptor->full_name()));
state_stack_.push_back(State{descriptor, decoder, field.size(), 0U});
return GetNext();
}
field_path_.fields.emplace_back(field.id(), field_descriptor->name(),
GetLeafTypeName(field_descriptor->type()));
result.emplace(field_size);
return result;
}
if (state.unknown) {
field_path_.fields.emplace_back(uint32_t(-1), /* field_name= */ "",
/* type_name= */ "");
result.emplace(state.unknown);
state.unknown = 0;
return result;
}
result.emplace(state.overhead);
state_stack_.pop_back();
return result;
}
size_t SizeProfileComputer::GetFieldSize(const protozero::Field& f) {
uint8_t buf[10];
switch (f.type()) {
case protozero::proto_utils::ProtoWireType::kVarInt:
return static_cast<size_t>(
protozero::proto_utils::WriteVarInt(f.as_uint64(), buf) - buf);
case protozero::proto_utils::ProtoWireType::kLengthDelimited:
return f.size();
case protozero::proto_utils::ProtoWireType::kFixed32:
return 4;
case protozero::proto_utils::ProtoWireType::kFixed64:
return 8;
}
PERFETTO_FATAL("unexpected field type"); // for gcc
}
} // namespace perfetto::trace_processor::util