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