| Index: base/trace_event/trace_event_argument.cc
|
| diff --git a/base/trace_event/trace_event_argument.cc b/base/trace_event/trace_event_argument.cc
|
| index e83aa7372480b56ef266bc96b8dca3a8c0d3bdc8..2da6258a887d76c55dd2a53941dc1aed2ab7bf71 100644
|
| --- a/base/trace_event/trace_event_argument.cc
|
| +++ b/base/trace_event/trace_event_argument.cc
|
| @@ -5,112 +5,464 @@
|
| #include "base/trace_event/trace_event_argument.h"
|
|
|
| #include "base/json/json_writer.h"
|
| +#include "base/trace_event/trace_event_memory_overhead.h"
|
| #include "base/values.h"
|
|
|
| namespace base {
|
| namespace trace_event {
|
|
|
| -TracedValue::TracedValue() : root_(new DictionaryValue()) {
|
| - stack_.push_back(root_.get());
|
| +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 = '*';
|
| +
|
| +#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>(reinterpret_cast<uintptr_t>(ptr)));
|
| +}
|
| +
|
| +inline void WriteKeyNameAsStdString(Pickle& pickle, const std::string& 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 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_EQ(1u, stack_.size());
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + DEBUG_POP_CONTAINER();
|
| + DCHECK_CONTAINER_STACK_DEPTH_EQ(0u);
|
| }
|
|
|
| void TracedValue::SetInteger(const char* name, int value) {
|
| - GetCurrentDictionary()->SetInteger(name, value);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeInt, 1);
|
| + pickle_.WriteInt(value);
|
| + WriteKeyNameAsRawPtr(pickle_, name);
|
| +}
|
| +
|
| +void TracedValue::SetIntegerWithCopiedName(const std::string& name, int value) {
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeInt, 1);
|
| + pickle_.WriteInt(value);
|
| + WriteKeyNameAsStdString(pickle_, name);
|
| }
|
|
|
| void TracedValue::SetDouble(const char* name, double value) {
|
| - GetCurrentDictionary()->SetDouble(name, value);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeDouble, 1);
|
| + pickle_.WriteDouble(value);
|
| + WriteKeyNameAsRawPtr(pickle_, name);
|
| +}
|
| +
|
| +void TracedValue::SetDoubleWithCopiedName(const std::string& name,
|
| + double value) {
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeDouble, 1);
|
| + pickle_.WriteDouble(value);
|
| + WriteKeyNameAsStdString(pickle_, name);
|
| }
|
|
|
| void TracedValue::SetBoolean(const char* name, bool value) {
|
| - GetCurrentDictionary()->SetBoolean(name, value);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeBool, 1);
|
| + pickle_.WriteBool(value);
|
| + WriteKeyNameAsRawPtr(pickle_, name);
|
| +}
|
| +
|
| +void TracedValue::SetBooleanWithCopiedName(const std::string& name,
|
| + bool value) {
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeBool, 1);
|
| + pickle_.WriteBool(value);
|
| + WriteKeyNameAsStdString(pickle_, name);
|
| }
|
|
|
| void TracedValue::SetString(const char* name, const std::string& value) {
|
| - GetCurrentDictionary()->SetString(name, value);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeString, 1);
|
| + pickle_.WriteString(value);
|
| + WriteKeyNameAsRawPtr(pickle_, name);
|
| +}
|
| +
|
| +void TracedValue::SetStringWithCopiedName(const std::string& name,
|
| + const std::string& value) {
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeString, 1);
|
| + pickle_.WriteString(value);
|
| + WriteKeyNameAsStdString(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::SetValue(const char* name, scoped_ptr<Value> value) {
|
| - GetCurrentDictionary()->Set(name, value.Pass());
|
| +void TracedValue::SetValueWithCopiedName(const std::string& 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) {
|
| - DictionaryValue* dictionary = new DictionaryValue();
|
| - GetCurrentDictionary()->Set(name, make_scoped_ptr(dictionary));
|
| - stack_.push_back(dictionary);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + DEBUG_PUSH_CONTAINER(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeStartDict, 1);
|
| + WriteKeyNameAsRawPtr(pickle_, name);
|
| +}
|
| +
|
| +void TracedValue::BeginDictionaryWithCopiedName(const std::string& name) {
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + DEBUG_PUSH_CONTAINER(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeStartDict, 1);
|
| + WriteKeyNameAsStdString(pickle_, name);
|
| }
|
|
|
| void TracedValue::BeginArray(const char* name) {
|
| - ListValue* array = new ListValue();
|
| - GetCurrentDictionary()->Set(name, make_scoped_ptr(array));
|
| - stack_.push_back(array);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + DEBUG_PUSH_CONTAINER(kStackTypeArray);
|
| + pickle_.WriteBytes(&kTypeStartArray, 1);
|
| + WriteKeyNameAsRawPtr(pickle_, name);
|
| +}
|
| +
|
| +void TracedValue::BeginArrayWithCopiedName(const std::string& name) {
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + DEBUG_PUSH_CONTAINER(kStackTypeArray);
|
| + pickle_.WriteBytes(&kTypeStartArray, 1);
|
| + WriteKeyNameAsStdString(pickle_, name);
|
| }
|
|
|
| void TracedValue::EndDictionary() {
|
| - DCHECK_GT(stack_.size(), 1u);
|
| - DCHECK(GetCurrentDictionary());
|
| - stack_.pop_back();
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + DEBUG_POP_CONTAINER();
|
| + pickle_.WriteBytes(&kTypeEndDict, 1);
|
| }
|
|
|
| void TracedValue::AppendInteger(int value) {
|
| - GetCurrentArray()->AppendInteger(value);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
|
| + pickle_.WriteBytes(&kTypeInt, 1);
|
| + pickle_.WriteInt(value);
|
| }
|
|
|
| void TracedValue::AppendDouble(double value) {
|
| - GetCurrentArray()->AppendDouble(value);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
|
| + pickle_.WriteBytes(&kTypeDouble, 1);
|
| + pickle_.WriteDouble(value);
|
| }
|
|
|
| void TracedValue::AppendBoolean(bool value) {
|
| - GetCurrentArray()->AppendBoolean(value);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
|
| + pickle_.WriteBytes(&kTypeBool, 1);
|
| + pickle_.WriteBool(value);
|
| }
|
|
|
| void TracedValue::AppendString(const std::string& value) {
|
| - GetCurrentArray()->AppendString(value);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
|
| + pickle_.WriteBytes(&kTypeString, 1);
|
| + pickle_.WriteString(value);
|
| }
|
|
|
| void TracedValue::BeginArray() {
|
| - ListValue* array = new ListValue();
|
| - GetCurrentArray()->Append(array);
|
| - stack_.push_back(array);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
|
| + DEBUG_PUSH_CONTAINER(kStackTypeArray);
|
| + pickle_.WriteBytes(&kTypeStartArray, 1);
|
| }
|
|
|
| void TracedValue::BeginDictionary() {
|
| - DictionaryValue* dictionary = new DictionaryValue();
|
| - GetCurrentArray()->Append(dictionary);
|
| - stack_.push_back(dictionary);
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
|
| + DEBUG_PUSH_CONTAINER(kStackTypeDict);
|
| + pickle_.WriteBytes(&kTypeStartDict, 1);
|
| }
|
|
|
| void TracedValue::EndArray() {
|
| - DCHECK_GT(stack_.size(), 1u);
|
| - DCHECK(GetCurrentArray());
|
| - stack_.pop_back();
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
|
| + DEBUG_POP_CONTAINER();
|
| + pickle_.WriteBytes(&kTypeEndArray, 1);
|
| +}
|
| +
|
| +void TracedValue::SetValue(const char* name, scoped_ptr<base::Value> value) {
|
| + SetBaseValueWithCopiedName(name, *value);
|
| }
|
|
|
| -DictionaryValue* TracedValue::GetCurrentDictionary() {
|
| - DCHECK(!stack_.empty());
|
| - DictionaryValue* dictionary = NULL;
|
| - stack_.back()->GetAsDictionary(&dictionary);
|
| - DCHECK(dictionary);
|
| - return dictionary;
|
| +void TracedValue::SetBaseValueWithCopiedName(const std::string& name,
|
| + const base::Value& value) {
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + switch (value.GetType()) {
|
| + case base::Value::TYPE_NULL:
|
| + 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 StringValue* 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 (base::Value* base_value : *list_value)
|
| + AppendBaseValue(*base_value);
|
| + EndArray();
|
| + } break;
|
| + }
|
| }
|
|
|
| -ListValue* TracedValue::GetCurrentArray() {
|
| - DCHECK(!stack_.empty());
|
| - ListValue* list = NULL;
|
| - stack_.back()->GetAsList(&list);
|
| - DCHECK(list);
|
| - return list;
|
| +void TracedValue::AppendBaseValue(const base::Value& value) {
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
|
| + switch (value.GetType()) {
|
| + case base::Value::TYPE_NULL:
|
| + 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 StringValue* 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 (base::Value* base_value : *list_value)
|
| + AppendBaseValue(*base_value);
|
| + EndArray();
|
| + } break;
|
| + }
|
| +}
|
| +
|
| +scoped_ptr<base::Value> TracedValue::ToBaseValue() const {
|
| + scoped_ptr<DictionaryValue> root(new DictionaryValue);
|
| + DictionaryValue* cur_dict = root.get();
|
| + ListValue* 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: {
|
| + auto new_dict = new DictionaryValue();
|
| + if (cur_dict) {
|
| + cur_dict->Set(ReadKeyName(it), make_scoped_ptr(new_dict));
|
| + stack.push_back(cur_dict);
|
| + cur_dict = new_dict;
|
| + } else {
|
| + cur_list->Append(make_scoped_ptr(new_dict));
|
| + stack.push_back(cur_list);
|
| + cur_list = nullptr;
|
| + cur_dict = new_dict;
|
| + }
|
| + } break;
|
| +
|
| + case kTypeEndArray:
|
| + case kTypeEndDict: {
|
| + if (stack.back()->GetAsDictionary(&cur_dict)) {
|
| + cur_list = nullptr;
|
| + } else if (stack.back()->GetAsList(&cur_list)) {
|
| + cur_dict = nullptr;
|
| + }
|
| + stack.pop_back();
|
| + } break;
|
| +
|
| + case kTypeStartArray: {
|
| + auto new_list = new ListValue();
|
| + if (cur_dict) {
|
| + cur_dict->Set(ReadKeyName(it), make_scoped_ptr(new_list));
|
| + stack.push_back(cur_dict);
|
| + cur_dict = nullptr;
|
| + cur_list = new_list;
|
| + } else {
|
| + cur_list->Append(make_scoped_ptr(new_list));
|
| + stack.push_back(cur_list);
|
| + cur_list = new_list;
|
| + }
|
| + } break;
|
| +
|
| + case kTypeBool: {
|
| + bool value;
|
| + CHECK(it.ReadBool(&value));
|
| + if (cur_dict) {
|
| + cur_dict->SetBoolean(ReadKeyName(it), value);
|
| + } else {
|
| + cur_list->AppendBoolean(value);
|
| + }
|
| + } break;
|
| +
|
| + case kTypeInt: {
|
| + int value;
|
| + CHECK(it.ReadInt(&value));
|
| + if (cur_dict) {
|
| + cur_dict->SetInteger(ReadKeyName(it), value);
|
| + } else {
|
| + cur_list->AppendInteger(value);
|
| + }
|
| + } break;
|
| +
|
| + case kTypeDouble: {
|
| + double value;
|
| + CHECK(it.ReadDouble(&value));
|
| + if (cur_dict) {
|
| + cur_dict->SetDouble(ReadKeyName(it), value);
|
| + } else {
|
| + cur_list->AppendDouble(value);
|
| + }
|
| + } break;
|
| +
|
| + case kTypeString: {
|
| + std::string value;
|
| + CHECK(it.ReadString(&value));
|
| + if (cur_dict) {
|
| + cur_dict->SetString(ReadKeyName(it), value);
|
| + } else {
|
| + cur_list->AppendString(value);
|
| + }
|
| + } break;
|
| +
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| + }
|
| + DCHECK(stack.empty());
|
| + return root.Pass();
|
| }
|
|
|
| void TracedValue::AppendAsTraceFormat(std::string* out) const {
|
| + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
|
| + DCHECK_CONTAINER_STACK_DEPTH_EQ(1u);
|
| +
|
| + // TODO(primiano): this could be smarter, skip the ToBaseValue encoding and
|
| + // produce the JSON on its own. This will require refactoring JSONWriter
|
| + // to decouple the base::Value traversal from the JSON writing bits
|
| std::string tmp;
|
| - JSONWriter::Write(*stack_.front(), &tmp);
|
| + JSONWriter::Write(*ToBaseValue(), &tmp);
|
| *out += tmp;
|
| - DCHECK_EQ(1u, stack_.size()) << tmp;
|
| +}
|
| +
|
| +void TracedValue::EstimateTraceMemoryOverhead(
|
| + TraceEventMemoryOverhead* overhead) {
|
| + overhead->Add("TracedValue",
|
| + pickle_.GetTotalAllocatedSize() /* allocated size */,
|
| + pickle_.size() /* resident size */);
|
| }
|
|
|
| } // namespace trace_event
|
|
|