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 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 49 inline void WriteKeyNameWithCopy(Pickle& pickle, base::StringPiece str) { 50 pickle.WriteBytes(&kTypeString, 1); 51 pickle.WriteString(str); 52 } 53 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 70 TracedValue::TracedValue() : TracedValue(0) { 71 } 72 73 TracedValue::TracedValue(size_t capacity) { 74 DEBUG_PUSH_CONTAINER(kStackTypeDict); 75 if (capacity) 76 pickle_.Reserve(capacity); 77 } 78 79 TracedValue::~TracedValue() { 80 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); 81 DEBUG_POP_CONTAINER(); 82 DCHECK_CONTAINER_STACK_DEPTH_EQ(0u); 83 } 84 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 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 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 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 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 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 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 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 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 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 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 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 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 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 189 void TracedValue::EndDictionary() { 190 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); 191 DEBUG_POP_CONTAINER(); 192 pickle_.WriteBytes(&kTypeEndDict, 1); 193 } 194 195 void TracedValue::AppendInteger(int value) { 196 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); 197 pickle_.WriteBytes(&kTypeInt, 1); 198 pickle_.WriteInt(value); 199 } 200 201 void TracedValue::AppendDouble(double value) { 202 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); 203 pickle_.WriteBytes(&kTypeDouble, 1); 204 pickle_.WriteDouble(value); 205 } 206 207 void TracedValue::AppendBoolean(bool value) { 208 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); 209 pickle_.WriteBytes(&kTypeBool, 1); 210 pickle_.WriteBool(value); 211 } 212 213 void TracedValue::AppendString(base::StringPiece value) { 214 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); 215 pickle_.WriteBytes(&kTypeString, 1); 216 pickle_.WriteString(value); 217 } 218 219 void TracedValue::BeginArray() { 220 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); 221 DEBUG_PUSH_CONTAINER(kStackTypeArray); 222 pickle_.WriteBytes(&kTypeStartArray, 1); 223 } 224 225 void TracedValue::BeginDictionary() { 226 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); 227 DEBUG_PUSH_CONTAINER(kStackTypeDict); 228 pickle_.WriteBytes(&kTypeStartDict, 1); 229 } 230 231 void TracedValue::EndArray() { 232 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); 233 DEBUG_POP_CONTAINER(); 234 pickle_.WriteBytes(&kTypeEndArray, 1); 235 } 236 237 void TracedValue::SetValue(const char* name, scoped_ptr<base::Value> value) { 238 SetBaseValueWithCopiedName(name, *value); 239 } 240 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 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 350 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 449 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 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