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
13namespace v8 {
14namespace platform {
15namespace tracing {
16
17// Writes the given string to a stream, taking care to escape characters
18// when necessary.
19V8_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
58void 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
116void JSONTraceWriter::AppendArgValue(ConvertableToTraceFormat* value) {
117  std::string arg_stringified;
118  value->AppendAsTraceFormat(&arg_stringified);
119  stream_ << arg_stringified;
120}
121
122JSONTraceWriter::JSONTraceWriter(std::ostream& stream) : stream_(stream) {
123  stream_ << "{\"traceEvents\":[";
124}
125
126JSONTraceWriter::~JSONTraceWriter() { stream_ << "]}"; }
127
128void 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
168void JSONTraceWriter::Flush() {}
169
170TraceWriter* TraceWriter::CreateJSONTraceWriter(std::ostream& stream) {
171  return new JSONTraceWriter(stream);
172}
173
174}  // namespace tracing
175}  // namespace platform
176}  // namespace v8
177