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