• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 <vector>
18 
19 #include <google/protobuf/descriptor.h>
20 #include <google/protobuf/descriptor.pb.h>
21 #include <google/protobuf/message.h>
22 
23 #include "perfetto/base/logging.h"
24 #include "perfetto/ext/base/string_utils.h"
25 #include "src/trace_processor/util/proto_to_json.h"
26 
27 namespace perfetto {
28 namespace trace_processor {
29 namespace proto_to_json {
30 
31 namespace {
32 
QuoteAndEscapeJsonString(const std::string & raw)33 std::string QuoteAndEscapeJsonString(const std::string& raw) {
34   std::string ret;
35   for (auto it = raw.cbegin(); it != raw.cend(); it++) {
36     char c = *it;
37     switch (c) {
38       case '"':
39         // Double quote needs to be escaped.
40         ret += "\\\"";
41         break;
42       case '\n':
43         // Escape new line specially because it appears often and so is worth
44         // treating specially.
45         ret += "\\n";
46         break;
47       default:
48         if (c < 0x20) {
49           // All 32 ASCII control codes need to be escaped. Instead of using the
50           // short forms, we just always use \u escape sequences instead to make
51           // things simpler.
52           ret += "\\u00";
53 
54           // Print |c| as a hex character. We reserve 3 bytes of space: 2 for
55           // the hex code and one for the null terminator.
56           base::StackString<3> buf("%02X", c);
57           ret += buf.c_str();
58         } else {
59           // Everything else can be passed through directly.
60           ret += c;
61         }
62         break;
63     }
64   }
65   return '"' + ret + '"';
66 }
67 
FieldToJson(const google::protobuf::Message & message,const google::protobuf::FieldDescriptor * field_desc,int idx,uint32_t indent)68 std::string FieldToJson(const google::protobuf::Message& message,
69                         const google::protobuf::FieldDescriptor* field_desc,
70                         int idx,
71                         uint32_t indent) {
72   using google::protobuf::FieldDescriptor;
73 
74   const google::protobuf::Reflection* ref = message.GetReflection();
75   bool is_repeated = field_desc->is_repeated();
76   switch (field_desc->cpp_type()) {
77     case FieldDescriptor::CppType::CPPTYPE_BOOL:
78       return std::to_string(is_repeated
79                                 ? ref->GetRepeatedBool(message, field_desc, idx)
80                                 : ref->GetBool(message, field_desc));
81     case FieldDescriptor::CppType::CPPTYPE_ENUM:
82       return QuoteAndEscapeJsonString(
83           is_repeated ? ref->GetRepeatedEnum(message, field_desc, idx)->name()
84                       : ref->GetEnum(message, field_desc)->name());
85     case FieldDescriptor::CppType::CPPTYPE_FLOAT:
86       return std::to_string(
87           is_repeated
88               ? static_cast<double>(
89                     ref->GetRepeatedFloat(message, field_desc, idx))
90               : static_cast<double>(ref->GetFloat(message, field_desc)));
91     case FieldDescriptor::CppType::CPPTYPE_INT32:
92       return std::to_string(
93           is_repeated ? ref->GetRepeatedInt32(message, field_desc, idx)
94                       : ref->GetInt32(message, field_desc));
95     case FieldDescriptor::CppType::CPPTYPE_INT64:
96       return std::to_string(
97           is_repeated ? ref->GetRepeatedInt64(message, field_desc, idx)
98                       : ref->GetInt64(message, field_desc));
99     case FieldDescriptor::CppType::CPPTYPE_DOUBLE:
100       return std::to_string(
101           is_repeated ? ref->GetRepeatedDouble(message, field_desc, idx)
102                       : ref->GetDouble(message, field_desc));
103     case FieldDescriptor::CppType::CPPTYPE_STRING:
104       return QuoteAndEscapeJsonString(
105           is_repeated ? ref->GetRepeatedString(message, field_desc, idx)
106                       : ref->GetString(message, field_desc));
107     case FieldDescriptor::CppType::CPPTYPE_UINT32:
108       return std::to_string(
109           is_repeated ? ref->GetRepeatedUInt32(message, field_desc, idx)
110                       : ref->GetUInt32(message, field_desc));
111     case FieldDescriptor::CppType::CPPTYPE_UINT64:
112       return std::to_string(
113           is_repeated ? ref->GetRepeatedInt64(message, field_desc, idx)
114                       : ref->GetInt64(message, field_desc));
115     case FieldDescriptor::CppType::CPPTYPE_MESSAGE:
116       return MessageToJson(
117           is_repeated ? ref->GetRepeatedMessage(message, field_desc, idx)
118                       : ref->GetMessage(message, field_desc),
119           indent);
120   }
121   PERFETTO_FATAL("For GCC");
122 }
123 
RepeatedFieldValuesToJson(const google::protobuf::Message & message,const google::protobuf::FieldDescriptor * field_desc,uint32_t indent)124 std::string RepeatedFieldValuesToJson(
125     const google::protobuf::Message& message,
126     const google::protobuf::FieldDescriptor* field_desc,
127     uint32_t indent) {
128   const google::protobuf::Reflection* ref = message.GetReflection();
129   std::string ret;
130   for (int i = 0; i < ref->FieldSize(message, field_desc); ++i) {
131     if (i != 0) {
132       ret += ",";
133     }
134     ret += "\n" + std::string(indent, ' ') +
135            FieldToJson(message, field_desc, i, indent);
136   }
137   return ret;
138 }
139 
MessageFieldsToJson(const google::protobuf::Message & message,uint32_t indent)140 std::string MessageFieldsToJson(const google::protobuf::Message& message,
141                                 uint32_t indent) {
142   const google::protobuf::Reflection* ref = message.GetReflection();
143   std::vector<const google::protobuf::FieldDescriptor*> field_descs;
144   ref->ListFields(message, &field_descs);
145 
146   std::string ret;
147   uint32_t next_field_idx = 0;
148   for (const google::protobuf::FieldDescriptor* field_desc : field_descs) {
149     if (next_field_idx++ != 0) {
150       ret += ",";
151     }
152     std::string value;
153     if (field_desc->is_repeated()) {
154       value = "[" + RepeatedFieldValuesToJson(message, field_desc, indent + 2) +
155               "\n" + std::string(indent, ' ') + "]";
156     } else {
157       value = FieldToJson(message, field_desc, 0, indent);
158     }
159     const std::string& name = field_desc->is_extension()
160                                   ? field_desc->full_name()
161                                   : field_desc->name();
162     ret += "\n" + std::string(indent, ' ') + "\"" + name + "\": " + value;
163   }
164   return ret;
165 }
166 
167 // This is a class helps avoid the situation where every function has to take
168 // field_options_prototype as an argument, which becomes distracting.
169 class OptionsConverter {
170  public:
OptionsConverter(const google::protobuf::Message * field_options_prototype)171   explicit OptionsConverter(
172       const google::protobuf::Message* field_options_prototype)
173       : field_options_prototype_(field_options_prototype) {}
174 
175   // Prints all field options for non-empty fields of a message. Example:
176   // --- Message definitions ---
177   // FooMessage {
178   //   repeated int64 foo = 1 [op1 = val1, op2 = val2];
179   //   optional BarMessage bar = 2 [op3 = val3];
180   // }
181   //
182   // BarMessage {
183   //   optional int64 baz = 1 [op4 = val4];
184   // }
185   // --- MessageInstance ---
186   // foo_msg = {  // (As JSON)
187   //   foo: [23, 24, 25],
188   //   bar: {
189   //     baz: 42
190   //   }
191   // }
192   // --- Output of MessageFieldOptionsToJson(foo_msg) ---
193   //   foo: {
194   //     __field_options: {
195   //       op1: val1,
196   //       op2: val2,
197   //     },
198   //     __repeated: true
199   //   }
200   //   bar: {
201   //     __field_options: {
202   //       op3 = val3,
203   //     },
204   //     baz: {
205   //       __field_options: {
206   //         op4 = val4
207   //       },
208   //     }
209   //   }
210   // --- Notes ---
211   // This function does not produce the surrounding braces for easier use in
212   // recursive use cases. The caller needs to surround the output with braces.
MessageFieldOptionsToJson(const google::protobuf::Message & message,uint32_t indent)213   std::string MessageFieldOptionsToJson(
214       const google::protobuf::Message& message,
215       uint32_t indent) {
216     using google::protobuf::FieldDescriptor;
217     std::vector<const FieldDescriptor*> field_descs;
218     message.GetReflection()->ListFields(message, &field_descs);
219     std::vector<std::string> field_outputs;
220     for (auto* field_desc : field_descs) {
221       std::vector<std::string> field_entries;
222       if (HasFieldOptions(field_desc)) {
223         std::string options_entry;
224         options_entry +=
225             std::string(indent + 2, ' ') + R"("__field_options": )";
226         options_entry += FieldOptionsToJson(field_desc, indent + 4);
227         field_entries.push_back(std::move(options_entry));
228       }
229       std::string nested_fields =
230           NestedMessageFieldOptionsToJson(message, field_desc, indent + 2);
231       if (!nested_fields.empty()) {
232         field_entries.push_back(std::move(nested_fields));
233       }
234       // We don't output annotations for a field if that field and all its
235       // descendants have no field options.
236       if (!field_entries.empty()) {
237         if (field_desc->is_repeated()) {
238           field_entries.push_back(std::string(indent, ' ') +
239                                   R"("__repeated": true)");
240         }
241         std::string field_output;
242         const std::string& name = field_desc->is_extension()
243                                       ? field_desc->full_name()
244                                       : field_desc->name();
245         field_output += std::string(indent, ' ') + "\"" + name + "\": {\n";
246         field_output += base::Join(field_entries, ",\n") + "\n";
247         field_output += std::string(indent, ' ') + "}";
248         field_outputs.push_back(std::move(field_output));
249       }
250     }
251     return base::Join(field_outputs, ",\n");
252   }
253 
254  private:
HasFieldOptions(const google::protobuf::FieldDescriptor * field_desc)255   static bool HasFieldOptions(
256       const google::protobuf::FieldDescriptor* field_desc) {
257     return field_desc->options().ByteSizeLong() > 0;
258   }
259 
NestedMessageFieldOptionsToJson(const google::protobuf::Message & message,const google::protobuf::FieldDescriptor * field_desc,uint32_t indent)260   std::string NestedMessageFieldOptionsToJson(
261       const google::protobuf::Message& message,
262       const google::protobuf::FieldDescriptor* field_desc,
263       uint32_t indent) {
264     using google::protobuf::FieldDescriptor;
265     if (field_desc->cpp_type() != FieldDescriptor::CppType::CPPTYPE_MESSAGE)
266       return "";
267     const auto* reflection = message.GetReflection();
268     const google::protobuf::Message& nested_message =
269         field_desc->is_repeated()
270             ? reflection->GetRepeatedMessage(message, field_desc, 0)
271             : reflection->GetMessage(message, field_desc);
272     return MessageFieldOptionsToJson(nested_message, indent);
273   }
274 
FieldOptionsToJson(const google::protobuf::FieldDescriptor * field_desc,uint32_t indent)275   std::string FieldOptionsToJson(
276       const google::protobuf::FieldDescriptor* field_desc,
277       uint32_t indent) {
278     PERFETTO_DCHECK(HasFieldOptions(field_desc));
279     std::unique_ptr<google::protobuf::Message> options(
280         field_options_prototype_->New());
281     // Field option extensions are compiled at runtime as opposed to being
282     // compiled in and being part of the generated pool, so the field option
283     // must be re-parsed as a dynamic message for the extensions to show up. If
284     // we do not do this, the extension fields remain "unknown fields" to the
285     // reflection API.
286     options->ParseFromString(field_desc->options().SerializeAsString());
287     return MessageToJson(*options, indent);
288   }
289 
290   const google::protobuf::Message* field_options_prototype_;
291 };
292 
293 }  // namespace
294 
MessageToJson(const google::protobuf::Message & message,uint32_t indent)295 std::string MessageToJson(const google::protobuf::Message& message,
296                           uint32_t indent) {
297   return "{" + MessageFieldsToJson(message, indent + 2) + '\n' +
298          std::string(indent, ' ') + "}";
299 }
300 
MessageToJsonWithAnnotations(const google::protobuf::Message & message,const google::protobuf::Message * field_options_prototype,uint32_t indent)301 std::string MessageToJsonWithAnnotations(
302     const google::protobuf::Message& message,
303     const google::protobuf::Message* field_options_prototype,
304     uint32_t indent) {
305   std::string ret;
306   OptionsConverter options_converter(field_options_prototype);
307 
308   ret = "{" + MessageFieldsToJson(message, indent + 2);
309   std::string annotation_fields =
310       options_converter.MessageFieldOptionsToJson(message, indent + 4);
311   if (annotation_fields.empty()) {
312     ret += "\n";
313   } else {
314     ret += ",\n";
315     ret += std::string(indent + 2, ' ') + "\"__annotations\": {\n";
316     ret += annotation_fields + "\n";
317     ret += std::string(indent + 2, ' ') + "}\n";
318   }
319   ret += std::string(indent, ' ') + "}\n";
320   return ret;
321 }
322 
323 }  // namespace proto_to_json
324 }  // namespace trace_processor
325 }  // namespace perfetto
326