1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/crashdata/json.h"
16 :
17 : // We use standard dependencies only, as we don't want to introduce a
18 : // dependency on base into the backend crash processing code.
19 : #include <assert.h>
20 : #include <iomanip>
21 : #include <iostream>
22 : #include <sstream>
23 :
24 : namespace crashdata {
25 :
26 : namespace {
27 :
28 : const size_t kIndentSize = 2;
29 :
30 E : void IncreaseIndent(std::string* indent) {
31 E : if (!indent)
32 E : return;
33 E : indent->append(kIndentSize, ' ');
34 E : }
35 :
36 E : void DecreaseIndent(std::string* indent) {
37 E : if (!indent)
38 i : return;
39 E : indent->resize(indent->size() - kIndentSize);
40 E : }
41 :
42 E : void EmitIndent(std::string* indent, std::string* output) {
43 E : assert(output != nullptr);
44 E : if (!indent)
45 E : return;
46 E : output->append(*indent);
47 E : }
48 :
49 E : void EmitHexValue8(unsigned char value, std::string* output) {
50 E : assert(output != nullptr);
51 E : output->push_back('"');
52 E : std::ostringstream oss;
53 : oss << "0x" << std::hex << std::setfill('0') << std::setw(2)
54 E : << std::uppercase << static_cast<unsigned int>(value);
55 E : output->append(oss.str());
56 E : output->push_back('"');
57 E : }
58 :
59 E : void EmitHexValue32(google::protobuf::uint64 value, std::string* output) {
60 E : assert(output != nullptr);
61 E : output->push_back('"');
62 E : std::ostringstream oss;
63 : oss << "0x" << std::hex << std::setfill('0') << std::setw(8)
64 E : << std::uppercase << value;
65 E : output->append(oss.str());
66 E : output->push_back('"');
67 E : }
68 :
69 : template <typename IntType>
70 E : void EmitDecValue(IntType value, std::string* output) {
71 E : assert(output != nullptr);
72 E : std::ostringstream oss;
73 E : oss << std::dec << std::setw(0) << value;
74 E : output->append(oss.str());
75 E : }
76 :
77 E : void EmitDouble(double value, std::string* output) {
78 E : assert(output != nullptr);
79 E : std::ostringstream oss;
80 E : oss << std::scientific << std::setprecision(16) << std::uppercase << value;
81 E : output->append(oss.str());
82 E : }
83 :
84 E : void EmitNull(std::string* output) {
85 E : assert(output != nullptr);
86 E : output->append("null");
87 E : }
88 :
89 E : void EmitString(const std::string& s, std::string* output) {
90 E : assert(output != nullptr);
91 E : output->reserve(output->size() + 2 + s.size());
92 E : output->push_back('"');
93 E : for (size_t i = 0; i < s.size(); ++i) {
94 E : if (s[i] == '"') {
95 E : output->append("\\\"");
96 E : } else if (s[i] == '\\') {
97 E : output->append("\\\\");
98 E : } else {
99 E : output->push_back(s[i]);
100 : }
101 E : }
102 E : output->push_back('"');
103 E : }
104 :
105 : // A helper for emitting a list like object. Calls the provided yield functor
106 : // for each element.
107 : template <typename YieldFunctor>
108 : bool EmitJsonList(char open_bracket,
109 : char close_bracket,
110 : size_t items_per_line,
111 : size_t item_count,
112 : YieldFunctor& yield,
113 : std::string* indent,
114 E : std::string* output) {
115 E : assert(items_per_line > 0);
116 E : assert(output != nullptr);
117 :
118 : // Open up the list, and indent if necessary.
119 E : output->push_back(open_bracket);
120 E : IncreaseIndent(indent);
121 E : EmitIndent(indent, output);
122 :
123 : // Emit the stack frames.
124 E : for (size_t i = 0; i < item_count; ++i) {
125 E : if (!yield(i, indent, output))
126 E : return false;
127 :
128 : // Emit a trailing comma for all entries but the last. For
129 : // the last entry reduce the indent amount to match the opening
130 : // bracket.
131 E : if (i + 1 < item_count) {
132 E : output->push_back(',');
133 E : } else if (indent) {
134 E : DecreaseIndent(indent);
135 : }
136 :
137 : // If at the last element in a line, or the last element in the list then
138 : // emit a line ending and indent.
139 E : if ((i + 1) % items_per_line == 0 || i + 1 == item_count) {
140 : // Output the appropriate indent for the next line or the
141 : // closing bracket.
142 E : EmitIndent(indent, output);
143 E : } else if (indent != nullptr) {
144 : // Emit a single space for the next element if we're emitting whitespace.
145 E : output->push_back(' ');
146 : }
147 E : }
148 :
149 : // Close the list.
150 E : output->push_back(close_bracket);
151 E : return true;
152 E : }
153 :
154 : // Emits a dictionary key, but not the value. Does not increase the indent
155 : // for the value.
156 : void EmitDictKey(const std::string& key,
157 : std::string* indent,
158 E : std::string* output) {
159 E : assert(output != nullptr);
160 E : EmitString(key, output);
161 E : output->push_back(':');
162 E : if (indent)
163 E : output->push_back(' ');
164 E : }
165 :
166 : // Forward declaration of this, as it's the common container type for other
167 : // values.
168 : bool ToJson(const Value* value, std::string* indent, std::string* output);
169 :
170 E : bool ToJson(const Address* address, std::string* indent, std::string* output) {
171 E : assert(address != nullptr);
172 E : assert(output != nullptr);
173 E : EmitHexValue32(address->address(), output);
174 E : return true;
175 E : }
176 :
177 : struct StackTraceYieldFunctor {
178 E : explicit StackTraceYieldFunctor(const StackTrace* stack_trace)
179 : : stack_trace_(stack_trace) {
180 E : assert(stack_trace != nullptr);
181 E : }
182 :
183 E : bool operator()(size_t index, std::string* indent, std::string* output) {
184 E : assert(output != nullptr);
185 E : EmitHexValue32(stack_trace_->frames().Get(index), output);
186 E : return true;
187 E : }
188 :
189 : const StackTrace* stack_trace_;
190 : };
191 :
192 : bool ToJson(const StackTrace* stack_trace,
193 : std::string* indent,
194 E : std::string* output) {
195 E : assert(stack_trace != nullptr);
196 E : assert(output != nullptr);
197 E : StackTraceYieldFunctor yield(stack_trace);
198 : if (!EmitJsonList('[', ']', 4, stack_trace->frames_size(), yield,
199 E : indent, output)) {
200 i : return false;
201 : }
202 E : return true;
203 E : }
204 :
205 : // A functor for emitting the binary data in a blob as an array.
206 : struct BlobDataYieldFunctor {
207 E : explicit BlobDataYieldFunctor(const Blob* blob)
208 : : blob_(blob) {
209 E : assert(blob != nullptr);
210 E : }
211 :
212 E : bool operator()(size_t index, std::string* indent, std::string* output) {
213 E : EmitHexValue8(static_cast<unsigned char>(blob_->data()[index]), output);
214 E : return true;
215 E : }
216 :
217 : const Blob* blob_;
218 : };
219 :
220 : // A functor for emitting the contents of a blob as a dictionary.
221 : struct BlobYieldFunctor {
222 E : explicit BlobYieldFunctor(const Blob* blob)
223 : : blob_(blob), need_comma_(false) {
224 E : assert(blob != nullptr);
225 E : }
226 :
227 E : bool operator()(size_t index, std::string* indent, std::string* output) {
228 E : assert(output != nullptr);
229 E : switch (index) {
230 : case 0: {
231 : // Emit a blob descriptor.
232 E : EmitDictKey("type", indent, output);
233 E : EmitString("blob", output);
234 E : return true;
235 : }
236 : case 1: {
237 E : EmitDictKey("address", indent, output);
238 E : if (blob_->has_address()) {
239 E : if (!ToJson(&blob_->address(), indent, output))
240 i : return false;
241 E : } else {
242 E : EmitNull(output);
243 : }
244 E : return true;
245 : }
246 : case 2: {
247 E : EmitDictKey("size", indent, output);
248 E : if (blob_->has_size()) {
249 E : EmitDecValue(blob_->size(), output);
250 E : } else {
251 E : EmitNull(output);
252 : }
253 E : return true;
254 : }
255 : case 3: {
256 E : EmitDictKey("data", indent, output);
257 E : if (blob_->has_data()) {
258 E : BlobDataYieldFunctor yield(blob_);
259 : if (!EmitJsonList('[', ']', 8, blob_->data().size(), yield,
260 E : indent, output)) {
261 i : return false;
262 : }
263 E : } else {
264 E : EmitNull(output);
265 : }
266 E : return true;
267 : }
268 : default: break;
269 : }
270 :
271 : // This should never happen!
272 i : assert(false);
273 i : return false;
274 E : }
275 :
276 : const Blob* blob_;
277 : bool need_comma_;
278 : };
279 :
280 E : bool ToJson(const Blob* blob, std::string* indent, std::string* output) {
281 E : assert(blob != nullptr);
282 E : assert(output != nullptr);
283 E : BlobYieldFunctor yield(blob);
284 : // 1 element per line, 4 elements total.
285 E : if (!EmitJsonList('{', '}', 1, 4, yield, indent, output))
286 i : return false;
287 E : return true;
288 E : }
289 :
290 E : bool ToJson(const Leaf* leaf, std::string* indent, std::string* output) {
291 E : assert(leaf != nullptr);
292 E : assert(output != nullptr);
293 :
294 E : if (!leaf->has_type())
295 i : return false;
296 :
297 E : switch (leaf->type()) {
298 : default:
299 : case Leaf_Type_UNKNOWN_TYPE: {
300 E : return false;
301 : }
302 :
303 : case Leaf_Type_INTEGER: {
304 E : if (!leaf->has_integer())
305 E : return false;
306 E : EmitDecValue(leaf->integer(), output);
307 E : return true;
308 : }
309 :
310 : case Leaf_Type_UNSIGNED_INTEGER: {
311 E : if (!leaf->has_unsigned_integer())
312 E : return false;
313 E : EmitDecValue(leaf->unsigned_integer(), output);
314 E : return true;
315 : }
316 :
317 : case Leaf_Type_REAL: {
318 E : if (!leaf->has_real())
319 E : return false;
320 E : EmitDouble(leaf->real(), output);
321 E : return true;
322 : }
323 :
324 : case Leaf_Type_STRING: {
325 E : if (!leaf->has_string())
326 E : return false;
327 E : EmitString(leaf->string(), output);
328 E : return true;
329 : }
330 :
331 : case Leaf_Type_ADDRESS: {
332 E : if (!leaf->has_address())
333 E : return false;
334 E : if (!ToJson(&leaf->address(), indent, output))
335 i : return false;
336 E : return true;
337 : }
338 :
339 : case Leaf_Type_STACK_TRACE: {
340 E : if (!leaf->has_stack_trace())
341 E : return false;
342 E : if (!ToJson(&leaf->stack_trace(), indent, output))
343 i : return false;
344 E : return true;
345 : }
346 :
347 : case Leaf_Type_BLOB: {
348 E : if (!leaf->has_blob())
349 E : return false;
350 E : if (!ToJson(&leaf->blob(), indent, output))
351 i : return false;
352 E : return true;
353 : }
354 : }
355 :
356 i : assert(false);
357 i : return false;
358 E : }
359 :
360 : struct ValueListYieldFunctor {
361 E : explicit ValueListYieldFunctor(const ValueList* list) : list_(list) {}
362 :
363 E : bool operator()(size_t index, std::string* indent, std::string* output) {
364 E : assert(output != nullptr);
365 E : if (!ToJson(&list_->values().Get(index), indent, output))
366 i : return false;
367 E : return true;
368 E : }
369 :
370 : const ValueList* list_;
371 : };
372 :
373 E : bool ToJson(const ValueList* list, std::string* indent, std::string* output) {
374 E : assert(list != nullptr);
375 E : assert(output != nullptr);
376 E : ValueListYieldFunctor yield(list);
377 E : if (!EmitJsonList('[', ']', 1, list->values_size(), yield, indent, output))
378 i : return false;
379 E : return true;
380 E : }
381 :
382 : bool ToJson(
383 E : const KeyValue* key_value, std::string* indent, std::string* output) {
384 E : assert(key_value != nullptr);
385 E : assert(output != nullptr);
386 E : if (!key_value->has_key())
387 E : return false;
388 E : if (!key_value->has_value())
389 E : return false;
390 E : EmitDictKey(key_value->key(), indent, output);
391 E : if (!ToJson(&key_value->value(), indent, output))
392 i : return false;
393 E : return true;
394 E : }
395 :
396 : struct DictYieldFunctor {
397 E : explicit DictYieldFunctor(const Dictionary* dict) : dict_(dict) {
398 E : }
399 :
400 : bool operator()(size_t index,
401 : std::string* indent,
402 E : std::string* output) {
403 E : assert(output != nullptr);
404 E : if (!ToJson(&dict_->values().Get(index), indent, output))
405 E : return false;
406 E : return true;
407 E : }
408 :
409 : const Dictionary* dict_;
410 : };
411 :
412 E : bool ToJson(const Dictionary* dict, std::string* indent, std::string* output) {
413 E : assert(dict != nullptr);
414 E : assert(output != nullptr);
415 E : DictYieldFunctor yield(dict);
416 E : if (!EmitJsonList('{', '}', 1, dict->values_size(), yield, indent, output))
417 E : return false;
418 E : return true;
419 E : }
420 :
421 E : bool ToJson(const Value* value, std::string* indent, std::string* output) {
422 E : assert(value != nullptr);
423 E : assert(output != nullptr);
424 E : if (!value->has_type())
425 i : return false;
426 E : switch (value->type()) {
427 : default:
428 : case Value_Type_UNKNOWN_TYPE: {
429 E : return false;
430 : }
431 :
432 : case Value_Type_LEAF: {
433 E : if (!value->has_leaf())
434 E : return false;
435 E : if (!ToJson(&value->leaf(), indent, output))
436 E : return false;
437 E : return true;
438 : }
439 :
440 : case Value_Type_VALUE_LIST: {
441 E : if (!value->has_list())
442 E : return false;
443 E : if (!ToJson(&value->list(), indent, output))
444 i : return false;
445 E : return true;
446 : }
447 :
448 : case Value_Type_DICTIONARY: {
449 E : if (!value->has_dictionary())
450 E : return false;
451 E : if (!ToJson(&value->dictionary(), indent, output))
452 E : return false;
453 E : return true;
454 : }
455 : }
456 :
457 i : assert(false);
458 i : return false;
459 E : }
460 :
461 : } // namespace
462 :
463 E : bool ToJson(bool pretty_print, const Value* value, std::string* output) {
464 E : assert(value != nullptr);
465 E : assert(output != nullptr);
466 E : std::string* indent = nullptr;
467 E : std::string indent_content;
468 E : if (pretty_print) {
469 E : indent_content = "\n";
470 E : indent = &indent_content;
471 : }
472 :
473 : // Produce the output to a temp variable, as partial output may be produced
474 : // in case of error.
475 E : std::string temp;
476 E : if (!ToJson(value, indent, &temp))
477 E : return false;
478 :
479 : // Place the output in the desired string as efficiently as possible.
480 E : if (output->empty()) {
481 E : output->swap(temp);
482 E : } else {
483 i : output->append(temp);
484 : }
485 :
486 E : return true;
487 E : }
488 :
489 : } // namespace crashdata
|