// Copyright 2014 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/trace_event/traced_value.h" #include #include #include #include #include "base/bits.h" #include "base/containers/circular_deque.h" #include "base/json/json_writer.h" #include "base/json/string_escape.h" #include "base/memory/ptr_util.h" #include "base/notreached.h" #include "base/pickle.h" #include "base/strings/stringprintf.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/trace_event/trace_log.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 std::atomic g_writer_factory_callback; #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(reinterpret_cast(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(static_cast(ptr_value)); } else if (res && *type == kTypeString) { res = pickle_iterator.ReadString(&key_name); } DCHECK(res); return key_name; } class PickleWriter final : public TracedValue::Writer { public: explicit PickleWriter(size_t capacity) { if (capacity) { pickle_.Reserve(capacity); } } bool IsPickleWriter() const override { return true; } bool IsProtoWriter() const override { return false; } void SetInteger(const char* name, int value) override { pickle_.WriteBytes(&kTypeInt, 1); pickle_.WriteInt(value); WriteKeyNameAsRawPtr(pickle_, name); } void SetIntegerWithCopiedName(base::StringPiece name, int value) override { pickle_.WriteBytes(&kTypeInt, 1); pickle_.WriteInt(value); WriteKeyNameWithCopy(pickle_, name); } void SetDouble(const char* name, double value) override { pickle_.WriteBytes(&kTypeDouble, 1); pickle_.WriteDouble(value); WriteKeyNameAsRawPtr(pickle_, name); } void SetDoubleWithCopiedName(base::StringPiece name, double value) override { pickle_.WriteBytes(&kTypeDouble, 1); pickle_.WriteDouble(value); WriteKeyNameWithCopy(pickle_, name); } void SetBoolean(const char* name, bool value) override { pickle_.WriteBytes(&kTypeBool, 1); pickle_.WriteBool(value); WriteKeyNameAsRawPtr(pickle_, name); } void SetBooleanWithCopiedName(base::StringPiece name, bool value) override { pickle_.WriteBytes(&kTypeBool, 1); pickle_.WriteBool(value); WriteKeyNameWithCopy(pickle_, name); } void SetString(const char* name, base::StringPiece value) override { pickle_.WriteBytes(&kTypeString, 1); pickle_.WriteString(value); WriteKeyNameAsRawPtr(pickle_, name); } void SetStringWithCopiedName(base::StringPiece name, base::StringPiece value) override { pickle_.WriteBytes(&kTypeString, 1); pickle_.WriteString(value); WriteKeyNameWithCopy(pickle_, name); } void SetValue(const char* name, Writer* value) override { DCHECK(value->IsPickleWriter()); const PickleWriter* pickle_writer = static_cast(value); BeginDictionary(name); pickle_.WriteBytes(pickle_writer->pickle_.payload(), pickle_writer->pickle_.payload_size()); EndDictionary(); } void SetValueWithCopiedName(base::StringPiece name, Writer* value) override { DCHECK(value->IsPickleWriter()); const PickleWriter* pickle_writer = static_cast(value); BeginDictionaryWithCopiedName(name); pickle_.WriteBytes(pickle_writer->pickle_.payload(), pickle_writer->pickle_.payload_size()); EndDictionary(); } void BeginArray() override { pickle_.WriteBytes(&kTypeStartArray, 1); } void BeginDictionary() override { pickle_.WriteBytes(&kTypeStartDict, 1); } void BeginDictionary(const char* name) override { pickle_.WriteBytes(&kTypeStartDict, 1); WriteKeyNameAsRawPtr(pickle_, name); } void BeginDictionaryWithCopiedName(base::StringPiece name) override { pickle_.WriteBytes(&kTypeStartDict, 1); WriteKeyNameWithCopy(pickle_, name); } void BeginArray(const char* name) override { pickle_.WriteBytes(&kTypeStartArray, 1); WriteKeyNameAsRawPtr(pickle_, name); } void BeginArrayWithCopiedName(base::StringPiece name) override { pickle_.WriteBytes(&kTypeStartArray, 1); WriteKeyNameWithCopy(pickle_, name); } void EndDictionary() override { pickle_.WriteBytes(&kTypeEndDict, 1); } void EndArray() override { pickle_.WriteBytes(&kTypeEndArray, 1); } void AppendInteger(int value) override { pickle_.WriteBytes(&kTypeInt, 1); pickle_.WriteInt(value); } void AppendDouble(double value) override { pickle_.WriteBytes(&kTypeDouble, 1); pickle_.WriteDouble(value); } void AppendBoolean(bool value) override { pickle_.WriteBytes(&kTypeBool, 1); pickle_.WriteBool(value); } void AppendString(base::StringPiece value) override { pickle_.WriteBytes(&kTypeString, 1); pickle_.WriteString(value); } void AppendAsTraceFormat(std::string* out) const override { 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_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); json_value.AppendAsJSON(TRACE_VALUE_TYPE_BOOL, 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; json_value.AppendAsJSON(TRACE_VALUE_TYPE_INT, 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); json_value.AppendAsJSON(TRACE_VALUE_TYPE_DOUBLE, 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(); json_value.AppendAsJSON(TRACE_VALUE_TYPE_STRING, out); break; } default: NOTREACHED(); } state_stack[current_state_index].needs_comma = true; } out->append("}"); state_stack.pop_back(); DCHECK(state_stack.empty()); } void EstimateTraceMemoryOverhead( TraceEventMemoryOverhead* overhead) override { overhead->Add(TraceEventMemoryOverhead::kTracedValue, /* allocated size */ pickle_.GetTotalAllocatedSize(), /* resident size */ pickle_.size()); } std::unique_ptr ToBaseValue() const { base::Value root(base::Value::Type::DICT); Value* cur_dict = &root; Value* cur_list = nullptr; std::vector 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: { if (cur_dict) { stack.push_back(cur_dict); cur_dict = cur_dict->GetDict().Set(ReadKeyName(it), Value(Value::Type::DICT)); } else { cur_list->GetList().Append(Value(Value::Type::DICT)); // Update |cur_dict| to point to the newly added dictionary. 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: { Value::List new_list; if (cur_dict) { stack.push_back(cur_dict); cur_list = cur_dict->GetDict().Set(ReadKeyName(it), std::move(new_list)); cur_dict = nullptr; } else { cur_list->GetList().Append(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)); if (cur_dict) { cur_dict->GetDict().Set(ReadKeyName(it), value); } else { cur_list->GetList().Append(value); } } break; case kTypeInt: { int value; CHECK(it.ReadInt(&value)); if (cur_dict) { cur_dict->GetDict().Set(ReadKeyName(it), value); } else { cur_list->GetList().Append(value); } } break; case kTypeDouble: { TraceEvent::TraceValue trace_value; CHECK(it.ReadDouble(&trace_value.as_double)); Value base_value; if (!std::isfinite(trace_value.as_double)) { // base::Value doesn't support nan and infinity values. Use strings // for them instead. This follows the same convention in // AppendAsTraceFormat(), supported by TraceValue::Append*(). std::string value_string; trace_value.AppendAsString(TRACE_VALUE_TYPE_DOUBLE, &value_string); base_value = Value(value_string); } else { base_value = Value(trace_value.as_double); } if (cur_dict) { cur_dict->GetDict().Set(ReadKeyName(it), std::move(base_value)); } else { cur_list->GetList().Append(std::move(base_value)); } } break; case kTypeString: { std::string value; CHECK(it.ReadString(&value)); if (cur_dict) { cur_dict->GetDict().Set(ReadKeyName(it), std::move(value)); } else { cur_list->GetList().Append(std::move(value)); } } break; default: NOTREACHED(); } } DCHECK(stack.empty()); return base::Value::ToUniquePtrValue(std::move(root)); } private: Pickle pickle_; }; std::unique_ptr CreateWriter(size_t capacity) { TracedValue::WriterFactoryCallback callback = g_writer_factory_callback.load(std::memory_order_relaxed); if (callback) { return callback(capacity); } return std::make_unique(capacity); } } // namespace bool TracedValue::Writer::AppendToProto(ProtoAppender* appender) { return false; } // static void TracedValue::SetWriterFactoryCallback(WriterFactoryCallback callback) { g_writer_factory_callback.store(callback); } TracedValue::TracedValue(size_t capacity) : TracedValue(capacity, /*forced_json*/ false) {} TracedValue::TracedValue(size_t capacity, bool forced_json) { DEBUG_PUSH_CONTAINER(kStackTypeDict); writer_ = forced_json ? std::make_unique(capacity) : CreateWriter(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); writer_->SetInteger(name, value); } void TracedValue::SetIntegerWithCopiedName(base::StringPiece name, int value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetIntegerWithCopiedName(name, value); } void TracedValue::SetDouble(const char* name, double value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetDouble(name, value); } void TracedValue::SetDoubleWithCopiedName(base::StringPiece name, double value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetDoubleWithCopiedName(name, value); } void TracedValue::SetBoolean(const char* name, bool value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetBoolean(name, value); } void TracedValue::SetBooleanWithCopiedName(base::StringPiece name, bool value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetBooleanWithCopiedName(name, value); } void TracedValue::SetString(const char* name, base::StringPiece value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetString(name, value); } void TracedValue::SetStringWithCopiedName(base::StringPiece name, base::StringPiece value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetStringWithCopiedName(name, value); } void TracedValue::SetValue(const char* name, TracedValue* value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetValue(name, value->writer_.get()); } void TracedValue::SetValueWithCopiedName(base::StringPiece name, TracedValue* value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetValueWithCopiedName(name, value->writer_.get()); } namespace { // TODO(altimin): Add native support for pointers for nested values in // DebugAnnotation proto. std::string PointerToString(void* value) { return base::StringPrintf( "0x%" PRIx64, static_cast(reinterpret_cast(value))); } } // namespace void TracedValue::SetPointer(const char* name, void* value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetString(name, PointerToString(value)); } void TracedValue::SetPointerWithCopiedName(base::StringPiece name, void* value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); writer_->SetStringWithCopiedName(name, PointerToString(value)); } void TracedValue::BeginDictionary(const char* name) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); DEBUG_PUSH_CONTAINER(kStackTypeDict); writer_->BeginDictionary(name); } void TracedValue::BeginDictionaryWithCopiedName(base::StringPiece name) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); DEBUG_PUSH_CONTAINER(kStackTypeDict); writer_->BeginDictionaryWithCopiedName(name); } void TracedValue::BeginArray(const char* name) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); DEBUG_PUSH_CONTAINER(kStackTypeArray); writer_->BeginArray(name); } void TracedValue::BeginArrayWithCopiedName(base::StringPiece name) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); DEBUG_PUSH_CONTAINER(kStackTypeArray); writer_->BeginArrayWithCopiedName(name); } void TracedValue::AppendInteger(int value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); writer_->AppendInteger(value); } void TracedValue::AppendDouble(double value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); writer_->AppendDouble(value); } void TracedValue::AppendBoolean(bool value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); writer_->AppendBoolean(value); } void TracedValue::AppendString(base::StringPiece value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); writer_->AppendString(value); } void TracedValue::AppendPointer(void* value) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); writer_->AppendString(PointerToString(value)); } void TracedValue::BeginArray() { DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); DEBUG_PUSH_CONTAINER(kStackTypeArray); writer_->BeginArray(); } void TracedValue::BeginDictionary() { DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); DEBUG_PUSH_CONTAINER(kStackTypeDict); writer_->BeginDictionary(); } void TracedValue::EndArray() { DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); DEBUG_POP_CONTAINER(); writer_->EndArray(); } void TracedValue::EndDictionary() { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); DEBUG_POP_CONTAINER(); writer_->EndDictionary(); } std::unique_ptr TracedValue::ToBaseValue() const { DCHECK(writer_->IsPickleWriter()); return static_cast(writer_.get())->ToBaseValue(); } void TracedValue::AppendAsTraceFormat(std::string* out) const { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); DCHECK_CONTAINER_STACK_DEPTH_EQ(1u); writer_->AppendAsTraceFormat(out); } bool TracedValue::AppendToProto(ProtoAppender* appender) const { return writer_->AppendToProto(appender); } void TracedValue::EstimateTraceMemoryOverhead( TraceEventMemoryOverhead* overhead) { writer_->EstimateTraceMemoryOverhead(overhead); } TracedValue::Array::Array(const std::initializer_list items) { items_ = std::move(items); } TracedValue::Array::Array(TracedValue::Array&& other) { items_ = std::move(other.items_); } void TracedValue::Array::WriteToValue(TracedValue* value) const { for (const auto& item : items_) { item.WriteToValue(value); } } TracedValue::Dictionary::Dictionary( const std::initializer_list items) { items_ = items; } TracedValue::Dictionary::Dictionary(TracedValue::Dictionary&& other) { items_ = std::move(other.items_); } void TracedValue::Dictionary::WriteToValue(TracedValue* value) const { for (const auto& item : items_) { item.WriteToValue(value); } } TracedValue::ValueHolder::ValueHolder(int value) { kept_value_.int_value = value; kept_value_type_ = KeptValueType::kIntType; } TracedValue::ValueHolder::ValueHolder(double value) { kept_value_.double_value = value; kept_value_type_ = KeptValueType::kDoubleType; } TracedValue::ValueHolder::ValueHolder(bool value) { kept_value_.bool_value = value; kept_value_type_ = KeptValueType::kBoolType; } TracedValue::ValueHolder::ValueHolder(base::StringPiece value) { kept_value_.string_piece_value = value; kept_value_type_ = KeptValueType::kStringPieceType; } TracedValue::ValueHolder::ValueHolder(std::string value) { new (&kept_value_.std_string_value) std::string(std::move(value)); kept_value_type_ = KeptValueType::kStdStringType; } TracedValue::ValueHolder::ValueHolder(void* value) { kept_value_.void_ptr_value = value; kept_value_type_ = KeptValueType::kVoidPtrType; } TracedValue::ValueHolder::ValueHolder(const char* value) { kept_value_.string_piece_value = value; kept_value_type_ = KeptValueType::kStringPieceType; } TracedValue::ValueHolder::ValueHolder(TracedValue::Dictionary& value) { new (&kept_value_.dictionary_value) TracedValue::Dictionary(std::move(value)); kept_value_type_ = KeptValueType::kDictionaryType; } TracedValue::ValueHolder::ValueHolder(TracedValue::Array& value) { new (&kept_value_.array_value) TracedValue::Array(std::move(value)); kept_value_type_ = KeptValueType::kArrayType; } TracedValue::ValueHolder::ValueHolder(TracedValue::ValueHolder&& other) { // Remember to call a destructor if necessary. if (kept_value_type_ == KeptValueType::kStdStringType) { delete (&kept_value_.std_string_value); } switch (other.kept_value_type_) { case KeptValueType::kIntType: { kept_value_.int_value = other.kept_value_.int_value; break; } case KeptValueType::kDoubleType: { kept_value_.double_value = other.kept_value_.double_value; break; } case KeptValueType::kBoolType: { kept_value_.bool_value = other.kept_value_.bool_value; break; } case KeptValueType::kStringPieceType: { kept_value_.string_piece_value = other.kept_value_.string_piece_value; break; } case KeptValueType::kStdStringType: { new (&kept_value_.std_string_value) std::string(std::move(other.kept_value_.std_string_value)); break; } case KeptValueType::kVoidPtrType: { kept_value_.void_ptr_value = other.kept_value_.void_ptr_value; break; } case KeptValueType::kArrayType: { new (&kept_value_.array_value) TracedValue::Array(std::move(other.kept_value_.array_value)); break; } case KeptValueType::kDictionaryType: { new (&kept_value_.dictionary_value) TracedValue::Dictionary( std::move(other.kept_value_.dictionary_value)); break; } } kept_value_type_ = other.kept_value_type_; } void TracedValue::ValueHolder::WriteToValue(TracedValue* value) const { switch (kept_value_type_) { case KeptValueType::kIntType: { value->AppendInteger(kept_value_.int_value); break; } case KeptValueType::kDoubleType: { value->AppendDouble(kept_value_.double_value); break; } case KeptValueType::kBoolType: { value->AppendBoolean(kept_value_.bool_value); break; } case KeptValueType::kStringPieceType: { value->AppendString(kept_value_.string_piece_value); break; } case KeptValueType::kStdStringType: { value->AppendString(kept_value_.std_string_value); break; } case KeptValueType::kVoidPtrType: { value->AppendPointer(kept_value_.void_ptr_value); break; } case KeptValueType::kArrayType: { value->BeginArray(); kept_value_.array_value.WriteToValue(value); value->EndArray(); break; } case KeptValueType::kDictionaryType: { value->BeginDictionary(); kept_value_.dictionary_value.WriteToValue(value); value->EndDictionary(); break; } } } void TracedValue::ValueHolder::WriteToValue(const char* name, TracedValue* value) const { switch (kept_value_type_) { case KeptValueType::kIntType: { value->SetInteger(name, kept_value_.int_value); break; } case KeptValueType::kDoubleType: { value->SetDouble(name, kept_value_.double_value); break; } case KeptValueType::kBoolType: { value->SetBoolean(name, kept_value_.bool_value); break; } case KeptValueType::kStringPieceType: { value->SetString(name, kept_value_.string_piece_value); break; } case KeptValueType::kStdStringType: { value->SetString(name, kept_value_.std_string_value); break; } case KeptValueType::kVoidPtrType: { value->SetPointer(name, kept_value_.void_ptr_value); break; } case KeptValueType::kArrayType: { value->BeginArray(name); kept_value_.array_value.WriteToValue(value); value->EndArray(); break; } case KeptValueType::kDictionaryType: { value->BeginDictionary(name); kept_value_.dictionary_value.WriteToValue(value); value->EndDictionary(); break; } } } void TracedValue::ArrayItem::WriteToValue(TracedValue* value) const { ValueHolder::WriteToValue(value); } void TracedValue::DictionaryItem::WriteToValue(TracedValue* value) const { ValueHolder::WriteToValue(name_, value); } std::unique_ptr TracedValue::Build( const std::initializer_list items) { std::unique_ptr value(new TracedValue()); for (const auto& item : items) { item.WriteToValue(value.get()); } return value; } std::string TracedValueJSON::ToJSON() const { std::string result; AppendAsTraceFormat(&result); return result; } std::string TracedValueJSON::ToFormattedJSON() const { std::string str; base::JSONWriter::WriteWithOptions( *ToBaseValue(), base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION | base::JSONWriter::OPTIONS_PRETTY_PRINT, &str); return str; } TracedValue::ArrayScope::ArrayScope(TracedValue* value) : value_(value) {} TracedValue::ArrayScope::~ArrayScope() { value_->EndArray(); } TracedValue::ArrayScope TracedValue::AppendArrayScoped() { BeginArray(); return TracedValue::ArrayScope(this); } TracedValue::ArrayScope TracedValue::BeginArrayScoped(const char* name) { BeginArray(name); return TracedValue::ArrayScope(this); } TracedValue::ArrayScope TracedValue::BeginArrayScopedWithCopiedName( base::StringPiece name) { BeginArrayWithCopiedName(name); return TracedValue::ArrayScope(this); } TracedValue::DictionaryScope::DictionaryScope(TracedValue* value) : value_(value) {} TracedValue::DictionaryScope::~DictionaryScope() { value_->EndDictionary(); } TracedValue::DictionaryScope TracedValue::AppendDictionaryScoped() { BeginDictionary(); return TracedValue::DictionaryScope(this); } TracedValue::DictionaryScope TracedValue::BeginDictionaryScoped( const char* name) { BeginDictionary(name); return TracedValue::DictionaryScope(this); } TracedValue::DictionaryScope TracedValue::BeginDictionaryScopedWithCopiedName( base::StringPiece name) { BeginDictionaryWithCopiedName(name); return TracedValue::DictionaryScope(this); } } // namespace trace_event } // namespace base