llnode lets you investigate the contents of a core dump produced by Node.js at a JavaScript level (rather than looking at Node.js as a C++ application) by giving you access to the JavaScript objects on the heap and the stack.

llnode lets you drill down and inspect any object of any type within your Node.js heap. It also lets you view the stack trace and see what JavaScript code was being run when your application crashed.

This makes llnode incredibly powerful but sometimes when you have access to everything what you need is somewhere to start or a way to orientate yourself. For example, a way to remind yourself what the command line was, what version of node you were running or even check that you are actually looking at the right core dump.

That’s what the nodeinfo command is for. Like findjsobjects and findjsinstances it’s another command that requires us to scan the process memory to find all the v8 objects in memory (We only do one scan – the results are cached and shared between all three commands.) All node info does is find the process object and print out selected details from it. This sounds basic and you could do this manually using v8 findjsinstances process and v8 inspect [addr] to find and navigate around the process object. It’s tedious to do manually though especially as some of the information is in child objects which you will need to inspect as well. (Tedious jobs extracting data and formatting it so it’s easy for humans to read is what computers are for!)

Essentially it formats the useful contents of the global process object. Since we can also do that in JavaScript we can compare the output of a script that also accesses the process object against the output of nodeinfo:

Script:

util = require('util')
console.log("process.pid: " + process.pid + " process.arch: " + process.arch +" process.platform: " + process.platform)
console.log("process.argv: " + process.argv)
console.log("process.execArgv: " + process.execArgv)
console.log("process.version: " + process.version)
console.log("process.versions: " + util.inspect(process.versions))

x = {}
x.not_a_function()

Script Output:

$ node --abort-on-uncaught-exception print_info_and_crash.js 
process.pid: 5780 process.arch: x64 process.platform: darwin
process.argv: [somedir]/node-v4.4.7-darwin-x64/bin/node,[somedir]/testscripts/print_info_and_crash.js
process.execArgv: --abort-on-uncaught-exception
process.version: v4.4.7
process.versions: { http_parser: '2.5.2',
  node: '4.4.7',
  v8: '4.5.103.36',
  uv: '1.8.0',
  zlib: '1.2.8',
  ares: '1.10.1-DEV',
  icu: '56.1',
  modules: '46',
  openssl: '1.0.2h' }
Uncaught TypeError: x.not_a_function is not a function

FROM
Object. ([somedir]/testscripts/print_info_and_crash.js:9:3)
Module._compile (module.js:409:26)
Object.Module._extensions..js (module.js:416:10)
Module.load (module.js:343:32)
Function.Module._load (module.js:300:12)
Function.Module.runMain (module.js:441:10)
startup (node.js:139:18)
node.js:968:3
Illegal instruction: 4 (core dumped)

lldb Output:

(lldb) nodeinfo
Information for process id 5780 (process=0x115a1f08469)
Platform = darwin, Architecture = x64, Node Version = v4.4.7
Component versions (process.versions=0x115a1f256b9):
    ares = 1.10.1-DEV
    http_parser = 2.5.2
    icu = 56.1
    modules = 46
    node = 4.4.7
    openssl = 1.0.2h
    uv = 1.8.0
    v8 = 4.5.103.36
    zlib = 1.2.8
Release Info (process.release=0x115a1f25729):
    name = node
    lts = Argon
    sourceUrl = https://nodejs.org/download/release/v4.4.7/node-v4.4.7.tar.gz
    headersUrl = https://nodejs.org/download/release/v4.4.7/node-v4.4.7-headers.tar.gz
Executable Path = [somedir]/node-v4.4.7-darwin-x64/bin/node
Command line arguments (process.argv=0x115a1f25831):
    [0] = '[somedir]/node-v4.4.7-darwin-x64/bin/node'
    [1] = '[somedir]/testscripts/print_info_and_crash.js'
Node.js Comamnd line arguments (process.execArgv=0x115a1f257f1):
    [0] = '--abort-on-uncaught-exception'

This can be used for getting started with your debugging. Depending on where the core dump has come from it may be useful to confirm the platform, command line, versions and so forth. The process id is useful for correlating multiple core dumps. Having this information can also be useful when you have multiple core dumps you are working on and just need to sanity check you are looking at the right one. (Not that I’ve ever downloaded too many core dumps and forgotten which belonged to which problem!)

The code for finding and inspecting the process object is also pretty simple. It finds the instances of process, confirms it’s found a match by checking for the pid field and then pulls out the fields we are interested in. It’s a simple pattern that could easily be applied to any object in a Node.js application. For example you could write a command to check the state of a cache object of some kind within your application, then using gcore take a core dump from a running instance and check the state of your cache. Of course you could have done this by having a management console for your app that gives you the cache status but if you haven’t and you really need to check the state of that cache in your customers system you can start to see how you can use post-mortem diagnostics as a way into applications that are otherwise black boxes.

1 comment on"nodeinfo command for llnode"

  1. […] using the llnode plugin for lldb and describes a new command available in the llnode plugin here nodeinfo command for llnode. He has also provided a key enhancement to the underlying LLDB debugger API to allow all areas of […]

Join The Discussion

Your email address will not be published. Required fields are marked *