| // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/trace_event/trace_event_argument.h" | 
 |  | 
 | #include <stdint.h> | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/bits.h" | 
 | #include "base/containers/circular_deque.h" | 
 | #include "base/json/string_escape.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/trace_event/trace_event.h" | 
 | #include "base/trace_event/trace_event_impl.h" | 
 | #include "base/trace_event/trace_event_memory_overhead.h" | 
 | #include "base/values.h" | 
 |  | 
 | namespace base { | 
 | namespace trace_event { | 
 |  | 
 | namespace { | 
 | const char kTypeStartDict = '{'; | 
 | const char kTypeEndDict = '}'; | 
 | const char kTypeStartArray = '['; | 
 | const char kTypeEndArray = ']'; | 
 | const char kTypeBool = 'b'; | 
 | const char kTypeInt = 'i'; | 
 | const char kTypeDouble = 'd'; | 
 | const char kTypeString = 's'; | 
 | const char kTypeCStr = '*';  // only used for key names | 
 |  | 
 | #ifndef NDEBUG | 
 | const bool kStackTypeDict = false; | 
 | const bool kStackTypeArray = true; | 
 | #define DCHECK_CURRENT_CONTAINER_IS(x) DCHECK_EQ(x, nesting_stack_.back()) | 
 | #define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) DCHECK_EQ(x, nesting_stack_.size()) | 
 | #define DEBUG_PUSH_CONTAINER(x) nesting_stack_.push_back(x) | 
 | #define DEBUG_POP_CONTAINER() nesting_stack_.pop_back() | 
 | #else | 
 | #define DCHECK_CURRENT_CONTAINER_IS(x) do {} while (0) | 
 | #define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) do {} while (0) | 
 | #define DEBUG_PUSH_CONTAINER(x) do {} while (0) | 
 | #define DEBUG_POP_CONTAINER() do {} while (0) | 
 | #endif | 
 |  | 
 | inline void WriteKeyNameAsRawPtr(Pickle& pickle, const char* ptr) { | 
 |   pickle.WriteBytes(&kTypeCStr, 1); | 
 |   pickle.WriteUInt64(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr))); | 
 | } | 
 |  | 
 | inline void WriteKeyNameWithCopy(Pickle& pickle, base::StringPiece str) { | 
 |   pickle.WriteBytes(&kTypeString, 1); | 
 |   pickle.WriteString(str); | 
 | } | 
 |  | 
 | std::string ReadKeyName(PickleIterator& pickle_iterator) { | 
 |   const char* type = nullptr; | 
 |   bool res = pickle_iterator.ReadBytes(&type, 1); | 
 |   std::string key_name; | 
 |   if (res && *type == kTypeCStr) { | 
 |     uint64_t ptr_value = 0; | 
 |     res = pickle_iterator.ReadUInt64(&ptr_value); | 
 |     key_name = reinterpret_cast<const char*>(static_cast<uintptr_t>(ptr_value)); | 
 |   } else if (res && *type == kTypeString) { | 
 |     res = pickle_iterator.ReadString(&key_name); | 
 |   } | 
 |   DCHECK(res); | 
 |   return key_name; | 
 | } | 
 | }  // namespace | 
 |  | 
 | TracedValue::TracedValue() : TracedValue(0) { | 
 | } | 
 |  | 
 | TracedValue::TracedValue(size_t capacity) { | 
 |   DEBUG_PUSH_CONTAINER(kStackTypeDict); | 
 |   if (capacity) | 
 |     pickle_.Reserve(capacity); | 
 | } | 
 |  | 
 | TracedValue::~TracedValue() { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   DEBUG_POP_CONTAINER(); | 
 |   DCHECK_CONTAINER_STACK_DEPTH_EQ(0u); | 
 | } | 
 |  | 
 | void TracedValue::SetInteger(const char* name, int value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeInt, 1); | 
 |   pickle_.WriteInt(value); | 
 |   WriteKeyNameAsRawPtr(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::SetIntegerWithCopiedName(base::StringPiece name, int value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeInt, 1); | 
 |   pickle_.WriteInt(value); | 
 |   WriteKeyNameWithCopy(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::SetDouble(const char* name, double value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeDouble, 1); | 
 |   pickle_.WriteDouble(value); | 
 |   WriteKeyNameAsRawPtr(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::SetDoubleWithCopiedName(base::StringPiece name, | 
 |                                           double value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeDouble, 1); | 
 |   pickle_.WriteDouble(value); | 
 |   WriteKeyNameWithCopy(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::SetBoolean(const char* name, bool value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeBool, 1); | 
 |   pickle_.WriteBool(value); | 
 |   WriteKeyNameAsRawPtr(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::SetBooleanWithCopiedName(base::StringPiece name, | 
 |                                            bool value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeBool, 1); | 
 |   pickle_.WriteBool(value); | 
 |   WriteKeyNameWithCopy(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::SetString(const char* name, base::StringPiece value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeString, 1); | 
 |   pickle_.WriteString(value); | 
 |   WriteKeyNameAsRawPtr(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::SetStringWithCopiedName(base::StringPiece name, | 
 |                                           base::StringPiece value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeString, 1); | 
 |   pickle_.WriteString(value); | 
 |   WriteKeyNameWithCopy(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::SetValue(const char* name, const TracedValue& value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   BeginDictionary(name); | 
 |   pickle_.WriteBytes(value.pickle_.payload(), | 
 |                      static_cast<int>(value.pickle_.payload_size())); | 
 |   EndDictionary(); | 
 | } | 
 |  | 
 | void TracedValue::SetValueWithCopiedName(base::StringPiece name, | 
 |                                          const TracedValue& value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   BeginDictionaryWithCopiedName(name); | 
 |   pickle_.WriteBytes(value.pickle_.payload(), | 
 |                      static_cast<int>(value.pickle_.payload_size())); | 
 |   EndDictionary(); | 
 | } | 
 |  | 
 | void TracedValue::BeginDictionary(const char* name) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   DEBUG_PUSH_CONTAINER(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeStartDict, 1); | 
 |   WriteKeyNameAsRawPtr(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::BeginDictionaryWithCopiedName(base::StringPiece name) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   DEBUG_PUSH_CONTAINER(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeStartDict, 1); | 
 |   WriteKeyNameWithCopy(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::BeginArray(const char* name) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   DEBUG_PUSH_CONTAINER(kStackTypeArray); | 
 |   pickle_.WriteBytes(&kTypeStartArray, 1); | 
 |   WriteKeyNameAsRawPtr(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::BeginArrayWithCopiedName(base::StringPiece name) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   DEBUG_PUSH_CONTAINER(kStackTypeArray); | 
 |   pickle_.WriteBytes(&kTypeStartArray, 1); | 
 |   WriteKeyNameWithCopy(pickle_, name); | 
 | } | 
 |  | 
 | void TracedValue::EndDictionary() { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   DEBUG_POP_CONTAINER(); | 
 |   pickle_.WriteBytes(&kTypeEndDict, 1); | 
 | } | 
 |  | 
 | void TracedValue::AppendInteger(int value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); | 
 |   pickle_.WriteBytes(&kTypeInt, 1); | 
 |   pickle_.WriteInt(value); | 
 | } | 
 |  | 
 | void TracedValue::AppendDouble(double value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); | 
 |   pickle_.WriteBytes(&kTypeDouble, 1); | 
 |   pickle_.WriteDouble(value); | 
 | } | 
 |  | 
 | void TracedValue::AppendBoolean(bool value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); | 
 |   pickle_.WriteBytes(&kTypeBool, 1); | 
 |   pickle_.WriteBool(value); | 
 | } | 
 |  | 
 | void TracedValue::AppendString(base::StringPiece value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); | 
 |   pickle_.WriteBytes(&kTypeString, 1); | 
 |   pickle_.WriteString(value); | 
 | } | 
 |  | 
 | void TracedValue::BeginArray() { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); | 
 |   DEBUG_PUSH_CONTAINER(kStackTypeArray); | 
 |   pickle_.WriteBytes(&kTypeStartArray, 1); | 
 | } | 
 |  | 
 | void TracedValue::BeginDictionary() { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); | 
 |   DEBUG_PUSH_CONTAINER(kStackTypeDict); | 
 |   pickle_.WriteBytes(&kTypeStartDict, 1); | 
 | } | 
 |  | 
 | void TracedValue::EndArray() { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); | 
 |   DEBUG_POP_CONTAINER(); | 
 |   pickle_.WriteBytes(&kTypeEndArray, 1); | 
 | } | 
 |  | 
 | void TracedValue::SetValue(const char* name, | 
 |                            std::unique_ptr<base::Value> value) { | 
 |   SetBaseValueWithCopiedName(name, *value); | 
 | } | 
 |  | 
 | void TracedValue::SetBaseValueWithCopiedName(base::StringPiece name, | 
 |                                              const base::Value& value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   switch (value.type()) { | 
 |     case base::Value::Type::NONE: | 
 |     case base::Value::Type::BINARY: | 
 |       NOTREACHED(); | 
 |       break; | 
 |  | 
 |     case base::Value::Type::BOOLEAN: { | 
 |       bool bool_value; | 
 |       value.GetAsBoolean(&bool_value); | 
 |       SetBooleanWithCopiedName(name, bool_value); | 
 |     } break; | 
 |  | 
 |     case base::Value::Type::INTEGER: { | 
 |       int int_value; | 
 |       value.GetAsInteger(&int_value); | 
 |       SetIntegerWithCopiedName(name, int_value); | 
 |     } break; | 
 |  | 
 |     case base::Value::Type::DOUBLE: { | 
 |       double double_value; | 
 |       value.GetAsDouble(&double_value); | 
 |       SetDoubleWithCopiedName(name, double_value); | 
 |     } break; | 
 |  | 
 |     case base::Value::Type::STRING: { | 
 |       const Value* string_value; | 
 |       value.GetAsString(&string_value); | 
 |       SetStringWithCopiedName(name, string_value->GetString()); | 
 |     } break; | 
 |  | 
 |     case base::Value::Type::DICTIONARY: { | 
 |       const DictionaryValue* dict_value; | 
 |       value.GetAsDictionary(&dict_value); | 
 |       BeginDictionaryWithCopiedName(name); | 
 |       for (DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd(); | 
 |            it.Advance()) { | 
 |         SetBaseValueWithCopiedName(it.key(), it.value()); | 
 |       } | 
 |       EndDictionary(); | 
 |     } break; | 
 |  | 
 |     case base::Value::Type::LIST: { | 
 |       const ListValue* list_value; | 
 |       value.GetAsList(&list_value); | 
 |       BeginArrayWithCopiedName(name); | 
 |       for (const auto& base_value : *list_value) | 
 |         AppendBaseValue(base_value); | 
 |       EndArray(); | 
 |     } break; | 
 |   } | 
 | } | 
 |  | 
 | void TracedValue::AppendBaseValue(const base::Value& value) { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); | 
 |   switch (value.type()) { | 
 |     case base::Value::Type::NONE: | 
 |     case base::Value::Type::BINARY: | 
 |       NOTREACHED(); | 
 |       break; | 
 |  | 
 |     case base::Value::Type::BOOLEAN: { | 
 |       bool bool_value; | 
 |       value.GetAsBoolean(&bool_value); | 
 |       AppendBoolean(bool_value); | 
 |     } break; | 
 |  | 
 |     case base::Value::Type::INTEGER: { | 
 |       int int_value; | 
 |       value.GetAsInteger(&int_value); | 
 |       AppendInteger(int_value); | 
 |     } break; | 
 |  | 
 |     case base::Value::Type::DOUBLE: { | 
 |       double double_value; | 
 |       value.GetAsDouble(&double_value); | 
 |       AppendDouble(double_value); | 
 |     } break; | 
 |  | 
 |     case base::Value::Type::STRING: { | 
 |       const Value* string_value; | 
 |       value.GetAsString(&string_value); | 
 |       AppendString(string_value->GetString()); | 
 |     } break; | 
 |  | 
 |     case base::Value::Type::DICTIONARY: { | 
 |       const DictionaryValue* dict_value; | 
 |       value.GetAsDictionary(&dict_value); | 
 |       BeginDictionary(); | 
 |       for (DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd(); | 
 |            it.Advance()) { | 
 |         SetBaseValueWithCopiedName(it.key(), it.value()); | 
 |       } | 
 |       EndDictionary(); | 
 |     } break; | 
 |  | 
 |     case base::Value::Type::LIST: { | 
 |       const ListValue* list_value; | 
 |       value.GetAsList(&list_value); | 
 |       BeginArray(); | 
 |       for (const auto& base_value : *list_value) | 
 |         AppendBaseValue(base_value); | 
 |       EndArray(); | 
 |     } break; | 
 |   } | 
 | } | 
 |  | 
 | std::unique_ptr<base::Value> TracedValue::ToBaseValue() const { | 
 |   base::Value root(base::Value::Type::DICTIONARY); | 
 |   Value* cur_dict = &root; | 
 |   Value* cur_list = nullptr; | 
 |   std::vector<Value*> stack; | 
 |   PickleIterator it(pickle_); | 
 |   const char* type; | 
 |  | 
 |   while (it.ReadBytes(&type, 1)) { | 
 |     DCHECK((cur_dict && !cur_list) || (cur_list && !cur_dict)); | 
 |     switch (*type) { | 
 |       case kTypeStartDict: { | 
 |         base::Value new_dict(base::Value::Type::DICTIONARY); | 
 |         if (cur_dict) { | 
 |           stack.push_back(cur_dict); | 
 |           cur_dict = cur_dict->SetKey(ReadKeyName(it), std::move(new_dict)); | 
 |         } else { | 
 |           cur_list->GetList().push_back(std::move(new_dict)); | 
 |           // |new_dict| is invalidated at this point, so |cur_dict| needs to be | 
 |           // reset. | 
 |           cur_dict = &cur_list->GetList().back(); | 
 |           stack.push_back(cur_list); | 
 |           cur_list = nullptr; | 
 |         } | 
 |       } break; | 
 |  | 
 |       case kTypeEndArray: | 
 |       case kTypeEndDict: { | 
 |         if (stack.back()->is_dict()) { | 
 |           cur_dict = stack.back(); | 
 |           cur_list = nullptr; | 
 |         } else if (stack.back()->is_list()) { | 
 |           cur_list = stack.back(); | 
 |           cur_dict = nullptr; | 
 |         } | 
 |         stack.pop_back(); | 
 |       } break; | 
 |  | 
 |       case kTypeStartArray: { | 
 |         base::Value new_list(base::Value::Type::LIST); | 
 |         if (cur_dict) { | 
 |           stack.push_back(cur_dict); | 
 |           cur_list = cur_dict->SetKey(ReadKeyName(it), std::move(new_list)); | 
 |           cur_dict = nullptr; | 
 |         } else { | 
 |           cur_list->GetList().push_back(std::move(new_list)); | 
 |           stack.push_back(cur_list); | 
 |           // |cur_list| is invalidated at this point by the Append, so it needs | 
 |           // to be reset. | 
 |           cur_list = &cur_list->GetList().back(); | 
 |         } | 
 |       } break; | 
 |  | 
 |       case kTypeBool: { | 
 |         bool value; | 
 |         CHECK(it.ReadBool(&value)); | 
 |         base::Value new_bool(value); | 
 |         if (cur_dict) { | 
 |           cur_dict->SetKey(ReadKeyName(it), std::move(new_bool)); | 
 |         } else { | 
 |           cur_list->GetList().push_back(std::move(new_bool)); | 
 |         } | 
 |       } break; | 
 |  | 
 |       case kTypeInt: { | 
 |         int value; | 
 |         CHECK(it.ReadInt(&value)); | 
 |         base::Value new_int(value); | 
 |         if (cur_dict) { | 
 |           cur_dict->SetKey(ReadKeyName(it), std::move(new_int)); | 
 |         } else { | 
 |           cur_list->GetList().push_back(std::move(new_int)); | 
 |         } | 
 |       } break; | 
 |  | 
 |       case kTypeDouble: { | 
 |         double value; | 
 |         CHECK(it.ReadDouble(&value)); | 
 |         base::Value new_double(value); | 
 |         if (cur_dict) { | 
 |           cur_dict->SetKey(ReadKeyName(it), std::move(new_double)); | 
 |         } else { | 
 |           cur_list->GetList().push_back(std::move(new_double)); | 
 |         } | 
 |       } break; | 
 |  | 
 |       case kTypeString: { | 
 |         std::string value; | 
 |         CHECK(it.ReadString(&value)); | 
 |         base::Value new_str(std::move(value)); | 
 |         if (cur_dict) { | 
 |           cur_dict->SetKey(ReadKeyName(it), std::move(new_str)); | 
 |         } else { | 
 |           cur_list->GetList().push_back(std::move(new_str)); | 
 |         } | 
 |       } break; | 
 |  | 
 |       default: | 
 |         NOTREACHED(); | 
 |     } | 
 |   } | 
 |   DCHECK(stack.empty()); | 
 |   return base::Value::ToUniquePtrValue(std::move(root)); | 
 | } | 
 |  | 
 | void TracedValue::AppendAsTraceFormat(std::string* out) const { | 
 |   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); | 
 |   DCHECK_CONTAINER_STACK_DEPTH_EQ(1u); | 
 |  | 
 |   struct State { | 
 |     enum Type { kTypeDict, kTypeArray }; | 
 |     Type type; | 
 |     bool needs_comma; | 
 |   }; | 
 |  | 
 |   auto maybe_append_key_name = [](State current_state, PickleIterator* it, | 
 |                                   std::string* out) { | 
 |     if (current_state.type == State::kTypeDict) { | 
 |       EscapeJSONString(ReadKeyName(*it), true, out); | 
 |       out->append(":"); | 
 |     } | 
 |   }; | 
 |  | 
 |   base::circular_deque<State> state_stack; | 
 |  | 
 |   out->append("{"); | 
 |   state_stack.push_back({State::kTypeDict}); | 
 |  | 
 |   PickleIterator it(pickle_); | 
 |   for (const char* type; it.ReadBytes(&type, 1);) { | 
 |     switch (*type) { | 
 |       case kTypeEndDict: | 
 |         out->append("}"); | 
 |         state_stack.pop_back(); | 
 |         continue; | 
 |  | 
 |       case kTypeEndArray: | 
 |         out->append("]"); | 
 |         state_stack.pop_back(); | 
 |         continue; | 
 |     } | 
 |  | 
 |     // Use an index so it will stay valid across resizes. | 
 |     size_t current_state_index = state_stack.size() - 1; | 
 |     if (state_stack[current_state_index].needs_comma) | 
 |       out->append(","); | 
 |  | 
 |     switch (*type) { | 
 |       case kTypeStartDict: { | 
 |         maybe_append_key_name(state_stack[current_state_index], &it, out); | 
 |         out->append("{"); | 
 |         state_stack.push_back({State::kTypeDict}); | 
 |         break; | 
 |       } | 
 |  | 
 |       case kTypeStartArray: { | 
 |         maybe_append_key_name(state_stack[current_state_index], &it, out); | 
 |         out->append("["); | 
 |         state_stack.push_back({State::kTypeArray}); | 
 |         break; | 
 |       } | 
 |  | 
 |       case kTypeBool: { | 
 |         TraceEvent::TraceValue json_value; | 
 |         CHECK(it.ReadBool(&json_value.as_bool)); | 
 |         maybe_append_key_name(state_stack[current_state_index], &it, out); | 
 |         TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_BOOL, json_value, out); | 
 |         break; | 
 |       } | 
 |  | 
 |       case kTypeInt: { | 
 |         int value; | 
 |         CHECK(it.ReadInt(&value)); | 
 |         maybe_append_key_name(state_stack[current_state_index], &it, out); | 
 |         TraceEvent::TraceValue json_value; | 
 |         json_value.as_int = value; | 
 |         TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_INT, json_value, out); | 
 |         break; | 
 |       } | 
 |  | 
 |       case kTypeDouble: { | 
 |         TraceEvent::TraceValue json_value; | 
 |         CHECK(it.ReadDouble(&json_value.as_double)); | 
 |         maybe_append_key_name(state_stack[current_state_index], &it, out); | 
 |         TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_DOUBLE, json_value, out); | 
 |         break; | 
 |       } | 
 |  | 
 |       case kTypeString: { | 
 |         std::string value; | 
 |         CHECK(it.ReadString(&value)); | 
 |         maybe_append_key_name(state_stack[current_state_index], &it, out); | 
 |         TraceEvent::TraceValue json_value; | 
 |         json_value.as_string = value.c_str(); | 
 |         TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_STRING, json_value, out); | 
 |         break; | 
 |       } | 
 |  | 
 |       default: | 
 |         NOTREACHED(); | 
 |     } | 
 |  | 
 |     state_stack[current_state_index].needs_comma = true; | 
 |   } | 
 |  | 
 |   out->append("}"); | 
 |   state_stack.pop_back(); | 
 |  | 
 |   DCHECK(state_stack.empty()); | 
 | } | 
 |  | 
 | void TracedValue::EstimateTraceMemoryOverhead( | 
 |     TraceEventMemoryOverhead* overhead) { | 
 |   overhead->Add(TraceEventMemoryOverhead::kTracedValue, | 
 |                 /* allocated size */ | 
 |                 pickle_.GetTotalAllocatedSize(), | 
 |                 /* resident size */ | 
 |                 pickle_.size()); | 
 | } | 
 |  | 
 | }  // namespace trace_event | 
 | }  // namespace base |