• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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