| /* |
| * 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 <utility> |
| |
| #include "perfetto/base/logging.h" |
| #include "perfetto/ext/base/string_utils.h" |
| #include "perfetto/protozero/packed_repeated_fields.h" |
| #include "perfetto/protozero/proto_decoder.h" |
| #include "perfetto/protozero/proto_utils.h" |
| |
| namespace perfetto { |
| namespace trace_processor { |
| namespace 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, |
| const FieldDescriptor* field_descriptor_in, |
| uint32_t type_in, |
| const ProtoDescriptor* proto_descriptor_in) |
| : field_idx(field_idx_in), |
| type(type_in), |
| field_descriptor(field_descriptor_in), |
| proto_descriptor(proto_descriptor_in) {} |
| |
| std::string SizeProfileComputer::Field::field_name() const { |
| if (field_descriptor) |
| return "#" + field_descriptor->name(); |
| return "#unknown"; |
| } |
| |
| std::string SizeProfileComputer::Field::type_name() const { |
| if (proto_descriptor) |
| return GetFieldTypeName(proto_descriptor->full_name()); |
| return GetLeafTypeName(type); |
| } |
| |
| 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_.clear(); |
| protozero::ProtoDecoder decoder(ptr, size); |
| const ProtoDescriptor* descriptor = &pool_->descriptors()[root_message_idx_]; |
| state_stack_.push_back(State{descriptor, std::move(decoder), size, 0}); |
| field_path_.emplace_back(0, nullptr, root_message_idx_, descriptor); |
| } |
| |
| base::Optional<size_t> SizeProfileComputer::GetNext() { |
| base::Optional<size_t> result; |
| if (state_stack_.empty()) |
| return result; |
| |
| if (field_path_.size() > state_stack_.size()) { |
| // The leaf path needs to be popped to continue iterating on the current |
| // proto. |
| field_path_.pop_back(); |
| PERFETTO_DCHECK(field_path_.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_.emplace_back(field.id(), field_descriptor, *message_idx, |
| descriptor); |
| state_stack_.push_back( |
| State{descriptor, std::move(decoder), field.size(), 0U}); |
| return GetNext(); |
| } else { |
| field_path_.emplace_back(field.id(), field_descriptor, |
| field_descriptor->type(), nullptr); |
| result.emplace(field_size); |
| return result; |
| } |
| } |
| if (state.unknown) { |
| field_path_.emplace_back(uint32_t(-1), nullptr, 0U, nullptr); |
| 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 util |
| } // namespace trace_processor |
| } // namespace perfetto |