summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/inspector_agent.cc124
-rw-r--r--src/inspector_agent.h2
-rw-r--r--src/node.cc20
3 files changed, 101 insertions, 45 deletions
diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc
index 32899600ac..d1c12d6e5a 100644
--- a/src/inspector_agent.cc
+++ b/src/inspector_agent.cc
@@ -51,7 +51,14 @@ void PrintDebuggerReadyMessage(int port) {
}
bool AcceptsConnection(inspector_socket_t* socket, const std::string& path) {
- return 0 == path.compare(0, sizeof(DEVTOOLS_PATH) - 1, DEVTOOLS_PATH);
+ return StringEqualNoCaseN(path.c_str(), DEVTOOLS_PATH,
+ sizeof(DEVTOOLS_PATH) - 1);
+}
+
+void Escape(std::string* string) {
+ for (char& c : *string) {
+ c = (c == '\"' || c == '\\') ? '_' : c;
+ }
}
void DisposeInspector(inspector_socket_t* socket, int status) {
@@ -98,7 +105,21 @@ void SendVersionResponse(inspector_socket_t* socket) {
SendHttpResponse(socket, buffer, len);
}
-void SendTargentsListResponse(inspector_socket_t* socket, int port) {
+std::string GetProcessTitle() {
+ // uv_get_process_title will trim the title if it is too long.
+ char title[2048];
+ int err = uv_get_process_title(title, sizeof(title));
+ if (err == 0) {
+ return title;
+ } else {
+ return "Node.js";
+ }
+}
+
+void SendTargentsListResponse(inspector_socket_t* socket,
+ const std::string& script_name_,
+ const std::string& script_path_,
+ int port) {
const char LIST_RESPONSE_TEMPLATE[] =
"[ {"
" \"description\": \"node.js instance\","
@@ -110,45 +131,60 @@ void SendTargentsListResponse(inspector_socket_t* socket, int port) {
" \"id\": \"%d\","
" \"title\": \"%s\","
" \"type\": \"node\","
+ " \"url\": \"%s\","
" \"webSocketDebuggerUrl\": \"ws://localhost:%d%s\""
"} ]";
- char buffer[sizeof(LIST_RESPONSE_TEMPLATE) + 4096];
- char title[2048]; // uv_get_process_title trims the title if too long
- int err = uv_get_process_title(title, sizeof(title));
- if (err != 0) {
- snprintf(title, sizeof(title), "Node.js");
- }
- char* c = title;
- while (*c != '\0') {
- if (*c < ' ' || *c == '\"') {
- *c = '_';
- }
- c++;
+ std::string title = script_name_.empty() ? GetProcessTitle() : script_name_;
+
+ // This attribute value is a "best effort" URL that is passed as a JSON
+ // string. It is not guaranteed to resolve to a valid resource.
+ std::string url = "file://" + script_path_;
+
+ Escape(&title);
+ Escape(&url);
+
+ const int NUMERIC_FIELDS_LENGTH = 5 * 2 + 20; // 2 x port + 1 x pid (64 bit)
+
+ int buf_len = sizeof(LIST_RESPONSE_TEMPLATE) + sizeof(DEVTOOLS_HASH) +
+ sizeof(DEVTOOLS_PATH) * 2 + title.length() +
+ url.length() + NUMERIC_FIELDS_LENGTH;
+ std::string buffer(buf_len, '\0');
+
+ int len = snprintf(&buffer[0], buf_len, LIST_RESPONSE_TEMPLATE,
+ DEVTOOLS_HASH, port, DEVTOOLS_PATH, getpid(),
+ title.c_str(), url.c_str(),
+ port, DEVTOOLS_PATH);
+ buffer.resize(len);
+ ASSERT_LT(len, buf_len); // Buffer should be big enough!
+ SendHttpResponse(socket, buffer.data(), len);
+}
+
+const char* match_path_segment(const char* path, const char* expected) {
+ size_t len = strlen(expected);
+ if (StringEqualNoCaseN(path, expected, len)) {
+ if (path[len] == '/') return path + len + 1;
+ if (path[len] == '\0') return path + len;
}
- size_t len = snprintf(buffer, sizeof(buffer), LIST_RESPONSE_TEMPLATE,
- DEVTOOLS_HASH, port, DEVTOOLS_PATH, getpid(),
- title, port, DEVTOOLS_PATH);
- ASSERT_LT(len, sizeof(buffer));
- SendHttpResponse(socket, buffer, len);
+ return nullptr;
}
-bool RespondToGet(inspector_socket_t* socket, const std::string& path,
+bool RespondToGet(inspector_socket_t* socket, const std::string& script_name_,
+ const std::string& script_path_, const std::string& path,
int port) {
- const char PATH[] = "/json";
- const char PATH_LIST[] = "/json/list";
- const char PATH_VERSION[] = "/json/version";
- const char PATH_ACTIVATE[] = "/json/activate/";
- if (0 == path.compare(0, sizeof(PATH_VERSION) - 1, PATH_VERSION)) {
+ const char* command = match_path_segment(path.c_str(), "/json");
+ if (command == nullptr)
+ return false;
+
+ if (match_path_segment(command, "list") || command[0] == '\0') {
+ SendTargentsListResponse(socket, script_name_, script_path_, port);
+ } else if (match_path_segment(command, "version")) {
SendVersionResponse(socket);
- } else if (0 == path.compare(0, sizeof(PATH_LIST) - 1, PATH_LIST) ||
- 0 == path.compare(0, sizeof(PATH) - 1, PATH)) {
- SendTargentsListResponse(socket, port);
- } else if (0 == path.compare(0, sizeof(PATH_ACTIVATE) - 1, PATH_ACTIVATE) &&
- atoi(path.substr(sizeof(PATH_ACTIVATE) - 1).c_str()) == getpid()) {
+ } else {
+ const char* pid = match_path_segment(command, "activate");
+ if (pid == nullptr || atoi(pid) != getpid())
+ return false;
const char TARGET_ACTIVATED[] = "Target activated";
SendHttpResponse(socket, TARGET_ACTIVATED, sizeof(TARGET_ACTIVATED) - 1);
- } else {
- return false;
}
return true;
}
@@ -166,7 +202,7 @@ class AgentImpl {
~AgentImpl();
// Start the inspector agent thread
- bool Start(v8::Platform* platform, int port, bool wait);
+ bool Start(v8::Platform* platform, const char* path, int port, bool wait);
// Stop the inspector agent
void Stop();
@@ -227,6 +263,9 @@ class AgentImpl {
int frontend_session_id_;
int backend_session_id_;
+ std::string script_name_;
+ std::string script_path_;
+
friend class ChannelImpl;
friend class DispatchOnInspectorBackendTask;
friend class SetConnectedTask;
@@ -442,10 +481,13 @@ void InspectorWrapConsoleCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
array).ToLocalChecked());
}
-bool AgentImpl::Start(v8::Platform* platform, int port, bool wait) {
+bool AgentImpl::Start(v8::Platform* platform, const char* path,
+ int port, bool wait) {
auto env = parent_env_;
inspector_ = new V8NodeInspector(this, env, platform);
platform_ = platform;
+ if (path != nullptr)
+ script_name_ = path;
InstallInspectorOnProcess();
@@ -566,7 +608,8 @@ bool AgentImpl::OnInspectorHandshakeIO(inspector_socket_t* socket,
AgentImpl* agent = static_cast<AgentImpl*>(socket->data);
switch (state) {
case kInspectorHandshakeHttpGet:
- return RespondToGet(socket, path, agent->port_);
+ return RespondToGet(socket, agent->script_name_, agent->script_path_, path,
+ agent->port_);
case kInspectorHandshakeUpgrading:
return AcceptsConnection(socket, path);
case kInspectorHandshakeUpgraded:
@@ -635,6 +678,12 @@ void AgentImpl::WorkerRunIO() {
err = uv_async_init(&child_loop_, &io_thread_req_, AgentImpl::WriteCbIO);
CHECK_EQ(err, 0);
io_thread_req_.data = this;
+ if (!script_name_.empty()) {
+ uv_fs_t req;
+ if (0 == uv_fs_realpath(&child_loop_, &req, script_name_.c_str(), nullptr))
+ script_path_ = std::string(reinterpret_cast<char*>(req.ptr));
+ uv_fs_req_cleanup(&req);
+ }
uv_tcp_init(&child_loop_, &server);
uv_ip4_addr("0.0.0.0", port_, &addr);
server.data = this;
@@ -752,8 +801,9 @@ Agent::~Agent() {
delete impl;
}
-bool Agent::Start(v8::Platform* platform, int port, bool wait) {
- return impl->Start(platform, port, wait);
+bool Agent::Start(v8::Platform* platform, const char* path,
+ int port, bool wait) {
+ return impl->Start(platform, path, port, wait);
}
void Agent::Stop() {
diff --git a/src/inspector_agent.h b/src/inspector_agent.h
index 43433fdc6e..3607cffba5 100644
--- a/src/inspector_agent.h
+++ b/src/inspector_agent.h
@@ -29,7 +29,7 @@ class Agent {
~Agent();
// Start the inspector agent thread
- bool Start(v8::Platform* platform, int port, bool wait);
+ bool Start(v8::Platform* platform, const char* path, int port, bool wait);
// Stop the inspector agent
void Stop();
diff --git a/src/node.cc b/src/node.cc
index 9dc66f907f..d048bd03c8 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -207,9 +207,10 @@ static struct {
platform_ = nullptr;
}
- bool StartInspector(Environment *env, int port, bool wait) {
+ bool StartInspector(Environment *env, const char* script_path,
+ int port, bool wait) {
#if HAVE_INSPECTOR
- return env->inspector_agent()->Start(platform_, port, wait);
+ return env->inspector_agent()->Start(platform_, script_path, port, wait);
#else
return true;
#endif // HAVE_INSPECTOR
@@ -220,7 +221,8 @@ static struct {
void Initialize(int thread_pool_size) {}
void PumpMessageLoop(Isolate* isolate) {}
void Dispose() {}
- bool StartInspector(Environment *env, int port, bool wait) {
+ bool StartInspector(Environment *env, const char* script_path,
+ int port, bool wait) {
env->ThrowError("Node compiled with NODE_USE_V8_PLATFORM=0");
return false; // make compiler happy
}
@@ -3766,10 +3768,11 @@ static void DispatchMessagesDebugAgentCallback(Environment* env) {
}
-static void StartDebug(Environment* env, bool wait) {
+static void StartDebug(Environment* env, const char* path, bool wait) {
CHECK(!debugger_running);
if (use_inspector) {
- debugger_running = v8_platform.StartInspector(env, inspector_port, wait);
+ debugger_running = v8_platform.StartInspector(env, path, inspector_port,
+ wait);
} else {
env->debugger_agent()->set_dispatch_handler(
DispatchMessagesDebugAgentCallback);
@@ -3831,7 +3834,7 @@ static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle) {
Environment* env = Environment::GetCurrent(isolate);
Context::Scope context_scope(env->context());
- StartDebug(env, false);
+ StartDebug(env, nullptr, false);
EnableDebug(env);
}
@@ -4396,7 +4399,10 @@ static void StartNodeInstance(void* arg) {
// Start debug agent when argv has --debug
if (instance_data->use_debug_agent()) {
- StartDebug(&env, debug_wait_connect);
+ const char* path = instance_data->argc() > 1
+ ? instance_data->argv()[1]
+ : nullptr;
+ StartDebug(&env, path, debug_wait_connect);
if (use_inspector && !debugger_running) {
exit(12);
}