| Index: webrtc/base/event_tracer.cc
|
| diff --git a/webrtc/base/event_tracer.cc b/webrtc/base/event_tracer.cc
|
| index 85d5c0b17b60a5531a2d1cb56803f97d0627e731..4393bff2985884a746ad3a330d962294afc6e60f 100644
|
| --- a/webrtc/base/event_tracer.cc
|
| +++ b/webrtc/base/event_tracer.cc
|
| @@ -11,6 +11,7 @@
|
|
|
| #include <inttypes.h>
|
|
|
| +#include <string>
|
| #include <vector>
|
|
|
| #include "webrtc/base/checks.h"
|
| @@ -18,9 +19,14 @@
|
| #include "webrtc/base/event.h"
|
| #include "webrtc/base/logging.h"
|
| #include "webrtc/base/platform_thread.h"
|
| +#include "webrtc/base/stringutils.h"
|
| #include "webrtc/base/timeutils.h"
|
| #include "webrtc/base/trace_event.h"
|
|
|
| +// This is a guesstimate that should be enough in most cases.
|
| +static const size_t kEventLoggerArgsStrBufferInitialSize = 256;
|
| +static const size_t kTraceArgBufferLength = 32;
|
| +
|
| namespace webrtc {
|
|
|
| namespace {
|
| @@ -90,12 +96,32 @@ class EventLogger final {
|
| void AddTraceEvent(const char* name,
|
| const unsigned char* category_enabled,
|
| char phase,
|
| + int num_args,
|
| + const char** arg_names,
|
| + const unsigned char* arg_types,
|
| + const unsigned long long* arg_values,
|
| uint64_t timestamp,
|
| int pid,
|
| rtc::PlatformThreadId thread_id) {
|
| + std::vector<TraceArg> args(num_args);
|
| + for (int i = 0; i < num_args; ++i) {
|
| + TraceArg& arg = args[i];
|
| + arg.name = arg_names[i];
|
| + arg.type = arg_types[i];
|
| + arg.value.as_uint = arg_values[i];
|
| +
|
| + // Value is a pointer to a temporary string, so we have to make a copy.
|
| + if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
|
| + // Space for the string and for the terminating null character.
|
| + size_t str_length = strlen(arg.value.as_string) + 1;
|
| + char* str_copy = new char[str_length];
|
| + memcpy(str_copy, arg.value.as_string, str_length);
|
| + arg.value.as_string = str_copy;
|
| + }
|
| + }
|
| rtc::CritScope lock(&crit_);
|
| trace_events_.push_back(
|
| - {name, category_enabled, phase, timestamp, 1, thread_id});
|
| + {name, category_enabled, phase, args, timestamp, 1, thread_id});
|
| }
|
|
|
| // The TraceEvent format is documented here:
|
| @@ -112,7 +138,30 @@ class EventLogger final {
|
| rtc::CritScope lock(&crit_);
|
| trace_events_.swap(events);
|
| }
|
| - for (const TraceEvent& e : events) {
|
| + std::string args_str;
|
| + args_str.reserve(kEventLoggerArgsStrBufferInitialSize);
|
| + for (TraceEvent& e : events) {
|
| + args_str.clear();
|
| + if (!e.args.empty()) {
|
| + args_str += ", \"args\": {";
|
| + bool is_first_argument = true;
|
| + for (TraceArg& arg : e.args) {
|
| + if (!is_first_argument)
|
| + args_str += ",";
|
| + is_first_argument = false;
|
| + args_str += " \"";
|
| + args_str += arg.name;
|
| + args_str += "\": ";
|
| + args_str += TraceArgValueAsString(arg);
|
| +
|
| + // Delete our copy of the string.
|
| + if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
|
| + delete[] arg.value.as_string;
|
| + arg.value.as_string = nullptr;
|
| + }
|
| + }
|
| + args_str += " }";
|
| + }
|
| fprintf(output_file_,
|
| "%s{ \"name\": \"%s\""
|
| ", \"cat\": \"%s\""
|
| @@ -124,9 +173,10 @@ class EventLogger final {
|
| #else
|
| ", \"tid\": %d"
|
| #endif // defined(WEBRTC_WIN)
|
| + "%s"
|
| "}\n",
|
| has_logged_event ? "," : " ", e.name, e.category_enabled,
|
| - e.phase, e.timestamp, e.pid, e.tid);
|
| + e.phase, e.timestamp, e.pid, e.tid, args_str.c_str());
|
| has_logged_event = true;
|
| }
|
| if (shutting_down)
|
| @@ -177,15 +227,96 @@ class EventLogger final {
|
| }
|
|
|
| private:
|
| + struct TraceArg {
|
| + const char* name;
|
| + unsigned char type;
|
| + // Copied from webrtc/base/trace_event.h TraceValueUnion.
|
| + union TraceArgValue {
|
| + bool as_bool;
|
| + unsigned long long as_uint;
|
| + long long as_int;
|
| + double as_double;
|
| + const void* as_pointer;
|
| + const char* as_string;
|
| + } value;
|
| +
|
| + // Assert that the size of the union is equal to the size of the as_uint
|
| + // field since we are assigning to arbitrary types using it.
|
| + static_assert(sizeof(TraceArgValue) == sizeof(unsigned long long),
|
| + "Size of TraceArg value union is not equal to the size of "
|
| + "the uint field of that union.");
|
| + };
|
| +
|
| struct TraceEvent {
|
| const char* name;
|
| const unsigned char* category_enabled;
|
| char phase;
|
| + std::vector<TraceArg> args;
|
| uint64_t timestamp;
|
| int pid;
|
| rtc::PlatformThreadId tid;
|
| };
|
|
|
| + static std::string TraceArgValueAsString(TraceArg arg) {
|
| + std::string output;
|
| +
|
| + if (arg.type == TRACE_VALUE_TYPE_STRING ||
|
| + arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
|
| + // Space for every character to be an espaced character + two for
|
| + // quatation marks.
|
| + output.reserve(strlen(arg.value.as_string) * 2 + 2);
|
| + output += '\"';
|
| + const char* c = arg.value.as_string;
|
| + do {
|
| + if (*c == '"' || *c == '\\') {
|
| + output += '\\';
|
| + output += *c;
|
| + } else {
|
| + output += *c;
|
| + }
|
| + } while (*++c);
|
| + output += '\"';
|
| + } else {
|
| + output.resize(kTraceArgBufferLength);
|
| + size_t print_length = 0;
|
| + switch (arg.type) {
|
| + case TRACE_VALUE_TYPE_BOOL:
|
| + if (arg.value.as_bool) {
|
| + strcpy(&output[0], "true");
|
| + print_length = 4;
|
| + } else {
|
| + strcpy(&output[0], "false");
|
| + print_length = 5;
|
| + }
|
| + break;
|
| + case TRACE_VALUE_TYPE_UINT:
|
| + print_length = sprintfn(&output[0], kTraceArgBufferLength, "%llu",
|
| + arg.value.as_uint);
|
| + break;
|
| + case TRACE_VALUE_TYPE_INT:
|
| + print_length = sprintfn(&output[0], kTraceArgBufferLength, "%lld",
|
| + arg.value.as_int);
|
| + break;
|
| + case TRACE_VALUE_TYPE_DOUBLE:
|
| + print_length = sprintfn(&output[0], kTraceArgBufferLength, "%f",
|
| + arg.value.as_double);
|
| + break;
|
| + case TRACE_VALUE_TYPE_POINTER:
|
| + print_length = sprintfn(&output[0], kTraceArgBufferLength, "\"%p\"",
|
| + arg.value.as_pointer);
|
| + break;
|
| + }
|
| + size_t output_length = print_length < kTraceArgBufferLength
|
| + ? print_length
|
| + : kTraceArgBufferLength - 1;
|
| + // This will hopefully be very close to nop. On most implementations, it
|
| + // just writes null byte and sets the length field of the string.
|
| + output.resize(output_length);
|
| + }
|
| +
|
| + return output;
|
| + }
|
| +
|
| rtc::CriticalSection crit_;
|
| std::vector<TraceEvent> trace_events_ GUARDED_BY(crit_);
|
| rtc::PlatformThread logging_thread_;
|
| @@ -229,7 +360,8 @@ void InternalAddTraceEvent(char phase,
|
| if (rtc::AtomicOps::AcquireLoad(&g_event_logging_active) == 0)
|
| return;
|
|
|
| - g_event_logger->AddTraceEvent(name, category_enabled, phase,
|
| + g_event_logger->AddTraceEvent(name, category_enabled, phase, num_args,
|
| + arg_names, arg_types, arg_values,
|
| rtc::TimeMicros(), 1, rtc::CurrentThreadId());
|
| }
|
|
|
|
|