Skip to content

Commit

Permalink
trace_events: add traced_value.cc/traced_value.h
Browse files Browse the repository at this point in the history
Port of the V8 internal v8::tracing::TracedValue that allows
structured data to be included in the trace event. The v8 class
is not exported in the public API so we cannot use it directly.

This is a simplified and slightly modified port. This commit only
adds the class, it does not add uses of it. Those will come in
separate PRs/commits.
  • Loading branch information
jasnell committed Jul 11, 2018
1 parent fcfd3e1 commit 509a715
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 0 deletions.
3 changes: 3 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@
'src/tracing/node_trace_buffer.cc',
'src/tracing/node_trace_writer.cc',
'src/tracing/trace_event.cc',
'src/tracing/traced_value.cc',
'src/tty_wrap.cc',
'src/udp_wrap.cc',
'src/util.cc',
Expand Down Expand Up @@ -440,6 +441,7 @@
'src/tracing/node_trace_buffer.h',
'src/tracing/node_trace_writer.h',
'src/tracing/trace_event.h',
'src/tracing/traced_value.h',
'src/util.h',
'src/util-inl.h',
'deps/http_parser/http_parser.h',
Expand Down Expand Up @@ -953,6 +955,7 @@
'test/cctest/test_node_postmortem_metadata.cc',
'test/cctest/test_environment.cc',
'test/cctest/test_platform.cc',
'test/cctest/test_traced_value.cc',
'test/cctest/test_util.cc',
'test/cctest/test_url.cc'
],
Expand Down
191 changes: 191 additions & 0 deletions src/tracing/traced_value.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// 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 "tracing/traced_value.h"

#include <math.h>
#include <sstream>
#include <stdio.h>
#include <string>

#if defined(_STLP_VENDOR_CSTD)
// STLPort doesn't import fpclassify into the std namespace.
#define FPCLASSIFY_NAMESPACE
#else
#define FPCLASSIFY_NAMESPACE std
#endif

namespace node {
namespace tracing {

namespace {

void EscapeAndAppendString(const char* value, std::string* result) {
*result += '"';
char number_buffer[10];
while (*value) {
char c = *value++;
switch (c) {
case '\t':
*result += "\\t";
break;
case '\n':
*result += "\\n";
break;
case '\"':
*result += "\\\"";
break;
case '\\':
*result += "\\\\";
break;
default:
if (c < '\x20') {
snprintf(
number_buffer, arraysize(number_buffer), "\\u%04X",
static_cast<unsigned>(static_cast<unsigned char>(c)));
*result += number_buffer;
} else {
*result += c;
}
}
}
*result += '"';
}

std::string DoubleToCString(double v) {
switch (FPCLASSIFY_NAMESPACE::fpclassify(v)) {
case FP_NAN: return "\"NaN\"";
case FP_INFINITE: return (v < 0.0 ? "\"-Infinity\"" : "\"Infinity\"");
case FP_ZERO: return "0";
default:
// This is a far less sophisticated version than the one used inside v8.
std::ostringstream stream;
stream.imbue(std::locale("C")); // Ignore locale
stream << v;
return stream.str();
}
}

} // namespace

std::unique_ptr<TracedValue> TracedValue::Create() {
return std::unique_ptr<TracedValue>(new TracedValue(false));
}

std::unique_ptr<TracedValue> TracedValue::CreateArray() {
return std::unique_ptr<TracedValue>(new TracedValue(true));
}

TracedValue::TracedValue(bool root_is_array) :
first_item_(true), root_is_array_(root_is_array) {}

TracedValue::~TracedValue() {}

void TracedValue::SetInteger(const char* name, int value) {
WriteName(name);
data_ += std::to_string(value);
}

void TracedValue::SetDouble(const char* name, double value) {
WriteName(name);
data_ += DoubleToCString(value);
}

void TracedValue::SetBoolean(const char* name, bool value) {
WriteName(name);
data_ += value ? "true" : "false";
}

void TracedValue::SetNull(const char* name) {
WriteName(name);
data_ += "null";
}

void TracedValue::SetString(const char* name, const char* value) {
WriteName(name);
EscapeAndAppendString(value, &data_);
}

void TracedValue::BeginDictionary(const char* name) {
WriteName(name);
data_ += '{';
first_item_ = true;
}

void TracedValue::BeginArray(const char* name) {
WriteName(name);
data_ += '[';
first_item_ = true;
}

void TracedValue::AppendInteger(int value) {
WriteComma();
data_ += std::to_string(value);
}

void TracedValue::AppendDouble(double value) {
WriteComma();
data_ += DoubleToCString(value);
}

void TracedValue::AppendBoolean(bool value) {
WriteComma();
data_ += value ? "true" : "false";
}

void TracedValue::AppendNull() {
WriteComma();
data_ += "null";
}

void TracedValue::AppendString(const char* value) {
WriteComma();
EscapeAndAppendString(value, &data_);
}

void TracedValue::BeginDictionary() {
WriteComma();
data_ += '{';
first_item_ = true;
}

void TracedValue::BeginArray() {
WriteComma();
data_ += '[';
first_item_ = true;
}

void TracedValue::EndDictionary() {
data_ += '}';
first_item_ = false;
}

void TracedValue::EndArray() {
data_ += ']';
first_item_ = false;
}

void TracedValue::WriteComma() {
if (first_item_) {
first_item_ = false;
} else {
data_ += ',';
}
}

void TracedValue::WriteName(const char* name) {
WriteComma();
data_ += '"';
data_ += name;
data_ += "\":";
}

void TracedValue::AppendAsTraceFormat(std::string* out) const {
*out += root_is_array_ ? '[' : '{';
*out += data_;
*out += root_is_array_ ? ']' : '}';
}

} // namespace tracing
} // namespace node
68 changes: 68 additions & 0 deletions src/tracing/traced_value.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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.

#ifndef SRC_TRACING_TRACED_VALUE_H_
#define SRC_TRACING_TRACED_VALUE_H_

#include "node_internals.h"
#include "v8.h"

#include <stddef.h>
#include <memory>
#include <string>

namespace node {
namespace tracing {

class TracedValue : public v8::ConvertableToTraceFormat {
public:
~TracedValue() override;

static std::unique_ptr<TracedValue> Create();
static std::unique_ptr<TracedValue> CreateArray();

void EndDictionary();
void EndArray();

// These methods assume that |name| is a long lived "quoted" string.
void SetInteger(const char* name, int value);
void SetDouble(const char* name, double value);
void SetBoolean(const char* name, bool value);
void SetNull(const char* name);
void SetString(const char* name, const char* value);
void SetString(const char* name, const std::string& value) {
SetString(name, value.c_str());
}
void BeginDictionary(const char* name);
void BeginArray(const char* name);

void AppendInteger(int);
void AppendDouble(double);
void AppendBoolean(bool);
void AppendNull();
void AppendString(const char*);
void AppendString(const std::string& value) { AppendString(value.c_str()); }
void BeginArray();
void BeginDictionary();

// ConvertableToTraceFormat implementation.
void AppendAsTraceFormat(std::string* out) const override;

private:
explicit TracedValue(bool root_is_array = false);

void WriteComma();
void WriteName(const char* name);

std::string data_;
bool first_item_;
bool root_is_array_;

DISALLOW_COPY_AND_ASSIGN(TracedValue);
};

} // namespace tracing
} // namespace node

#endif // SRC_TRACING_TRACED_VALUE_H_
63 changes: 63 additions & 0 deletions test/cctest/test_traced_value.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "tracing/traced_value.h"

#include <math.h>
#include <stddef.h>
#include <string.h>

#include "gtest/gtest.h"

using node::tracing::TracedValue;

TEST(TracedValue, Object) {
auto traced_value = TracedValue::Create();
traced_value->SetString("a", "b");
traced_value->SetInteger("b", 1);
traced_value->SetDouble("c", 1.234);
traced_value->SetDouble("d", NAN);
traced_value->SetDouble("e", INFINITY);
traced_value->SetDouble("f", -INFINITY);
traced_value->SetDouble("g", 1.23e7);
traced_value->SetBoolean("h", false);
traced_value->SetBoolean("i", true);
traced_value->SetNull("j");
traced_value->BeginDictionary("k");
traced_value->SetString("l", "m");
traced_value->EndDictionary();

std::string string;
traced_value->AppendAsTraceFormat(&string);

static const char* check = "{\"a\":\"b\",\"b\":1,\"c\":1.234,\"d\":\"NaN\","
"\"e\":\"Infinity\",\"f\":\"-Infinity\",\"g\":"
"1.23e+07,\"h\":false,\"i\":true,\"j\":null,\"k\":"
"{\"l\":\"m\"}}";

EXPECT_EQ(string, check);
}

TEST(TracedValue, Array) {
auto traced_value = TracedValue::CreateArray();
traced_value->AppendString("a");
traced_value->AppendInteger(1);
traced_value->AppendDouble(1.234);
traced_value->AppendDouble(NAN);
traced_value->AppendDouble(INFINITY);
traced_value->AppendDouble(-INFINITY);
traced_value->AppendDouble(1.23e7);
traced_value->AppendBoolean(false);
traced_value->AppendBoolean(true);
traced_value->AppendNull();
traced_value->BeginDictionary();
traced_value->BeginArray("foo");
traced_value->EndArray();
traced_value->EndDictionary();

std::string string;
traced_value->AppendAsTraceFormat(&string);

static const char* check = "[\"a\",1,1.234,\"NaN\",\"Infinity\","
"\"-Infinity\",1.23e+07,false,true,null,"
"{\"foo\":[]}]";

EXPECT_EQ(string, check);
}

0 comments on commit 509a715

Please sign in to comment.