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