diff options
author | Eugene Ostroukhov <eostroukhov@google.com> | 2018-04-27 17:20:37 -0700 |
---|---|---|
committer | Eugene Ostroukhov <eostroukhov@google.com> | 2018-05-17 13:14:26 -0700 |
commit | 47bdc716f83462b6ab938315d11de6c92be082ac (patch) | |
tree | 8bdfd8c487cdcfb4e5b573b24d7809d053c675a6 /src/inspector | |
parent | 5248401174ff1ec02f5e1a247a97594341bbfd89 (diff) | |
download | node-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.pdl | 39 | ||||
-rw-r--r-- | src/inspector/node_protocol_config.json | 27 | ||||
-rw-r--r-- | src/inspector/node_string.cc | 92 | ||||
-rw-r--r-- | src/inspector/node_string.h | 79 | ||||
-rw-r--r-- | src/inspector/tracing_agent.cc | 106 | ||||
-rw-r--r-- | src/inspector/tracing_agent.h | 45 |
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_ |