xref: /aosp_15_r20/external/perfetto/src/trace_processor/util/proto_profiler.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/trace_processor/util/proto_profiler.h"
18 
19 #include <utility>
20 
21 #include "perfetto/base/logging.h"
22 #include "perfetto/ext/base/string_utils.h"
23 #include "perfetto/protozero/packed_repeated_fields.h"
24 #include "perfetto/protozero/proto_decoder.h"
25 #include "perfetto/protozero/proto_utils.h"
26 
27 namespace perfetto {
28 namespace trace_processor {
29 namespace util {
30 
31 namespace {
32 using ::perfetto::protos::pbzero::FieldDescriptorProto;
33 using ::protozero::proto_utils::ProtoWireType;
34 
35 // Takes a type full name, and returns only the final part.
36 // For example, .perfetto.protos.TracePacket -> TracePacket
GetFieldTypeName(const std::string & full_type_name)37 std::string GetFieldTypeName(const std::string& full_type_name) {
38   auto pos = full_type_name.rfind('.');
39   if (pos == std::string::npos) {
40     return full_type_name;
41   }
42   return full_type_name.substr(pos + 1);
43 }
44 
GetLeafTypeName(uint32_t type_id)45 std::string GetLeafTypeName(uint32_t type_id) {
46   std::string raw_name = FieldDescriptorProto::Type_Name(
47       static_cast<FieldDescriptorProto::Type>(type_id));
48   return base::StripPrefix(base::ToLower(raw_name), "type_");
49 }
50 
51 }  // namespace
52 
Field(uint32_t field_idx_in,const FieldDescriptor * field_descriptor_in,uint32_t type_in,const ProtoDescriptor * proto_descriptor_in)53 SizeProfileComputer::Field::Field(uint32_t field_idx_in,
54                                   const FieldDescriptor* field_descriptor_in,
55                                   uint32_t type_in,
56                                   const ProtoDescriptor* proto_descriptor_in)
57     : field_idx(field_idx_in),
58       type(type_in),
59       field_descriptor(field_descriptor_in),
60       proto_descriptor(proto_descriptor_in) {}
61 
field_name() const62 std::string SizeProfileComputer::Field::field_name() const {
63   if (field_descriptor)
64     return "#" + field_descriptor->name();
65   return "#unknown";
66 }
67 
type_name() const68 std::string SizeProfileComputer::Field::type_name() const {
69   if (proto_descriptor)
70     return GetFieldTypeName(proto_descriptor->full_name());
71   return GetLeafTypeName(type);
72 }
73 
SizeProfileComputer(DescriptorPool * pool,const std::string & message_type)74 SizeProfileComputer::SizeProfileComputer(DescriptorPool* pool,
75                                          const std::string& message_type)
76     : pool_(pool) {
77   auto message_idx = pool_->FindDescriptorIdx(message_type);
78   if (!message_idx) {
79     PERFETTO_ELOG("Cannot find descriptor for type %s", message_type.c_str());
80     return;
81   }
82   root_message_idx_ = *message_idx;
83 }
84 
Reset(const uint8_t * ptr,size_t size)85 void SizeProfileComputer::Reset(const uint8_t* ptr, size_t size) {
86   state_stack_.clear();
87   field_path_.clear();
88   protozero::ProtoDecoder decoder(ptr, size);
89   const ProtoDescriptor* descriptor = &pool_->descriptors()[root_message_idx_];
90   state_stack_.push_back(State{descriptor, std::move(decoder), size, 0});
91   field_path_.emplace_back(0, nullptr, root_message_idx_, descriptor);
92 }
93 
GetNext()94 std::optional<size_t> SizeProfileComputer::GetNext() {
95   std::optional<size_t> result;
96   if (state_stack_.empty())
97     return result;
98 
99   if (field_path_.size() > state_stack_.size()) {
100     // The leaf path needs to be popped to continue iterating on the current
101     // proto.
102     field_path_.pop_back();
103     PERFETTO_DCHECK(field_path_.size() == state_stack_.size());
104   }
105   State& state = state_stack_.back();
106 
107   for (;;) {
108     if (state.decoder.bytes_left() == 0) {
109       break;
110     }
111 
112     protozero::Field field = state.decoder.ReadField();
113     if (!field.valid()) {
114       PERFETTO_ELOG("Field not valid (can mean field id >1000)");
115       break;
116     }
117 
118     ProtoWireType type = field.type();
119     size_t field_size = GetFieldSize(field);
120 
121     state.overhead -= field_size;
122     const FieldDescriptor* field_descriptor =
123         state.descriptor->FindFieldByTag(field.id());
124     if (!field_descriptor) {
125       state.unknown += field_size;
126       continue;
127     }
128 
129     bool is_message_type =
130         field_descriptor->type() == FieldDescriptorProto::TYPE_MESSAGE;
131     if (type == ProtoWireType::kLengthDelimited && is_message_type) {
132       auto message_idx =
133           pool_->FindDescriptorIdx(field_descriptor->resolved_type_name());
134 
135       if (!message_idx) {
136         PERFETTO_ELOG("Cannot find descriptor for type %s",
137                       field_descriptor->resolved_type_name().c_str());
138         return result;
139       }
140 
141       protozero::ProtoDecoder decoder(field.data(), field.size());
142       const ProtoDescriptor* descriptor = &pool_->descriptors()[*message_idx];
143       field_path_.emplace_back(field.id(), field_descriptor, *message_idx,
144                                descriptor);
145       state_stack_.push_back(
146           State{descriptor, std::move(decoder), field.size(), 0U});
147       return GetNext();
148     } else {
149       field_path_.emplace_back(field.id(), field_descriptor,
150                                field_descriptor->type(), nullptr);
151       result.emplace(field_size);
152       return result;
153     }
154   }
155   if (state.unknown) {
156     field_path_.emplace_back(uint32_t(-1), nullptr, 0U, nullptr);
157     result.emplace(state.unknown);
158     state.unknown = 0;
159     return result;
160   }
161 
162   result.emplace(state.overhead);
163   state_stack_.pop_back();
164   return result;
165 }
166 
GetFieldSize(const protozero::Field & f)167 size_t SizeProfileComputer::GetFieldSize(const protozero::Field& f) {
168   uint8_t buf[10];
169   switch (f.type()) {
170     case protozero::proto_utils::ProtoWireType::kVarInt:
171       return static_cast<size_t>(
172           protozero::proto_utils::WriteVarInt(f.as_uint64(), buf) - buf);
173     case protozero::proto_utils::ProtoWireType::kLengthDelimited:
174       return f.size();
175     case protozero::proto_utils::ProtoWireType::kFixed32:
176       return 4;
177     case protozero::proto_utils::ProtoWireType::kFixed64:
178       return 8;
179   }
180   PERFETTO_FATAL("unexpected field type");  // for gcc
181 }
182 
183 }  // namespace util
184 }  // namespace trace_processor
185 }  // namespace perfetto
186