1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/libplatform/tracing/trace-writer.h"
6
7 #include <cmath>
8
9 #include "base/trace_event/common/trace_event_common.h"
10 #include "include/v8-platform.h"
11 #include "src/base/platform/platform.h"
12
13 namespace v8 {
14 namespace platform {
15 namespace tracing {
16
17 // Writes the given string to a stream, taking care to escape characters
18 // when necessary.
WriteJSONStringToStream(const char * str,std::ostream & stream)19 V8_INLINE static void WriteJSONStringToStream(const char* str,
20 std::ostream& stream) {
21 size_t len = strlen(str);
22 stream << "\"";
23 for (size_t i = 0; i < len; ++i) {
24 // All of the permitted escape sequences in JSON strings, as per
25 // https://mathiasbynens.be/notes/javascript-escapes
26 switch (str[i]) {
27 case '\b':
28 stream << "\\b";
29 break;
30 case '\f':
31 stream << "\\f";
32 break;
33 case '\n':
34 stream << "\\n";
35 break;
36 case '\r':
37 stream << "\\r";
38 break;
39 case '\t':
40 stream << "\\t";
41 break;
42 case '\"':
43 stream << "\\\"";
44 break;
45 case '\\':
46 stream << "\\\\";
47 break;
48 // Note that because we use double quotes for JSON strings,
49 // we don't need to escape single quotes.
50 default:
51 stream << str[i];
52 break;
53 }
54 }
55 stream << "\"";
56 }
57
AppendArgValue(uint8_t type,TraceObject::ArgValue value)58 void JSONTraceWriter::AppendArgValue(uint8_t type,
59 TraceObject::ArgValue value) {
60 switch (type) {
61 case TRACE_VALUE_TYPE_BOOL:
62 stream_ << (value.as_bool ? "true" : "false");
63 break;
64 case TRACE_VALUE_TYPE_UINT:
65 stream_ << value.as_uint;
66 break;
67 case TRACE_VALUE_TYPE_INT:
68 stream_ << value.as_int;
69 break;
70 case TRACE_VALUE_TYPE_DOUBLE: {
71 std::string real;
72 double val = value.as_double;
73 if (std::isfinite(val)) {
74 std::ostringstream convert_stream;
75 convert_stream << val;
76 real = convert_stream.str();
77 // Ensure that the number has a .0 if there's no decimal or 'e'. This
78 // makes sure that when we read the JSON back, it's interpreted as a
79 // real rather than an int.
80 if (real.find('.') == std::string::npos &&
81 real.find('e') == std::string::npos &&
82 real.find('E') == std::string::npos) {
83 real += ".0";
84 }
85 } else if (std::isnan(val)) {
86 // The JSON spec doesn't allow NaN and Infinity (since these are
87 // objects in EcmaScript). Use strings instead.
88 real = "\"NaN\"";
89 } else if (val < 0) {
90 real = "\"-Infinity\"";
91 } else {
92 real = "\"Infinity\"";
93 }
94 stream_ << real;
95 break;
96 }
97 case TRACE_VALUE_TYPE_POINTER:
98 // JSON only supports double and int numbers.
99 // So as not to lose bits from a 64-bit pointer, output as a hex string.
100 stream_ << "\"" << value.as_pointer << "\"";
101 break;
102 case TRACE_VALUE_TYPE_STRING:
103 case TRACE_VALUE_TYPE_COPY_STRING:
104 if (value.as_string == nullptr) {
105 stream_ << "\"NULL\"";
106 } else {
107 WriteJSONStringToStream(value.as_string, stream_);
108 }
109 break;
110 default:
111 UNREACHABLE();
112 break;
113 }
114 }
115
AppendArgValue(ConvertableToTraceFormat * value)116 void JSONTraceWriter::AppendArgValue(ConvertableToTraceFormat* value) {
117 std::string arg_stringified;
118 value->AppendAsTraceFormat(&arg_stringified);
119 stream_ << arg_stringified;
120 }
121
JSONTraceWriter(std::ostream & stream)122 JSONTraceWriter::JSONTraceWriter(std::ostream& stream) : stream_(stream) {
123 stream_ << "{\"traceEvents\":[";
124 }
125
~JSONTraceWriter()126 JSONTraceWriter::~JSONTraceWriter() { stream_ << "]}"; }
127
AppendTraceEvent(TraceObject * trace_event)128 void JSONTraceWriter::AppendTraceEvent(TraceObject* trace_event) {
129 if (append_comma_) stream_ << ",";
130 append_comma_ = true;
131 stream_ << "{\"pid\":" << trace_event->pid()
132 << ",\"tid\":" << trace_event->tid()
133 << ",\"ts\":" << trace_event->ts()
134 << ",\"tts\":" << trace_event->tts() << ",\"ph\":\""
135 << trace_event->phase() << "\",\"cat\":\""
136 << TracingController::GetCategoryGroupName(
137 trace_event->category_enabled_flag())
138 << "\",\"name\":\"" << trace_event->name()
139 << "\",\"dur\":" << trace_event->duration()
140 << ",\"tdur\":" << trace_event->cpu_duration();
141 if (trace_event->flags() & TRACE_EVENT_FLAG_HAS_ID) {
142 if (trace_event->scope() != nullptr) {
143 stream_ << ",\"scope\":\"" << trace_event->scope() << "\"";
144 }
145 // So as not to lose bits from a 64-bit integer, output as a hex string.
146 stream_ << ",\"id\":\"0x" << std::hex << trace_event->id() << "\""
147 << std::dec;
148 }
149 stream_ << ",\"args\":{";
150 const char** arg_names = trace_event->arg_names();
151 const uint8_t* arg_types = trace_event->arg_types();
152 TraceObject::ArgValue* arg_values = trace_event->arg_values();
153 std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables =
154 trace_event->arg_convertables();
155 for (int i = 0; i < trace_event->num_args(); ++i) {
156 if (i > 0) stream_ << ",";
157 stream_ << "\"" << arg_names[i] << "\":";
158 if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
159 AppendArgValue(arg_convertables[i].get());
160 } else {
161 AppendArgValue(arg_types[i], arg_values[i]);
162 }
163 }
164 stream_ << "}}";
165 // TODO(fmeawad): Add support for Flow Events.
166 }
167
Flush()168 void JSONTraceWriter::Flush() {}
169
CreateJSONTraceWriter(std::ostream & stream)170 TraceWriter* TraceWriter::CreateJSONTraceWriter(std::ostream& stream) {
171 return new JSONTraceWriter(stream);
172 }
173
174 } // namespace tracing
175 } // namespace platform
176 } // namespace v8
177