Skip to content

Commit

Permalink
inspector: use script name for target title
Browse files Browse the repository at this point in the history
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: nodejs#8243
Reviewed-By: bnoordhuis - Ben Noordhuis <[email protected]>
  • Loading branch information
Eugene Ostroukhov authored and ofrobots committed Sep 2, 2016
1 parent 9826a79 commit 609a265
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 45 deletions.
124 changes: 87 additions & 37 deletions src/inspector_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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\","
Expand All @@ -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;
}
Expand All @@ -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();

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion src/inspector_agent.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
20 changes: 13 additions & 7 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit 609a265

Please sign in to comment.