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