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