Trace Node.js System Calls with OS Profiling Tools
Tracing system calls (syscalls) is a powerful way to diagnose
performance bottlenecks, file access errors, and network issues in a
Node.js application. This article provides a practical guide on how to
monitor and analyze the interactions between the Node.js runtime and the
operating system kernel using industry-standard profiling tools like
strace on Linux and dtruss on macOS.
Understanding Node.js and System Calls
Node.js runs on the V8 JavaScript engine and uses the
libuv library to handle asynchronous I/O operations. When
your JavaScript code reads a file, queries a database, or accepts a
network connection, libuv translates these actions into
low-level system calls to the operating system kernel.
By tracing these syscalls, you can see exactly what the OS is doing on behalf of your Node.js process, bypassing the abstractions of the JavaScript runtime.
Tracing System Calls
on Linux using strace
strace is the standard diagnostic and debugging utility
for Linux. It intercepts and records the system calls called by a
process and the signals received by a process.
1. Tracing a New Node.js Process
To start a Node.js application and immediately begin tracing its system calls, run:
strace node app.js2. Following Threads and Child Processes (Crucial for Node.js)
Because Node.js uses a multi-threaded thread pool (via
libuv) for asynchronous operations, you must use the
-f flag to instruct strace to trace child
threads and forks.
strace -f node app.js3. Filtering Specific System Calls
Node.js generates a massive volume of system calls. You can filter
the output to show only specific types of calls, such as file operations
(open, read, write) or network
operations (connect, accept).
Trace only file-related system calls:
strace -f -e trace=file node app.jsTrace network-related system calls:
strace -f -e trace=network node app.js
4. Attaching to an Already Running Node.js Process
If your application is already running, find its Process ID (PID)
using pgrep or ps, then attach
strace to it:
strace -f -p <PID>5. Generating a System Call Summary
To get a high-level overview of where your application is spending its time, you can generate a summary report of system call statistics:
strace -f -c node app.jsWhen you stop the process (Ctrl+C), strace will output a
table displaying the percentage of time, seconds, usecs/call, and total
counts for each system call.
Tracing System Calls
on macOS using dtruss
On macOS, dtrace is the primary dynamic tracing
framework. The easiest way to trace system calls is through
dtruss, a command-line wrapper for dtrace that
behaves similarly to strace.
Note: Due to macOS System Integrity Protection (SIP), you must
run dtruss with sudo, and you may need to run
it on a non-system binary copy of Node.js if SIP prevents
tracing.
1. Tracing a New Node.js Process
Launch your application with dtruss:
sudo dtruss node app.js2. Attaching to a Running Process
To attach to a running Node.js process via its PID:
sudo dtruss -p <PID>3. Filtering and Options
Follow child threads/processes:
sudo dtruss -f node app.jsPrint system call counts/summary on exit:
sudo dtruss -c node app.js