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