1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/trace_event/traced_value.h"
6
7 #include <stdint.h>
8
9 #include <atomic>
10 #include <utility>
11
12 #include "base/bits.h"
13 #include "base/containers/circular_deque.h"
14 #include "base/json/json_writer.h"
15 #include "base/json/string_escape.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/trace_event/trace_event.h"
18 #include "base/trace_event/trace_event_impl.h"
19 #include "base/trace_event/trace_event_memory_overhead.h"
20 #include "base/trace_event/trace_log.h"
21 #include "base/values.h"
22
23 namespace base {
24 namespace trace_event {
25
26 namespace {
27 const char kTypeStartDict = '{';
28 const char kTypeEndDict = '}';
29 const char kTypeStartArray = '[';
30 const char kTypeEndArray = ']';
31 const char kTypeBool = 'b';
32 const char kTypeInt = 'i';
33 const char kTypeDouble = 'd';
34 const char kTypeString = 's';
35 const char kTypeCStr = '*'; // only used for key names
36
37 std::atomic<TracedValue::WriterFactoryCallback> g_writer_factory_callback;
38
39 #ifndef NDEBUG
40 const bool kStackTypeDict = false;
41 const bool kStackTypeArray = true;
42 #define DCHECK_CURRENT_CONTAINER_IS(x) DCHECK_EQ(x, nesting_stack_.back())
43 #define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) DCHECK_EQ(x, nesting_stack_.size())
44 #define DEBUG_PUSH_CONTAINER(x) nesting_stack_.push_back(x)
45 #define DEBUG_POP_CONTAINER() nesting_stack_.pop_back()
46 #else
47 #define DCHECK_CURRENT_CONTAINER_IS(x) \
48 do { \
49 } while (0)
50 #define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) \
51 do { \
52 } while (0)
53 #define DEBUG_PUSH_CONTAINER(x) \
54 do { \
55 } while (0)
56 #define DEBUG_POP_CONTAINER() \
57 do { \
58 } while (0)
59 #endif
60
WriteKeyNameAsRawPtr(Pickle & pickle,const char * ptr)61 inline void WriteKeyNameAsRawPtr(Pickle& pickle, const char* ptr) {
62 pickle.WriteBytes(&kTypeCStr, 1);
63 pickle.WriteUInt64(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr)));
64 }
65
WriteKeyNameWithCopy(Pickle & pickle,base::StringPiece str)66 inline void WriteKeyNameWithCopy(Pickle& pickle, base::StringPiece str) {
67 pickle.WriteBytes(&kTypeString, 1);
68 pickle.WriteString(str);
69 }
70
ReadKeyName(PickleIterator & pickle_iterator)71 std::string ReadKeyName(PickleIterator& pickle_iterator) {
72 const char* type = nullptr;
73 bool res = pickle_iterator.ReadBytes(&type, 1);
74 std::string key_name;
75 if (res && *type == kTypeCStr) {
76 uint64_t ptr_value = 0;
77 res = pickle_iterator.ReadUInt64(&ptr_value);
78 key_name = reinterpret_cast<const char*>(static_cast<uintptr_t>(ptr_value));
79 } else if (res && *type == kTypeString) {
80 res = pickle_iterator.ReadString(&key_name);
81 }
82 DCHECK(res);
83 return key_name;
84 }
85
86 class PickleWriter final : public TracedValue::Writer {
87 public:
PickleWriter(size_t capacity)88 explicit PickleWriter(size_t capacity) {
89 if (capacity) {
90 pickle_.Reserve(capacity);
91 }
92 }
93
IsPickleWriter() const94 bool IsPickleWriter() const override { return true; }
IsProtoWriter() const95 bool IsProtoWriter() const override { return false; }
96
SetInteger(const char * name,int value)97 void SetInteger(const char* name, int value) override {
98 pickle_.WriteBytes(&kTypeInt, 1);
99 pickle_.WriteInt(value);
100 WriteKeyNameAsRawPtr(pickle_, name);
101 }
102
SetIntegerWithCopiedName(base::StringPiece name,int value)103 void SetIntegerWithCopiedName(base::StringPiece name, int value) override {
104 pickle_.WriteBytes(&kTypeInt, 1);
105 pickle_.WriteInt(value);
106 WriteKeyNameWithCopy(pickle_, name);
107 }
108
SetDouble(const char * name,double value)109 void SetDouble(const char* name, double value) override {
110 pickle_.WriteBytes(&kTypeDouble, 1);
111 pickle_.WriteDouble(value);
112 WriteKeyNameAsRawPtr(pickle_, name);
113 }
114
SetDoubleWithCopiedName(base::StringPiece name,double value)115 void SetDoubleWithCopiedName(base::StringPiece name, double value) override {
116 pickle_.WriteBytes(&kTypeDouble, 1);
117 pickle_.WriteDouble(value);
118 WriteKeyNameWithCopy(pickle_, name);
119 }
120
SetBoolean(const char * name,bool value)121 void SetBoolean(const char* name, bool value) override {
122 pickle_.WriteBytes(&kTypeBool, 1);
123 pickle_.WriteBool(value);
124 WriteKeyNameAsRawPtr(pickle_, name);
125 }
126
SetBooleanWithCopiedName(base::StringPiece name,bool value)127 void SetBooleanWithCopiedName(base::StringPiece name, bool value) override {
128 pickle_.WriteBytes(&kTypeBool, 1);
129 pickle_.WriteBool(value);
130 WriteKeyNameWithCopy(pickle_, name);
131 }
132
SetString(const char * name,base::StringPiece value)133 void SetString(const char* name, base::StringPiece value) override {
134 pickle_.WriteBytes(&kTypeString, 1);
135 pickle_.WriteString(value);
136 WriteKeyNameAsRawPtr(pickle_, name);
137 }
138
SetStringWithCopiedName(base::StringPiece name,base::StringPiece value)139 void SetStringWithCopiedName(base::StringPiece name,
140 base::StringPiece value) override {
141 pickle_.WriteBytes(&kTypeString, 1);
142 pickle_.WriteString(value);
143 WriteKeyNameWithCopy(pickle_, name);
144 }
145
SetValue(const char * name,Writer * value)146 void SetValue(const char* name, Writer* value) override {
147 DCHECK(value->IsPickleWriter());
148 const PickleWriter* pickle_writer = static_cast<const PickleWriter*>(value);
149
150 BeginDictionary(name);
151 pickle_.WriteBytes(pickle_writer->pickle_.payload(),
152 static_cast<int>(pickle_writer->pickle_.payload_size()));
153 EndDictionary();
154 }
155
SetValueWithCopiedName(base::StringPiece name,Writer * value)156 void SetValueWithCopiedName(base::StringPiece name, Writer* value) override {
157 DCHECK(value->IsPickleWriter());
158 const PickleWriter* pickle_writer = static_cast<const PickleWriter*>(value);
159
160 BeginDictionaryWithCopiedName(name);
161 pickle_.WriteBytes(pickle_writer->pickle_.payload(),
162 static_cast<int>(pickle_writer->pickle_.payload_size()));
163 EndDictionary();
164 }
165
BeginArray()166 void BeginArray() override { pickle_.WriteBytes(&kTypeStartArray, 1); }
167
BeginDictionary()168 void BeginDictionary() override { pickle_.WriteBytes(&kTypeStartDict, 1); }
169
BeginDictionary(const char * name)170 void BeginDictionary(const char* name) override {
171 pickle_.WriteBytes(&kTypeStartDict, 1);
172 WriteKeyNameAsRawPtr(pickle_, name);
173 }
174
BeginDictionaryWithCopiedName(base::StringPiece name)175 void BeginDictionaryWithCopiedName(base::StringPiece name) override {
176 pickle_.WriteBytes(&kTypeStartDict, 1);
177 WriteKeyNameWithCopy(pickle_, name);
178 }
179
BeginArray(const char * name)180 void BeginArray(const char* name) override {
181 pickle_.WriteBytes(&kTypeStartArray, 1);
182 WriteKeyNameAsRawPtr(pickle_, name);
183 }
184
BeginArrayWithCopiedName(base::StringPiece name)185 void BeginArrayWithCopiedName(base::StringPiece name) override {
186 pickle_.WriteBytes(&kTypeStartArray, 1);
187 WriteKeyNameWithCopy(pickle_, name);
188 }
189
EndDictionary()190 void EndDictionary() override { pickle_.WriteBytes(&kTypeEndDict, 1); }
EndArray()191 void EndArray() override { pickle_.WriteBytes(&kTypeEndArray, 1); }
192
AppendInteger(int value)193 void AppendInteger(int value) override {
194 pickle_.WriteBytes(&kTypeInt, 1);
195 pickle_.WriteInt(value);
196 }
197
AppendDouble(double value)198 void AppendDouble(double value) override {
199 pickle_.WriteBytes(&kTypeDouble, 1);
200 pickle_.WriteDouble(value);
201 }
202
AppendBoolean(bool value)203 void AppendBoolean(bool value) override {
204 pickle_.WriteBytes(&kTypeBool, 1);
205 pickle_.WriteBool(value);
206 }
207
AppendString(base::StringPiece value)208 void AppendString(base::StringPiece value) override {
209 pickle_.WriteBytes(&kTypeString, 1);
210 pickle_.WriteString(value);
211 }
212
AppendAsTraceFormat(std::string * out) const213 void AppendAsTraceFormat(std::string* out) const override {
214 struct State {
215 enum Type { kTypeDict, kTypeArray };
216 Type type;
217 bool needs_comma;
218 };
219
220 auto maybe_append_key_name = [](State current_state, PickleIterator* it,
221 std::string* out) {
222 if (current_state.type == State::kTypeDict) {
223 EscapeJSONString(ReadKeyName(*it), true, out);
224 out->append(":");
225 }
226 };
227
228 base::circular_deque<State> state_stack;
229
230 out->append("{");
231 state_stack.push_back({State::kTypeDict});
232
233 PickleIterator it(pickle_);
234 for (const char* type; it.ReadBytes(&type, 1);) {
235 switch (*type) {
236 case kTypeEndDict:
237 out->append("}");
238 state_stack.pop_back();
239 continue;
240
241 case kTypeEndArray:
242 out->append("]");
243 state_stack.pop_back();
244 continue;
245 }
246
247 // Use an index so it will stay valid across resizes.
248 size_t current_state_index = state_stack.size() - 1;
249 if (state_stack[current_state_index].needs_comma) {
250 out->append(",");
251 }
252
253 switch (*type) {
254 case kTypeStartDict: {
255 maybe_append_key_name(state_stack[current_state_index], &it, out);
256 out->append("{");
257 state_stack.push_back({State::kTypeDict});
258 break;
259 }
260
261 case kTypeStartArray: {
262 maybe_append_key_name(state_stack[current_state_index], &it, out);
263 out->append("[");
264 state_stack.push_back({State::kTypeArray});
265 break;
266 }
267
268 case kTypeBool: {
269 TraceEvent::TraceValue json_value;
270 CHECK(it.ReadBool(&json_value.as_bool));
271 maybe_append_key_name(state_stack[current_state_index], &it, out);
272 json_value.AppendAsJSON(TRACE_VALUE_TYPE_BOOL, out);
273 break;
274 }
275
276 case kTypeInt: {
277 int value;
278 CHECK(it.ReadInt(&value));
279 maybe_append_key_name(state_stack[current_state_index], &it, out);
280 TraceEvent::TraceValue json_value;
281 json_value.as_int = value;
282 json_value.AppendAsJSON(TRACE_VALUE_TYPE_INT, out);
283 break;
284 }
285
286 case kTypeDouble: {
287 TraceEvent::TraceValue json_value;
288 CHECK(it.ReadDouble(&json_value.as_double));
289 maybe_append_key_name(state_stack[current_state_index], &it, out);
290 json_value.AppendAsJSON(TRACE_VALUE_TYPE_DOUBLE, out);
291 break;
292 }
293
294 case kTypeString: {
295 std::string value;
296 CHECK(it.ReadString(&value));
297 maybe_append_key_name(state_stack[current_state_index], &it, out);
298 TraceEvent::TraceValue json_value;
299 json_value.as_string = value.c_str();
300 json_value.AppendAsJSON(TRACE_VALUE_TYPE_STRING, out);
301 break;
302 }
303
304 default:
305 NOTREACHED();
306 }
307
308 state_stack[current_state_index].needs_comma = true;
309 }
310
311 out->append("}");
312 state_stack.pop_back();
313
314 DCHECK(state_stack.empty());
315 }
316
EstimateTraceMemoryOverhead(TraceEventMemoryOverhead * overhead)317 void EstimateTraceMemoryOverhead(
318 TraceEventMemoryOverhead* overhead) override {
319 overhead->Add(TraceEventMemoryOverhead::kTracedValue,
320 /* allocated size */
321 pickle_.GetTotalAllocatedSize(),
322 /* resident size */
323 pickle_.size());
324 }
325
ToBaseValue() const326 std::unique_ptr<base::Value> ToBaseValue() const {
327 base::Value root(base::Value::Type::DICTIONARY);
328 Value* cur_dict = &root;
329 Value* cur_list = nullptr;
330 std::vector<Value*> stack;
331 PickleIterator it(pickle_);
332 const char* type;
333
334 while (it.ReadBytes(&type, 1)) {
335 DCHECK((cur_dict && !cur_list) || (cur_list && !cur_dict));
336 switch (*type) {
337 case kTypeStartDict: {
338 base::Value new_dict(base::Value::Type::DICTIONARY);
339 if (cur_dict) {
340 stack.push_back(cur_dict);
341 cur_dict = cur_dict->SetKey(ReadKeyName(it), std::move(new_dict));
342 } else {
343 cur_list->Append(std::move(new_dict));
344 // |new_dict| is invalidated at this point, so |cur_dict| needs to
345 // be reset.
346 cur_dict = &cur_list->GetList().back();
347 stack.push_back(cur_list);
348 cur_list = nullptr;
349 }
350 } break;
351
352 case kTypeEndArray:
353 case kTypeEndDict: {
354 if (stack.back()->is_dict()) {
355 cur_dict = stack.back();
356 cur_list = nullptr;
357 } else if (stack.back()->is_list()) {
358 cur_list = stack.back();
359 cur_dict = nullptr;
360 }
361 stack.pop_back();
362 } break;
363
364 case kTypeStartArray: {
365 base::Value new_list(base::Value::Type::LIST);
366 if (cur_dict) {
367 stack.push_back(cur_dict);
368 cur_list = cur_dict->SetKey(ReadKeyName(it), std::move(new_list));
369 cur_dict = nullptr;
370 } else {
371 cur_list->Append(std::move(new_list));
372 stack.push_back(cur_list);
373 // |cur_list| is invalidated at this point by the Append, so it
374 // needs to be reset.
375 cur_list = &cur_list->GetList().back();
376 }
377 } break;
378
379 case kTypeBool: {
380 bool value;
381 CHECK(it.ReadBool(&value));
382 if (cur_dict) {
383 cur_dict->SetBoolKey(ReadKeyName(it), value);
384 } else {
385 cur_list->Append(value);
386 }
387 } break;
388
389 case kTypeInt: {
390 int value;
391 CHECK(it.ReadInt(&value));
392 if (cur_dict) {
393 cur_dict->SetIntKey(ReadKeyName(it), value);
394 } else {
395 cur_list->Append(value);
396 }
397 } break;
398
399 case kTypeDouble: {
400 TraceEvent::TraceValue trace_value;
401 CHECK(it.ReadDouble(&trace_value.as_double));
402 Value base_value;
403 if (!std::isfinite(trace_value.as_double)) {
404 // base::Value doesn't support nan and infinity values. Use strings
405 // for them instead. This follows the same convention in
406 // AppendAsTraceFormat(), supported by TraceValue::Append*().
407 std::string value_string;
408 trace_value.AppendAsString(TRACE_VALUE_TYPE_DOUBLE, &value_string);
409 base_value = Value(value_string);
410 } else {
411 base_value = Value(trace_value.as_double);
412 }
413 if (cur_dict) {
414 cur_dict->SetKey(ReadKeyName(it), std::move(base_value));
415 } else {
416 cur_list->Append(std::move(base_value));
417 }
418 } break;
419
420 case kTypeString: {
421 std::string value;
422 CHECK(it.ReadString(&value));
423 if (cur_dict) {
424 cur_dict->SetStringKey(ReadKeyName(it), std::move(value));
425 } else {
426 cur_list->Append(std::move(value));
427 }
428 } break;
429
430 default:
431 NOTREACHED();
432 }
433 }
434 DCHECK(stack.empty());
435 return base::Value::ToUniquePtrValue(std::move(root));
436 }
437
438 private:
439 Pickle pickle_;
440 };
441
CreateWriter(size_t capacity)442 std::unique_ptr<TracedValue::Writer> CreateWriter(size_t capacity) {
443 TracedValue::WriterFactoryCallback callback =
444 g_writer_factory_callback.load(std::memory_order_relaxed);
445 if (callback) {
446 return callback(capacity);
447 }
448
449 return std::make_unique<PickleWriter>(capacity);
450 }
451
452 } // namespace
453
AppendToProto(ProtoAppender * appender)454 bool TracedValue::Writer::AppendToProto(ProtoAppender* appender) {
455 return false;
456 }
457
458 // static
SetWriterFactoryCallback(WriterFactoryCallback callback)459 void TracedValue::SetWriterFactoryCallback(WriterFactoryCallback callback) {
460 g_writer_factory_callback.store(callback);
461 }
462
TracedValue(size_t capacity)463 TracedValue::TracedValue(size_t capacity)
464 : TracedValue(capacity, /*forced_json*/ false) {}
465
TracedValue(size_t capacity,bool forced_json)466 TracedValue::TracedValue(size_t capacity, bool forced_json) {
467 DEBUG_PUSH_CONTAINER(kStackTypeDict);
468
469 writer_ = forced_json ? std::make_unique<PickleWriter>(capacity)
470 : CreateWriter(capacity);
471 }
472
~TracedValue()473 TracedValue::~TracedValue() {
474 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
475 DEBUG_POP_CONTAINER();
476 DCHECK_CONTAINER_STACK_DEPTH_EQ(0u);
477 }
478
SetInteger(const char * name,int value)479 void TracedValue::SetInteger(const char* name, int value) {
480 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
481 writer_->SetInteger(name, value);
482 }
483
SetIntegerWithCopiedName(base::StringPiece name,int value)484 void TracedValue::SetIntegerWithCopiedName(base::StringPiece name, int value) {
485 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
486 writer_->SetIntegerWithCopiedName(name, value);
487 }
488
SetDouble(const char * name,double value)489 void TracedValue::SetDouble(const char* name, double value) {
490 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
491 writer_->SetDouble(name, value);
492 }
493
SetDoubleWithCopiedName(base::StringPiece name,double value)494 void TracedValue::SetDoubleWithCopiedName(base::StringPiece name,
495 double value) {
496 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
497 writer_->SetDoubleWithCopiedName(name, value);
498 }
499
SetBoolean(const char * name,bool value)500 void TracedValue::SetBoolean(const char* name, bool value) {
501 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
502 writer_->SetBoolean(name, value);
503 }
504
SetBooleanWithCopiedName(base::StringPiece name,bool value)505 void TracedValue::SetBooleanWithCopiedName(base::StringPiece name, bool value) {
506 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
507 writer_->SetBooleanWithCopiedName(name, value);
508 }
509
SetString(const char * name,base::StringPiece value)510 void TracedValue::SetString(const char* name, base::StringPiece value) {
511 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
512 writer_->SetString(name, value);
513 }
514
SetStringWithCopiedName(base::StringPiece name,base::StringPiece value)515 void TracedValue::SetStringWithCopiedName(base::StringPiece name,
516 base::StringPiece value) {
517 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
518 writer_->SetStringWithCopiedName(name, value);
519 }
520
SetValue(const char * name,TracedValue * value)521 void TracedValue::SetValue(const char* name, TracedValue* value) {
522 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
523 writer_->SetValue(name, value->writer_.get());
524 }
525
SetValueWithCopiedName(base::StringPiece name,TracedValue * value)526 void TracedValue::SetValueWithCopiedName(base::StringPiece name,
527 TracedValue* value) {
528 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
529 writer_->SetValueWithCopiedName(name, value->writer_.get());
530 }
531
BeginDictionary(const char * name)532 void TracedValue::BeginDictionary(const char* name) {
533 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
534 DEBUG_PUSH_CONTAINER(kStackTypeDict);
535 writer_->BeginDictionary(name);
536 }
537
BeginDictionaryWithCopiedName(base::StringPiece name)538 void TracedValue::BeginDictionaryWithCopiedName(base::StringPiece name) {
539 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
540 DEBUG_PUSH_CONTAINER(kStackTypeDict);
541 writer_->BeginDictionaryWithCopiedName(name);
542 }
543
BeginArray(const char * name)544 void TracedValue::BeginArray(const char* name) {
545 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
546 DEBUG_PUSH_CONTAINER(kStackTypeArray);
547 writer_->BeginArray(name);
548 }
549
BeginArrayWithCopiedName(base::StringPiece name)550 void TracedValue::BeginArrayWithCopiedName(base::StringPiece name) {
551 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
552 DEBUG_PUSH_CONTAINER(kStackTypeArray);
553 writer_->BeginArrayWithCopiedName(name);
554 }
555
AppendInteger(int value)556 void TracedValue::AppendInteger(int value) {
557 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
558 writer_->AppendInteger(value);
559 }
560
AppendDouble(double value)561 void TracedValue::AppendDouble(double value) {
562 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
563 writer_->AppendDouble(value);
564 }
565
AppendBoolean(bool value)566 void TracedValue::AppendBoolean(bool value) {
567 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
568 writer_->AppendBoolean(value);
569 }
570
AppendString(base::StringPiece value)571 void TracedValue::AppendString(base::StringPiece value) {
572 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
573 writer_->AppendString(value);
574 }
575
BeginArray()576 void TracedValue::BeginArray() {
577 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
578 DEBUG_PUSH_CONTAINER(kStackTypeArray);
579 writer_->BeginArray();
580 }
581
BeginDictionary()582 void TracedValue::BeginDictionary() {
583 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
584 DEBUG_PUSH_CONTAINER(kStackTypeDict);
585 writer_->BeginDictionary();
586 }
587
EndArray()588 void TracedValue::EndArray() {
589 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
590 DEBUG_POP_CONTAINER();
591 writer_->EndArray();
592 }
593
EndDictionary()594 void TracedValue::EndDictionary() {
595 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
596 DEBUG_POP_CONTAINER();
597 writer_->EndDictionary();
598 }
599
ToBaseValue() const600 std::unique_ptr<base::Value> TracedValue::ToBaseValue() const {
601 DCHECK(writer_->IsPickleWriter());
602 return static_cast<const PickleWriter*>(writer_.get())->ToBaseValue();
603 }
604
AppendAsTraceFormat(std::string * out) const605 void TracedValue::AppendAsTraceFormat(std::string* out) const {
606 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
607 DCHECK_CONTAINER_STACK_DEPTH_EQ(1u);
608
609 writer_->AppendAsTraceFormat(out);
610 }
611
AppendToProto(ProtoAppender * appender)612 bool TracedValue::AppendToProto(ProtoAppender* appender) {
613 return writer_->AppendToProto(appender);
614 }
615
EstimateTraceMemoryOverhead(TraceEventMemoryOverhead * overhead)616 void TracedValue::EstimateTraceMemoryOverhead(
617 TraceEventMemoryOverhead* overhead) {
618 writer_->EstimateTraceMemoryOverhead(overhead);
619 }
620
ToJSON() const621 std::string TracedValueJSON::ToJSON() const {
622 std::string result;
623 AppendAsTraceFormat(&result);
624 return result;
625 }
626
ToFormattedJSON() const627 std::string TracedValueJSON::ToFormattedJSON() const {
628 std::string str;
629 base::JSONWriter::WriteWithOptions(
630 *ToBaseValue(),
631 base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION |
632 base::JSONWriter::OPTIONS_PRETTY_PRINT,
633 &str);
634 return str;
635 }
636
637 } // namespace trace_event
638 } // namespace base
639