Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@joshenders
Last active November 30, 2023 00:32
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save joshenders/b683c753fe37df9d76dc1e9a0157dff1 to your computer and use it in GitHub Desktop.
Save joshenders/b683c753fe37df9d76dc1e9a0157dff1 to your computer and use it in GitHub Desktop.
How to patch the NodeJS binary to write perfdata to a path other than /tmp

Background

The current verison of NodeJS hardcodes the path where perf data is written when the --perf-basic-prof flag is used.

https://github.com/v8/v8/blob/061c2ab23a1d4cd192b935e7912e7dfb1fed845d/src/log.cc#L236

At Pinterest, /tmp has limited capacity and so we weren't able to utilize perf data to troubleshoot NodeJS without filling the disk and crashing the running system.

How to patch

First I'll copy the system node binary to my local directory so this set of actions isn't destructive to the running system:

$ cp $(which node) .

To demonstrate the behavior before patching, I'll run node with the --perf-basic-prof flag:

$ ./node --perf-basic-prof

NodeJS is indeed writing perf files to /tmp/:

$ ls -l /tmp/perf-*
-rw-r--r-- 1 root root 70363 Dec  7 17:37 /tmp/perf-33630.map

We'll search in the binary for the offset of the hardcoded value:

$ xxd ./node | grep -- '/tmp/perf-'
0b62ed0: 2f74 6d70 2f70 6572 662d 2564 2e6d 6170  /tmp/perf-%d.map

The string "tmp" that we want to patch starts at hex 0b62ed1, decimal 11939537. mnt is conveniently three bytes and has a lot of space in our environment, so let's patch with that:

$ dd if=<(printf 'mnt') of=./node obs=1 seek=11939537 conv=notrunc
0+1 records in
3+0 records out
3 bytes (3 B) copied, 3.8673e-05 s, 77.6 kB/s

Now let's verify the patch was successful:

$ diff -u <(xxd $(which node)) <(xxd ./node)
--- /dev/fd/63	2016-12-07 17:38:00.364054335 +0000
+++ /dev/fd/62	2016-12-07 17:38:00.364054335 +0000
@@ -746219,7 +746219,7 @@
 0b62ea0: 50cb af00 0000 0000 e088 6e00 0000 0000  P.........n.....
 0b62eb0: e088 6e00 0000 0000 7379 6d62 6f6c 2868  ..n.....symbol(h
 0b62ec0: 6173 6820 0000 0000 0000 0000 0000 0000  ash ............
-0b62ed0: 2f74 6d70 2f70 6572 662d 2564 2e6d 6170  /tmp/perf-%d.map
+0b62ed0: 2f6d 6e74 2f70 6572 662d 2564 2e6d 6170  /mnt/perf-%d.map
 0b62ee0: 0000 0000 1000 0000 0000 0000 0000 0000  ................
 0b62ef0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 0b62f00: e07f af00 0000 0000 907f af00 0000 0000  ................

Looks good, let's test it out:

$ ./node --perf-basic-prof

Voila!

$ ls -l /mnt/perf-*
-rw-r--r-- 1 root root 70302 Dec  7 17:38 /mnt/perf-33979.map

In order to make this "production ready" you'll just need to perform this patch each time a new version of NodeJS is installed.

Troubleshooting

Q: I got the following error message, what did I do wrong?

#
# Fatal error in ../deps/v8/src/log.cc, line 272
# Check failed: (perf_output_handle_) != nullptr.
#

==== C stack trace ===============================

 1: V8_Fatal
 2: v8::internal::PerfBasicLogger::PerfBasicLogger()
 3: v8::internal::Logger::SetUp(v8::internal::Isolate*)
 4: v8::internal::Isolate::Init(v8::internal::Deserializer*)
 5: v8::internal::Snapshot::Initialize(v8::internal::Isolate*)
 6: v8::Isolate::New(v8::Isolate::CreateParams const&)
 7: node::Start(int, char**)
 8: __libc_start_main
 9: 0x6e907d
Illegal instruction

A: The path that you patched in is not writable by the NodeJS process. Either escalate the privileges of the NodeJS process or chown/chmod the directory.

Feedback

What NodeJS developers are saying:

This is the first application of hex outside of DOM colors I've seen!

— Jessica Chan

@Kocal
Copy link

Kocal commented Dec 12, 2016

Impressive. 😮

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment