Home | History | Annotate | Download | only in trace_event
      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