Commit 8ef19007 authored by Oystein Eftevaag's avatar Oystein Eftevaag Committed by Commit Bot

Perfetto: Add a protozero implementation of TracedValue

The broad strokes behind this CL is that TracedValue now can be
supplied with a factory function, which is used to override its
backing implementation.

When Perfetto is enabled, this is used to let TracedValue write
directly to a proto which can then be memcpy'd into the Perfetto
shared buffer, rather than being written into a base::Pickle which
is then serialized as a JSON strong.

Dependent on these CLs landing and rolled into //third_party/perfetto:
https://android-review.googlesource.com/c/platform/external/perfetto/+/792813
https://android-review.googlesource.com/c/platform/external/perfetto/+/792814

TBR=csharrison@chromium.org

Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I2bfa0cd7398a0ac4788c56d5be13fe951dc56770
Reviewed-on: https://chromium-review.googlesource.com/c/1263354Reviewed-by: default avataroysteine <oysteine@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Reviewed-by: default avatarPrimiano Tucci <primiano@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Reviewed-by: default avatarTimothy Dresser <tdresser@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Reviewed-by: default avatarEric Seckler <eseckler@chromium.org>
Commit-Queue: oysteine <oysteine@chromium.org>
Cr-Commit-Position: refs/heads/master@{#603768}
parent e8414881
...@@ -41,6 +41,10 @@ void CopyTraceEventParameter(char** buffer, ...@@ -41,6 +41,10 @@ void CopyTraceEventParameter(char** buffer,
} // namespace } // namespace
bool ConvertableToTraceFormat::AppendToProto(ProtoAppender* appender) {
return false;
}
TraceEvent::TraceEvent() TraceEvent::TraceEvent()
: duration_(TimeDelta::FromInternalValue(-1)), : duration_(TimeDelta::FromInternalValue(-1)),
scope_(trace_event_internal::kGlobalScope), scope_(trace_event_internal::kGlobalScope),
......
...@@ -49,6 +49,21 @@ class BASE_EXPORT ConvertableToTraceFormat { ...@@ -49,6 +49,21 @@ class BASE_EXPORT ConvertableToTraceFormat {
// appended. // appended.
virtual void AppendAsTraceFormat(std::string* out) const = 0; virtual void AppendAsTraceFormat(std::string* out) const = 0;
// Append the class info directly into the Perfetto-defined proto
// format; this is attempted first and if this returns true,
// AppendAsTraceFormat is not called. The ProtoAppender interface
// acts as a bridge to avoid proto/Perfetto dependencies in base.
class BASE_EXPORT ProtoAppender {
public:
virtual ~ProtoAppender() = default;
virtual void AddBuffer(uint8_t* begin, uint8_t* end) = 0;
// Copy all of the previous buffers registered with AddBuffer
// into the proto, with the given |field_id|.
virtual size_t Finalize(uint32_t field_id) = 0;
};
virtual bool AppendToProto(ProtoAppender* appender);
virtual void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead); virtual void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
std::string ToString() const { std::string ToString() const {
...@@ -146,7 +161,7 @@ class BASE_EXPORT TraceEvent { ...@@ -146,7 +161,7 @@ class BASE_EXPORT TraceEvent {
const char* arg_name(size_t index) const { return arg_names_[index]; } const char* arg_name(size_t index) const { return arg_names_[index]; }
const TraceValue& arg_value(size_t index) const { return arg_values_[index]; } const TraceValue& arg_value(size_t index) const { return arg_values_[index]; }
const ConvertableToTraceFormat* arg_convertible_value(size_t index) const { ConvertableToTraceFormat* arg_convertible_value(size_t index) {
return convertable_values_[index].get(); return convertable_values_[index].get();
} }
......
...@@ -1302,7 +1302,7 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp( ...@@ -1302,7 +1302,7 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
bind_id, num_args, arg_names, arg_types, bind_id, num_args, arg_names, arg_types,
arg_values, convertable_values, flags); arg_values, convertable_values, flags);
trace_event_override(new_trace_event); trace_event_override(&new_trace_event);
return handle; return handle;
} }
...@@ -1508,7 +1508,7 @@ void TraceLog::UpdateTraceEventDurationExplicit( ...@@ -1508,7 +1508,7 @@ void TraceLog::UpdateTraceEventDurationExplicit(
trace_event_internal::kNoId /* id */, trace_event_internal::kNoId /* id */,
trace_event_internal::kNoId /* bind_id */, 0, nullptr, nullptr, trace_event_internal::kNoId /* bind_id */, 0, nullptr, nullptr,
nullptr, nullptr, TRACE_EVENT_FLAG_NONE); nullptr, nullptr, TRACE_EVENT_FLAG_NONE);
trace_event_override(new_trace_event); trace_event_override(&new_trace_event);
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
new_trace_event.SendToATrace(); new_trace_event.SendToATrace();
...@@ -1560,7 +1560,7 @@ void TraceLog::AddMetadataEventWhileLocked(int thread_id, ...@@ -1560,7 +1560,7 @@ void TraceLog::AddMetadataEventWhileLocked(int thread_id,
TraceEvent trace_event; TraceEvent trace_event;
InitializeMetadataEvent(&trace_event, thread_id, metadata_name, arg_name, InitializeMetadataEvent(&trace_event, thread_id, metadata_name, arg_name,
value); value);
trace_event_override(trace_event); trace_event_override(&trace_event);
} else { } else {
InitializeMetadataEvent( InitializeMetadataEvent(
AddEventToThreadSharedChunkWhileLocked(nullptr, false), thread_id, AddEventToThreadSharedChunkWhileLocked(nullptr, false), thread_id,
...@@ -1577,7 +1577,7 @@ void TraceLog::AddMetadataEventsWhileLocked() { ...@@ -1577,7 +1577,7 @@ void TraceLog::AddMetadataEventsWhileLocked() {
// Move metadata added by |AddMetadataEvent| into the trace log. // Move metadata added by |AddMetadataEvent| into the trace log.
if (trace_event_override) { if (trace_event_override) {
while (!metadata_events_.empty()) { while (!metadata_events_.empty()) {
trace_event_override(*metadata_events_.back()); trace_event_override(metadata_events_.back().get());
metadata_events_.pop_back(); metadata_events_.pop_back();
} }
} else { } else {
......
...@@ -185,7 +185,7 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider { ...@@ -185,7 +185,7 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
// Cancels tracing and discards collected data. // Cancels tracing and discards collected data.
void CancelTracing(const OutputCallback& cb); void CancelTracing(const OutputCallback& cb);
typedef void (*AddTraceEventOverrideCallback)(const TraceEvent&); typedef void (*AddTraceEventOverrideCallback)(TraceEvent*);
typedef void (*OnFlushCallback)(); typedef void (*OnFlushCallback)();
// The callback will be called up until the point where the flush is // The callback will be called up until the point where the flush is
// finished, i.e. must be callable until OutputCallback is called with // finished, i.e. must be callable until OutputCallback is called with
......
This diff is collapsed.
...@@ -36,7 +36,7 @@ class BASE_EXPORT TracedValue : public ConvertableToTraceFormat { ...@@ -36,7 +36,7 @@ class BASE_EXPORT TracedValue : public ConvertableToTraceFormat {
void SetDouble(const char* name, double value); void SetDouble(const char* name, double value);
void SetBoolean(const char* name, bool value); void SetBoolean(const char* name, bool value);
void SetString(const char* name, base::StringPiece value); void SetString(const char* name, base::StringPiece value);
void SetValue(const char* name, const TracedValue& value); void SetValue(const char* name, TracedValue* value);
void BeginDictionary(const char* name); void BeginDictionary(const char* name);
void BeginArray(const char* name); void BeginArray(const char* name);
...@@ -45,7 +45,7 @@ class BASE_EXPORT TracedValue : public ConvertableToTraceFormat { ...@@ -45,7 +45,7 @@ class BASE_EXPORT TracedValue : public ConvertableToTraceFormat {
void SetDoubleWithCopiedName(base::StringPiece name, double value); void SetDoubleWithCopiedName(base::StringPiece name, double value);
void SetBooleanWithCopiedName(base::StringPiece name, bool value); void SetBooleanWithCopiedName(base::StringPiece name, bool value);
void SetStringWithCopiedName(base::StringPiece name, base::StringPiece value); void SetStringWithCopiedName(base::StringPiece name, base::StringPiece value);
void SetValueWithCopiedName(base::StringPiece name, const TracedValue& value); void SetValueWithCopiedName(base::StringPiece name, TracedValue* value);
void BeginDictionaryWithCopiedName(base::StringPiece name); void BeginDictionaryWithCopiedName(base::StringPiece name);
void BeginArrayWithCopiedName(base::StringPiece name); void BeginArrayWithCopiedName(base::StringPiece name);
...@@ -58,23 +58,73 @@ class BASE_EXPORT TracedValue : public ConvertableToTraceFormat { ...@@ -58,23 +58,73 @@ class BASE_EXPORT TracedValue : public ConvertableToTraceFormat {
// ConvertableToTraceFormat implementation. // ConvertableToTraceFormat implementation.
void AppendAsTraceFormat(std::string* out) const override; void AppendAsTraceFormat(std::string* out) const override;
bool AppendToProto(ProtoAppender* appender) override;
void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override; void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override;
// DEPRECATED: do not use, here only for legacy reasons. These methods causes // A custom serialization class can be supplied by implementing the
// a copy-and-translation of the base::Value into the equivalent TracedValue. // Writer interface and supplying a factory class to SetWriterFactoryCallback.
// TODO(primiano): migrate the (three) existing clients to the cheaper // Primarily used by Perfetto to write TracedValues directly into its proto
// SetValue(TracedValue) API. crbug.com/495628. // format, which lets us do a direct memcpy() in AppendToProto() rather than
void SetValue(const char* name, std::unique_ptr<base::Value> value); // a JSON serialization step in AppendAsTraceFormat.
void SetBaseValueWithCopiedName(base::StringPiece name, class BASE_EXPORT Writer {
const base::Value& value); public:
void AppendBaseValue(const base::Value& value); virtual ~Writer() = default;
virtual void BeginArray() = 0;
virtual void BeginDictionary() = 0;
virtual void EndDictionary() = 0;
virtual void EndArray() = 0;
// These methods assume that |name| is a long lived "quoted" string.
virtual void SetInteger(const char* name, int value) = 0;
virtual void SetDouble(const char* name, double value) = 0;
virtual void SetBoolean(const char* name, bool value) = 0;
virtual void SetString(const char* name, base::StringPiece value) = 0;
virtual void SetValue(const char* name, Writer* value) = 0;
virtual void BeginDictionary(const char* name) = 0;
virtual void BeginArray(const char* name) = 0;
// These, instead, can be safely passed a temporary string.
virtual void SetIntegerWithCopiedName(base::StringPiece name,
int value) = 0;
virtual void SetDoubleWithCopiedName(base::StringPiece name,
double value) = 0;
virtual void SetBooleanWithCopiedName(base::StringPiece name,
bool value) = 0;
virtual void SetStringWithCopiedName(base::StringPiece name,
base::StringPiece value) = 0;
virtual void SetValueWithCopiedName(base::StringPiece name,
Writer* value) = 0;
virtual void BeginDictionaryWithCopiedName(base::StringPiece name) = 0;
virtual void BeginArrayWithCopiedName(base::StringPiece name) = 0;
virtual void AppendInteger(int) = 0;
virtual void AppendDouble(double) = 0;
virtual void AppendBoolean(bool) = 0;
virtual void AppendString(base::StringPiece) = 0;
virtual void AppendAsTraceFormat(std::string* out) const = 0;
virtual bool AppendToProto(ProtoAppender* appender);
virtual void EstimateTraceMemoryOverhead(
TraceEventMemoryOverhead* overhead) = 0;
virtual std::unique_ptr<base::Value> ToBaseValue() const = 0;
virtual bool IsPickleWriter() const = 0;
virtual bool IsProtoWriter() const = 0;
};
typedef std::unique_ptr<Writer> (*WriterFactoryCallback)(size_t capacity);
static void SetWriterFactoryCallback(WriterFactoryCallback callback);
// Public for tests only. // Public for tests only.
std::unique_ptr<base::Value> ToBaseValue() const; std::unique_ptr<base::Value> ToBaseValue() const;
private: private:
Pickle pickle_; std::unique_ptr<Writer> writer_;
#ifndef NDEBUG #ifndef NDEBUG
// In debug builds checks the pairings of {Start,End}{Dictionary,Array} // In debug builds checks the pairings of {Start,End}{Dictionary,Array}
......
...@@ -95,40 +95,6 @@ TEST(TraceEventArgumentTest, LongStrings) { ...@@ -95,40 +95,6 @@ TEST(TraceEventArgumentTest, LongStrings) {
json); json);
} }
TEST(TraceEventArgumentTest, PassBaseValue) {
Value int_value(42);
Value bool_value(true);
Value double_value(42.0f);
auto dict_value = std::make_unique<DictionaryValue>();
dict_value->SetBoolean("bool", true);
dict_value->SetInteger("int", 42);
dict_value->SetDouble("double", 42.0f);
dict_value->SetString("string", std::string("a") + "b");
dict_value->SetString("string", std::string("a") + "b");
auto list_value = std::make_unique<ListValue>();
list_value->AppendBoolean(false);
list_value->AppendInteger(1);
list_value->AppendString("in_list");
list_value->Append(std::move(dict_value));
std::unique_ptr<TracedValue> value(new TracedValue());
value->BeginDictionary("outer_dict");
value->SetValue("inner_list", std::move(list_value));
value->EndDictionary();
dict_value.reset();
list_value.reset();
std::string json;
value->AppendAsTraceFormat(&json);
EXPECT_EQ(
"{\"outer_dict\":{\"inner_list\":[false,1,\"in_list\",{\"bool\":true,"
"\"double\":42.0,\"int\":42,\"string\":\"ab\"}]}}",
json);
}
TEST(TraceEventArgumentTest, PassTracedValue) { TEST(TraceEventArgumentTest, PassTracedValue) {
auto dict_value = std::make_unique<TracedValue>(); auto dict_value = std::make_unique<TracedValue>();
dict_value->SetInteger("a", 1); dict_value->SetInteger("a", 1);
...@@ -139,7 +105,7 @@ TEST(TraceEventArgumentTest, PassTracedValue) { ...@@ -139,7 +105,7 @@ TEST(TraceEventArgumentTest, PassTracedValue) {
nested_dict_value->AppendString("foo"); nested_dict_value->AppendString("foo");
nested_dict_value->EndArray(); nested_dict_value->EndArray();
dict_value->SetValue("e", *nested_dict_value); dict_value->SetValue("e", nested_dict_value.get());
// Check the merged result. // Check the merged result.
std::string json; std::string json;
......
...@@ -789,7 +789,7 @@ void LayerImpl::AsValueInto(base::trace_event::TracedValue* state) const { ...@@ -789,7 +789,7 @@ void LayerImpl::AsValueInto(base::trace_event::TracedValue* state) const {
*state); *state);
if (debug_info_) if (debug_info_)
state->SetValue("debug_info", *debug_info_); state->SetValue("debug_info", debug_info_);
} }
size_t LayerImpl::GPUMemoryUsageInBytes() const { return 0; } size_t LayerImpl::GPUMemoryUsageInBytes() const { return 0; }
......
...@@ -23,7 +23,7 @@ SubresourceFilterSafeBrowsingClient::CheckResult::ToTracedValue() const { ...@@ -23,7 +23,7 @@ SubresourceFilterSafeBrowsingClient::CheckResult::ToTracedValue() const {
auto value = std::make_unique<base::trace_event::TracedValue>(); auto value = std::make_unique<base::trace_event::TracedValue>();
value->SetInteger("request_id", request_id); value->SetInteger("request_id", request_id);
value->SetInteger("threat_type", threat_type); value->SetInteger("threat_type", threat_type);
value->SetValue("threat_metadata", *threat_metadata.ToTracedValue()); value->SetValue("threat_metadata", threat_metadata.ToTracedValue().get());
value->SetInteger("check_time (us)", check_time.InMicroseconds()); value->SetInteger("check_time (us)", check_time.InMicroseconds());
value->SetBoolean("finished", finished); value->SetBoolean("finished", finished);
return value; return value;
......
...@@ -342,7 +342,7 @@ std::unique_ptr<base::trace_event::TracedValue> Configuration::ToTracedValue() ...@@ -342,7 +342,7 @@ std::unique_ptr<base::trace_event::TracedValue> Configuration::ToTracedValue()
const { const {
auto value = std::make_unique<base::trace_event::TracedValue>(); auto value = std::make_unique<base::trace_event::TracedValue>();
auto traced_conditions = activation_conditions.ToTracedValue(); auto traced_conditions = activation_conditions.ToTracedValue();
value->SetValue("activation_conditions", *traced_conditions); value->SetValue("activation_conditions", traced_conditions.get());
value->SetString("activation_level", value->SetString("activation_level",
StreamToString(activation_options.activation_level)); StreamToString(activation_options.activation_level));
value->SetDouble("performance_measurement_rate", value->SetDouble("performance_measurement_rate",
......
...@@ -81,7 +81,10 @@ source_set("tests") { ...@@ -81,7 +81,10 @@ source_set("tests") {
] ]
if (is_mac || is_linux || is_android) { if (is_mac || is_linux || is_android) {
sources += [ "public/cpp/perfetto/trace_event_data_source_unittest.cc" ] sources += [
"public/cpp/perfetto/trace_event_data_source_unittest.cc",
"public/cpp/perfetto/traced_value_proto_writer_unittest.cc",
]
} }
if (!is_android) { if (!is_android) {
......
...@@ -2,4 +2,5 @@ include_rules = [ ...@@ -2,4 +2,5 @@ include_rules = [
"+services/service_manager/public", "+services/service_manager/public",
"+third_party/perfetto/include", "+third_party/perfetto/include",
"+third_party/perfetto/protos/perfetto", "+third_party/perfetto/protos/perfetto",
"+third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h",
] ]
...@@ -7,12 +7,11 @@ ...@@ -7,12 +7,11 @@
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include "base/format_macros.h"
#include "base/json/json_reader.h" #include "base/json/json_reader.h"
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/json/string_escape.h" #include "base/json/string_escape.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/format_macros.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "services/tracing/public/mojom/perfetto_service.mojom.h" #include "services/tracing/public/mojom/perfetto_service.mojom.h"
...@@ -23,7 +22,61 @@ ...@@ -23,7 +22,61 @@
using TraceEvent = base::trace_event::TraceEvent; using TraceEvent = base::trace_event::TraceEvent;
namespace tracing {
namespace { namespace {
void AppendProtoArrayAsJSON(std::string* out,
const perfetto::protos::ChromeTracedValue& array);
void AppendProtoValueAsJSON(std::string* out,
const perfetto::protos::ChromeTracedValue& value) {
base::trace_event::TraceEvent::TraceValue json_value;
if (value.has_int_value()) {
json_value.as_int = value.int_value();
TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_INT, json_value, out);
} else if (value.has_double_value()) {
json_value.as_double = value.double_value();
TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_DOUBLE, json_value, out);
} else if (value.has_bool_value()) {
json_value.as_bool = value.bool_value();
TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_BOOL, json_value, out);
} else if (value.has_string_value()) {
json_value.as_string = value.string_value().c_str();
TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_STRING, json_value, out);
} else if (value.has_nested_type()) {
if (value.nested_type() == perfetto::protos::ChromeTracedValue::ARRAY) {
AppendProtoArrayAsJSON(out, value);
return;
} else if (value.nested_type() ==
perfetto::protos::ChromeTracedValue::DICT) {
AppendProtoDictAsJSON(out, value);
} else {
NOTREACHED();
}
} else {
NOTREACHED();
}
}
void AppendProtoArrayAsJSON(std::string* out,
const perfetto::protos::ChromeTracedValue& array) {
out->append("[");
bool is_first_entry = true;
for (auto& value : array.array_values()) {
if (!is_first_entry) {
out->append(",");
} else {
is_first_entry = false;
}
AppendProtoValueAsJSON(out, value);
}
out->append("]");
}
const char* GetStringFromStringTable( const char* GetStringFromStringTable(
const std::unordered_map<int, std::string>& string_table, const std::unordered_map<int, std::string>& string_table,
int index) { int index) {
...@@ -193,6 +246,13 @@ void OutputJSONFromTraceEventProto( ...@@ -193,6 +246,13 @@ void OutputJSONFromTraceEventProto(
*out += arg.json_value(); *out += arg.json_value();
continue; continue;
} }
if (arg.has_traced_value()) {
AppendProtoDictAsJSON(out, arg.traced_value());
continue;
}
NOTREACHED();
} }
*out += "}}"; *out += "}}";
...@@ -200,7 +260,24 @@ void OutputJSONFromTraceEventProto( ...@@ -200,7 +260,24 @@ void OutputJSONFromTraceEventProto(
} // namespace } // namespace
namespace tracing { void AppendProtoDictAsJSON(std::string* out,
const perfetto::protos::ChromeTracedValue& dict) {
out->append("{");
DCHECK_EQ(dict.dict_keys_size(), dict.dict_values_size());
for (int i = 0; i < dict.dict_keys_size(); ++i) {
if (i != 0) {
out->append(",");
}
base::EscapeJSONString(dict.dict_keys(i), true, out);
out->append(":");
AppendProtoValueAsJSON(out, dict.dict_values(i));
}
out->append("}");
}
JSONTraceExporter::JSONTraceExporter(const std::string& config, JSONTraceExporter::JSONTraceExporter(const std::string& config,
perfetto::TracingService* service) perfetto::TracingService* service)
......
...@@ -12,12 +12,22 @@ ...@@ -12,12 +12,22 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/values.h" #include "base/values.h"
#include "third_party/perfetto/include/perfetto/tracing/core/consumer.h" #include "third_party/perfetto/include/perfetto/tracing/core/consumer.h"
#include "third_party/perfetto/include/perfetto/tracing/core/tracing_service.h" #include "third_party/perfetto/include/perfetto/tracing/core/tracing_service.h"
namespace perfetto {
namespace protos {
class ChromeTracedValue;
} // namespace protos
} // namespace perfetto
namespace tracing { namespace tracing {
// Serializes the supplied proto into JSON, using the same
// format as TracedValue::AppendAsTraceFormat.
void AppendProtoDictAsJSON(std::string* out,
const perfetto::protos::ChromeTracedValue& dict);
// This is a Perfetto consumer which will enable Perfetto tracing // This is a Perfetto consumer which will enable Perfetto tracing
// and subscribe to ChromeTraceEvent data sources. Any received // and subscribe to ChromeTraceEvent data sources. Any received
// protos will be converted to the legacy JSON Chrome Tracing // protos will be converted to the legacy JSON Chrome Tracing
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/trace_event_analyzer.h" #include "base/test/trace_event_analyzer.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "services/tracing/public/mojom/perfetto_service.mojom.h" #include "services/tracing/public/mojom/perfetto_service.mojom.h"
...@@ -579,4 +580,125 @@ TEST_F(JSONTraceExporterTest, TestEventWithConvertableArgs) { ...@@ -579,4 +580,125 @@ TEST_F(JSONTraceExporterTest, TestEventWithConvertableArgs) {
EXPECT_EQ("conv_value2", trace_event->GetKnownArgAsString("foo2")); EXPECT_EQ("conv_value2", trace_event->GetKnownArgAsString("foo2"));
} }
TEST_F(JSONTraceExporterTest, TestEventWithTracedValueArg) {
CreateJSONTraceExporter("foo");
service()->WaitForTracingEnabled();
StopAndFlush();
perfetto::protos::TracePacket trace_packet_proto;
auto* new_trace_event =
trace_packet_proto.mutable_chrome_events()->add_trace_events();
SetTestPacketBasicData(new_trace_event);
auto* new_arg = new_trace_event->add_args();
new_arg->set_name("foo1");
auto* traced_value = new_arg->mutable_traced_value();
traced_value->add_dict_keys("bool");
traced_value->add_dict_values()->set_bool_value(true);
FinalizePacket(trace_packet_proto);
service()->WaitForTracingDisabled();
auto* trace_event = ValidateAndGetBasicTestPacket();
auto arg_value = trace_event->GetKnownArgAsValue("foo1");
EXPECT_EQ(true, arg_value->FindKey("bool")->GetBool());
}
TEST_F(JSONTraceExporterTest, TracedValueFlatDictionary) {
perfetto::protos::ChromeTracedValue traced_value;
{
traced_value.add_dict_keys("bool");
traced_value.add_dict_values()->set_bool_value(true);
}
{
traced_value.add_dict_keys("double");
traced_value.add_dict_values()->set_double_value(8.0);
}
{
traced_value.add_dict_keys("int");
traced_value.add_dict_values()->set_int_value(2014);
}
{
traced_value.add_dict_keys("string");
traced_value.add_dict_values()->set_string_value("bar");
}
std::string json;
AppendProtoDictAsJSON(&json, traced_value);
EXPECT_EQ("{\"bool\":true,\"double\":8.0,\"int\":2014,\"string\":\"bar\"}",
json);
}
TEST_F(JSONTraceExporterTest, TracedValueHierarchy) {
perfetto::protos::ChromeTracedValue traced_value;
{
traced_value.add_dict_keys("a1");
auto* a1_array = traced_value.add_dict_values();
a1_array->set_nested_type(perfetto::protos::ChromeTracedValue::ARRAY);
a1_array->add_array_values()->set_int_value(1);
a1_array->add_array_values()->set_bool_value(true);
auto* sub_dict = a1_array->add_array_values();
sub_dict->set_nested_type(perfetto::protos::ChromeTracedValue::DICT);
sub_dict->add_dict_keys("i2");
sub_dict->add_dict_values()->set_int_value(3);
}
{
traced_value.add_dict_keys("b0");
traced_value.add_dict_values()->set_bool_value(true);
}
{
traced_value.add_dict_keys("d0");
traced_value.add_dict_values()->set_double_value(6.0);
}
{
traced_value.add_dict_keys("a1");
auto* dict1_subdict = traced_value.add_dict_values();
dict1_subdict->set_nested_type(perfetto::protos::ChromeTracedValue::DICT);
dict1_subdict->add_dict_keys("dict2");
auto* dict2_sub_sub_dict = dict1_subdict->add_dict_values();
dict2_sub_sub_dict->set_nested_type(
perfetto::protos::ChromeTracedValue::DICT);
dict2_sub_sub_dict->add_dict_keys("b2");
dict2_sub_sub_dict->add_dict_values()->set_bool_value(true);
dict1_subdict->add_dict_keys("i1");
dict1_subdict->add_dict_values()->set_int_value(2014);
dict1_subdict->add_dict_keys("s1");
dict1_subdict->add_dict_values()->set_string_value("foo");
}
{
traced_value.add_dict_keys("i0");
traced_value.add_dict_values()->set_int_value(2014);
}
{
traced_value.add_dict_keys("s0");
traced_value.add_dict_values()->set_string_value("foo");
}
std::string json;
AppendProtoDictAsJSON(&json, traced_value);
EXPECT_EQ(
"{\"a1\":[1,true,{\"i2\":3}],\"b0\":true,\"d0\":6.0,\"a1\":{\"dict2\":{"
"\"b2\":true},\"i1\":2014,\"s1\":\"foo\"},\"i0\":2014,\"s0\":\"foo\"}",
json);
}
} // namespace tracing } // namespace tracing
...@@ -26,6 +26,8 @@ component("cpp") { ...@@ -26,6 +26,8 @@ component("cpp") {
if (is_mac || is_linux || is_android || is_win) { if (is_mac || is_linux || is_android || is_win) {
sources += [ sources += [
"perfetto/heap_scattered_stream_delegate.cc",
"perfetto/heap_scattered_stream_delegate.h",
"perfetto/producer_client.cc", "perfetto/producer_client.cc",
"perfetto/producer_client.h", "perfetto/producer_client.h",
"perfetto/shared_memory.cc", "perfetto/shared_memory.cc",
...@@ -34,9 +36,12 @@ component("cpp") { ...@@ -34,9 +36,12 @@ component("cpp") {
"perfetto/task_runner.h", "perfetto/task_runner.h",
"perfetto/trace_event_data_source.cc", "perfetto/trace_event_data_source.cc",
"perfetto/trace_event_data_source.h", "perfetto/trace_event_data_source.h",
"perfetto/traced_value_proto_writer.cc",
"perfetto/traced_value_proto_writer.h",
] ]
deps = [ deps = [
"//third_party/perfetto/include/perfetto/protozero:protozero",
"//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite", "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
] ]
......
// Copyright 2018 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 "services/tracing/public/cpp/perfetto/heap_scattered_stream_delegate.h"
#include <algorithm>
#include <utility>
#include "base/logging.h"
namespace tracing {
namespace {
// TODO(oysteine): Tune these values.
static const size_t kDefaultChunkSizeBytes = 128;
static const size_t kMaximumChunkSizeBytes = 128 * 1024;
} // namespace
HeapScatteredStreamWriterDelegate::Chunk::Chunk(size_t size)
: buffer_(std::unique_ptr<uint8_t[]>(new uint8_t[size])),
size_(size),
unused_bytes_(size) {
DCHECK(size);
}
HeapScatteredStreamWriterDelegate::Chunk::Chunk(Chunk&& chunk)
: buffer_(std::move(chunk.buffer_)),
size_(std::move(chunk.size_)),
unused_bytes_(std::move(chunk.unused_bytes_)) {}
HeapScatteredStreamWriterDelegate::Chunk::~Chunk() = default;
protozero::ContiguousMemoryRange
HeapScatteredStreamWriterDelegate::Chunk::GetTotalRange() const {
protozero::ContiguousMemoryRange range = {buffer_.get(),
buffer_.get() + size_};
return range;
}
protozero::ContiguousMemoryRange
HeapScatteredStreamWriterDelegate::Chunk::GetUsedRange() const {
protozero::ContiguousMemoryRange range = {
buffer_.get(), buffer_.get() + size_ - unused_bytes_};
return range;
}
HeapScatteredStreamWriterDelegate::HeapScatteredStreamWriterDelegate(
size_t initial_size_hint) {
next_chunk_size_ =
initial_size_hint ? initial_size_hint : kDefaultChunkSizeBytes;
}
HeapScatteredStreamWriterDelegate::~HeapScatteredStreamWriterDelegate() =
default;
size_t HeapScatteredStreamWriterDelegate::GetTotalSize() {
size_t total_size = 0;
for (auto& chunk : chunks_) {
total_size += chunk.size();
}
return total_size;
}
void HeapScatteredStreamWriterDelegate::AdjustUsedSizeOfCurrentChunk() {
if (!chunks_.empty()) {
DCHECK(writer_);
chunks_.back().set_unused_bytes(writer_->bytes_available());
}
}
protozero::ContiguousMemoryRange
HeapScatteredStreamWriterDelegate::GetNewBuffer() {
AdjustUsedSizeOfCurrentChunk();
chunks_.emplace_back(next_chunk_size_);
next_chunk_size_ = std::min(kMaximumChunkSizeBytes, next_chunk_size_ * 2);
return chunks_.back().GetTotalRange();
}
} // namespace tracing
// Copyright 2018 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.
#ifndef SERVICES_TRACING_PUBLIC_CPP_PERFETTO_HEAP_SCATTERED_STREAM_DELEGATE_H_
#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_HEAP_SCATTERED_STREAM_DELEGATE_H_
#include <memory>
#include <vector>
#include "base/component_export.h"
#include "base/macros.h"
#include "third_party/perfetto/include/perfetto/protozero/scattered_stream_writer.h"
namespace tracing {
// This is used to supply Perfetto's protozero library with
// a temporary heap-backed storage, which can then later be memcpy'd
// into the shared memory buffer.
class COMPONENT_EXPORT(TRACING_CPP) HeapScatteredStreamWriterDelegate
: public protozero::ScatteredStreamWriter::Delegate {
public:
class Chunk {
public:
explicit Chunk(size_t size);
Chunk(Chunk&& chunk);
~Chunk();
protozero::ContiguousMemoryRange GetTotalRange() const;
protozero::ContiguousMemoryRange GetUsedRange() const;
uint8_t* start() const { return buffer_.get(); }
size_t size() const { return size_; }
size_t unused_bytes() const { return unused_bytes_; }
void set_unused_bytes(size_t unused_bytes) { unused_bytes_ = unused_bytes; }
private:
std::unique_ptr<uint8_t[]> buffer_;
const size_t size_;
size_t unused_bytes_;
};
explicit HeapScatteredStreamWriterDelegate(size_t initial_size_hint);
~HeapScatteredStreamWriterDelegate() override;
const std::vector<Chunk>& chunks() const { return chunks_; }
void set_writer(protozero::ScatteredStreamWriter* writer) {
writer_ = writer;
}
size_t GetTotalSize();
void AdjustUsedSizeOfCurrentChunk();
// protozero::ScatteredStreamWriter::Delegate implementation.
protozero::ContiguousMemoryRange GetNewBuffer() override;
private:
std::vector<Chunk> chunks_;
size_t next_chunk_size_;
protozero::ScatteredStreamWriter* writer_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(HeapScatteredStreamWriterDelegate);
};
} // namespace tracing
#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_HEAP_SCATTERED_STREAM_DELEGATE_H_
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/process/process_handle.h" #include "base/process/process_handle.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "services/tracing/public/cpp/perfetto/traced_value_proto_writer.h"
#include "services/tracing/public/mojom/constants.mojom.h" #include "services/tracing/public/mojom/constants.mojom.h"
#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory_arbiter.h" #include "third_party/perfetto/include/perfetto/tracing/core/shared_memory_arbiter.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h" #include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
...@@ -153,13 +154,18 @@ class TraceEventDataSource::ThreadLocalEventSink { ...@@ -153,13 +154,18 @@ class TraceEventDataSource::ThreadLocalEventSink {
} }
void AddConvertableToTraceFormat( void AddConvertableToTraceFormat(
const base::trace_event::ConvertableToTraceFormat* value, base::trace_event::ConvertableToTraceFormat* value,
perfetto::protos::pbzero::ChromeTraceEvent_Arg* arg) { perfetto::protos::pbzero::ChromeTraceEvent_Arg* arg) {
PerfettoProtoAppender proto_appender(arg);
if (value->AppendToProto(&proto_appender)) {
return;
}
std::string json = value->ToString(); std::string json = value->ToString();
arg->set_json_value(json.c_str()); arg->set_json_value(json.c_str());
} }
void AddTraceEvent(const TraceEvent& trace_event) { void AddTraceEvent(TraceEvent* trace_event) {
// TODO(oysteine): Adding trace events to Perfetto will // TODO(oysteine): Adding trace events to Perfetto will
// stall in some situations, specifically when we overflow // stall in some situations, specifically when we overflow
// the buffer and need to make a sync call to flush it, and we're // the buffer and need to make a sync call to flush it, and we're
...@@ -170,7 +176,7 @@ class TraceEventDataSource::ThreadLocalEventSink { ...@@ -170,7 +176,7 @@ class TraceEventDataSource::ThreadLocalEventSink {
// TODO(oysteine): Temporary workaround for a specific trace event // TODO(oysteine): Temporary workaround for a specific trace event
// which is added while a scheduler lock is held, and will deadlock // which is added while a scheduler lock is held, and will deadlock
// if Perfetto does a PostTask to commit a finished chunk. // if Perfetto does a PostTask to commit a finished chunk.
if (strcmp(trace_event.name(), "RealTimeDomain::DelayTillNextTask") == 0) { if (strcmp(trace_event->name(), "RealTimeDomain::DelayTillNextTask") == 0) {
return; return;
} }
...@@ -186,17 +192,18 @@ class TraceEventDataSource::ThreadLocalEventSink { ...@@ -186,17 +192,18 @@ class TraceEventDataSource::ThreadLocalEventSink {
// If the TRACE_EVENT_FLAG_COPY flag is set, the char* pointers aren't // If the TRACE_EVENT_FLAG_COPY flag is set, the char* pointers aren't
// necessarily valid after the TRACE_EVENT* call, and so we need to store // necessarily valid after the TRACE_EVENT* call, and so we need to store
// the string every time. // the string every time.
bool string_table_enabled = !(trace_event.flags() & TRACE_EVENT_FLAG_COPY); bool string_table_enabled = !(trace_event->flags() & TRACE_EVENT_FLAG_COPY);
if (string_table_enabled) { if (string_table_enabled) {
name_index = GetStringTableIndexForString(trace_event.name()); name_index = GetStringTableIndexForString(trace_event->name());
category_name_index = GetStringTableIndexForString( category_name_index =
TraceLog::GetCategoryGroupName(trace_event.category_group_enabled())); GetStringTableIndexForString(TraceLog::GetCategoryGroupName(
trace_event->category_group_enabled()));
for (int i = 0; for (int i = 0;
i < base::trace_event::kTraceMaxNumArgs && trace_event.arg_name(i); i < base::trace_event::kTraceMaxNumArgs && trace_event->arg_name(i);
++i) { ++i) {
arg_name_indices[i] = arg_name_indices[i] =
GetStringTableIndexForString(trace_event.arg_name(i)); GetStringTableIndexForString(trace_event->arg_name(i));
} }
} }
...@@ -205,58 +212,58 @@ class TraceEventDataSource::ThreadLocalEventSink { ...@@ -205,58 +212,58 @@ class TraceEventDataSource::ThreadLocalEventSink {
if (name_index) { if (name_index) {
new_trace_event->set_name_index(name_index); new_trace_event->set_name_index(name_index);
} else { } else {
new_trace_event->set_name(trace_event.name()); new_trace_event->set_name(trace_event->name());
} }
if (category_name_index) { if (category_name_index) {
new_trace_event->set_category_group_name_index(category_name_index); new_trace_event->set_category_group_name_index(category_name_index);
} else { } else {
new_trace_event->set_category_group_name( new_trace_event->set_category_group_name(TraceLog::GetCategoryGroupName(
TraceLog::GetCategoryGroupName(trace_event.category_group_enabled())); trace_event->category_group_enabled()));
} }
new_trace_event->set_timestamp( new_trace_event->set_timestamp(
trace_event.timestamp().since_origin().InMicroseconds()); trace_event->timestamp().since_origin().InMicroseconds());
uint32_t flags = trace_event.flags(); uint32_t flags = trace_event->flags();
new_trace_event->set_flags(flags); new_trace_event->set_flags(flags);
int process_id; int process_id;
int thread_id; int thread_id;
if ((flags & TRACE_EVENT_FLAG_HAS_PROCESS_ID) && if ((flags & TRACE_EVENT_FLAG_HAS_PROCESS_ID) &&
trace_event.thread_id() != base::kNullProcessId) { trace_event->thread_id() != base::kNullProcessId) {
process_id = trace_event.thread_id(); process_id = trace_event->thread_id();
thread_id = -1; thread_id = -1;
} else { } else {
process_id = TraceLog::GetInstance()->process_id(); process_id = TraceLog::GetInstance()->process_id();
thread_id = trace_event.thread_id(); thread_id = trace_event->thread_id();
} }
new_trace_event->set_process_id(process_id); new_trace_event->set_process_id(process_id);
new_trace_event->set_thread_id(thread_id); new_trace_event->set_thread_id(thread_id);
char phase = trace_event.phase(); char phase = trace_event->phase();
new_trace_event->set_phase(phase); new_trace_event->set_phase(phase);
for (int i = 0; for (int i = 0;
i < base::trace_event::kTraceMaxNumArgs && trace_event.arg_name(i); i < base::trace_event::kTraceMaxNumArgs && trace_event->arg_name(i);
++i) { ++i) {
auto type = trace_event.arg_type(i); auto type = trace_event->arg_type(i);
auto* new_arg = new_trace_event->add_args(); auto* new_arg = new_trace_event->add_args();
if (arg_name_indices[i]) { if (arg_name_indices[i]) {
new_arg->set_name_index(arg_name_indices[i]); new_arg->set_name_index(arg_name_indices[i]);
} else { } else {
new_arg->set_name(trace_event.arg_name(i)); new_arg->set_name(trace_event->arg_name(i));
} }
if (type == TRACE_VALUE_TYPE_CONVERTABLE) { if (type == TRACE_VALUE_TYPE_CONVERTABLE) {
AddConvertableToTraceFormat(trace_event.arg_convertible_value(i), AddConvertableToTraceFormat(trace_event->arg_convertible_value(i),
new_arg); new_arg);
continue; continue;
} }
auto& value = trace_event.arg_value(i); auto& value = trace_event->arg_value(i);
switch (type) { switch (type) {
case TRACE_VALUE_TYPE_BOOL: case TRACE_VALUE_TYPE_BOOL:
new_arg->set_bool_value(value.as_bool); new_arg->set_bool_value(value.as_bool);
...@@ -285,7 +292,7 @@ class TraceEventDataSource::ThreadLocalEventSink { ...@@ -285,7 +292,7 @@ class TraceEventDataSource::ThreadLocalEventSink {
} }
if (phase == TRACE_EVENT_PHASE_COMPLETE) { if (phase == TRACE_EVENT_PHASE_COMPLETE) {
int64_t duration = trace_event.duration().InMicroseconds(); int64_t duration = trace_event->duration().InMicroseconds();
if (duration != -1) { if (duration != -1) {
new_trace_event->set_duration(duration); new_trace_event->set_duration(duration);
} else { } else {
...@@ -295,33 +302,33 @@ class TraceEventDataSource::ThreadLocalEventSink { ...@@ -295,33 +302,33 @@ class TraceEventDataSource::ThreadLocalEventSink {
new_trace_event->set_duration(0); new_trace_event->set_duration(0);
} }
if (!trace_event.thread_timestamp().is_null()) { if (!trace_event->thread_timestamp().is_null()) {
int64_t thread_duration = int64_t thread_duration =
trace_event.thread_duration().InMicroseconds(); trace_event->thread_duration().InMicroseconds();
if (thread_duration != -1) { if (thread_duration != -1) {
new_trace_event->set_thread_duration(thread_duration); new_trace_event->set_thread_duration(thread_duration);
} }
} }
} }
if (!trace_event.thread_timestamp().is_null()) { if (!trace_event->thread_timestamp().is_null()) {
int64_t thread_time_int64 = int64_t thread_time_int64 =
trace_event.thread_timestamp().since_origin().InMicroseconds(); trace_event->thread_timestamp().since_origin().InMicroseconds();
new_trace_event->set_thread_timestamp(thread_time_int64); new_trace_event->set_thread_timestamp(thread_time_int64);
} }
if (trace_event.scope() != trace_event_internal::kGlobalScope) { if (trace_event->scope() != trace_event_internal::kGlobalScope) {
new_trace_event->set_scope(trace_event.scope()); new_trace_event->set_scope(trace_event->scope());
} }
if (flags & (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID | if (flags & (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID |
TRACE_EVENT_FLAG_HAS_GLOBAL_ID)) { TRACE_EVENT_FLAG_HAS_GLOBAL_ID)) {
new_trace_event->set_id(trace_event.id()); new_trace_event->set_id(trace_event->id());
} }
if ((flags & TRACE_EVENT_FLAG_FLOW_OUT) || if ((flags & TRACE_EVENT_FLAG_FLOW_OUT) ||
(flags & TRACE_EVENT_FLAG_FLOW_IN)) { (flags & TRACE_EVENT_FLAG_FLOW_IN)) {
new_trace_event->set_bind_id(trace_event.bind_id()); new_trace_event->set_bind_id(trace_event->bind_id());
} }
} }
...@@ -362,7 +369,9 @@ TraceEventDataSource* TraceEventDataSource::GetInstance() { ...@@ -362,7 +369,9 @@ TraceEventDataSource* TraceEventDataSource::GetInstance() {
} }
TraceEventDataSource::TraceEventDataSource() TraceEventDataSource::TraceEventDataSource()
: DataSourceBase(mojom::kTraceEventDataSourceName) {} : DataSourceBase(mojom::kTraceEventDataSourceName) {
RegisterTracedValueProtoWriter();
}
TraceEventDataSource::~TraceEventDataSource() = default; TraceEventDataSource::~TraceEventDataSource() = default;
...@@ -456,7 +465,7 @@ TraceEventDataSource::CreateThreadLocalEventSink() { ...@@ -456,7 +465,7 @@ TraceEventDataSource::CreateThreadLocalEventSink() {
} }
// static // static
void TraceEventDataSource::OnAddTraceEvent(const TraceEvent& trace_event) { void TraceEventDataSource::OnAddTraceEvent(TraceEvent* trace_event) {
auto* thread_local_event_sink = auto* thread_local_event_sink =
static_cast<ThreadLocalEventSink*>(ThreadLocalEventSinkSlot()->Get()); static_cast<ThreadLocalEventSink*>(ThreadLocalEventSinkSlot()->Get());
......
...@@ -84,7 +84,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TraceEventDataSource ...@@ -84,7 +84,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TraceEventDataSource
ThreadLocalEventSink* CreateThreadLocalEventSink(); ThreadLocalEventSink* CreateThreadLocalEventSink();
// Callback from TraceLog, can be called from any thread. // Callback from TraceLog, can be called from any thread.
static void OnAddTraceEvent(const base::trace_event::TraceEvent& trace_event); static void OnAddTraceEvent(base::trace_event::TraceEvent* trace_event);
base::Lock lock_; base::Lock lock_;
uint32_t target_buffer_ = 0; uint32_t target_buffer_ = 0;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "services/tracing/public/mojom/perfetto_service.mojom.h" #include "services/tracing/public/mojom/perfetto_service.mojom.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/perfetto/include/perfetto/protozero/scattered_stream_null_delegate.h" #include "third_party/perfetto/include/perfetto/protozero/scattered_stream_null_delegate.h"
#include "third_party/perfetto/include/perfetto/protozero/scattered_stream_writer.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h" #include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h" #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h" #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
......
// Copyright 2018 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 "services/tracing/public/cpp/perfetto/traced_value_proto_writer.h"
#include <stack>
#include "base/hash.h"
#include "base/json/string_escape.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
#include "base/values.h"
#include "services/tracing/public/cpp/perfetto/heap_scattered_stream_delegate.h"
#include "third_party/perfetto/include/perfetto/protozero/message_handle.h"
#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
using TracedValue = base::trace_event::TracedValue;
using ChromeTracedValue = perfetto::protos::pbzero::ChromeTracedValue;
using TracedValueHandle =
protozero::MessageHandle<perfetto::protos::pbzero::ChromeTracedValue>;
using TraceEvent = base::trace_event::TraceEvent;
namespace tracing {
PerfettoProtoAppender::PerfettoProtoAppender(
perfetto::protos::pbzero::ChromeTraceEvent_Arg* proto)
: proto_(proto) {}
PerfettoProtoAppender::~PerfettoProtoAppender() = default;
void PerfettoProtoAppender::AddBuffer(uint8_t* begin, uint8_t* end) {
ranges_.emplace_back();
ranges_.back().begin = begin;
ranges_.back().end = end;
}
size_t PerfettoProtoAppender::Finalize(uint32_t field_id) {
return proto_->AppendScatteredBytes(field_id, ranges_.data(), ranges_.size());
}
namespace {
class ProtoWriter final : public TracedValue::Writer {
public:
explicit ProtoWriter(size_t capacity)
: delegate_(capacity), stream_(&delegate_) {
proto_.Reset(&stream_);
delegate_.set_writer(&stream_);
node_stack_.emplace(TracedValueHandle(&proto_));
proto_.set_nested_type(perfetto::protos::pbzero::ChromeTracedValue::DICT);
}
~ProtoWriter() override {
// At this point there should just be the root dict still open if any,
// which can happen if the TracedValue is created but never
// used (i.e. proto never gets finalized).
if (!node_stack_.empty()) {
node_stack_.pop();
}
DCHECK(node_stack_.empty());
}
bool IsPickleWriter() const override { return false; }
bool IsProtoWriter() const override { return true; }
void SetInteger(const char* name, int value) override {
AddDictEntry(name)->set_int_value(value);
}
void SetIntegerWithCopiedName(base::StringPiece name, int value) override {
AddDictEntry(name)->set_int_value(value);
}
void SetDouble(const char* name, double value) override {
AddDictEntry(name)->set_double_value(value);
}
void SetDoubleWithCopiedName(base::StringPiece name, double value) override {
AddDictEntry(name)->set_double_value(value);
}
void SetBoolean(const char* name, bool value) override {
AddDictEntry(name)->set_bool_value(value);
}
void SetBooleanWithCopiedName(base::StringPiece name, bool value) override {
AddDictEntry(name)->set_bool_value(value);
}
void SetString(const char* name, base::StringPiece value) override {
AddDictEntry(name)->set_string_value(value.data(), value.size());
}
void SetStringWithCopiedName(base::StringPiece name,
base::StringPiece value) override {
AddDictEntry(name)->set_string_value(value.data(), value.size());
}
void SetValue(const char* name, Writer* value) override {
DCHECK(value->IsProtoWriter());
ProtoWriter* child_proto_writer = static_cast<ProtoWriter*>(value);
uint32_t full_child_size = child_proto_writer->Finalize();
DCHECK(!node_stack_.empty());
node_stack_.top()->add_dict_keys(name);
std::vector<protozero::ContiguousMemoryRange> ranges;
for (auto& chunk : child_proto_writer->delegate_.chunks()) {
ranges.emplace_back(chunk.GetUsedRange());
}
size_t appended_size = node_stack_.top()->AppendScatteredBytes(
perfetto::protos::pbzero::ChromeTracedValue::kDictValuesFieldNumber,
ranges.data(), ranges.size());
DCHECK_EQ(full_child_size, appended_size);
}
void SetValueWithCopiedName(base::StringPiece name, Writer* value) override {
SetValue(name.as_string().c_str(), value);
}
void BeginArray() override {
node_stack_.emplace(TracedValueHandle(AddArrayEntry()));
node_stack_.top()->set_nested_type(
perfetto::protos::pbzero::ChromeTracedValue::ARRAY);
}
void BeginDictionary() override {
node_stack_.emplace(TracedValueHandle(AddArrayEntry()));
node_stack_.top()->set_nested_type(
perfetto::protos::pbzero::ChromeTracedValue::DICT);
}
void BeginDictionary(const char* name) override {
node_stack_.emplace(TracedValueHandle(AddDictEntry(name)));
node_stack_.top()->set_nested_type(
perfetto::protos::pbzero::ChromeTracedValue::DICT);
}
void BeginDictionaryWithCopiedName(base::StringPiece name) override {
node_stack_.emplace(TracedValueHandle(AddDictEntry(name)));
node_stack_.top()->set_nested_type(
perfetto::protos::pbzero::ChromeTracedValue::DICT);
}
void BeginArray(const char* name) override {
node_stack_.emplace(TracedValueHandle(AddDictEntry(name)));
node_stack_.top()->set_nested_type(
perfetto::protos::pbzero::ChromeTracedValue::ARRAY);
}
void BeginArrayWithCopiedName(base::StringPiece name) override {
node_stack_.emplace(TracedValueHandle(AddDictEntry(name)));
node_stack_.top()->set_nested_type(
perfetto::protos::pbzero::ChromeTracedValue::ARRAY);
}
void EndDictionary() override {
DCHECK_GE(node_stack_.size(), 2u);
node_stack_.pop();
}
void EndArray() override {
DCHECK_GE(node_stack_.size(), 2u);
node_stack_.pop();
}
void AppendInteger(int value) override {
AddArrayEntry()->set_int_value(value);
}
void AppendDouble(double value) override {
AddArrayEntry()->set_double_value(value);
}
void AppendBoolean(bool value) override {
AddArrayEntry()->set_bool_value(value);
}
void AppendString(base::StringPiece value) override {
AddArrayEntry()->set_string_value(value.data(), value.size());
}
uint32_t Finalize() {
if (!node_stack_.empty()) {
node_stack_.pop();
}
DCHECK(node_stack_.empty());
uint32_t full_size = proto_.Finalize();
delegate_.AdjustUsedSizeOfCurrentChunk();
return full_size;
}
void AppendAsTraceFormat(std::string* out) const override { NOTREACHED(); }
bool AppendToProto(
base::trace_event::TracedValue::ProtoAppender* appender) override {
uint32_t full_size = Finalize();
for (auto& chunk : delegate_.chunks()) {
appender->AddBuffer(chunk.start(),
chunk.start() + chunk.size() - chunk.unused_bytes());
}
size_t appended_size =
appender->Finalize(perfetto::protos::pbzero::ChromeTraceEvent_Arg::
kTracedValueFieldNumber);
DCHECK_EQ(full_size, appended_size);
return true;
}
void EstimateTraceMemoryOverhead(
base::trace_event::TraceEventMemoryOverhead* overhead) override {
overhead->Add(base::trace_event::TraceEventMemoryOverhead::kTracedValue,
/* allocated size */
delegate_.GetTotalSize(),
/* resident size */
delegate_.GetTotalSize());
}
std::unique_ptr<base::Value> ToBaseValue() const override {
base::Value root(base::Value::Type::DICTIONARY);
return base::Value::ToUniquePtrValue(std::move(root));
}
private:
ChromeTracedValue* AddDictEntry(const char* name) {
DCHECK(!node_stack_.empty() && !node_stack_.top()->is_finalized());
node_stack_.top()->add_dict_keys(name);
return node_stack_.top()->add_dict_values();
}
ChromeTracedValue* AddDictEntry(base::StringPiece name) {
DCHECK(!node_stack_.empty() && !node_stack_.top()->is_finalized());
node_stack_.top()->add_dict_keys(name.data(), name.length());
return node_stack_.top()->add_dict_values();
}
ChromeTracedValue* AddArrayEntry() {
DCHECK(!node_stack_.empty() && !node_stack_.top()->is_finalized());
return node_stack_.top()->add_array_values();
}
std::stack<TracedValueHandle> node_stack_;
perfetto::protos::pbzero::ChromeTracedValue proto_;
HeapScatteredStreamWriterDelegate delegate_;
protozero::ScatteredStreamWriter stream_;
};
std::unique_ptr<TracedValue::Writer> CreateProtoWriter(size_t capacity) {
return std::make_unique<ProtoWriter>(capacity);
}
} // namespace
void RegisterTracedValueProtoWriter() {
TracedValue::SetWriterFactoryCallback(&CreateProtoWriter);
}
} // namespace tracing
// Copyright 2018 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.
#ifndef SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TRACED_VALUE_PROTO_WRITER_H_
#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TRACED_VALUE_PROTO_WRITER_H_
#include "base/component_export.h"
#include "base/trace_event/trace_event_impl.h"
#include "third_party/perfetto/include/perfetto/protozero/contiguous_memory_range.h"
namespace perfetto {
namespace protos {
namespace pbzero {
class ChromeTraceEvent_Arg;
} // namespace pbzero
} // namespace protos
} // namespace perfetto
namespace tracing {
class COMPONENT_EXPORT(TRACING_CPP) PerfettoProtoAppender
: public base::trace_event::ConvertableToTraceFormat::ProtoAppender {
public:
explicit PerfettoProtoAppender(
perfetto::protos::pbzero::ChromeTraceEvent_Arg* proto);
~PerfettoProtoAppender() override;
// ProtoAppender implementation
void AddBuffer(uint8_t* begin, uint8_t* end) override;
size_t Finalize(uint32_t field_id) override;
private:
std::vector<protozero::ContiguousMemoryRange> ranges_;
perfetto::protos::pbzero::ChromeTraceEvent_Arg* proto_;
};
void COMPONENT_EXPORT(TRACING_CPP) RegisterTracedValueProtoWriter();
} // namespace tracing
#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TRACED_VALUE_PROTO_WRITER_H_
...@@ -112,6 +112,10 @@ void TracedValue::AppendAsTraceFormat(std::string* out) const { ...@@ -112,6 +112,10 @@ void TracedValue::AppendAsTraceFormat(std::string* out) const {
traced_value_.AppendAsTraceFormat(out); traced_value_.AppendAsTraceFormat(out);
} }
bool TracedValue::AppendToProto(ProtoAppender* appender) {
return traced_value_.AppendToProto(appender);
}
void TracedValue::EstimateTraceMemoryOverhead( void TracedValue::EstimateTraceMemoryOverhead(
base::trace_event::TraceEventMemoryOverhead* overhead) { base::trace_event::TraceEventMemoryOverhead* overhead) {
traced_value_.EstimateTraceMemoryOverhead(overhead); traced_value_.EstimateTraceMemoryOverhead(overhead);
......
...@@ -52,6 +52,7 @@ class PLATFORM_EXPORT TracedValue final ...@@ -52,6 +52,7 @@ class PLATFORM_EXPORT TracedValue final
// ConvertableToTraceFormat // ConvertableToTraceFormat
void AppendAsTraceFormat(std::string*) const final; void AppendAsTraceFormat(std::string*) const final;
bool AppendToProto(ProtoAppender* appender) final;
void EstimateTraceMemoryOverhead( void EstimateTraceMemoryOverhead(
base::trace_event::TraceEventMemoryOverhead*) final; base::trace_event::TraceEventMemoryOverhead*) final;
......
...@@ -425,36 +425,44 @@ class FrameMetricsTraceData ...@@ -425,36 +425,44 @@ class FrameMetricsTraceData
FrameMetricsTraceData() = default; FrameMetricsTraceData() = default;
~FrameMetricsTraceData() override = default; ~FrameMetricsTraceData() override = default;
void AppendAsTraceFormat(std::string* out) const override { void ToTracedValue(base::trace_event::TracedValue* state) const {
base::trace_event::TracedValue state; state->BeginDictionary("Source");
settings.AsValueInto(state);
state.BeginDictionary("Source"); state->EndDictionary();
settings.AsValueInto(&state);
state.EndDictionary();
state.BeginDictionary("Skips"); state->BeginDictionary("Skips");
skips.AsValueInto(&state); skips.AsValueInto(state);
state.EndDictionary(); state->EndDictionary();
state.BeginDictionary("Latency"); state->BeginDictionary("Latency");
latency.AsValueInto(&state); latency.AsValueInto(state);
state.EndDictionary(); state->EndDictionary();
if (settings.is_frame_latency_speed_on()) { if (settings.is_frame_latency_speed_on()) {
state.BeginDictionary("Speed"); state->BeginDictionary("Speed");
speed.AsValueInto(&state); speed.AsValueInto(state);
state.EndDictionary(); state->EndDictionary();
} }
if (settings.is_frame_latency_acceleration_on()) { if (settings.is_frame_latency_acceleration_on()) {
state.BeginDictionary("Acceleration"); state->BeginDictionary("Acceleration");
acceleration.AsValueInto(&state); acceleration.AsValueInto(state);
state.EndDictionary(); state->EndDictionary();
} }
}
void AppendAsTraceFormat(std::string* out) const override {
base::trace_event::TracedValue state;
ToTracedValue(&state);
state.AppendAsTraceFormat(out); state.AppendAsTraceFormat(out);
} }
bool AppendToProto(ProtoAppender* appender) override {
base::trace_event::TracedValue state;
ToTracedValue(&state);
return state.AppendToProto(appender);
}
void EstimateTraceMemoryOverhead( void EstimateTraceMemoryOverhead(
base::trace_event::TraceEventMemoryOverhead* overhead) override { base::trace_event::TraceEventMemoryOverhead* overhead) override {
overhead->Add(base::trace_event::TraceEventMemoryOverhead::kFrameMetrics, overhead->Add(base::trace_event::TraceEventMemoryOverhead::kFrameMetrics,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment