Open-Source Wikis

/

Node.js

/

How to contribute

/

Debugging

nodejs/node

Debugging

This page collects the moves you actually use when something is wrong: getting a debug build, attaching a debugger, chasing memory issues, and reading Node's own diagnostic surfaces.

Getting a debug build

./configure --debug
make -j"$(nproc)"

That produces out/Debug/node with full assertions, debug symbols, and the DCHECKs in src/util.h enabled. Most C++ debugging starts here. For diff-against-release behaviour you can also build both:

./configure --debug && make -j
mv out/Debug out/Debug.preserved
./configure && make -j
mv out/Debug.preserved out/Debug

A separate set of flags exists for sanitizers:

Flag Effect
--enable-asan AddressSanitizer build (catches use-after-free, OOB)
--enable-ubsan UndefinedBehaviorSanitizer
--enable-tsan ThreadSanitizer (rarely used; partial)

Suppression files ship at tools/lsan_suppressions.txt and tools/valgrind.supp. The make test-asan target runs the suite under ASan.

Native debugger workflow

Linux:

gdb --args ./out/Debug/node --no-deprecation --inspect-brk=0 app.js
(gdb) catch throw
(gdb) catch syscall
(gdb) bt

macOS:

lldb -- ./out/Debug/node app.js
(lldb) breakpoint set -n node::Start
(lldb) run

Useful breakpoints:

  • node::errors::TriggerUncaughtException (src/node_errors.cc) — JS uncaught exceptions reach C++ here.
  • node::Environment::AbortOnUncaughtException--abort-on-uncaught-exception.
  • node::TLSWrap::EncOut (src/crypto/crypto_tls.cc) — common TLS flow point.
  • uv__io_poll (in deps/uv/src/unix/) — bottom of the event loop.
  • node::Realm::ExecuteBootstrapper (src/node_realm.cc) — early bootstrap.

Use print *env after grabbing an Environment*Environment has a debug printer in src/env.cc.

Attaching to V8 via the inspector

node --inspect=0.0.0.0:9229 app.js
node --inspect-brk app.js
  • Open chrome://inspect (or use VS Code's "Attach to Node Process") to connect.
  • The inspector C++ implementation lives at src/inspector/, src/inspector_*.{cc,h}, with the JS bindings at lib/inspector.js and lib/internal/inspector/.
  • --inspect-publish-uid=http,stderr controls how the WebSocket URL is published.
  • The --inspect-wait (≥ Node 22) flag pauses until a debugger attaches even mid-script.

Programmatic access is via the inspector module: inspector.open(port, host, wait) and inspector.url(). Sessions can be opened in-process via new inspector.Session().

Reading runtime diagnostics

  • --report-on-fatalerror, --report-uncaught-exception, --report-on-signal — emit a JSON process.report dump. Implementation: src/node_report.cc.
  • --prof — writes V8 isolate-*.log ticks; post-process with node --prof-process (which uses lib/internal/main/prof_process.js).
  • --trace-events-enabled --trace-event-categories=... — perfetto-format trace files. Implementation: src/tracing/.
  • NODE_DEBUG=<area> — enables util.debuglog(area) calls scattered through lib/internal/. Common values: module, tls, stream, tracing.
  • NODE_DEBUG_NATIVE=<area> — toggles the C++ counterpart in src/debug_utils.cc. Active areas are listed in src/debug_utils.h.

process.report (always available) returns a JSON dump including loaded native code locations, async-resource trees, and libuv handles.

Async-context debugging

  • --async-stack-traces — V8 async stack traces (on by default).
  • process.getActiveResourcesInfo() — lists everything keeping the loop alive.
  • async_hooks.executionAsyncResource() — current async resource.
  • AsyncLocalStorage is implemented in lib/internal/async_local_storage/ over AsyncContextFrame (src/async_context_frame.cc).

Memory and leaks

  • --inspect + Chrome DevTools heap snapshots is the easiest first step.
  • --heapsnapshot-near-heap-limit=<n> triggers automatic snapshots before OOM.
  • --heapsnapshot-signal=SIGUSR2 writes a snapshot on demand.
  • For C++ leaks, the ASan build (--enable-asan) is canonical.
  • For libuv handle leaks, process._getActiveHandles() and process._getActiveRequests() are direct windows into uv handle/req trees.

Crash dumps

  • --report-on-fatalerror writes a JSON report immediately before abort.
  • On Linux, ensure kernel.core_pattern is set to capture cores; then gdb -c core ./out/Debug/node.
  • For postmortem analysis with mdb/llnode, see doc/contributing/postmortem.md.

Reproducing flaky tests

# Loop a single test 50 times under stress
python3 tools/test.py --repeat=50 -J test/parallel/test-foo.js

# Use `flaky-tests-mode` to allow CI to retry transient failures
python3 tools/test.py --flaky-tests=run test/parallel/...

Flaky tests are tracked via the flaky-test GitHub label and .github/workflows/label-flaky-test-issue.yml.

When all else fails

  • Diff against out/Release/node from a known-good build.
  • Bisect with git bisect run python3 tools/test.py test/parallel/test-foo.js.
  • Ask in #nodejs-core on the OpenJS Foundation Slack with a stable repro.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Debugging – Node.js wiki | Factory