TracedValue.
Add a class for writing JSON-like structured data traces, composed from
primitive types like integers and strings as well as complex types like
dicts and arrays.
R=primiano@google.com,eseckler@google.com,skyostil@google.com
Change-Id: I85b011fb828f60ff570185d51ebf47b7dc074b7a
diff --git a/Android.bp b/Android.bp
index 221cbb3..db620dd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8162,6 +8162,7 @@
"src/tracing/internal/tracing_muxer_impl.cc",
"src/tracing/internal/track_event_internal.cc",
"src/tracing/platform.cc",
+ "src/tracing/traced_value.cc",
"src/tracing/tracing.cc",
"src/tracing/track.cc",
"src/tracing/track_event_category_registry.cc",
@@ -8344,6 +8345,14 @@
],
}
+// GN: //src/tracing:unittests
+filegroup {
+ name: "perfetto_src_tracing_unittests",
+ srcs: [
+ "src/tracing/traced_value_unittest.cc",
+ ],
+}
+
// GN: //test:end_to_end_integrationtests
filegroup {
name: "perfetto_test_end_to_end_integrationtests",
@@ -8543,63 +8552,90 @@
":perfetto_include_perfetto_tracing_core_forward_decls",
":perfetto_include_perfetto_tracing_tracing",
":perfetto_protos_perfetto_common_cpp_gen",
+ ":perfetto_protos_perfetto_common_lite_gen",
":perfetto_protos_perfetto_common_zero_gen",
":perfetto_protos_perfetto_config_android_cpp_gen",
+ ":perfetto_protos_perfetto_config_android_lite_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
+ ":perfetto_protos_perfetto_config_ftrace_lite_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
+ ":perfetto_protos_perfetto_config_gpu_lite_gen",
":perfetto_protos_perfetto_config_gpu_zero_gen",
":perfetto_protos_perfetto_config_inode_file_cpp_gen",
+ ":perfetto_protos_perfetto_config_inode_file_lite_gen",
":perfetto_protos_perfetto_config_inode_file_zero_gen",
":perfetto_protos_perfetto_config_interceptors_cpp_gen",
+ ":perfetto_protos_perfetto_config_interceptors_lite_gen",
":perfetto_protos_perfetto_config_interceptors_zero_gen",
+ ":perfetto_protos_perfetto_config_lite_gen",
":perfetto_protos_perfetto_config_power_cpp_gen",
+ ":perfetto_protos_perfetto_config_power_lite_gen",
":perfetto_protos_perfetto_config_power_zero_gen",
":perfetto_protos_perfetto_config_process_stats_cpp_gen",
+ ":perfetto_protos_perfetto_config_process_stats_lite_gen",
":perfetto_protos_perfetto_config_process_stats_zero_gen",
":perfetto_protos_perfetto_config_profiling_cpp_gen",
+ ":perfetto_protos_perfetto_config_profiling_lite_gen",
":perfetto_protos_perfetto_config_profiling_zero_gen",
":perfetto_protos_perfetto_config_sys_stats_cpp_gen",
+ ":perfetto_protos_perfetto_config_sys_stats_lite_gen",
":perfetto_protos_perfetto_config_sys_stats_zero_gen",
":perfetto_protos_perfetto_config_track_event_cpp_gen",
+ ":perfetto_protos_perfetto_config_track_event_lite_gen",
":perfetto_protos_perfetto_config_track_event_zero_gen",
":perfetto_protos_perfetto_config_zero_gen",
":perfetto_protos_perfetto_ipc_cpp_gen",
":perfetto_protos_perfetto_ipc_ipc_gen",
":perfetto_protos_perfetto_ipc_wire_protocol_cpp_gen",
":perfetto_protos_perfetto_trace_android_cpp_gen",
+ ":perfetto_protos_perfetto_trace_android_lite_gen",
":perfetto_protos_perfetto_trace_android_zero_gen",
":perfetto_protos_perfetto_trace_chrome_cpp_gen",
+ ":perfetto_protos_perfetto_trace_chrome_lite_gen",
":perfetto_protos_perfetto_trace_chrome_zero_gen",
":perfetto_protos_perfetto_trace_filesystem_cpp_gen",
+ ":perfetto_protos_perfetto_trace_filesystem_lite_gen",
":perfetto_protos_perfetto_trace_filesystem_zero_gen",
":perfetto_protos_perfetto_trace_ftrace_cpp_gen",
+ ":perfetto_protos_perfetto_trace_ftrace_lite_gen",
":perfetto_protos_perfetto_trace_ftrace_zero_gen",
":perfetto_protos_perfetto_trace_gpu_cpp_gen",
+ ":perfetto_protos_perfetto_trace_gpu_lite_gen",
":perfetto_protos_perfetto_trace_gpu_zero_gen",
":perfetto_protos_perfetto_trace_interned_data_cpp_gen",
+ ":perfetto_protos_perfetto_trace_interned_data_lite_gen",
":perfetto_protos_perfetto_trace_interned_data_zero_gen",
":perfetto_protos_perfetto_trace_minimal_cpp_gen",
+ ":perfetto_protos_perfetto_trace_minimal_lite_gen",
":perfetto_protos_perfetto_trace_minimal_zero_gen",
":perfetto_protos_perfetto_trace_non_minimal_cpp_gen",
+ ":perfetto_protos_perfetto_trace_non_minimal_lite_gen",
":perfetto_protos_perfetto_trace_non_minimal_zero_gen",
":perfetto_protos_perfetto_trace_perfetto_cpp_gen",
+ ":perfetto_protos_perfetto_trace_perfetto_lite_gen",
":perfetto_protos_perfetto_trace_perfetto_zero_gen",
":perfetto_protos_perfetto_trace_power_cpp_gen",
+ ":perfetto_protos_perfetto_trace_power_lite_gen",
":perfetto_protos_perfetto_trace_power_zero_gen",
":perfetto_protos_perfetto_trace_processor_metrics_impl_zero_gen",
":perfetto_protos_perfetto_trace_processor_zero_gen",
":perfetto_protos_perfetto_trace_profiling_cpp_gen",
+ ":perfetto_protos_perfetto_trace_profiling_lite_gen",
":perfetto_protos_perfetto_trace_profiling_zero_gen",
":perfetto_protos_perfetto_trace_ps_cpp_gen",
+ ":perfetto_protos_perfetto_trace_ps_lite_gen",
":perfetto_protos_perfetto_trace_ps_zero_gen",
":perfetto_protos_perfetto_trace_sys_stats_cpp_gen",
+ ":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
":perfetto_protos_perfetto_trace_system_info_cpp_gen",
+ ":perfetto_protos_perfetto_trace_system_info_lite_gen",
":perfetto_protos_perfetto_trace_system_info_zero_gen",
":perfetto_protos_perfetto_trace_track_event_cpp_gen",
+ ":perfetto_protos_perfetto_trace_track_event_lite_gen",
":perfetto_protos_perfetto_trace_track_event_zero_gen",
":perfetto_src_android_internal_headers",
":perfetto_src_android_internal_lazy_library_loader",
@@ -8728,6 +8764,7 @@
":perfetto_src_tracing_platform_impl",
":perfetto_src_tracing_test_test_support",
":perfetto_src_tracing_test_tracing_integration_test",
+ ":perfetto_src_tracing_unittests",
":perfetto_tools_sanitizers_unittests_sanitizers_unittests",
],
shared_libs: [
@@ -8750,63 +8787,90 @@
],
generated_headers: [
"perfetto_protos_perfetto_common_cpp_gen_headers",
+ "perfetto_protos_perfetto_common_lite_gen_headers",
"perfetto_protos_perfetto_common_zero_gen_headers",
"perfetto_protos_perfetto_config_android_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_android_lite_gen_headers",
"perfetto_protos_perfetto_config_android_zero_gen_headers",
"perfetto_protos_perfetto_config_cpp_gen_headers",
"perfetto_protos_perfetto_config_ftrace_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_ftrace_lite_gen_headers",
"perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_config_gpu_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_gpu_lite_gen_headers",
"perfetto_protos_perfetto_config_gpu_zero_gen_headers",
"perfetto_protos_perfetto_config_inode_file_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_inode_file_lite_gen_headers",
"perfetto_protos_perfetto_config_inode_file_zero_gen_headers",
"perfetto_protos_perfetto_config_interceptors_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_interceptors_lite_gen_headers",
"perfetto_protos_perfetto_config_interceptors_zero_gen_headers",
+ "perfetto_protos_perfetto_config_lite_gen_headers",
"perfetto_protos_perfetto_config_power_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_power_lite_gen_headers",
"perfetto_protos_perfetto_config_power_zero_gen_headers",
"perfetto_protos_perfetto_config_process_stats_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_process_stats_lite_gen_headers",
"perfetto_protos_perfetto_config_process_stats_zero_gen_headers",
"perfetto_protos_perfetto_config_profiling_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_profiling_lite_gen_headers",
"perfetto_protos_perfetto_config_profiling_zero_gen_headers",
"perfetto_protos_perfetto_config_sys_stats_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_sys_stats_lite_gen_headers",
"perfetto_protos_perfetto_config_sys_stats_zero_gen_headers",
"perfetto_protos_perfetto_config_track_event_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_track_event_lite_gen_headers",
"perfetto_protos_perfetto_config_track_event_zero_gen_headers",
"perfetto_protos_perfetto_config_zero_gen_headers",
"perfetto_protos_perfetto_ipc_cpp_gen_headers",
"perfetto_protos_perfetto_ipc_ipc_gen_headers",
"perfetto_protos_perfetto_ipc_wire_protocol_cpp_gen_headers",
"perfetto_protos_perfetto_trace_android_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_android_lite_gen_headers",
"perfetto_protos_perfetto_trace_android_zero_gen_headers",
"perfetto_protos_perfetto_trace_chrome_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
"perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
"perfetto_protos_perfetto_trace_filesystem_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_filesystem_lite_gen_headers",
"perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
"perfetto_protos_perfetto_trace_ftrace_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
"perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_trace_gpu_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_gpu_lite_gen_headers",
"perfetto_protos_perfetto_trace_gpu_zero_gen_headers",
"perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_interned_data_lite_gen_headers",
"perfetto_protos_perfetto_trace_interned_data_zero_gen_headers",
"perfetto_protos_perfetto_trace_minimal_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
"perfetto_protos_perfetto_trace_minimal_zero_gen_headers",
"perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_non_minimal_lite_gen_headers",
"perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers",
"perfetto_protos_perfetto_trace_perfetto_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_perfetto_lite_gen_headers",
"perfetto_protos_perfetto_trace_perfetto_zero_gen_headers",
"perfetto_protos_perfetto_trace_power_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_power_lite_gen_headers",
"perfetto_protos_perfetto_trace_power_zero_gen_headers",
"perfetto_protos_perfetto_trace_processor_metrics_impl_zero_gen_headers",
"perfetto_protos_perfetto_trace_processor_zero_gen_headers",
"perfetto_protos_perfetto_trace_profiling_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
"perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
"perfetto_protos_perfetto_trace_ps_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
"perfetto_protos_perfetto_trace_ps_zero_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
"perfetto_protos_perfetto_trace_system_info_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_system_info_lite_gen_headers",
"perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
"perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
+ "perfetto_protos_perfetto_trace_track_event_lite_gen_headers",
"perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
"perfetto_src_base_version_gen_h",
"perfetto_src_ipc_test_messages_cpp_gen_headers",
diff --git a/BUILD b/BUILD
index 2579427..22940c9 100644
--- a/BUILD
+++ b/BUILD
@@ -524,6 +524,7 @@
"include/perfetto/tracing/locked_handle.h",
"include/perfetto/tracing/platform.h",
"include/perfetto/tracing/trace_writer_base.h",
+ "include/perfetto/tracing/traced_value.h",
"include/perfetto/tracing/tracing.h",
"include/perfetto/tracing/tracing_backend.h",
"include/perfetto/tracing/track.h",
@@ -1570,6 +1571,7 @@
"src/tracing/internal/tracing_muxer_impl.h",
"src/tracing/internal/track_event_internal.cc",
"src/tracing/platform.cc",
+ "src/tracing/traced_value.cc",
"src/tracing/tracing.cc",
"src/tracing/track.cc",
"src/tracing/track_event_category_registry.cc",
diff --git a/gn/perfetto_unittests.gni b/gn/perfetto_unittests.gni
index 44721ce..1a2b482 100644
--- a/gn/perfetto_unittests.gni
+++ b/gn/perfetto_unittests.gni
@@ -20,6 +20,7 @@
"src/base:unittests",
"src/protozero:unittests",
"src/tracing/core:unittests",
+ "src/tracing:unittests",
"src/profiling:unittests",
"src/profiling/symbolizer:unittests",
]
diff --git a/include/perfetto/tracing/BUILD.gn b/include/perfetto/tracing/BUILD.gn
index 01f0f71..566636b 100644
--- a/include/perfetto/tracing/BUILD.gn
+++ b/include/perfetto/tracing/BUILD.gn
@@ -46,6 +46,7 @@
"locked_handle.h",
"platform.h",
"trace_writer_base.h",
+ "traced_value.h",
"tracing.h",
"tracing_backend.h",
"track.h",
diff --git a/include/perfetto/tracing/traced_value.h b/include/perfetto/tracing/traced_value.h
new file mode 100644
index 0000000..9369640
--- /dev/null
+++ b/include/perfetto/tracing/traced_value.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_TRACING_TRACED_VALUE_H_
+#define INCLUDE_PERFETTO_TRACING_TRACED_VALUE_H_
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/base/export.h"
+#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
+
+#include <type_traits>
+#include <utility>
+
+namespace perfetto {
+
+class DebugAnnotation;
+
+// *** NOTE ***
+// This is work-in-progress and the examples below do not work yet.
+//
+//
+// These classes provide a JSON-inspired way to write structed data into traces.
+//
+// Each TracedValue can be consumed exactly once to write a value into a trace
+// using one of the Write* methods.
+//
+// Write* methods fall into two categories:
+// - Primitive types (int, string, bool, double, etc): they just write the
+// provided value, consuming the TracedValue in the process.
+// - Complex types (arrays and dicts): they consume the TracedValue and
+// return a corresponding scoped object (TracedArray or TracedDictionary).
+// This scope then can be used to write multiple items into the container:
+// TracedArray::AppendItem and TracedDictionary::AddItem return a new
+// TracedValue which then can be used to write an element of the
+// dictionary or array.
+//
+// To define how a custom class should be written into the trace, users should
+// define one of the two following functions:
+// - Foo::WriteIntoTrace(TracedValue) const
+// (preferred for code which depends on perfetto directly)
+// - perfetto::TraceFormatTraits<T>::WriteIntoTrace(TracedValue, const T&);
+// (should be used if T is defined in a library which doesn't know anything
+// about tracing).
+//
+// After definiting a conversion method, the object can be used directly as a
+// TRACE_EVENT argument:
+//
+// Foo foo;
+// TRACE_EVENT("cat", "Event", "arg", foo);
+//
+// Examples:
+//
+// TRACE_EVENT("cat", "event", "params", [&](perfetto::TracedValue writer)
+// {
+// auto dict = std::move(writer).WriteDictionary();
+// dict->Add("param1", param1);
+// dict->Add("param2", param2);
+// ...
+// dict->Add("paramN", paramN);
+//
+// {
+// auto inner_array = dict->AddArray("inner");
+// inner_array->Append(value1);
+// inner_array->Append(value2);
+// }
+// });
+//
+// template <class T>
+// TraceFormatTraits<std::optional<T>>::WriteIntoTrace(
+// TracedValue writer, const std::optional<T>& value) {
+// if (!value) {
+// std::move(writer).WritePointer(nullptr);
+// return;
+// }
+// perfetto::Write(std::move(writer), *value);
+// }
+//
+// template <class T>
+// TraceFormatTraits<std::vector<T>>::WriteIntoTrace(
+// TracedValue writer, const std::array<T>& value) {
+// auto array = std::move(writer).WriteArray();
+// for (const auto& item: value) {
+// array_scope.Append(item);
+// }
+// }
+//
+// class Foo {
+// void WriteIntoTrace(TracedValue writer) const {
+// auto dict = std::move(writer).WriteDictionary();
+// dict->Set("key", 42);
+// dict->Set("foo", "bar");
+// dict->Set("member", member_);
+// }
+// }
+class TracedArray;
+class TracedDictionary;
+
+class PERFETTO_EXPORT TracedValue {
+ public:
+ TracedValue(const TracedValue&) = delete;
+ TracedValue& operator=(const TracedValue&) = delete;
+ TracedValue& operator=(TracedValue&&) = delete;
+ TracedValue(TracedValue&&) = default;
+ ~TracedValue() = default;
+
+ void WriteInt64(int64_t value) &&;
+ void WriteUInt64(uint64_t value) &&;
+ void WriteDouble(double value) &&;
+ void WriteBoolean(bool value) &&;
+ void WriteString(const char*) &&;
+ void WriteString(const char*, size_t len) &&;
+ void WriteString(const std::string&) &&;
+ void WritePointer(const void* value) &&;
+
+ // Rules for writing nested dictionaries and arrays:
+ // - Only one scope (TracedArray, TracedDictionary or TracedValue) can be
+ // active at the same time. It's only allowed to call methods on the active
+ // scope.
+ // - When a scope creates a nested scope, the new scope becomes active.
+ // - When a scope is destroyed, it's parent scope becomes active again.
+ //
+ // Typically users will have to create a scope only at the beginning of a
+ // conversion function and this scope should be destroyed at the end of it.
+ // TracedArray::Append and TracedDictionary::Add create, write and complete
+ // inner scopes automatically.
+
+ // Scope which allows multiple values to be appended.
+ TracedArray WriteArray() && PERFETTO_WARN_UNUSED_RESULT;
+
+ // Scope which allows multiple key-value pairs to be added.
+ TracedDictionary WriteDictionary() && PERFETTO_WARN_UNUSED_RESULT;
+
+ static TracedValue CreateForTest(protos::pbzero::DebugAnnotation*);
+
+ private:
+ friend class TracedArray;
+ friend class TracedDictionary;
+
+ inline explicit TracedValue(protos::pbzero::DebugAnnotation* root_context)
+ : root_context_(root_context) {}
+ inline explicit TracedValue(
+ protos::pbzero::DebugAnnotation::NestedValue* nested_context)
+ : nested_context_(nested_context) {}
+
+ // Temporary support for perfetto::DebugAnnotation C++ class before it's going
+ // to be replaced by TracedValue.
+ // TODO(altimin): Convert v8 to use TracedValue directly and delete it.
+ friend class DebugAnnotation;
+
+ // Only one of them can be null.
+ // TODO(altimin): replace DebugAnnotation with something that doesn't require
+ // this duplication.
+ protos::pbzero::DebugAnnotation* root_context_ = nullptr;
+ protos::pbzero::DebugAnnotation::NestedValue* nested_context_ = nullptr;
+};
+
+class TracedArray {
+ public:
+ TracedArray(const TracedArray&) = delete;
+ TracedArray& operator=(const TracedArray&) = delete;
+ TracedArray& operator=(TracedArray&&) = delete;
+ TracedArray(TracedArray&&) = default;
+ ~TracedArray() { value_->Finalize(); }
+
+ TracedValue AppendItem();
+
+ TracedDictionary AppendDictionary() PERFETTO_WARN_UNUSED_RESULT;
+ TracedArray AppendArray();
+
+ private:
+ friend class TracedValue;
+
+ inline explicit TracedArray(
+ protos::pbzero::DebugAnnotation::NestedValue* value)
+ : value_(value) {}
+
+ protos::pbzero::DebugAnnotation::NestedValue* value_;
+};
+
+class TracedDictionary {
+ public:
+ TracedDictionary(const TracedDictionary&) = delete;
+ TracedDictionary& operator=(const TracedDictionary&) = delete;
+ TracedDictionary& operator=(TracedDictionary&&) = delete;
+ TracedDictionary(TracedDictionary&&) = default;
+ ~TracedDictionary() {}
+
+ TracedValue AddItem(const char* key);
+
+ TracedDictionary AddDictionary(const char* key);
+ TracedArray AddArray(const char* key);
+
+ private:
+ friend class TracedValue;
+
+ inline explicit TracedDictionary(
+ protos::pbzero::DebugAnnotation::NestedValue* value)
+ : value_(value) {}
+
+ protos::pbzero::DebugAnnotation::NestedValue* value_;
+};
+
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_TRACING_TRACED_VALUE_H_
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index b43466f..bfbac75 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -108,6 +108,7 @@
"internal/tracing_muxer_impl.h",
"internal/track_event_internal.cc",
"platform.cc",
+ "traced_value.cc",
"tracing.cc",
"track.cc",
"track_event_category_registry.cc",
@@ -124,6 +125,34 @@
}
}
+perfetto_unittest_source_set("unittests") {
+ testonly = true
+ deps = [
+ "../../protos/perfetto/trace:lite",
+ "../../protos/perfetto/trace/track_event:lite",
+ "../base",
+ "../base:test_support",
+ "test:test_support",
+ ]
+
+ sources = []
+
+ # TODO(primiano): remove the build_with_chromium conditional once the root
+ # //BUILD.gn:libperfetto (in chromium) stops adding tracing:platform_fake.
+ # The problem is the following: in chrome builds we end up with duplicate
+ # symbol definitions in the test because both platorm (impl and fake) are
+ # present: impl added here and fake coming from chromium's base (full path:
+ # perfetto_unittests -> //(chromium)base:test_support -> //(chromium)base
+ # -> libperfetto -> platform_fake.
+ if (!build_with_chromium) {
+ deps += [
+ ":client_api_without_backends",
+ ":platform_impl",
+ ]
+ sources += [ "traced_value_unittest.cc" ]
+ }
+}
+
# System backend: connects to an external "traced" instance via a UNIX socket.
# Requires the IPC layer and is supported only on posix systems.
if (enable_perfetto_ipc) {
diff --git a/src/tracing/traced_value.cc b/src/tracing/traced_value.cc
new file mode 100644
index 0000000..c113388
--- /dev/null
+++ b/src/tracing/traced_value.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/tracing/traced_value.h"
+
+#include "perfetto/base/logging.h"
+#include "perfetto/tracing/debug_annotation.h"
+#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
+
+namespace perfetto {
+
+// static
+TracedValue TracedValue::CreateForTest(
+ protos::pbzero::DebugAnnotation* context) {
+ return TracedValue(context);
+}
+
+void TracedValue::WriteInt64(int64_t value) && {
+ if (nested_context_) {
+ nested_context_->set_int_value(value);
+ } else {
+ root_context_->set_int_value(value);
+ }
+}
+
+void TracedValue::WriteUInt64(uint64_t value) && {
+ if (nested_context_) {
+ nested_context_->set_int_value(static_cast<int64_t>(value));
+ } else {
+ root_context_->set_uint_value(value);
+ }
+}
+
+void TracedValue::WriteDouble(double value) && {
+ if (nested_context_) {
+ nested_context_->set_double_value(value);
+ } else {
+ root_context_->set_double_value(value);
+ }
+}
+
+void TracedValue::WriteBoolean(bool value) && {
+ if (nested_context_) {
+ nested_context_->set_bool_value(value);
+ } else {
+ root_context_->set_bool_value(value);
+ }
+}
+
+void TracedValue::WriteString(const char* value) && {
+ if (nested_context_) {
+ nested_context_->set_string_value(value);
+ } else {
+ root_context_->set_string_value(value);
+ }
+}
+
+void TracedValue::WriteString(const std::string& value) && {
+ if (nested_context_) {
+ nested_context_->set_string_value(value);
+ } else {
+ root_context_->set_string_value(value);
+ }
+}
+
+void TracedValue::WritePointer(const void* value) && {
+ if (nested_context_) {
+ nested_context_->set_int_value(reinterpret_cast<int64_t>(value));
+ } else {
+ root_context_->set_uint_value(reinterpret_cast<uint64_t>(value));
+ }
+}
+
+TracedDictionary TracedValue::WriteDictionary() && {
+ if (nested_context_) {
+ PERFETTO_DCHECK(!nested_context_->is_finalized());
+ nested_context_->set_nested_type(
+ protos::pbzero::DebugAnnotation_NestedValue_NestedType_DICT);
+ return TracedDictionary(nested_context_);
+ } else {
+ PERFETTO_DCHECK(!root_context_->is_finalized());
+ protos::pbzero::DebugAnnotation::NestedValue* value =
+ root_context_->set_nested_value();
+ value->set_nested_type(
+ protos::pbzero::DebugAnnotation_NestedValue_NestedType_DICT);
+ return TracedDictionary(value);
+ }
+}
+
+TracedArray TracedValue::WriteArray() && {
+ if (nested_context_) {
+ PERFETTO_DCHECK(!nested_context_->is_finalized());
+ nested_context_->set_nested_type(
+ protos::pbzero::DebugAnnotation_NestedValue_NestedType_ARRAY);
+ return TracedArray(nested_context_);
+ } else {
+ PERFETTO_DCHECK(!root_context_->is_finalized());
+ protos::pbzero::DebugAnnotation::NestedValue* value =
+ root_context_->set_nested_value();
+ value->set_nested_type(
+ protos::pbzero::DebugAnnotation_NestedValue_NestedType_ARRAY);
+ return TracedArray(value);
+ }
+}
+
+TracedValue TracedArray::AppendItem() {
+ return TracedValue(value_->add_array_values());
+}
+
+TracedDictionary TracedArray::AppendDictionary() {
+ return AppendItem().WriteDictionary();
+}
+
+TracedArray TracedArray::AppendArray() {
+ return AppendItem().WriteArray();
+}
+
+TracedValue TracedDictionary::AddItem(const char* key) {
+ value_->add_dict_keys(key);
+ return TracedValue(value_->add_dict_values());
+}
+
+TracedDictionary TracedDictionary::AddDictionary(const char* key) {
+ return AddItem(key).WriteDictionary();
+}
+
+TracedArray TracedDictionary::AddArray(const char* key) {
+ return AddItem(key).WriteArray();
+}
+
+} // namespace perfetto
diff --git a/src/tracing/traced_value_unittest.cc b/src/tracing/traced_value_unittest.cc
new file mode 100644
index 0000000..0fe358b
--- /dev/null
+++ b/src/tracing/traced_value_unittest.cc
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/tracing/traced_value.h"
+
+#include <array>
+#include <deque>
+#include <forward_list>
+#include <map>
+#include <queue>
+#include <set>
+#include <sstream>
+#include <stack>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "perfetto/tracing/debug_annotation.h"
+#include "perfetto/tracing/track_event.h"
+#include "protos/perfetto/trace/track_event/debug_annotation.gen.h"
+#include "protos/perfetto/trace/track_event/debug_annotation.pb.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+
+namespace {
+
+void WriteAsJSON(const protos::DebugAnnotation::NestedValue& value,
+ std::stringstream& ss) {
+ if (value.nested_type() ==
+ protos::DebugAnnotation_NestedValue_NestedType_DICT) {
+ ss << "{";
+ for (int i = 0; i < value.dict_keys_size() && i < value.dict_values_size();
+ ++i) {
+ if (i > 0)
+ ss << ",";
+ ss << value.dict_keys(i);
+ ss << ":";
+ WriteAsJSON(value.dict_values(i), ss);
+ }
+ ss << "}";
+ return;
+ } else if (value.nested_type() ==
+ protos::DebugAnnotation_NestedValue_NestedType_ARRAY) {
+ ss << "[";
+ for (int i = 0; i < value.array_values_size(); ++i) {
+ if (i > 0)
+ ss << ",";
+ WriteAsJSON(value.array_values(i), ss);
+ }
+ ss << "]";
+ return;
+ } else if (value.has_int_value()) {
+ ss << value.int_value();
+ return;
+ } else if (value.has_double_value()) {
+ ss << value.double_value();
+ return;
+ } else if (value.has_bool_value()) {
+ ss << static_cast<bool>(value.bool_value());
+ return;
+ } else if (value.has_string_value()) {
+ ss << value.string_value();
+ return;
+ }
+}
+
+void WriteAsJSON(const protos::DebugAnnotation& value, std::stringstream& ss) {
+ if (value.has_bool_value()) {
+ ss << static_cast<bool>(value.bool_value());
+ return;
+ } else if (value.has_uint_value()) {
+ ss << value.uint_value();
+ return;
+ } else if (value.has_int_value()) {
+ ss << value.int_value();
+ return;
+ } else if (value.has_double_value()) {
+ ss << value.double_value();
+ return;
+ } else if (value.has_string_value()) {
+ ss << value.string_value();
+ return;
+ } else if (value.has_pointer_value()) {
+ ss << value.pointer_value();
+ return;
+ } else if (value.has_nested_value()) {
+ WriteAsJSON(value.nested_value(), ss);
+ return;
+ } else if (value.has_legacy_json_value()) {
+ ss << value.legacy_json_value();
+ return;
+ }
+}
+
+std::string MessageToJSON(const std::string& data) {
+ std::stringstream ss;
+ protos::DebugAnnotation result;
+ result.ParseFromString(data);
+ WriteAsJSON(result, ss);
+ return ss.str();
+}
+
+} // namespace
+
+TEST(TracedValueTest, FlatDictionary_Explicit) {
+ protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
+ {
+ auto dict = TracedValue::CreateForTest(message.get()).WriteDictionary();
+ dict.AddItem("bool").WriteBoolean(true);
+ dict.AddItem("double").WriteDouble(0.0);
+ dict.AddItem("int").WriteInt64(2014);
+ dict.AddItem("string").WriteString("string");
+ dict.AddItem("ptr").WritePointer(reinterpret_cast<void*>(0x1234));
+ }
+ EXPECT_EQ("{bool:1,double:0,int:2014,string:string,ptr:4660}",
+ MessageToJSON(message.SerializeAsString()));
+}
+
+TEST(TracedValueTest, Hierarchy_Explicit) {
+ protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
+ {
+ auto root_dict =
+ TracedValue::CreateForTest(message.get()).WriteDictionary();
+ {
+ auto array = root_dict.AddItem("a1").WriteArray();
+ array.AppendItem().WriteInt64(1);
+ array.AppendItem().WriteBoolean(true);
+ {
+ auto dict = array.AppendItem().WriteDictionary();
+ dict.AddItem("i2").WriteInt64(3);
+ }
+ }
+ root_dict.AddItem("b0").WriteBoolean(true);
+ root_dict.AddItem("d0").WriteDouble(0.0);
+ {
+ auto dict1 = root_dict.AddItem("dict1").WriteDictionary();
+ {
+ auto dict2 = dict1.AddItem("dict2").WriteDictionary();
+ dict2.AddItem("b2").WriteBoolean(false);
+ }
+ dict1.AddItem("i1").WriteInt64(2014);
+ dict1.AddItem("s1").WriteString("foo");
+ }
+ root_dict.AddItem("i0").WriteInt64(2014);
+ root_dict.AddItem("s0").WriteString("foo");
+ }
+
+ EXPECT_EQ(
+ "{"
+ "a1:[1,1,{i2:3}],"
+ "b0:1,"
+ "d0:0,"
+ "dict1:{dict2:{b2:0},i1:2014,s1:foo},"
+ "i0:2014,"
+ "s0:foo}",
+ MessageToJSON(message.SerializeAsString()));
+}
+
+} // namespace perfetto