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