| // 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 <inttypes.h> |
| #include <stdint.h> |
| |
| #include <atomic> |
| #include <utility> |
| |
| #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<TracedValue::WriterFactoryCallback> 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<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; |
| } |
| |
| 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<const PickleWriter*>(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<const PickleWriter*>(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> 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<base::Value> ToBaseValue() const { |
| base::Value root(base::Value::Type::DICT); |
| 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: { |
| 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<TracedValue::Writer> 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<PickleWriter>(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<PickleWriter>(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<uint64_t>(reinterpret_cast<uintptr_t>(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<base::Value> TracedValue::ToBaseValue() const { |
| DCHECK(writer_->IsPickleWriter()); |
| return static_cast<const PickleWriter*>(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<ArrayItem> 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<DictionaryItem> 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> TracedValue::Build( |
| const std::initializer_list<DictionaryItem> items) { |
| std::unique_ptr<TracedValue> 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 |