• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <cstddef>
20 #include <cstdint>
21 #include <optional>
22 #include <string>
23 
24 #include "perfetto/base/logging.h"
25 #include "perfetto/ext/base/string_utils.h"
26 #include "perfetto/protozero/field.h"
27 #include "perfetto/protozero/proto_decoder.h"
28 #include "perfetto/protozero/proto_utils.h"
29 #include "src/trace_processor/util/descriptors.h"
30 
31 #include "protos/perfetto/common/descriptor.pbzero.h"
32 
33 namespace perfetto::trace_processor::util {
34 
35 namespace {
36 using ::perfetto::protos::pbzero::FieldDescriptorProto;
37 using ::protozero::proto_utils::ProtoWireType;
38 
39 // Takes a type full name, and returns only the final part.
40 // For example, .perfetto.protos.TracePacket -> TracePacket
GetFieldTypeName(const std::string & full_type_name)41 std::string GetFieldTypeName(const std::string& full_type_name) {
42   auto pos = full_type_name.rfind('.');
43   if (pos == std::string::npos) {
44     return full_type_name;
45   }
46   return full_type_name.substr(pos + 1);
47 }
48 
GetLeafTypeName(uint32_t type_id)49 std::string GetLeafTypeName(uint32_t type_id) {
50   std::string raw_name = FieldDescriptorProto::Type_Name(
51       static_cast<FieldDescriptorProto::Type>(type_id));
52   return base::StripPrefix(base::ToLower(raw_name), "type_");
53 }
54 
55 }  // namespace
56 
Field(uint32_t field_idx_in,const FieldDescriptor * field_descriptor_in,uint32_t type_in,const ProtoDescriptor * proto_descriptor_in)57 SizeProfileComputer::Field::Field(uint32_t field_idx_in,
58                                   const FieldDescriptor* field_descriptor_in,
59                                   uint32_t type_in,
60                                   const ProtoDescriptor* proto_descriptor_in)
61     : field_idx(field_idx_in),
62       type(type_in),
63       field_descriptor(field_descriptor_in),
64       proto_descriptor(proto_descriptor_in) {}
65 
field_name() const66 std::string SizeProfileComputer::Field::field_name() const {
67   if (field_descriptor)
68     return "#" + field_descriptor->name();
69   return "#unknown";
70 }
71 
type_name() const72 std::string SizeProfileComputer::Field::type_name() const {
73   if (proto_descriptor)
74     return GetFieldTypeName(proto_descriptor->full_name());
75   return GetLeafTypeName(type);
76 }
77 
SizeProfileComputer(DescriptorPool * pool,const std::string & message_type)78 SizeProfileComputer::SizeProfileComputer(DescriptorPool* pool,
79                                          const std::string& message_type)
80     : pool_(pool) {
81   auto message_idx = pool_->FindDescriptorIdx(message_type);
82   if (!message_idx) {
83     PERFETTO_ELOG("Cannot find descriptor for type %s", message_type.c_str());
84     return;
85   }
86   root_message_idx_ = *message_idx;
87 }
88 
Reset(const uint8_t * ptr,size_t size)89 void SizeProfileComputer::Reset(const uint8_t* ptr, size_t size) {
90   state_stack_.clear();
91   field_path_.clear();
92   protozero::ProtoDecoder decoder(ptr, size);
93   const ProtoDescriptor* descriptor = &pool_->descriptors()[root_message_idx_];
94   state_stack_.push_back(State{descriptor, decoder, size, 0});
95   field_path_.emplace_back(0, nullptr, root_message_idx_, descriptor);
96 }
97 
GetNext()98 std::optional<size_t> SizeProfileComputer::GetNext() {
99   std::optional<size_t> result;
100   if (state_stack_.empty())
101     return result;
102 
103   if (field_path_.size() > state_stack_.size()) {
104     // The leaf path needs to be popped to continue iterating on the current
105     // proto.
106     field_path_.pop_back();
107     PERFETTO_DCHECK(field_path_.size() == state_stack_.size());
108   }
109   State& state = state_stack_.back();
110 
111   for (;;) {
112     if (state.decoder.bytes_left() == 0) {
113       break;
114     }
115 
116     protozero::Field field = state.decoder.ReadField();
117     if (!field.valid()) {
118       PERFETTO_ELOG("Field not valid (can mean field id >1000)");
119       break;
120     }
121 
122     ProtoWireType type = field.type();
123     size_t field_size = GetFieldSize(field);
124 
125     state.overhead -= field_size;
126     const FieldDescriptor* field_descriptor =
127         state.descriptor->FindFieldByTag(field.id());
128     if (!field_descriptor) {
129       state.unknown += field_size;
130       continue;
131     }
132 
133     bool is_message_type =
134         field_descriptor->type() == FieldDescriptorProto::TYPE_MESSAGE;
135     if (type == ProtoWireType::kLengthDelimited && is_message_type) {
136       auto message_idx =
137           pool_->FindDescriptorIdx(field_descriptor->resolved_type_name());
138 
139       if (!message_idx) {
140         PERFETTO_ELOG("Cannot find descriptor for type %s",
141                       field_descriptor->resolved_type_name().c_str());
142         return result;
143       }
144 
145       protozero::ProtoDecoder decoder(field.data(), field.size());
146       const ProtoDescriptor* descriptor = &pool_->descriptors()[*message_idx];
147       field_path_.emplace_back(field.id(), field_descriptor, *message_idx,
148                                descriptor);
149       state_stack_.push_back(State{descriptor, decoder, field.size(), 0U});
150       return GetNext();
151     }
152     field_path_.emplace_back(field.id(), field_descriptor,
153                              field_descriptor->type(), nullptr);
154     result.emplace(field_size);
155     return result;
156   }
157   if (state.unknown) {
158     field_path_.emplace_back(uint32_t(-1), nullptr, 0U, nullptr);
159     result.emplace(state.unknown);
160     state.unknown = 0;
161     return result;
162   }
163 
164   result.emplace(state.overhead);
165   state_stack_.pop_back();
166   return result;
167 }
168 
GetFieldSize(const protozero::Field & f)169 size_t SizeProfileComputer::GetFieldSize(const protozero::Field& f) {
170   uint8_t buf[10];
171   switch (f.type()) {
172     case protozero::proto_utils::ProtoWireType::kVarInt:
173       return static_cast<size_t>(
174           protozero::proto_utils::WriteVarInt(f.as_uint64(), buf) - buf);
175     case protozero::proto_utils::ProtoWireType::kLengthDelimited:
176       return f.size();
177     case protozero::proto_utils::ProtoWireType::kFixed32:
178       return 4;
179     case protozero::proto_utils::ProtoWireType::kFixed64:
180       return 8;
181   }
182   PERFETTO_FATAL("unexpected field type");  // for gcc
183 }
184 
185 }  // namespace perfetto::trace_processor::util
186