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