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