summaryrefslogtreecommitdiff
path: root/base/trace_event/trace_event_argument.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/trace_event/trace_event_argument.cc')
-rw-r--r--base/trace_event/trace_event_argument.cc576
1 files changed, 576 insertions, 0 deletions
diff --git a/base/trace_event/trace_event_argument.cc b/base/trace_event/trace_event_argument.cc
new file mode 100644
index 0000000000..e614b272d5
--- /dev/null
+++ b/base/trace_event/trace_event_argument.cc
@@ -0,0 +1,576 @@
+// 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