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