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 E : : 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 : assert(index <= std::numeric_limits<int>::max());
186 E : EmitHexValue32(stack_trace_->frames().Get(static_cast<int>(index)), output);
187 E : return true;
188 E : }
189 :
190 : const StackTrace* stack_trace_;
191 : };
192 :
193 : bool ToJson(const StackTrace* stack_trace,
194 : std::string* indent,
195 E : std::string* output) {
196 E : assert(stack_trace != nullptr);
197 E : assert(output != nullptr);
198 E : StackTraceYieldFunctor yield(stack_trace);
199 E : if (!EmitJsonList('[', ']', 4, stack_trace->frames_size(), yield,
200 : indent, output)) {
201 i : return false;
202 : }
203 E : return true;
204 E : }
205 :
206 : // A functor for emitting the binary data in a blob as an array.
207 : struct BlobDataYieldFunctor {
208 E : explicit BlobDataYieldFunctor(const Blob* blob)
209 E : : blob_(blob) {
210 E : assert(blob != nullptr);
211 E : }
212 :
213 E : bool operator()(size_t index, std::string* indent, std::string* output) {
214 E : EmitHexValue8(static_cast<unsigned char>(blob_->data()[index]), output);
215 E : return true;
216 E : }
217 :
218 : const Blob* blob_;
219 : };
220 :
221 : // A functor for emitting the contents of a blob as a dictionary.
222 : struct BlobYieldFunctor {
223 E : explicit BlobYieldFunctor(const Blob* blob)
224 E : : blob_(blob), need_comma_(false) {
225 E : assert(blob != nullptr);
226 E : }
227 :
228 E : bool operator()(size_t index, std::string* indent, std::string* output) {
229 E : assert(output != nullptr);
230 E : switch (index) {
231 : case 0: {
232 : // Emit a blob descriptor.
233 E : EmitDictKey("type", indent, output);
234 E : EmitString("blob", output);
235 E : return true;
236 : }
237 : case 1: {
238 E : EmitDictKey("address", indent, output);
239 E : if (blob_->has_address()) {
240 E : if (!ToJson(&blob_->address(), indent, output))
241 i : return false;
242 E : } else {
243 E : EmitNull(output);
244 : }
245 E : return true;
246 : }
247 : case 2: {
248 E : EmitDictKey("size", indent, output);
249 E : if (blob_->has_size()) {
250 E : EmitDecValue(blob_->size(), output);
251 E : } else {
252 E : EmitNull(output);
253 : }
254 E : return true;
255 : }
256 : case 3: {
257 E : EmitDictKey("data", indent, output);
258 E : if (blob_->has_data()) {
259 E : BlobDataYieldFunctor yield(blob_);
260 E : if (!EmitJsonList('[', ']', 8, blob_->data().size(), yield,
261 : indent, output)) {
262 i : return false;
263 : }
264 E : } else {
265 E : EmitNull(output);
266 : }
267 E : return true;
268 : }
269 : default: break;
270 : }
271 :
272 : // This should never happen!
273 i : assert(false);
274 i : return false;
275 E : }
276 :
277 : const Blob* blob_;
278 : bool need_comma_;
279 : };
280 :
281 E : bool ToJson(const Blob* blob, std::string* indent, std::string* output) {
282 E : assert(blob != nullptr);
283 E : assert(output != nullptr);
284 E : BlobYieldFunctor yield(blob);
285 : // 1 element per line, 4 elements total.
286 E : if (!EmitJsonList('{', '}', 1, 4, yield, indent, output))
287 i : return false;
288 E : return true;
289 E : }
290 :
291 E : bool ToJson(const Leaf* leaf, std::string* indent, std::string* output) {
292 E : assert(leaf != nullptr);
293 E : assert(output != nullptr);
294 :
295 E : if (!leaf->has_type())
296 i : return false;
297 :
298 E : switch (leaf->type()) {
299 : default:
300 : case Leaf_Type_UNKNOWN_TYPE: {
301 E : return false;
302 : }
303 :
304 : case Leaf_Type_INTEGER: {
305 E : if (!leaf->has_integer())
306 E : return false;
307 E : EmitDecValue(leaf->integer(), output);
308 E : return true;
309 : }
310 :
311 : case Leaf_Type_UNSIGNED_INTEGER: {
312 E : if (!leaf->has_unsigned_integer())
313 E : return false;
314 E : EmitDecValue(leaf->unsigned_integer(), output);
315 E : return true;
316 : }
317 :
318 : case Leaf_Type_REAL: {
319 E : if (!leaf->has_real())
320 E : return false;
321 E : EmitDouble(leaf->real(), output);
322 E : return true;
323 : }
324 :
325 : case Leaf_Type_STRING: {
326 E : if (!leaf->has_string())
327 E : return false;
328 E : EmitString(leaf->string(), output);
329 E : return true;
330 : }
331 :
332 : case Leaf_Type_ADDRESS: {
333 E : if (!leaf->has_address())
334 E : return false;
335 E : if (!ToJson(&leaf->address(), indent, output))
336 i : return false;
337 E : return true;
338 : }
339 :
340 : case Leaf_Type_STACK_TRACE: {
341 E : if (!leaf->has_stack_trace())
342 E : return false;
343 E : if (!ToJson(&leaf->stack_trace(), indent, output))
344 i : return false;
345 E : return true;
346 : }
347 :
348 : case Leaf_Type_BLOB: {
349 E : if (!leaf->has_blob())
350 E : return false;
351 E : if (!ToJson(&leaf->blob(), indent, output))
352 i : return false;
353 E : return true;
354 : }
355 : }
356 :
357 i : assert(false);
358 i : return false;
359 E : }
360 :
361 : struct ValueListYieldFunctor {
362 E : explicit ValueListYieldFunctor(const ValueList* list) : list_(list) {}
363 :
364 E : bool operator()(size_t index, std::string* indent, std::string* output) {
365 E : assert(output != nullptr);
366 E : assert(index <= std::numeric_limits<int>::max());
367 E : if (!ToJson(&list_->values().Get(static_cast<int>(index)), indent, output))
368 i : return false;
369 E : return true;
370 E : }
371 :
372 : const ValueList* list_;
373 : };
374 :
375 E : bool ToJson(const ValueList* list, std::string* indent, std::string* output) {
376 E : assert(list != nullptr);
377 E : assert(output != nullptr);
378 E : ValueListYieldFunctor yield(list);
379 E : if (!EmitJsonList('[', ']', 1, list->values_size(), yield, indent, output))
380 i : return false;
381 E : return true;
382 E : }
383 :
384 : bool ToJson(
385 E : const KeyValue* key_value, std::string* indent, std::string* output) {
386 E : assert(key_value != nullptr);
387 E : assert(output != nullptr);
388 E : if (!key_value->has_key())
389 E : return false;
390 E : if (!key_value->has_value())
391 E : return false;
392 E : EmitDictKey(key_value->key(), indent, output);
393 E : if (!ToJson(&key_value->value(), indent, output))
394 i : return false;
395 E : return true;
396 E : }
397 :
398 : struct DictYieldFunctor {
399 E : explicit DictYieldFunctor(const Dictionary* dict) : dict_(dict) {
400 E : }
401 :
402 : bool operator()(size_t index,
403 : std::string* indent,
404 E : std::string* output) {
405 E : assert(output != nullptr);
406 E : assert(index <= std::numeric_limits<int>::max());
407 E : if (!ToJson(&dict_->values().Get(static_cast<int>(index)), indent, output))
408 E : return false;
409 E : return true;
410 E : }
411 :
412 : const Dictionary* dict_;
413 : };
414 :
415 E : bool ToJson(const Dictionary* dict, std::string* indent, std::string* output) {
416 E : assert(dict != nullptr);
417 E : assert(output != nullptr);
418 E : DictYieldFunctor yield(dict);
419 E : if (!EmitJsonList('{', '}', 1, dict->values_size(), yield, indent, output))
420 E : return false;
421 E : return true;
422 E : }
423 :
424 E : bool ToJson(const Value* value, std::string* indent, std::string* output) {
425 E : assert(value != nullptr);
426 E : assert(output != nullptr);
427 E : if (!value->has_type())
428 i : return false;
429 E : switch (value->type()) {
430 : default:
431 : case Value_Type_UNKNOWN_TYPE: {
432 E : return false;
433 : }
434 :
435 : case Value_Type_LEAF: {
436 E : if (!value->has_leaf())
437 E : return false;
438 E : if (!ToJson(&value->leaf(), indent, output))
439 E : return false;
440 E : return true;
441 : }
442 :
443 : case Value_Type_VALUE_LIST: {
444 E : if (!value->has_list())
445 E : return false;
446 E : if (!ToJson(&value->list(), indent, output))
447 i : return false;
448 E : return true;
449 : }
450 :
451 : case Value_Type_DICTIONARY: {
452 E : if (!value->has_dictionary())
453 E : return false;
454 E : if (!ToJson(&value->dictionary(), indent, output))
455 E : return false;
456 E : return true;
457 : }
458 : }
459 :
460 i : assert(false);
461 i : return false;
462 E : }
463 :
464 : } // namespace
465 :
466 E : bool ToJson(bool pretty_print, const Value* value, std::string* output) {
467 E : assert(value != nullptr);
468 E : assert(output != nullptr);
469 E : std::string* indent = nullptr;
470 E : std::string indent_content;
471 E : if (pretty_print) {
472 E : indent_content = "\n";
473 E : indent = &indent_content;
474 : }
475 :
476 : // Produce the output to a temp variable, as partial output may be produced
477 : // in case of error.
478 E : std::string temp;
479 E : if (!ToJson(value, indent, &temp))
480 E : return false;
481 :
482 : // Place the output in the desired string as efficiently as possible.
483 E : if (output->empty()) {
484 E : output->swap(temp);
485 E : } else {
486 i : output->append(temp);
487 : }
488 :
489 E : return true;
490 E : }
491 :
492 : } // namespace crashdata
|