Skip to content

Instantly share code, notes, and snippets.

@sobolews
Last active July 14, 2017 03:07
Show Gist options
  • Save sobolews/957e8cc9768f505d816c to your computer and use it in GitHub Desktop.
Save sobolews/957e8cc9768f505d816c to your computer and use it in GitHub Desktop.
Debugging an infinite loop in node.js 6.0
Install lldb-3.6 and llnode. On Ubuntu:
git clone https://github.com/indutny/llnode
cd llnode
sudo apt-get install lldb-3.6 lldb-3.6-dev
git clone https://chromium.googlesource.com/external/gyp.git tools/gyp
./gyp_llnode -Dlldb_dir=/usr/lib/llvm-3.6/ -Dlldb_lib=lldb-3.6
make -C out/ -j9
sudo make install-linux
Make sure cores are enabled:
su -
echo "core.%p" > /proc/sys/kernel/core_pattern # or whatever pattern you like, just not apport
exit
ulimit -c unlimited
In a test clone of Pokemon-Showdown:
battle-engine.js:
Battle.prototype.runDecision = function (decision) {
+ while(1) {}
// returns whether or not we ended in a callback
Then start a battle, and choose leads. The process `node battle-engine.js' will pin a CPU.
ps aux | grep node
carl 21678 10.6 3.3 1217876 130476 pts/14 Sl+ 01:03 0:03 /home/carl/node6/bin/node app.js
carl 21687 0.4 0.4 642620 16872 pts/14 Sl+ 01:03 0:00 /home/carl/node6/bin/node verifier.js
carl 21692 36.5 3.1 976936 126036 pts/14 Rl+ 01:03 0:10 /home/carl/node6/bin/node battle-engine.js
carl 21697 1.2 0.7 910908 30152 pts/14 Sl+ 01:03 0:00 /home/carl/node6/bin/node sockets-nocluster.js
carl 21702 10.2 3.2 979852 128448 pts/14 Sl+ 01:03 0:03 /home/carl/node6/bin/node team-validator.js
# The rogue process's PID is 21692
kill -6 21692 # SIGABRT, this should produce a core in the current directory: core.21692
lldb-3.6 $(which node) -c ./core.21692
(lldb) target create "/home/carl/node6/bin/node" --core "./core.21692"
Core file '/home/carl/projects/Pokemon-Showdown/./core.21692' (x86_64) was loaded.
(lldb) bt
* thread #1: tid = 0, 0x00002ca29aaf3403, name = 'node', stop reason = signal SIGABRT
* frame #0: 0x00002ca29aaf3403
frame #1: 0x00002ca29aaf2daa
frame #2: 0x00002ca29aaee04b
frame #3: 0x00002ca29aae7c12
...
^ The useless unnamed addresses above are the JS stack frames.
(lldb) v8 bt
* SBThread: tid = 0x0000 * frame #0: 0x00002ca29aaf3403 Battle.runDecision(this=0x000011dec5f8b9a9:<Object: Object>, 0x000011dec5fec411:<Object: Object>) at /home/carl/projects/Pokemon-Showdown/battle-engine.js:3910:42 fn=0x0000205952d75c01
frame #1: 0x00002ca29aaf2daa Battle.go(this=0x000011dec5f8b9a9:<Object: Object>) at /home/carl/projects/Pokemon-Showdown/battle-engine.js:4146:33 fn=0x0000205952d75cc1
frame #2: 0x00002ca29aaee04b Battle.commitDecisions(this=0x000011dec5f8b9a9:<Object: Object>) at /home/carl/projects/Pokemon-Showdown/battle-engine.js:4220:46 fn=0x0000205952d75ee9
frame #3: 0x00002ca29aae7c12 Battle.choose(this=0x000011dec5f8b9a9:<Object: Object>, 0x00002a4ce867e519:<String: "p2">, 0x000011dec5ff0799:<String: "team 1">, 0x00001a4ed8805ce9:<String: "1">) at /home/carl/projects/Pokemon-Showdown/battle-engine.js:4191:37 fn=0x0000205952d75e39
...
Aha! The llnode extension allows lldb to read the JS frames. We now have our stack trace!
You can also poke around using `v8 i': Let's look at the `this' from frame #0 (the Battle object)
v8 i 0x000011dec5f8b9a9
0x000004abfbf8b9a9:<Object: Object properties {
.log=0x000004abfbf8be41:<Array: length=30>,
.sides=0x000004abfbf8bea9:<Array: length=2>,
.roomid=0x000004abfbf82a19:<String: "battle-customgam...">,
...
See also: https://asciinema.org/a/29589
Note: for some reason that I couldn't fathom, cores made by using gcore on the running battle-engine process fail to load properly in lldb. I could only load the ones that I actually `kill -6'd manually.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment