Permalink
Browse files

inspector: use script name for target title

Changes inspector integration to use Node.js script file name as target
title (reported in JSON and shown in developer tools UIs). It will also
report file:// URL for the script as some tools seem to use that field
to open the script in the editor.

PR-URL: #8243
Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl>

 Conflicts:
	src/node.cc
  • Loading branch information...
Eugene Ostroukhov authored and Fishrock123 committed Jun 10, 2016
1 parent 680a4c8 commit b603ac24cb6699934daad5db42d5645e2b575b03
Showing with 101 additions and 45 deletions.
  1. +87 −37 src/inspector_agent.cc
  2. +1 −1 src/inspector_agent.h
  3. +13 −7 src/node.cc
View
@@ -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() {
View
@@ -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();
View
@@ -208,9 +208,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
@@ -221,7 +222,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
}
@@ -3852,10 +3854,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);
@@ -3917,7 +3920,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);
}
@@ -4566,7 +4569,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);
}

0 comments on commit b603ac2

Please sign in to comment.