// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/libplatform/tracing/trace-writer.h" #include #include "base/trace_event/common/trace_event_common.h" #include "include/v8-platform.h" #include "src/base/platform/platform.h" namespace v8 { namespace platform { namespace tracing { // Writes the given string to a stream, taking care to escape characters // when necessary. V8_INLINE static void WriteJSONStringToStream(const char* str, std::ostream& stream) { size_t len = strlen(str); stream << "\""; for (size_t i = 0; i < len; ++i) { // All of the permitted escape sequences in JSON strings, as per // https://mathiasbynens.be/notes/javascript-escapes switch (str[i]) { case '\b': stream << "\\b"; break; case '\f': stream << "\\f"; break; case '\n': stream << "\\n"; break; case '\r': stream << "\\r"; break; case '\t': stream << "\\t"; break; case '\"': stream << "\\\""; break; case '\\': stream << "\\\\"; break; // Note that because we use double quotes for JSON strings, // we don't need to escape single quotes. default: stream << str[i]; break; } } stream << "\""; } void JSONTraceWriter::AppendArgValue(uint8_t type, TraceObject::ArgValue value) { switch (type) { case TRACE_VALUE_TYPE_BOOL: stream_ << (value.as_bool ? "true" : "false"); break; case TRACE_VALUE_TYPE_UINT: stream_ << value.as_uint; break; case TRACE_VALUE_TYPE_INT: stream_ << value.as_int; break; case TRACE_VALUE_TYPE_DOUBLE: { std::string real; double val = value.as_double; if (std::isfinite(val)) { std::ostringstream convert_stream; convert_stream << val; real = convert_stream.str(); // Ensure that the number has a .0 if there's no decimal or 'e'. This // makes sure that when we read the JSON back, it's interpreted as a // real rather than an int. if (real.find('.') == std::string::npos && real.find('e') == std::string::npos && real.find('E') == std::string::npos) { real += ".0"; } } else if (std::isnan(val)) { // The JSON spec doesn't allow NaN and Infinity (since these are // objects in EcmaScript). Use strings instead. real = "\"NaN\""; } else if (val < 0) { real = "\"-Infinity\""; } else { real = "\"Infinity\""; } stream_ << real; break; } case TRACE_VALUE_TYPE_POINTER: // JSON only supports double and int numbers. // So as not to lose bits from a 64-bit pointer, output as a hex string. stream_ << "\"" << value.as_pointer << "\""; break; case TRACE_VALUE_TYPE_STRING: case TRACE_VALUE_TYPE_COPY_STRING: if (value.as_string == nullptr) { stream_ << "\"nullptr\""; } else { WriteJSONStringToStream(value.as_string, stream_); } break; default: UNREACHABLE(); break; } } void JSONTraceWriter::AppendArgValue(ConvertableToTraceFormat* value) { std::string arg_stringified; value->AppendAsTraceFormat(&arg_stringified); stream_ << arg_stringified; } JSONTraceWriter::JSONTraceWriter(std::ostream& stream) : JSONTraceWriter(stream, "traceEvents") {} JSONTraceWriter::JSONTraceWriter(std::ostream& stream, const std::string& tag) : stream_(stream) { stream_ << "{\"" << tag << "\":["; } JSONTraceWriter::~JSONTraceWriter() { stream_ << "]}"; } void JSONTraceWriter::AppendTraceEvent(TraceObject* trace_event) { if (append_comma_) stream_ << ","; append_comma_ = true; stream_ << "{\"pid\":" << trace_event->pid() << ",\"tid\":" << trace_event->tid() << ",\"ts\":" << trace_event->ts() << ",\"tts\":" << trace_event->tts() << ",\"ph\":\"" << trace_event->phase() << "\",\"cat\":\"" << TracingController::GetCategoryGroupName( trace_event->category_enabled_flag()) << "\",\"name\":\"" << trace_event->name() << "\",\"dur\":" << trace_event->duration() << ",\"tdur\":" << trace_event->cpu_duration(); if (trace_event->flags() & TRACE_EVENT_FLAG_HAS_ID) { if (trace_event->scope() != nullptr) { stream_ << ",\"scope\":\"" << trace_event->scope() << "\""; } // So as not to lose bits from a 64-bit integer, output as a hex string. stream_ << ",\"id\":\"0x" << std::hex << trace_event->id() << "\"" << std::dec; } stream_ << ",\"args\":{"; const char** arg_names = trace_event->arg_names(); const uint8_t* arg_types = trace_event->arg_types(); TraceObject::ArgValue* arg_values = trace_event->arg_values(); std::unique_ptr* arg_convertables = trace_event->arg_convertables(); for (int i = 0; i < trace_event->num_args(); ++i) { if (i > 0) stream_ << ","; stream_ << "\"" << arg_names[i] << "\":"; if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) { AppendArgValue(arg_convertables[i].get()); } else { AppendArgValue(arg_types[i], arg_values[i]); } } stream_ << "}}"; // TODO(fmeawad): Add support for Flow Events. } void JSONTraceWriter::Flush() {} TraceWriter* TraceWriter::CreateJSONTraceWriter(std::ostream& stream) { return new JSONTraceWriter(stream); } TraceWriter* TraceWriter::CreateJSONTraceWriter(std::ostream& stream, const std::string& tag) { return new JSONTraceWriter(stream, tag); } } // namespace tracing } // namespace platform } // namespace v8