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. 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 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 116 void JSONTraceWriter::AppendArgValue(ConvertableToTraceFormat* value) { 117 std::string arg_stringified; 118 value->AppendAsTraceFormat(&arg_stringified); 119 stream_ << arg_stringified; 120 } 121 122 JSONTraceWriter::JSONTraceWriter(std::ostream& stream) : stream_(stream) { 123 stream_ << "{\"traceEvents\":["; 124 } 125 126 JSONTraceWriter::~JSONTraceWriter() { stream_ << "]}"; } 127 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 168 void JSONTraceWriter::Flush() {} 169 170 TraceWriter* TraceWriter::CreateJSONTraceWriter(std::ostream& stream) { 171 return new JSONTraceWriter(stream); 172 } 173 174 } // namespace tracing 175 } // namespace platform 176 } // namespace v8 177