trace_events: add traced_value.cc/traced_value.h

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.

PR-URL: https://github.com/nodejs/node/pull/21475
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
James M Snell
2018-06-22 12:50:01 -07:00
parent d4164ca559
commit d85449dcdf
4 changed files with 391 additions and 0 deletions

View File

@@ -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',
@@ -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',
@@ -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'
],

224
src/tracing/traced_value.cc Normal file
View File

@@ -0,0 +1,224 @@
// 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(NODE_HAVE_I18N_SUPPORT)
#include <unicode/utf8.h>
#include <unicode/utypes.h>
#endif
#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 {
std::string EscapeString(const char* value) {
std::string result;
result += '"';
char number_buffer[10];
#if defined(NODE_HAVE_I18N_SUPPORT)
int32_t len = strlen(value);
int32_t p = 0;
int32_t i = 0;
for (; i < len; p = i) {
UChar32 c;
U8_NEXT_OR_FFFD(value, i, len, c);
switch (c) {
case '\b': result += "\\b"; break;
case '\f': result += "\\f"; break;
case '\n': result += "\\n"; break;
case '\r': result += "\\r"; break;
case '\t': result += "\\t"; break;
case '\\': result += "\\\\"; break;
case '"': result += "\\\""; break;
default:
if (c < 32 || c > 126) {
snprintf(
number_buffer, arraysize(number_buffer), "\\u%04X",
static_cast<uint16_t>(static_cast<uint16_t>(c)));
result += number_buffer;
} else {
result.append(value + p, i - p);
}
}
}
#else
// If we do not have ICU, use a modified version of the non-UTF8 aware
// code from V8's own TracedValue implementation. Note, however, This
// will not produce correctly serialized results for UTF8 values.
while (*value) {
char c = *value++;
switch (c) {
case '\b': result += "\\b"; break;
case '\f': result += "\\f"; break;
case '\n': result += "\\n"; break;
case '\r': result += "\\r"; break;
case '\t': result += "\\t"; 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;
}
}
}
#endif // defined(NODE_HAVE_I18N_SUPPORT)
result += '"';
return 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);
data_ += EscapeString(value);
}
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();
data_ += EscapeString(value);
}
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

View File

@@ -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_

View File

@@ -0,0 +1,96 @@
#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(check, string);
}
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(check, string);
}
#define UTF8_SEQUENCE "1" "\xE2\x82\xAC" "23\"\x01\b\f\n\r\t\\"
#if defined(NODE_HAVE_I18N_SUPPORT)
# define UTF8_RESULT \
"\"1\\u20AC23\\\"\\u0001\\b\\f\\n\\r\\t\\\\\""
#else
# define UTF8_RESULT \
"\"1\\u00E2\\u0082\\u00AC23\\\"\\u0001\\b\\f\\n\\r\\t\\\\\""
#endif
TEST(TracedValue, EscapingObject) {
auto traced_value = TracedValue::Create();
traced_value->SetString("a", UTF8_SEQUENCE);
std::string string;
traced_value->AppendAsTraceFormat(&string);
static const char* check = "{\"a\":" UTF8_RESULT "}";
EXPECT_EQ(check, string);
}
TEST(TracedValue, EscapingArray) {
auto traced_value = TracedValue::CreateArray();
traced_value->AppendString(UTF8_SEQUENCE);
std::string string;
traced_value->AppendAsTraceFormat(&string);
static const char* check = "[" UTF8_RESULT "]";
EXPECT_EQ(check, string);
}