summaryrefslogtreecommitdiff
path: root/src/inspector
diff options
context:
space:
mode:
authorEugene Ostroukhov <eostroukhov@google.com>2018-04-27 17:20:37 -0700
committerEugene Ostroukhov <eostroukhov@google.com>2018-05-17 13:14:26 -0700
commit47bdc716f83462b6ab938315d11de6c92be082ac (patch)
tree8bdfd8c487cdcfb4e5b573b24d7809d053c675a6 /src/inspector
parent5248401174ff1ec02f5e1a247a97594341bbfd89 (diff)
downloadnode-new-47bdc716f83462b6ab938315d11de6c92be082ac.tar.gz
inspector: add a "NodeTracing" domain support
This change adds a new inspector domain for receiving Node tracing data. 1. Node.js now can extend Inspector protocol with new domains with the API defined in the src/inspector/node_protocol.pdl. 2. Plumbing code will be generated at the build time. /json/protocol HTTP endpoint returns both V8 and Node.js inspector protocol. 3. "NodeTracing" domain was introduced. It is based on the Chrome "Tracing" domain. PR-URL: https://github.com/nodejs/node/pull/20608 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'src/inspector')
-rw-r--r--src/inspector/node_protocol.pdl39
-rw-r--r--src/inspector/node_protocol_config.json27
-rw-r--r--src/inspector/node_string.cc92
-rw-r--r--src/inspector/node_string.h79
-rw-r--r--src/inspector/tracing_agent.cc106
-rw-r--r--src/inspector/tracing_agent.h45
6 files changed, 388 insertions, 0 deletions
diff --git a/src/inspector/node_protocol.pdl b/src/inspector/node_protocol.pdl
new file mode 100644
index 0000000000..27b3d814c8
--- /dev/null
+++ b/src/inspector/node_protocol.pdl
@@ -0,0 +1,39 @@
+# Please notify @nodejs/v8-inspector and @nodejs/trace-events before modifying this file
+version
+ major 1
+ minor 0
+
+experimental domain NodeTracing
+ type TraceConfig extends object
+ properties
+ # Controls how the trace buffer stores data.
+ optional enum recordMode
+ recordUntilFull
+ recordContinuously
+ recordAsMuchAsPossible
+ # Included category filters.
+ array of string includedCategories
+
+ # Gets supported tracing categories.
+ command getCategories
+ returns
+ # A list of supported tracing categories.
+ array of string categories
+
+ # Start trace events collection.
+ command start
+ parameters
+ TraceConfig traceConfig
+
+ # Stop trace events collection. Remaining collected events will be sent as a sequence of
+ # dataCollected events followed by tracingComplete event.
+ command stop
+
+ # Contains an bucket of collected trace events.
+ event dataCollected
+ parameters
+ array of object value
+
+ # Signals that tracing is stopped and there is no trace buffers pending flush, all data were
+ # delivered via dataCollected events.
+ event tracingComplete
diff --git a/src/inspector/node_protocol_config.json b/src/inspector/node_protocol_config.json
new file mode 100644
index 0000000000..7cea20ae93
--- /dev/null
+++ b/src/inspector/node_protocol_config.json
@@ -0,0 +1,27 @@
+{
+ "protocol": {
+ "path": "node_protocol.json",
+ "package": "src/node/inspector/protocol",
+ "output": "node/inspector/protocol",
+ "namespace": ["node", "inspector", "protocol"],
+ "options": [
+ {
+ "domain": "NodeTracing"
+ }
+ ]
+ },
+ "exported": {
+ "package": "include/inspector",
+ "output": "../../include/inspector",
+ "string_header": "v8-inspector.h",
+ "string_in": "StringView",
+ "string_out": "std::unique_ptr<StringBuffer>",
+ "to_string_out": "StringBufferImpl::adopt(%s)",
+ "export_macro": "V8_EXPORT"
+ },
+ "lib": {
+ "package": "src/node/inspector/protocol",
+ "output": "node/inspector/protocol",
+ "string_header": "inspector/node_string.h"
+ }
+}
diff --git a/src/inspector/node_string.cc b/src/inspector/node_string.cc
new file mode 100644
index 0000000000..cb9e90c20e
--- /dev/null
+++ b/src/inspector/node_string.cc
@@ -0,0 +1,92 @@
+#include "node_string.h"
+#include "node/inspector/protocol/Protocol.h"
+
+#include <unicode/unistr.h>
+
+namespace node {
+namespace inspector {
+namespace protocol {
+namespace StringUtil {
+
+size_t kNotFound = std::string::npos;
+
+// NOLINTNEXTLINE(runtime/references) V8 API requirement
+void builderAppendQuotedString(StringBuilder& builder, const String& string) {
+ builder.put('"');
+ if (!string.empty()) {
+ icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8(
+ icu::StringPiece(string.data(), string.length()));
+ escapeWideStringForJSON(
+ reinterpret_cast<const uint16_t*>(utf16.getBuffer()), utf16.length(),
+ &builder);
+ }
+ builder.put('"');
+}
+
+std::unique_ptr<Value> parseJSON(const String& string) {
+ if (string.empty())
+ return nullptr;
+
+ icu::UnicodeString utf16 =
+ icu::UnicodeString::fromUTF8(icu::StringPiece(string.data(),
+ string.length()));
+ return parseJSONCharacters(
+ reinterpret_cast<const uint16_t*>(utf16.getBuffer()), utf16.length());
+}
+
+std::unique_ptr<Value> parseJSON(v8_inspector::StringView string) {
+ if (string.length() == 0)
+ return nullptr;
+ if (string.is8Bit())
+ return parseJSONCharacters(string.characters8(), string.length());
+ return parseJSONCharacters(string.characters16(), string.length());
+}
+
+String StringViewToUtf8(v8_inspector::StringView view) {
+ if (view.length() == 0)
+ return "";
+ if (view.is8Bit()) {
+ return std::string(reinterpret_cast<const char*>(view.characters8()),
+ view.length());
+ }
+ const uint16_t* source = view.characters16();
+ const UChar* unicodeSource = reinterpret_cast<const UChar*>(source);
+ static_assert(sizeof(*source) == sizeof(*unicodeSource),
+ "sizeof(*source) == sizeof(*unicodeSource)");
+
+ size_t result_length = view.length() * sizeof(*source);
+ std::string result(result_length, '\0');
+ icu::UnicodeString utf16(unicodeSource, view.length());
+ // ICU components for std::string compatibility are not enabled in build...
+ bool done = false;
+ while (!done) {
+ icu::CheckedArrayByteSink sink(&result[0], result_length);
+ utf16.toUTF8(sink);
+ result_length = sink.NumberOfBytesAppended();
+ result.resize(result_length);
+ done = !sink.Overflowed();
+ }
+ return result;
+}
+
+String fromDouble(double d) {
+ std::ostringstream stream;
+ stream.imbue(std::locale("C")); // Ignore locale
+ stream << d;
+ return stream.str();
+}
+
+double toDouble(const char* buffer, size_t length, bool* ok) {
+ std::istringstream stream(std::string(buffer, length));
+ stream.imbue(std::locale("C")); // Ignore locale
+ double d;
+ stream >> d;
+ *ok = !stream.fail();
+ return d;
+}
+
+} // namespace StringUtil
+} // namespace protocol
+} // namespace inspector
+} // namespace node
+
diff --git a/src/inspector/node_string.h b/src/inspector/node_string.h
new file mode 100644
index 0000000000..468aec96b5
--- /dev/null
+++ b/src/inspector/node_string.h
@@ -0,0 +1,79 @@
+// Bridges V8 Inspector generated code with the std::string used by the Node
+// Compare to V8 counterpart - deps/v8/src/inspector/string-util.h
+#ifndef SRC_INSPECTOR_NODE_STRING_H_
+#define SRC_INSPECTOR_NODE_STRING_H_
+
+#include "util.h"
+#include "v8-inspector.h"
+
+#include <cstring>
+#include <sstream>
+#include <string>
+
+namespace node {
+namespace inspector {
+namespace protocol {
+
+class Value;
+
+using String = std::string;
+using StringBuilder = std::ostringstream;
+
+namespace StringUtil {
+// NOLINTNEXTLINE(runtime/references) This is V8 API...
+inline void builderAppend(StringBuilder& builder, char c) {
+ builder.put(c);
+}
+
+// NOLINTNEXTLINE(runtime/references)
+inline void builderAppend(StringBuilder& builder, const char* value,
+ size_t length) {
+ builder.write(value, length);
+}
+
+// NOLINTNEXTLINE(runtime/references)
+inline void builderAppend(StringBuilder& builder, const char* value) {
+ builderAppend(builder, value, std::strlen(value));
+}
+
+// NOLINTNEXTLINE(runtime/references)
+inline void builderAppend(StringBuilder& builder, const String& string) {
+ builder << string;
+}
+
+// NOLINTNEXTLINE(runtime/references)
+inline void builderReserve(StringBuilder& builder, size_t) {
+ // ostringstream does not have a counterpart
+}
+inline String substring(const String& string, size_t start, size_t count) {
+ return string.substr(start, count);
+}
+inline String fromInteger(int n) {
+ return std::to_string(n);
+}
+inline String builderToString(const StringBuilder& builder) {
+ return builder.str();
+}
+inline size_t find(const String& string, const char* substring) {
+ return string.find(substring);
+}
+String fromDouble(double d);
+double toDouble(const char* buffer, size_t length, bool* ok);
+
+String StringViewToUtf8(v8_inspector::StringView view);
+
+// NOLINTNEXTLINE(runtime/references)
+void builderAppendQuotedString(StringBuilder& builder, const String&);
+std::unique_ptr<Value> parseJSON(const String&);
+std::unique_ptr<Value> parseJSON(v8_inspector::StringView view);
+
+extern size_t kNotFound;
+} // namespace StringUtil
+} // namespace protocol
+} // namespace inspector
+} // namespace node
+
+#define DCHECK CHECK
+#define DCHECK_LT CHECK_LT
+
+#endif // SRC_INSPECTOR_NODE_STRING_H_
diff --git a/src/inspector/tracing_agent.cc b/src/inspector/tracing_agent.cc
new file mode 100644
index 0000000000..2d98fa1ee5
--- /dev/null
+++ b/src/inspector/tracing_agent.cc
@@ -0,0 +1,106 @@
+#include "tracing_agent.h"
+
+#include "env-inl.h"
+#include "v8.h"
+
+#include <set>
+#include <sstream>
+
+namespace node {
+namespace inspector {
+namespace protocol {
+
+namespace {
+using v8::platform::tracing::TraceWriter;
+
+class InspectorTraceWriter : public node::tracing::AsyncTraceWriter {
+ public:
+ explicit InspectorTraceWriter(NodeTracing::Frontend* frontend)
+ : frontend_(frontend) {}
+
+ void AppendTraceEvent(
+ v8::platform::tracing::TraceObject* trace_event) override {
+ if (!json_writer_)
+ json_writer_.reset(TraceWriter::CreateJSONTraceWriter(stream_, "value"));
+ json_writer_->AppendTraceEvent(trace_event);
+ }
+
+ void Flush(bool) override {
+ if (!json_writer_)
+ return;
+ json_writer_.reset();
+ std::ostringstream result(
+ "{\"method\":\"NodeTracing.dataCollected\",\"data\":",
+ std::ostringstream::ate);
+ result << stream_.str();
+ result << "}";
+ frontend_->sendRawNotification(result.str());
+ stream_.str("");
+ }
+
+ private:
+ std::unique_ptr<TraceWriter> json_writer_;
+ std::ostringstream stream_;
+ NodeTracing::Frontend* frontend_;
+};
+} // namespace
+
+TracingAgent::TracingAgent(Environment* env)
+ : env_(env),
+ trace_writer_(
+ tracing::Agent::EmptyClientHandle()) {
+}
+
+TracingAgent::~TracingAgent() {
+ trace_writer_.reset();
+}
+
+void TracingAgent::Wire(UberDispatcher* dispatcher) {
+ frontend_ = std::make_unique<NodeTracing::Frontend>(dispatcher->channel());
+ NodeTracing::Dispatcher::wire(dispatcher, this);
+}
+
+DispatchResponse TracingAgent::start(
+ std::unique_ptr<protocol::NodeTracing::TraceConfig> traceConfig) {
+ if (trace_writer_ != nullptr) {
+ return DispatchResponse::Error(
+ "Call NodeTracing::end to stop tracing before updating the config");
+ }
+
+ std::set<std::string> categories_set;
+ protocol::Array<std::string>* categories =
+ traceConfig->getIncludedCategories();
+ for (size_t i = 0; i < categories->length(); i++)
+ categories_set.insert(categories->get(i));
+
+ if (categories_set.empty())
+ return DispatchResponse::Error("At least one category should be enabled");
+
+ trace_writer_ = env_->tracing_agent()->AddClient(
+ categories_set, std::make_unique<InspectorTraceWriter>(frontend_.get()));
+ return DispatchResponse::OK();
+}
+
+DispatchResponse TracingAgent::stop() {
+ trace_writer_.reset();
+ frontend_->tracingComplete();
+ return DispatchResponse::OK();
+}
+
+DispatchResponse TracingAgent::getCategories(
+ std::unique_ptr<protocol::Array<String>>* categories) {
+ *categories = Array<String>::create();
+ categories->get()->addItem("node");
+ categories->get()->addItem("node.async");
+ categories->get()->addItem("node.bootstrap");
+ categories->get()->addItem("node.fs.sync");
+ categories->get()->addItem("node.perf");
+ categories->get()->addItem("node.perf.usertiming");
+ categories->get()->addItem("node.perf.timerify");
+ categories->get()->addItem("v8");
+ return DispatchResponse::OK();
+}
+
+} // namespace protocol
+} // namespace inspector
+} // namespace node
diff --git a/src/inspector/tracing_agent.h b/src/inspector/tracing_agent.h
new file mode 100644
index 0000000000..478107c5ac
--- /dev/null
+++ b/src/inspector/tracing_agent.h
@@ -0,0 +1,45 @@
+#ifndef SRC_INSPECTOR_TRACING_AGENT_H_
+#define SRC_INSPECTOR_TRACING_AGENT_H_
+
+#include "node/inspector/protocol/NodeTracing.h"
+#include "v8.h"
+
+
+namespace node {
+class Environment;
+
+namespace tracing {
+class Agent;
+} // namespace tracing
+
+namespace inspector {
+namespace protocol {
+
+class TracingAgent : public NodeTracing::Backend {
+ public:
+ explicit TracingAgent(Environment*);
+ ~TracingAgent() override;
+
+ void Wire(UberDispatcher* dispatcher);
+
+ DispatchResponse start(
+ std::unique_ptr<protocol::NodeTracing::TraceConfig> traceConfig) override;
+ DispatchResponse stop() override;
+ DispatchResponse getCategories(
+ std::unique_ptr<protocol::Array<String>>* categories) override;
+
+ private:
+ void DisconnectTraceClient();
+
+ Environment* env_;
+ std::unique_ptr<std::pair<tracing::Agent*, int>,
+ void (*)(std::pair<tracing::Agent*, int>*)> trace_writer_;
+ std::unique_ptr<NodeTracing::Frontend> frontend_;
+};
+
+
+} // namespace protocol
+} // namespace inspector
+} // namespace node
+
+#endif // SRC_INSPECTOR_TRACING_AGENT_H_