| // 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 <cmath> | 
 |  | 
 | #include "v8/src/tracing/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_uint ? "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(); | 
 |   } | 
 | } | 
 |  | 
 | 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_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT)) { | 
 |     stream_ << ",\"bind_id\":\"0x" << std::hex << trace_event->bind_id() << "\"" | 
 |             << std::dec; | 
 |     if (trace_event->flags() & TRACE_EVENT_FLAG_FLOW_IN) { | 
 |       stream_ << ",\"flow_in\":true"; | 
 |     } | 
 |     if (trace_event->flags() & TRACE_EVENT_FLAG_FLOW_OUT) { | 
 |       stream_ << ",\"flow_out\":true"; | 
 |     } | 
 |   } | 
 |   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<v8::ConvertableToTraceFormat>* 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 |