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/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