Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

trace_events: add traced_value.cc/traced_value.h #21475

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
224 changes: 224 additions & 0 deletions src/tracing/traced_value.cc
Original file line number Diff line number Diff line change
@@ -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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does return std::to_string(v); do the trick here too, for the default case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll double check

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, the challenge with std::to_string(v) is that it ignores any reasonable precision on the double and always prints six decimals, whereas the version used here will print with actual scientific notation (e.g. std::to_string(v) when v = 1.23e7, will print 12300000.000000 whereas the version used here will print 1.23e+07 as one would more reasonably expect.

}
}

} // 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_ ? ']' : '}';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, seems like it might be nicer to make out a return value rather than an in/out parameter

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is part of the v8 trace event API and really isn't under our control, unfortunately.

}

} // 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_;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not following how this is being used, and how its value is correct after a call sequence like BeginArray, BeginDictionary, EndDictionary.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look within the WriteComma function for use (https://github.com/nodejs/node/pull/21475/files#diff-7e8d44b3c53ea475e1b34c59578ed6e8R169) ...

The original implementation in V8 has DCHECK statements that are only enabled in debug builds that check for proper state. I pulled those to keep things simple but could add them back in as regular CHECKs

bool root_is_array_;

DISALLOW_COPY_AND_ASSIGN(TracedValue);
};

} // namespace tracing
} // namespace node

#endif // SRC_TRACING_TRACED_VALUE_H_
Loading