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"
8 #include "base/json/json_writer.h"
9 #include "base/trace_event/trace_event_memory_overhead.h"
10 #include "base/values.h"
13 namespace trace_event
{
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
= '*';
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()
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)
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);
54 if (res
&& *type
== kTypeCStr
) {
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
);
66 TracedValue::TracedValue() : TracedValue(0) {
69 TracedValue::TracedValue(size_t capacity
) {
70 DEBUG_PUSH_CONTAINER(kStackTypeDict
);
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
,
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
,
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()));
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()));
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
:
246 case base::Value::TYPE_BOOLEAN
: {
248 value
.GetAsBoolean(&bool_value
);
249 SetBooleanWithCopiedName(name
, bool_value
);
252 case base::Value::TYPE_INTEGER
: {
254 value
.GetAsInteger(&int_value
);
255 SetIntegerWithCopiedName(name
, int_value
);
258 case base::Value::TYPE_DOUBLE
: {
260 value
.GetAsDouble(&double_value
);
261 SetDoubleWithCopiedName(name
, double_value
);
264 case base::Value::TYPE_STRING
: {
265 const StringValue
* string_value
;
266 value
.GetAsString(&string_value
);
267 SetStringWithCopiedName(name
, string_value
->GetString());
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();
276 SetBaseValueWithCopiedName(it
.key(), it
.value());
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
);
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
:
300 case base::Value::TYPE_BOOLEAN
: {
302 value
.GetAsBoolean(&bool_value
);
303 AppendBoolean(bool_value
);
306 case base::Value::TYPE_INTEGER
: {
308 value
.GetAsInteger(&int_value
);
309 AppendInteger(int_value
);
312 case base::Value::TYPE_DOUBLE
: {
314 value
.GetAsDouble(&double_value
);
315 AppendDouble(double_value
);
318 case base::Value::TYPE_STRING
: {
319 const StringValue
* string_value
;
320 value
.GetAsString(&string_value
);
321 AppendString(string_value
->GetString());
324 case base::Value::TYPE_DICTIONARY
: {
325 const DictionaryValue
* dict_value
;
326 value
.GetAsDictionary(&dict_value
);
328 for (DictionaryValue::Iterator
it(*dict_value
); !it
.IsAtEnd();
330 SetBaseValueWithCopiedName(it
.key(), it
.value());
335 case base::Value::TYPE_LIST
: {
336 const ListValue
* list_value
;
337 value
.GetAsList(&list_value
);
339 for (base::Value
* base_value
: *list_value
)
340 AppendBaseValue(*base_value
);
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_
);
354 while (it
.ReadBytes(&type
, 1)) {
355 DCHECK((cur_dict
&& !cur_list
) || (cur_list
&& !cur_dict
));
357 case kTypeStartDict
: {
358 auto new_dict
= new DictionaryValue();
360 cur_dict
->SetWithoutPathExpansion(ReadKeyName(it
),
361 make_scoped_ptr(new_dict
));
362 stack
.push_back(cur_dict
);
365 cur_list
->Append(make_scoped_ptr(new_dict
));
366 stack
.push_back(cur_list
);
374 if (stack
.back()->GetAsDictionary(&cur_dict
)) {
376 } else if (stack
.back()->GetAsList(&cur_list
)) {
382 case kTypeStartArray
: {
383 auto new_list
= new ListValue();
385 cur_dict
->SetWithoutPathExpansion(ReadKeyName(it
),
386 make_scoped_ptr(new_list
));
387 stack
.push_back(cur_dict
);
391 cur_list
->Append(make_scoped_ptr(new_list
));
392 stack
.push_back(cur_list
);
399 CHECK(it
.ReadBool(&value
));
401 cur_dict
->SetBooleanWithoutPathExpansion(ReadKeyName(it
), value
);
403 cur_list
->AppendBoolean(value
);
409 CHECK(it
.ReadInt(&value
));
411 cur_dict
->SetIntegerWithoutPathExpansion(ReadKeyName(it
), value
);
413 cur_list
->AppendInteger(value
);
419 CHECK(it
.ReadDouble(&value
));
421 cur_dict
->SetDoubleWithoutPathExpansion(ReadKeyName(it
), value
);
423 cur_list
->AppendDouble(value
);
429 CHECK(it
.ReadString(&value
));
431 cur_dict
->SetStringWithoutPathExpansion(ReadKeyName(it
), value
);
433 cur_list
->AppendString(value
);
441 DCHECK(stack
.empty());
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
453 JSONWriter::Write(*ToBaseValue(), &tmp
);
457 void TracedValue::EstimateTraceMemoryOverhead(
458 TraceEventMemoryOverhead
* overhead
) {
459 const size_t kPickleHeapAlign
= 4096; // Must be == Pickle::kPickleHeapAlign.
460 overhead
->Add("TracedValue",
463 bits::Align(pickle_
.GetTotalAllocatedSize(), kPickleHeapAlign
),
466 bits::Align(pickle_
.size(), kPickleHeapAlign
));
469 } // namespace trace_event