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