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