xref: /aosp_15_r20/external/perfetto/src/trace_processor/util/debug_annotation_parser.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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