From 1038c305bfff313f44e465a66b56afc8e6524877 Mon Sep 17 00:00:00 2001 From: Matheus Marchini Date: Thu, 17 Aug 2017 16:51:43 -0300 Subject: [PATCH] src: add commands to inspect the workqueue Added two new commands (getactivehandles and getactiverequests) which prints all pending handles and requests (same return given by process._getActiveHandles() and process._getActiveRequests()). Those changes were built upon the symbols added on nodejs/node#14901, which means it's currently not working with node's latest build. Fixes: https://github.com/nodejs/llnode/issues/100 Ref: https://github.com/nodejs/node/pull/14901 --- README.md | 5 ++ src/llnode.cc | 191 ++++++++++++++++++++++++++++++++++++++++++ src/llnode.h | 16 ++++ src/llv8-constants.cc | 2 +- src/llv8-constants.h | 6 ++ 5 files changed, 219 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 68618d45..e8bff5a2 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,11 @@ The following subcommands are supported: * -n, --name name - all properties with the specified name * -s, --string string - all properties that refer to the specified JavaScript string value + + getactivehandles -- *EXPERIMENTAL* Equivalent to running process._getActiveHandles. This command is still being + developed and for now it only works building node from source. + getactiverequests -- *EXPERIMENTAL* Equivalent to running process._getActiveRequests. This command is still being + developed and for now it only works building node from source. inspect -- Print detailed description and contents of the JavaScript value. Possible flags (all optional): diff --git a/src/llnode.cc b/src/llnode.cc index 8570d15b..03b531cc 100644 --- a/src/llnode.cc +++ b/src/llnode.cc @@ -4,12 +4,15 @@ #include #include +#include +#include #include #include "src/llnode.h" #include "src/llscan.h" #include "src/llv8.h" +#include "src/llv8-constants.h" namespace llnode { @@ -26,6 +29,12 @@ using lldb::SBThread; using lldb::SBValue; using lldb::eReturnStatusFailed; using lldb::eReturnStatusSuccessFinishResult; +using lldb::SBProcess; +using lldb::SBAddress; +using lldb::SBSymbolContext; +using lldb::SBSymbolContextList; +using lldb::addr_t; +using v8::constants::LookupConstant; v8::LLV8 llv8; @@ -299,6 +308,176 @@ bool ListCmd::DoExecute(SBDebugger d, char** cmd, return true; } +bool GetActiveHandlesCmd::DoExecute(SBDebugger d, char** cmd, + SBCommandReturnObject& result) { + SBTarget target = d.GetSelectedTarget(); + SBProcess process = target.GetProcess(); + SBThread thread = process.GetSelectedThread(); + SBError sberr; + std::ostringstream resultMsg; + v8::Value::InspectOptions inspect_options; + inspect_options.detailed = true; + + llv8.Load(target); + + int size = 8; // TODO size is arch-dependent + int64_t envPtr = 0; + uint64_t env = 0; + int64_t queue = 0; + int64_t head = 0; + int64_t next = 0; + int64_t node = 0; + int64_t persistant_handle = 0; + v8::Error err2; + + envPtr = LookupConstant(target, "nodedbg_currentEnvironment", envPtr, err2); + process.ReadMemory(envPtr, &env, size, sberr); + + queue = LookupConstant(target, "nodedbg_class__Environment__handleWrapQueue", queue, err2); + head = LookupConstant(target, "nodedbg_class__HandleWrapQueue__headOffset", head, err2); + next = LookupConstant(target, "nodedbg_class__HandleWrapQueue__nextOffset", next, err2); + node = LookupConstant(target, "nodedbg_class__HandleWrap__node", node, err2); + persistant_handle = LookupConstant(target, "nodedbg_class__BaseObject__persistant_handle", persistant_handle, err2); + + // uint8_t *buffer = new uint8_t[size]; + // XXX Ozadia time + uint64_t buffer = 0; + bool go=true; + if (!thread.IsValid()) { + result.SetError("No valid process, please start something\n"); + return false; + } + + int activeHandles = 0; + uint64_t currentNode = env; + currentNode += queue; // env.handle_wrap_queue_ + currentNode += head; // env.handle_wrap_queue_.head_ + currentNode += next; // env.handle_wrap_queue_.head_.next_ + process.ReadMemory(currentNode, &buffer, size, sberr); + currentNode = buffer; + // TODO needs a stop condition, currently it's being stopped by a break + while(go) { + addr_t myMemory = currentNode; + myMemory = myMemory - node; // wrap + myMemory += persistant_handle; + // w->persistent().IsEmpty() + if(myMemory == 0) { + continue; + } + + process.ReadMemory(myMemory, &buffer, size, sberr); + myMemory = buffer; + process.ReadMemory(myMemory, &buffer, size, sberr); + // TODO needs a better check + if(sberr.Fail()) { + break; + } + + v8::JSObject v8_object(&llv8, buffer); + v8::Error err; + std::string res = v8_object.Inspect(&inspect_options, err); + if (err.Fail()) { + // result.SetError("Failed to evaluate expression"); + break; + } + + activeHandles++; + resultMsg << res.c_str() << std::endl; + + currentNode += next; // env.handle_wrap_queue_.head_.next_->next_->(...)->next_ + process.ReadMemory(currentNode, &buffer, size, sberr); + currentNode = buffer; + } + result.Printf("Active handles: %d\n\n", activeHandles); + result.Printf("%s", resultMsg.str().c_str()); + return true; +} + +bool GetActiveRequestsCmd::DoExecute(SBDebugger d, char** cmd, + SBCommandReturnObject& result) { + SBTarget target = d.GetSelectedTarget(); + SBProcess process = target.GetProcess(); + SBThread thread = process.GetSelectedThread(); + SBError sberr; + std::ostringstream resultMsg; + v8::Value::InspectOptions inspect_options; + inspect_options.detailed = true; + + llv8.Load(target); + + int size = 8; // TODO size is arch-dependent + int64_t envPtr = 0; + uint64_t env = 0; + int64_t queue = 0; + int64_t head = 0; + int64_t next = 0; + int64_t node = 0; + int64_t persistant_handle = 0; + v8::Error err2; + + envPtr = LookupConstant(target, "nodedbg_currentEnvironment", envPtr, err2); + process.ReadMemory(envPtr, &env, size, sberr); + + queue = LookupConstant(target, "nodedbg_class__Environment__reqWrapQueue", queue, err2); + head = LookupConstant(target, "nodedbg_class__ReqWrapQueue__headOffset", head, err2); + next = LookupConstant(target, "nodedbg_class__ReqWrapQueue__nextOffset", next, err2); + node = LookupConstant(target, "nodedbg_class__ReqWrap__node", node, err2); + persistant_handle = LookupConstant(target, "nodedbg_class__BaseObject__persistant_handle", persistant_handle, err2); + + // uint8_t *buffer = new uint8_t[size]; + // XXX Ozadia time + uint64_t buffer = 0; + bool go=true; + if (!thread.IsValid()) { + result.SetError("No valid process, please start something\n"); + return false; + } + + int activeHandles = 0; + uint64_t currentNode = env; + currentNode += queue; // env.handle_wrap_queue_ + currentNode += head; // env.handle_wrap_queue_.head_ + currentNode += next; // env.handle_wrap_queue_.head_.next_ + process.ReadMemory(currentNode, &buffer, size, sberr); + currentNode = buffer; + // TODO needs a stop condition + while(go) { + addr_t myMemory = currentNode; + myMemory = myMemory - node; + myMemory += persistant_handle; + // w->persistent().IsEmpty() + if(myMemory == 0) { + continue; + } + + process.ReadMemory(myMemory, &buffer, size, sberr); + myMemory = buffer; + process.ReadMemory(myMemory, &buffer, size, sberr); + // TODO needs a better check + if(sberr.Fail()) { + break; + } + + v8::JSObject v8_object(&llv8, buffer); + v8::Error err; + std::string res = v8_object.Inspect(&inspect_options, err); + if (err.Fail()) { + // result.SetError("Failed to evaluate expression"); + break; + } + + activeHandles++; + resultMsg << res.c_str() << std::endl; + + currentNode += next; // env.handle_wrap_queue_.head_.next_->next_->(...)->next_ + process.ReadMemory(currentNode, &buffer, size, sberr); + currentNode = buffer; + } + result.Printf("Active handles: %d\n\n", activeHandles); + result.Printf("%s", resultMsg.str().c_str()); + return true; +} + } // namespace llnode namespace lldb { @@ -384,6 +563,18 @@ bool PluginInitialize(SBDebugger d) { "JavaScript string value\n" "\n"); + v8.AddCommand( + "getactivehandles", new llnode::GetActiveHandlesCmd(), + "*EXPERIMENTAL* Equivalent to running process._getActiveHandles. " + "This command is still being developed and for now it only works " + "building node from source.\n"); + + v8.AddCommand( + "getactiverequests", new llnode::GetActiveRequestsCmd(), + "*EXPERIMENTAL* Equivalent to running process._getActiveRequests. " + "This command is still being developed and for now it only works " + "building node from source.\n"); + return true; } diff --git a/src/llnode.h b/src/llnode.h index b46bc7ef..755a342d 100644 --- a/src/llnode.h +++ b/src/llnode.h @@ -35,6 +35,22 @@ class PrintCmd : public CommandBase { bool detailed_; }; +class GetActiveHandlesCmd : public CommandBase { + public: + ~GetActiveHandlesCmd() override {} + + bool DoExecute(lldb::SBDebugger d, char** cmd, + lldb::SBCommandReturnObject& result) override; +}; + +class GetActiveRequestsCmd : public CommandBase { + public: + ~GetActiveRequestsCmd() override {} + + bool DoExecute(lldb::SBDebugger d, char** cmd, + lldb::SBCommandReturnObject& result) override; +}; + class ListCmd : public CommandBase { public: ~ListCmd() override {} diff --git a/src/llv8-constants.cc b/src/llv8-constants.cc index 96a7fc8f..0a63b379 100644 --- a/src/llv8-constants.cc +++ b/src/llv8-constants.cc @@ -38,7 +38,7 @@ void Module::Assign(SBTarget target, Common* common) { } -static int64_t LookupConstant(SBTarget target, const char* name, int64_t def, +int64_t LookupConstant(SBTarget target, const char* name, int64_t def, Error& err) { int64_t res; diff --git a/src/llv8-constants.h b/src/llv8-constants.h index f7f2315c..9956b4de 100644 --- a/src/llv8-constants.h +++ b/src/llv8-constants.h @@ -3,10 +3,16 @@ #include +using lldb::SBTarget; + namespace llnode { namespace v8 { +class Error; namespace constants { +int64_t LookupConstant(SBTarget target, const char* name, int64_t def, + Error& err); + // Forward declarations class Common;