Skip to content

Conversation

@SamuelLHuber
Copy link

Summary

This PR adds Bun runtime support to @datadog/pprof by introducing a pure JavaScript backend that avoids the native addon crashes when loading in Bun.

Problem

Bun crashes with a segfault when trying to load the native Node.js addon:

bun -e "import * as p from '@datadog/pprof'"
# => segfault in prebuilds/darwin-arm64/node-137.node

This is because the existing implementation uses NAN + direct V8 APIs which Bun doesn't fully support (tracked in oven-sh/bun#4290).

Solution

  1. Runtime detection (ts/src/runtime.ts): Automatically detects Bun via process.versions.bun, globalThis.Bun, or DATADOG_PPROF_RUNTIME env var

  2. Bun native backend (ts/src/bun-native-backend.ts): Pure JavaScript implementation providing:

    • BunTimeProfiler - Wall profiling with context tracking and CPU time collection
    • bunGetAllocationProfile() - Heap profiling using process.memoryUsage()
    • bunMonitorOutOfMemory() - No-op (Bun lacks V8 near-heap-limit hooks)
  3. Backend loader (ts/src/native-backend-loader.ts): Runtime-aware loader that:

    • Returns Bun JS backend when runtime === 'bun'
    • Falls back to native Node addon via node-gyp-build for Node
    • Includes fallback to local build artifacts for development

Changes

  • Added ts/src/runtime.ts - Runtime detection logic
  • Added ts/src/bun-native-backend.ts - Bun JS backend implementation
  • Added ts/src/native-backend-loader.ts - Backend selection
  • Added tests: ts/test/test-runtime.ts, ts/test/test-bun-native-backend.ts
  • Added smoke tests: scripts/bun-smoke.mjs, scripts/bun-smoke-runner.mjs, scripts/bun-smoke-pack.mjs
  • Added CI: Bun smoke tests on linux-x64, linux-arm64, darwin-arm64
  • Updated binding.gyp comments (no functional changes)

Verification

Tested on:

  • ✅ Bun v1.3.5+
  • ✅ Node.js 18, 20, 22, 24, 25
  • ✅ linux-x64, linux-arm64, darwin-arm64

Smoke Test

# Import works without crash
bun -e "import * as p from '@datadog/pprof'; console.log(typeof p.time.start)"
# => function

# Wall profiling
bun -e "import * as p from '@datadog/pprof'; p.time.start({durationMillis:100, intervalMicros:1000}); const prof=p.time.stop(); console.log('samples', prof.samples.length)"
# => samples N

# Heap profiling
bun -e "import * as p from '@datadog/pprof'; p.heap.start(512*1024, 64); const prof=p.heap.profile(); p.heap.stop(); console.log('heap ok')"
# => heap ok

# Encode
bun -e "import * as p from '@datadog/pprof'; p.time.start({durationMillis:100, intervalMicros:1000}); const prof=p.time.stop(); const buf=await p.encode(prof); console.log('bytes', buf.length)"
# => bytes N

Related Issues

Limitations

  1. OOM monitoring: No-op on Bun (Bun lacks V8 near-heap-limit callback hooks)
  2. Call stacks: Synthetic rather than actual JS call stacks (Bun doesn't expose V8 profiler APIs)
  3. CPU time: Estimated from process.cpuUsage() deltas rather than V8 CPU profiler

These limitations don't affect the primary use case of continuous profiling with Pyroscope.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant