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/json/json_writer.h"
13 #include "base/trace_event/trace_event_memory_overhead.h"
14 #include "base/values.h"
15 
16 namespace base {
17 namespace trace_event {
18 
19 namespace {
20 const char kTypeStartDict = '{';
21 const char kTypeEndDict = '}';
22 const char kTypeStartArray = '[';
23 const char kTypeEndArray = ']';
24 const char kTypeBool = 'b';
25 const char kTypeInt = 'i';
26 const char kTypeDouble = 'd';
27 const char kTypeString = 's';
28 const char kTypeCStr = '*';
29 
30 #ifndef NDEBUG
31 const bool kStackTypeDict = false;
32 const bool kStackTypeArray = true;
33 #define DCHECK_CURRENT_CONTAINER_IS(x) DCHECK_EQ(x, nesting_stack_.back())
34 #define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) DCHECK_EQ(x, nesting_stack_.size())
35 #define DEBUG_PUSH_CONTAINER(x) nesting_stack_.push_back(x)
36 #define DEBUG_POP_CONTAINER() nesting_stack_.pop_back()
37 #else
38 #define DCHECK_CURRENT_CONTAINER_IS(x) do {} while (0)
39 #define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) do {} while (0)
40 #define DEBUG_PUSH_CONTAINER(x) do {} while (0)
41 #define DEBUG_POP_CONTAINER() do {} while (0)
42 #endif
43 
WriteKeyNameAsRawPtr(Pickle & pickle,const char * ptr)44 inline void WriteKeyNameAsRawPtr(Pickle& pickle, const char* ptr) {
45   pickle.WriteBytes(&kTypeCStr, 1);
46   pickle.WriteUInt64(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr)));
47 }
48 
WriteKeyNameWithCopy(Pickle & pickle,base::StringPiece str)49 inline void WriteKeyNameWithCopy(Pickle& pickle, base::StringPiece str) {
50   pickle.WriteBytes(&kTypeString, 1);
51   pickle.WriteString(str);
52 }
53 
ReadKeyName(PickleIterator & pickle_iterator)54 std::string ReadKeyName(PickleIterator& pickle_iterator) {
55   const char* type = nullptr;
56   bool res = pickle_iterator.ReadBytes(&type, 1);
57   std::string key_name;
58   if (res && *type == kTypeCStr) {
59     uint64_t ptr_value = 0;
60     res = pickle_iterator.ReadUInt64(&ptr_value);
61     key_name = reinterpret_cast<const char*>(static_cast<uintptr_t>(ptr_value));
62   } else if (res && *type == kTypeString) {
63     res = pickle_iterator.ReadString(&key_name);
64   }
65   DCHECK(res);
66   return key_name;
67 }
68 }  // namespace
69 
TracedValue()70 TracedValue::TracedValue() : TracedValue(0) {
71 }
72 
TracedValue(size_t capacity)73 TracedValue::TracedValue(size_t capacity) {
74   DEBUG_PUSH_CONTAINER(kStackTypeDict);
75   if (capacity)
76     pickle_.Reserve(capacity);
77 }
78 
~TracedValue()79 TracedValue::~TracedValue() {
80   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
81   DEBUG_POP_CONTAINER();
82   DCHECK_CONTAINER_STACK_DEPTH_EQ(0u);
83 }
84 
SetInteger(const char * name,int value)85 void TracedValue::SetInteger(const char* name, int value) {
86   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
87   pickle_.WriteBytes(&kTypeInt, 1);
88   pickle_.WriteInt(value);
89   WriteKeyNameAsRawPtr(pickle_, name);
90 }
91 
SetIntegerWithCopiedName(base::StringPiece name,int value)92 void TracedValue::SetIntegerWithCopiedName(base::StringPiece name, int value) {
93   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
94   pickle_.WriteBytes(&kTypeInt, 1);
95   pickle_.WriteInt(value);
96   WriteKeyNameWithCopy(pickle_, name);
97 }
98 
SetDouble(const char * name,double value)99 void TracedValue::SetDouble(const char* name, double value) {
100   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
101   pickle_.WriteBytes(&kTypeDouble, 1);
102   pickle_.WriteDouble(value);
103   WriteKeyNameAsRawPtr(pickle_, name);
104 }
105 
SetDoubleWithCopiedName(base::StringPiece name,double value)106 void TracedValue::SetDoubleWithCopiedName(base::StringPiece name,
107                                           double value) {
108   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
109   pickle_.WriteBytes(&kTypeDouble, 1);
110   pickle_.WriteDouble(value);
111   WriteKeyNameWithCopy(pickle_, name);
112 }
113 
SetBoolean(const char * name,bool value)114 void TracedValue::SetBoolean(const char* name, bool value) {
115   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
116   pickle_.WriteBytes(&kTypeBool, 1);
117   pickle_.WriteBool(value);
118   WriteKeyNameAsRawPtr(pickle_, name);
119 }
120 
SetBooleanWithCopiedName(base::StringPiece name,bool value)121 void TracedValue::SetBooleanWithCopiedName(base::StringPiece name,
122                                            bool value) {
123   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
124   pickle_.WriteBytes(&kTypeBool, 1);
125   pickle_.WriteBool(value);
126   WriteKeyNameWithCopy(pickle_, name);
127 }
128 
SetString(const char * name,base::StringPiece value)129 void TracedValue::SetString(const char* name, base::StringPiece value) {
130   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
131   pickle_.WriteBytes(&kTypeString, 1);
132   pickle_.WriteString(value);
133   WriteKeyNameAsRawPtr(pickle_, name);
134 }
135 
SetStringWithCopiedName(base::StringPiece name,base::StringPiece value)136 void TracedValue::SetStringWithCopiedName(base::StringPiece name,
137                                           base::StringPiece value) {
138   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
139   pickle_.WriteBytes(&kTypeString, 1);
140   pickle_.WriteString(value);
141   WriteKeyNameWithCopy(pickle_, name);
142 }
143 
SetValue(const char * name,const TracedValue & value)144 void TracedValue::SetValue(const char* name, const TracedValue& value) {
145   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
146   BeginDictionary(name);
147   pickle_.WriteBytes(value.pickle_.payload(),
148                      static_cast<int>(value.pickle_.payload_size()));
149   EndDictionary();
150 }
151 
SetValueWithCopiedName(base::StringPiece name,const TracedValue & value)152 void TracedValue::SetValueWithCopiedName(base::StringPiece name,
153                                          const TracedValue& value) {
154   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
155   BeginDictionaryWithCopiedName(name);
156   pickle_.WriteBytes(value.pickle_.payload(),
157                      static_cast<int>(value.pickle_.payload_size()));
158   EndDictionary();
159 }
160 
BeginDictionary(const char * name)161 void TracedValue::BeginDictionary(const char* name) {
162   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
163   DEBUG_PUSH_CONTAINER(kStackTypeDict);
164   pickle_.WriteBytes(&kTypeStartDict, 1);
165   WriteKeyNameAsRawPtr(pickle_, name);
166 }
167 
BeginDictionaryWithCopiedName(base::StringPiece name)168 void TracedValue::BeginDictionaryWithCopiedName(base::StringPiece name) {
169   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
170   DEBUG_PUSH_CONTAINER(kStackTypeDict);
171   pickle_.WriteBytes(&kTypeStartDict, 1);
172   WriteKeyNameWithCopy(pickle_, name);
173 }
174 
BeginArray(const char * name)175 void TracedValue::BeginArray(const char* name) {
176   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
177   DEBUG_PUSH_CONTAINER(kStackTypeArray);
178   pickle_.WriteBytes(&kTypeStartArray, 1);
179   WriteKeyNameAsRawPtr(pickle_, name);
180 }
181 
BeginArrayWithCopiedName(base::StringPiece name)182 void TracedValue::BeginArrayWithCopiedName(base::StringPiece name) {
183   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
184   DEBUG_PUSH_CONTAINER(kStackTypeArray);
185   pickle_.WriteBytes(&kTypeStartArray, 1);
186   WriteKeyNameWithCopy(pickle_, name);
187 }
188 
EndDictionary()189 void TracedValue::EndDictionary() {
190   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
191   DEBUG_POP_CONTAINER();
192   pickle_.WriteBytes(&kTypeEndDict, 1);
193 }
194 
AppendInteger(int value)195 void TracedValue::AppendInteger(int value) {
196   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
197   pickle_.WriteBytes(&kTypeInt, 1);
198   pickle_.WriteInt(value);
199 }
200 
AppendDouble(double value)201 void TracedValue::AppendDouble(double value) {
202   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
203   pickle_.WriteBytes(&kTypeDouble, 1);
204   pickle_.WriteDouble(value);
205 }
206 
AppendBoolean(bool value)207 void TracedValue::AppendBoolean(bool value) {
208   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
209   pickle_.WriteBytes(&kTypeBool, 1);
210   pickle_.WriteBool(value);
211 }
212 
AppendString(base::StringPiece value)213 void TracedValue::AppendString(base::StringPiece value) {
214   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
215   pickle_.WriteBytes(&kTypeString, 1);
216   pickle_.WriteString(value);
217 }
218 
BeginArray()219 void TracedValue::BeginArray() {
220   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
221   DEBUG_PUSH_CONTAINER(kStackTypeArray);
222   pickle_.WriteBytes(&kTypeStartArray, 1);
223 }
224 
BeginDictionary()225 void TracedValue::BeginDictionary() {
226   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
227   DEBUG_PUSH_CONTAINER(kStackTypeDict);
228   pickle_.WriteBytes(&kTypeStartDict, 1);
229 }
230 
EndArray()231 void TracedValue::EndArray() {
232   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
233   DEBUG_POP_CONTAINER();
234   pickle_.WriteBytes(&kTypeEndArray, 1);
235 }
236 
SetValue(const char * name,scoped_ptr<base::Value> value)237 void TracedValue::SetValue(const char* name, scoped_ptr<base::Value> value) {
238   SetBaseValueWithCopiedName(name, *value);
239 }
240 
SetBaseValueWithCopiedName(base::StringPiece name,const base::Value & value)241 void TracedValue::SetBaseValueWithCopiedName(base::StringPiece name,
242                                              const base::Value& value) {
243   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
244   switch (value.GetType()) {
245     case base::Value::TYPE_NULL:
246     case base::Value::TYPE_BINARY:
247       NOTREACHED();
248       break;
249 
250     case base::Value::TYPE_BOOLEAN: {
251       bool bool_value;
252       value.GetAsBoolean(&bool_value);
253       SetBooleanWithCopiedName(name, bool_value);
254     } break;
255 
256     case base::Value::TYPE_INTEGER: {
257       int int_value;
258       value.GetAsInteger(&int_value);
259       SetIntegerWithCopiedName(name, int_value);
260     } break;
261 
262     case base::Value::TYPE_DOUBLE: {
263       double double_value;
264       value.GetAsDouble(&double_value);
265       SetDoubleWithCopiedName(name, double_value);
266     } break;
267 
268     case base::Value::TYPE_STRING: {
269       const StringValue* string_value;
270       value.GetAsString(&string_value);
271       SetStringWithCopiedName(name, string_value->GetString());
272     } break;
273 
274     case base::Value::TYPE_DICTIONARY: {
275       const DictionaryValue* dict_value;
276       value.GetAsDictionary(&dict_value);
277       BeginDictionaryWithCopiedName(name);
278       for (DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
279            it.Advance()) {
280         SetBaseValueWithCopiedName(it.key(), it.value());
281       }
282       EndDictionary();
283     } break;
284 
285     case base::Value::TYPE_LIST: {
286       const ListValue* list_value;
287       value.GetAsList(&list_value);
288       BeginArrayWithCopiedName(name);
289       for (base::Value* base_value : *list_value)
290         AppendBaseValue(*base_value);
291       EndArray();
292     } break;
293   }
294 }
295 
AppendBaseValue(const base::Value & value)296 void TracedValue::AppendBaseValue(const base::Value& value) {
297   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
298   switch (value.GetType()) {
299     case base::Value::TYPE_NULL:
300     case base::Value::TYPE_BINARY:
301       NOTREACHED();
302       break;
303 
304     case base::Value::TYPE_BOOLEAN: {
305       bool bool_value;
306       value.GetAsBoolean(&bool_value);
307       AppendBoolean(bool_value);
308     } break;
309 
310     case base::Value::TYPE_INTEGER: {
311       int int_value;
312       value.GetAsInteger(&int_value);
313       AppendInteger(int_value);
314     } break;
315 
316     case base::Value::TYPE_DOUBLE: {
317       double double_value;
318       value.GetAsDouble(&double_value);
319       AppendDouble(double_value);
320     } break;
321 
322     case base::Value::TYPE_STRING: {
323       const StringValue* string_value;
324       value.GetAsString(&string_value);
325       AppendString(string_value->GetString());
326     } break;
327 
328     case base::Value::TYPE_DICTIONARY: {
329       const DictionaryValue* dict_value;
330       value.GetAsDictionary(&dict_value);
331       BeginDictionary();
332       for (DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
333            it.Advance()) {
334         SetBaseValueWithCopiedName(it.key(), it.value());
335       }
336       EndDictionary();
337     } break;
338 
339     case base::Value::TYPE_LIST: {
340       const ListValue* list_value;
341       value.GetAsList(&list_value);
342       BeginArray();
343       for (base::Value* base_value : *list_value)
344         AppendBaseValue(*base_value);
345       EndArray();
346     } break;
347   }
348 }
349 
ToBaseValue() const350 scoped_ptr<base::Value> TracedValue::ToBaseValue() const {
351   scoped_ptr<DictionaryValue> root(new DictionaryValue);
352   DictionaryValue* cur_dict = root.get();
353   ListValue* cur_list = nullptr;
354   std::vector<Value*> stack;
355   PickleIterator it(pickle_);
356   const char* type;
357 
358   while (it.ReadBytes(&type, 1)) {
359     DCHECK((cur_dict && !cur_list) || (cur_list && !cur_dict));
360     switch (*type) {
361       case kTypeStartDict: {
362         auto new_dict = new DictionaryValue();
363         if (cur_dict) {
364           cur_dict->SetWithoutPathExpansion(ReadKeyName(it),
365                                             make_scoped_ptr(new_dict));
366           stack.push_back(cur_dict);
367           cur_dict = new_dict;
368         } else {
369           cur_list->Append(make_scoped_ptr(new_dict));
370           stack.push_back(cur_list);
371           cur_list = nullptr;
372           cur_dict = new_dict;
373         }
374       } break;
375 
376       case kTypeEndArray:
377       case kTypeEndDict: {
378         if (stack.back()->GetAsDictionary(&cur_dict)) {
379           cur_list = nullptr;
380         } else if (stack.back()->GetAsList(&cur_list)) {
381           cur_dict = nullptr;
382         }
383         stack.pop_back();
384       } break;
385 
386       case kTypeStartArray: {
387         auto new_list = new ListValue();
388         if (cur_dict) {
389           cur_dict->SetWithoutPathExpansion(ReadKeyName(it),
390                                             make_scoped_ptr(new_list));
391           stack.push_back(cur_dict);
392           cur_dict = nullptr;
393           cur_list = new_list;
394         } else {
395           cur_list->Append(make_scoped_ptr(new_list));
396           stack.push_back(cur_list);
397           cur_list = new_list;
398         }
399       } break;
400 
401       case kTypeBool: {
402         bool value;
403         CHECK(it.ReadBool(&value));
404         if (cur_dict) {
405           cur_dict->SetBooleanWithoutPathExpansion(ReadKeyName(it), value);
406         } else {
407           cur_list->AppendBoolean(value);
408         }
409       } break;
410 
411       case kTypeInt: {
412         int value;
413         CHECK(it.ReadInt(&value));
414         if (cur_dict) {
415           cur_dict->SetIntegerWithoutPathExpansion(ReadKeyName(it), value);
416         } else {
417           cur_list->AppendInteger(value);
418         }
419       } break;
420 
421       case kTypeDouble: {
422         double value;
423         CHECK(it.ReadDouble(&value));
424         if (cur_dict) {
425           cur_dict->SetDoubleWithoutPathExpansion(ReadKeyName(it), value);
426         } else {
427           cur_list->AppendDouble(value);
428         }
429       } break;
430 
431       case kTypeString: {
432         std::string value;
433         CHECK(it.ReadString(&value));
434         if (cur_dict) {
435           cur_dict->SetStringWithoutPathExpansion(ReadKeyName(it), value);
436         } else {
437           cur_list->AppendString(value);
438         }
439       } break;
440 
441       default:
442         NOTREACHED();
443     }
444   }
445   DCHECK(stack.empty());
446   return std::move(root);
447 }
448 
AppendAsTraceFormat(std::string * out) const449 void TracedValue::AppendAsTraceFormat(std::string* out) const {
450   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
451   DCHECK_CONTAINER_STACK_DEPTH_EQ(1u);
452 
453   // TODO(primiano): this could be smarter, skip the ToBaseValue encoding and
454   // produce the JSON on its own. This will require refactoring JSONWriter
455   // to decouple the base::Value traversal from the JSON writing bits
456   std::string tmp;
457   JSONWriter::Write(*ToBaseValue(), &tmp);
458   *out += tmp;
459 }
460 
EstimateTraceMemoryOverhead(TraceEventMemoryOverhead * overhead)461 void TracedValue::EstimateTraceMemoryOverhead(
462     TraceEventMemoryOverhead* overhead) {
463   const size_t kPickleHeapAlign = 4096;  // Must be == Pickle::kPickleHeapAlign.
464   overhead->Add("TracedValue",
465 
466                 /* allocated size */
467                 bits::Align(pickle_.GetTotalAllocatedSize(), kPickleHeapAlign),
468 
469                 /* resident size */
470                 bits::Align(pickle_.size(), kPickleHeapAlign));
471 }
472 
473 }  // namespace trace_event
474 }  // namespace base
475