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