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