blob: b682dd2e161924f1b2b6816c2693ecf47c2e26f8 [file] [log] [blame]
// 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_PERFETTO_JSON_TRACE_EXPORTER_H_
#define SERVICES_TRACING_PERFETTO_JSON_TRACE_EXPORTER_H_
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
namespace perfetto {
namespace protos {
class ChromeLegacyJsonTrace;
class ChromeMetadata;
class ChromeTraceEvent_Arg;
class DebugAnnotation;
class TraceStats;
} // namespace protos
} // namespace perfetto
namespace tracing {
void OutputJSONFromArgumentProto(
const perfetto::protos::ChromeTraceEvent_Arg& arg,
std::string* out);
void OutputJSONFromArgumentProto(const perfetto::protos::DebugAnnotation& arg,
std::string* out);
// Converts proto-encoded trace data into the legacy JSON trace format.
// Conversion happens on-the-fly as new trace packets are received.
class JSONTraceExporter {
public:
// Given argument name for the trace event, returns if the argument should be
// filtered or not.
using ArgumentNameFilterPredicate =
base::RepeatingCallback<bool(const char* arg_name)>;
// Given trace event name and category group name, returns a argument name
// filter predicate callback that can filter arguments for the given event.
using ArgumentFilterPredicate =
base::RepeatingCallback<bool(const char* category_group_name,
const char* event_name,
ArgumentNameFilterPredicate*)>;
// Given a metadata name, returns if the event should be filtered or not.
using MetadataFilterPredicate =
base::RepeatingCallback<bool(const std::string& metadata_name)>;
using OnTraceEventJSONCallback = base::RepeatingCallback<
void(std::string* json, base::DictionaryValue* metadata, bool has_more)>;
JSONTraceExporter(ArgumentFilterPredicate argument_filter_predicate,
MetadataFilterPredicate metadata_filter_predicate,
OnTraceEventJSONCallback callback);
virtual ~JSONTraceExporter();
// Called to notify the exporter of new trace packets. Will call the
// |json_callback| passed in the constructor with the converted trace data.
void OnTraceData(std::vector<perfetto::TracePacket> packets, bool has_more);
void SetArgumentFilterForTesting(ArgumentFilterPredicate predicate) {
argument_filter_predicate_ = std::move(predicate);
}
void SetMetdataFilterPredicateForTesting(MetadataFilterPredicate predicate) {
metadata_filter_predicate_ = std::move(predicate);
}
void set_label_filter(const std::string& label_filter) {
label_filter_ = label_filter;
}
protected:
class StringBuffer {
public:
StringBuffer(OnTraceEventJSONCallback callback);
StringBuffer(const StringBuffer& copy) = delete;
StringBuffer(StringBuffer&& move);
~StringBuffer();
StringBuffer& operator+=(const std::string& input);
StringBuffer& operator+=(std::string&& input);
StringBuffer& operator+=(const char* input);
std::string* mutable_out();
const std::string& out();
void reserve(size_t size);
template <typename... Args>
void AppendF(const char* format, Args&&... args) {
MaybeRunCallback();
base::StringAppendF(&out_, format, std::forward<Args>(args)...);
}
void EscapeJSONAndAppend(const std::string& unescaped);
void Flush(base::DictionaryValue* metadata, bool has_more);
private:
// Depending on the size of the current output we might need to send a part
// of it back.
void MaybeRunCallback();
std::string out_;
OnTraceEventJSONCallback callback_;
};
class ArgumentBuilder {
public:
ArgumentBuilder(const ArgumentFilterPredicate& argument_filter_predicate,
const char* name,
const char* category_group_name,
StringBuffer* out);
~ArgumentBuilder();
// Takes an arg name, and returns nullptr if
//
// a) all args are being stripped
// b) if this arg name was stripped.
//
// If the StringBuffer pointer is valid then you should append a string that
// is properly formatted json for this arg value.
StringBuffer* MaybeAddArg(const std::string& name);
private:
StringBuffer* AddArg();
bool ArgumentNameIsStripped(const std::string& name);
bool SkipBecauseStripped(const std::string& name);
StringBuffer* out_;
bool strip_args_ = false;
bool has_args_ = false;
ArgumentNameFilterPredicate argument_name_filter_predicate_;
};
// Adds all required fields to |out| in proper JSON format. Only one
// ScopedJSONTraceEventAppender should exist per |out| string at a time,
// since the TraceEvent will not be finished until the
// ScopedJSONTraceEventAppender goes out of scope.
class ScopedJSONTraceEventAppender {
public:
// Only one reference should exist at a time. So moving is okay but copying
// is disallowed.
ScopedJSONTraceEventAppender(ScopedJSONTraceEventAppender&& move);
ScopedJSONTraceEventAppender(const ScopedJSONTraceEventAppender& copy) =
delete;
// Ensures that the JSON object is properly closed.
~ScopedJSONTraceEventAppender();
// Optional traceEvent fields can also be set with the methods below. All
// methods should only be called once.
void AddDuration(int64_t duration);
void AddThreadDuration(int64_t thread_duration);
void AddThreadTimestamp(int64_t thread_timestamp);
void AddBindId(uint64_t bind_id);
// A set of bit flags for this trace event, along with a |scope|. |scope| is
// ignored if empty.
void AddFlags(uint32_t flags,
base::Optional<uint64_t> id,
const std::string& scope);
// Begins constructing the args sections, and finishes when ArgumentBuilder
// is destroyed. No other Add* function should be called until
// ArgumentBuilder goes out of scope.
//
// This should be used as follows.
// {
// auto arg_builder = scoped_appender.BuildArgs();
// for (const auto& arg : args) {
// JSONTraceExporter::StringBuffer* maybe_arg =
// arg_builder->MaybeAddArg(arg.name);
// if (maybe_arg) {
// // Then one of the following to add the value in |arg|.
// *maybe_arg += "\"json_formatted_raw_value\"";
// maybe_arg->AppendF("\"%d\"", arg.integer);
// maybe_arg->EscapeJSONAndAppend("json_that will be : escaped");
// }
// }
// }
//
// IMPORTANT: ArgumentBuilder must be deconstructed before the
// ScopedJSONTraceEventAppender that created it is.
std::unique_ptr<ArgumentBuilder> BuildArgs();
private:
// Subclasses of JSONTraceExporter can create a new instance by calling
// AddTraceEvent().
ScopedJSONTraceEventAppender(
StringBuffer* out,
ArgumentFilterPredicate argument_filter_predicate,
const char* name,
const char* categories,
int32_t phase,
int64_t timestamp,
int32_t pid,
int32_t tid);
friend class JSONTraceExporter;
char phase_;
bool added_args_;
StringBuffer* out_;
const char* event_name_;
const char* category_group_name_;
ArgumentFilterPredicate argument_filter_predicate_;
};
// Subclasses implement this to add data from |packets| to the JSON output.
// For example they can add traceEvents through AddTraceEvent(), or add
// metadata through AddChromeMetadata().
virtual void ProcessPackets(
const std::vector<perfetto::TracePacket>& packets) = 0;
// If true then all trace events should be skipped. AddTraceEvent should not
// be called.
bool ShouldOutputTraceEvents() const;
// Used for passing legacy JSON traces. This will update either the
// traceEvents directly if needed by calling AddJSONTraceEvent or will store
// the system trace information to be appended after the packets have been
// processed.
void AddChromeLegacyJSONTrace(
const perfetto::protos::ChromeLegacyJsonTrace& json_trace);
// Adds system Ftrace data to be appended to the trace JSON after all the
// traceEvents have been processed.
void AddLegacyFtrace(const std::string& legacy_ftrace_output);
// Used to append ChromeMetadata to the trace. Can be called at any point.
// Metadata is always appended after all packets have been processed.
void AddChromeMetadata(const perfetto::protos::ChromeMetadata& metadata);
// Writes (overwriting if already set) the perfetto trace stats to the
// metadata that will be appended after all packets have been processed.
void SetTraceStatsMetadata(const perfetto::protos::TraceStats& stats);
// Used when sub-classes are adding a new trace event to the traceEvents
// array. This will ensure that only proper json is appended.
ScopedJSONTraceEventAppender AddTraceEvent(const char* name,
const char* categories,
int32_t phase,
int64_t timestamp,
int32_t pid,
int32_t tid);
private:
// Used by the implementation to ensure the proper separators exist between
// trace events in the array.
StringBuffer* AddJSONTraceEvent();
StringBuffer out_;
bool has_output_first_event_ = false;
bool has_output_json_preamble_ = false;
std::string legacy_system_trace_events_;
std::string label_filter_;
std::string legacy_system_ftrace_output_;
std::unique_ptr<base::DictionaryValue> metadata_;
ArgumentFilterPredicate argument_filter_predicate_;
MetadataFilterPredicate metadata_filter_predicate_;
DISALLOW_COPY_AND_ASSIGN(JSONTraceExporter);
};
} // namespace tracing
#endif // SERVICES_TRACING_PERFETTO_JSON_TRACE_EXPORTER_H_