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