• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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