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