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#if defined(V8_ENABLE_SYSTEM_INSTRUMENTATION)
14#include "src/libplatform/tracing/recorder.h"
15#endif
16
17namespace v8 {
18namespace platform {
19namespace tracing {
20
21// Writes the given string to a stream, taking care to escape characters
22// when necessary.
23V8_INLINE static void WriteJSONStringToStream(const char* str,
24                                              std::ostream& stream) {
25  size_t len = strlen(str);
26  stream << "\"";
27  for (size_t i = 0; i < len; ++i) {
28    // All of the permitted escape sequences in JSON strings, as per
29    // https://mathiasbynens.be/notes/javascript-escapes
30    switch (str[i]) {
31      case '\b':
32        stream << "\\b";
33        break;
34      case '\f':
35        stream << "\\f";
36        break;
37      case '\n':
38        stream << "\\n";
39        break;
40      case '\r':
41        stream << "\\r";
42        break;
43      case '\t':
44        stream << "\\t";
45        break;
46      case '\"':
47        stream << "\\\"";
48        break;
49      case '\\':
50        stream << "\\\\";
51        break;
52      // Note that because we use double quotes for JSON strings,
53      // we don't need to escape single quotes.
54      default:
55        stream << str[i];
56        break;
57    }
58  }
59  stream << "\"";
60}
61
62void JSONTraceWriter::AppendArgValue(uint8_t type,
63                                     TraceObject::ArgValue value) {
64  switch (type) {
65    case TRACE_VALUE_TYPE_BOOL:
66      stream_ << (value.as_uint ? "true" : "false");
67      break;
68    case TRACE_VALUE_TYPE_UINT:
69      stream_ << value.as_uint;
70      break;
71    case TRACE_VALUE_TYPE_INT:
72      stream_ << value.as_int;
73      break;
74    case TRACE_VALUE_TYPE_DOUBLE: {
75      std::string real;
76      double val = value.as_double;
77      if (std::isfinite(val)) {
78        std::ostringstream convert_stream;
79        convert_stream << val;
80        real = convert_stream.str();
81        // Ensure that the number has a .0 if there's no decimal or 'e'.  This
82        // makes sure that when we read the JSON back, it's interpreted as a
83        // real rather than an int.
84        if (real.find('.') == std::string::npos &&
85            real.find('e') == std::string::npos &&
86            real.find('E') == std::string::npos) {
87          real += ".0";
88        }
89      } else if (std::isnan(val)) {
90        // The JSON spec doesn't allow NaN and Infinity (since these are
91        // objects in EcmaScript).  Use strings instead.
92        real = "\"NaN\"";
93      } else if (val < 0) {
94        real = "\"-Infinity\"";
95      } else {
96        real = "\"Infinity\"";
97      }
98      stream_ << real;
99      break;
100    }
101    case TRACE_VALUE_TYPE_POINTER:
102      // JSON only supports double and int numbers.
103      // So as not to lose bits from a 64-bit pointer, output as a hex string.
104      stream_ << "\"" << value.as_pointer << "\"";
105      break;
106    case TRACE_VALUE_TYPE_STRING:
107    case TRACE_VALUE_TYPE_COPY_STRING:
108      if (value.as_string == nullptr) {
109        stream_ << "\"nullptr\"";
110      } else {
111        WriteJSONStringToStream(value.as_string, stream_);
112      }
113      break;
114    default:
115      UNREACHABLE();
116  }
117}
118
119void JSONTraceWriter::AppendArgValue(ConvertableToTraceFormat* value) {
120  std::string arg_stringified;
121  value->AppendAsTraceFormat(&arg_stringified);
122  stream_ << arg_stringified;
123}
124
125JSONTraceWriter::JSONTraceWriter(std::ostream& stream)
126    : JSONTraceWriter(stream, "traceEvents") {}
127
128JSONTraceWriter::JSONTraceWriter(std::ostream& stream, const std::string& tag)
129    : stream_(stream) {
130  stream_ << "{\"" << tag << "\":[";
131}
132
133JSONTraceWriter::~JSONTraceWriter() { stream_ << "]}"; }
134
135void JSONTraceWriter::AppendTraceEvent(TraceObject* trace_event) {
136  if (append_comma_) stream_ << ",";
137  append_comma_ = true;
138  stream_ << "{\"pid\":" << trace_event->pid()
139          << ",\"tid\":" << trace_event->tid()
140          << ",\"ts\":" << trace_event->ts()
141          << ",\"tts\":" << trace_event->tts() << ",\"ph\":\""
142          << trace_event->phase() << "\",\"cat\":\""
143          << TracingController::GetCategoryGroupName(
144                 trace_event->category_enabled_flag())
145          << "\",\"name\":\"" << trace_event->name()
146          << "\",\"dur\":" << trace_event->duration()
147          << ",\"tdur\":" << trace_event->cpu_duration();
148  if (trace_event->flags() &
149      (TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT)) {
150    stream_ << ",\"bind_id\":\"0x" << std::hex << trace_event->bind_id() << "\""
151            << std::dec;
152    if (trace_event->flags() & TRACE_EVENT_FLAG_FLOW_IN) {
153      stream_ << ",\"flow_in\":true";
154    }
155    if (trace_event->flags() & TRACE_EVENT_FLAG_FLOW_OUT) {
156      stream_ << ",\"flow_out\":true";
157    }
158  }
159  if (trace_event->flags() & TRACE_EVENT_FLAG_HAS_ID) {
160    if (trace_event->scope() != nullptr) {
161      stream_ << ",\"scope\":\"" << trace_event->scope() << "\"";
162    }
163    // So as not to lose bits from a 64-bit integer, output as a hex string.
164    stream_ << ",\"id\":\"0x" << std::hex << trace_event->id() << "\""
165            << std::dec;
166  }
167  stream_ << ",\"args\":{";
168  const char** arg_names = trace_event->arg_names();
169  const uint8_t* arg_types = trace_event->arg_types();
170  TraceObject::ArgValue* arg_values = trace_event->arg_values();
171  std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables =
172      trace_event->arg_convertables();
173  for (int i = 0; i < trace_event->num_args(); ++i) {
174    if (i > 0) stream_ << ",";
175    stream_ << "\"" << arg_names[i] << "\":";
176    if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
177      AppendArgValue(arg_convertables[i].get());
178    } else {
179      AppendArgValue(arg_types[i], arg_values[i]);
180    }
181  }
182  stream_ << "}}";
183  // TODO(fmeawad): Add support for Flow Events.
184}
185
186void JSONTraceWriter::Flush() {}
187
188TraceWriter* TraceWriter::CreateJSONTraceWriter(std::ostream& stream) {
189  return new JSONTraceWriter(stream);
190}
191
192TraceWriter* TraceWriter::CreateJSONTraceWriter(std::ostream& stream,
193                                                const std::string& tag) {
194  return new JSONTraceWriter(stream, tag);
195}
196
197#if defined(V8_ENABLE_SYSTEM_INSTRUMENTATION)
198SystemInstrumentationTraceWriter::SystemInstrumentationTraceWriter() {
199  recorder_ = std::make_unique<Recorder>();
200}
201
202SystemInstrumentationTraceWriter::~SystemInstrumentationTraceWriter() {
203  recorder_.reset(nullptr);
204}
205
206void SystemInstrumentationTraceWriter::AppendTraceEvent(
207    TraceObject* trace_event) {
208  if (recorder_->IsEnabled()) {
209    recorder_->AddEvent(trace_event);
210  }
211}
212
213void SystemInstrumentationTraceWriter::Flush() {}
214
215TraceWriter* TraceWriter::CreateSystemInstrumentationTraceWriter() {
216  return new SystemInstrumentationTraceWriter();
217}
218#endif
219
220}  // namespace tracing
221}  // namespace platform
222}  // namespace v8
223