1 /*
2 * Copyright (C) 2021 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/debug_annotation_parser.h"
18
19 #include <algorithm>
20 #include <cstddef>
21 #include <cstdint>
22 #include <string>
23
24 #include "perfetto/base/build_config.h"
25 #include "perfetto/base/status.h"
26 #include "perfetto/protozero/field.h"
27 #include "perfetto/public/compiler.h"
28 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
29
30 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
31 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
32 #include "src/trace_processor/util/proto_to_args_parser.h"
33
34 namespace perfetto::trace_processor::util {
35
36 namespace {
37
SanitizeDebugAnnotationName(const std::string & raw_name)38 std::string SanitizeDebugAnnotationName(const std::string& raw_name) {
39 std::string result = raw_name;
40 std::replace(result.begin(), result.end(), '.', '_');
41 std::replace(result.begin(), result.end(), '[', '_');
42 std::replace(result.begin(), result.end(), ']', '_');
43 return result;
44 }
45
IsJsonSupported()46 constexpr bool IsJsonSupported() {
47 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
48 return true;
49 #else
50 return false;
51 #endif
52 }
53
54 } // namespace
55
DebugAnnotationParser(ProtoToArgsParser & parser)56 DebugAnnotationParser::DebugAnnotationParser(ProtoToArgsParser& parser)
57 : proto_to_args_parser_(parser) {}
58
ParseDebugAnnotationName(protos::pbzero::DebugAnnotation::Decoder & annotation,ProtoToArgsParser::Delegate & delegate,std::string & result)59 base::Status DebugAnnotationParser::ParseDebugAnnotationName(
60 protos::pbzero::DebugAnnotation::Decoder& annotation,
61 ProtoToArgsParser::Delegate& delegate,
62 std::string& result) {
63 uint64_t name_iid = annotation.name_iid();
64 if (PERFETTO_LIKELY(name_iid)) {
65 auto* decoder = delegate.GetInternedMessage(
66 protos::pbzero::InternedData::kDebugAnnotationNames, name_iid);
67 if (!decoder)
68 return base::ErrStatus("Debug annotation with invalid name_iid");
69
70 result = SanitizeDebugAnnotationName(decoder->name().ToStdString());
71 } else if (annotation.has_name()) {
72 result = SanitizeDebugAnnotationName(annotation.name().ToStdString());
73 } else {
74 return base::ErrStatus("Debug annotation without name");
75 }
76 return base::OkStatus();
77 }
78
79 DebugAnnotationParser::ParseResult
ParseDebugAnnotationValue(protos::pbzero::DebugAnnotation::Decoder & annotation,ProtoToArgsParser::Delegate & delegate,const ProtoToArgsParser::Key & context_name)80 DebugAnnotationParser::ParseDebugAnnotationValue(
81 protos::pbzero::DebugAnnotation::Decoder& annotation,
82 ProtoToArgsParser::Delegate& delegate,
83 const ProtoToArgsParser::Key& context_name) {
84 if (annotation.has_bool_value()) {
85 delegate.AddBoolean(context_name, annotation.bool_value());
86 } else if (annotation.has_uint_value()) {
87 delegate.AddUnsignedInteger(context_name, annotation.uint_value());
88 } else if (annotation.has_int_value()) {
89 delegate.AddInteger(context_name, annotation.int_value());
90 } else if (annotation.has_double_value()) {
91 delegate.AddDouble(context_name, annotation.double_value());
92 } else if (annotation.has_string_value()) {
93 delegate.AddString(context_name, annotation.string_value());
94 } else if (annotation.has_string_value_iid()) {
95 auto* decoder = delegate.GetInternedMessage(
96 protos::pbzero::InternedData::kDebugAnnotationStringValues,
97 annotation.string_value_iid());
98 if (!decoder) {
99 return {base::ErrStatus("Debug annotation with invalid string_value_iid"),
100 false};
101 }
102 delegate.AddString(context_name, decoder->str().ToStdString());
103 } else if (annotation.has_pointer_value()) {
104 delegate.AddPointer(context_name, annotation.pointer_value());
105 } else if (annotation.has_dict_entries()) {
106 bool added_entry = false;
107 for (auto it = annotation.dict_entries(); it; ++it) {
108 protos::pbzero::DebugAnnotation::Decoder key_value(*it);
109 std::string key;
110 base::Status key_parse_result =
111 ParseDebugAnnotationName(key_value, delegate, key);
112 if (!key_parse_result.ok())
113 return {key_parse_result, added_entry};
114
115 auto nested_key = proto_to_args_parser_.EnterDictionary(key);
116 ParseResult value_parse_result =
117 ParseDebugAnnotationValue(key_value, delegate, nested_key.key());
118 added_entry |= value_parse_result.added_entry;
119 if (!value_parse_result.status.ok())
120 return {value_parse_result.status, added_entry};
121 }
122 } else if (annotation.has_array_values()) {
123 size_t index = delegate.GetArrayEntryIndex(context_name.key);
124 bool added_entry = false;
125 for (auto it = annotation.array_values(); it; ++it) {
126 std::string array_key = context_name.key;
127 protos::pbzero::DebugAnnotation::Decoder value(*it);
128
129 auto nested_key = proto_to_args_parser_.EnterArray(index);
130 ParseResult value_parse_result =
131 ParseDebugAnnotationValue(value, delegate, nested_key.key());
132
133 if (value_parse_result.added_entry) {
134 index = delegate.IncrementArrayEntryIndex(array_key);
135 added_entry = true;
136 }
137 if (!value_parse_result.status.ok())
138 return {value_parse_result.status, added_entry};
139 }
140 } else if (annotation.has_legacy_json_value()) {
141 if (!IsJsonSupported())
142 return {base::ErrStatus("Ignoring legacy_json_value (no json support)"),
143 false};
144
145 bool added_entry =
146 delegate.AddJson(context_name, annotation.legacy_json_value());
147 return {base::OkStatus(), added_entry};
148 } else if (annotation.has_nested_value()) {
149 return ParseNestedValueArgs(annotation.nested_value(), context_name,
150 delegate);
151 } else if (annotation.has_proto_value()) {
152 std::string type_name;
153 if (annotation.has_proto_type_name()) {
154 type_name = annotation.proto_type_name().ToStdString();
155 } else if (annotation.has_proto_type_name_iid()) {
156 auto* interned_name = delegate.GetInternedMessage(
157 protos::pbzero::InternedData::kDebugAnnotationValueTypeNames,
158 annotation.proto_type_name_iid());
159 if (!interned_name)
160 return {base::ErrStatus("Interned proto type name not found"), false};
161 type_name = interned_name->name().ToStdString();
162 } else {
163 return {base::ErrStatus("DebugAnnotation has proto_value, but doesn't "
164 "have proto type name"),
165 false};
166 }
167 return {proto_to_args_parser_.ParseMessage(annotation.proto_value(),
168 type_name, nullptr, delegate),
169 true};
170 } else {
171 return {base::OkStatus(), /*added_entry=*/false};
172 }
173
174 return {base::OkStatus(), /*added_entry=*/true};
175 }
176
177 // static
Parse(protozero::ConstBytes data,ProtoToArgsParser::Delegate & delegate)178 base::Status DebugAnnotationParser::Parse(
179 protozero::ConstBytes data,
180 ProtoToArgsParser::Delegate& delegate) {
181 protos::pbzero::DebugAnnotation::Decoder annotation(data);
182
183 std::string name;
184 base::Status name_parse_result =
185 ParseDebugAnnotationName(annotation, delegate, name);
186 if (!name_parse_result.ok())
187 return name_parse_result;
188
189 auto context = proto_to_args_parser_.EnterDictionary(name);
190
191 return ParseDebugAnnotationValue(annotation, delegate, context.key()).status;
192 }
193
ParseNestedValueArgs(protozero::ConstBytes nested_value,const ProtoToArgsParser::Key & context_name,ProtoToArgsParser::Delegate & delegate)194 DebugAnnotationParser::ParseResult DebugAnnotationParser::ParseNestedValueArgs(
195 protozero::ConstBytes nested_value,
196 const ProtoToArgsParser::Key& context_name,
197 ProtoToArgsParser::Delegate& delegate) {
198 protos::pbzero::DebugAnnotation::NestedValue::Decoder value(nested_value);
199 switch (value.nested_type()) {
200 case protos::pbzero::DebugAnnotation::NestedValue::UNSPECIFIED: {
201 // Leaf value.
202 if (value.has_bool_value()) {
203 delegate.AddBoolean(context_name, value.bool_value());
204 return {base::OkStatus(), true};
205 }
206 if (value.has_int_value()) {
207 delegate.AddInteger(context_name, value.int_value());
208 return {base::OkStatus(), true};
209 }
210 if (value.has_double_value()) {
211 delegate.AddDouble(context_name, value.double_value());
212 return {base::OkStatus(), true};
213 }
214 if (value.has_string_value()) {
215 delegate.AddString(context_name, value.string_value());
216 return {base::OkStatus(), true};
217 }
218 return {base::OkStatus(), false};
219 }
220 case protos::pbzero::DebugAnnotation::NestedValue::DICT: {
221 bool added_entry = false;
222 auto key_it = value.dict_keys();
223 auto value_it = value.dict_values();
224 for (; key_it && value_it; ++key_it, ++value_it) {
225 std::string child_name =
226 SanitizeDebugAnnotationName((*key_it).ToStdString());
227 auto nested_key = proto_to_args_parser_.EnterDictionary(child_name);
228 ParseResult result =
229 ParseNestedValueArgs(*value_it, nested_key.key(), delegate);
230 added_entry |= result.added_entry;
231 if (!result.status.ok())
232 return {result.status, added_entry};
233 }
234 return {base::OkStatus(), true};
235 }
236
237 case protos::pbzero::DebugAnnotation::NestedValue::ARRAY: {
238 std::string array_key = context_name.key;
239 size_t array_index = delegate.GetArrayEntryIndex(context_name.key);
240 bool added_entry = false;
241
242 for (auto value_it = value.array_values(); value_it; ++value_it) {
243 auto nested_key = proto_to_args_parser_.EnterArray(array_index);
244 ParseResult result =
245 ParseNestedValueArgs(*value_it, nested_key.key(), delegate);
246
247 if (result.added_entry) {
248 ++array_index;
249 delegate.IncrementArrayEntryIndex(array_key);
250 added_entry = true;
251 }
252 if (!result.status.ok())
253 return {result.status, added_entry};
254 }
255 return {base::OkStatus(), added_entry};
256 }
257 }
258 return {base::OkStatus(), false};
259 }
260
261 } // namespace perfetto::trace_processor::util
262