Skip to content

feat(profiling): observe async zlib and crypto events in timeline profiler - #8042

Merged
szegedi merged 10 commits into
masterfrom
profiler/events-zlib-crypto
Apr 29, 2026
Merged

feat(profiling): observe async zlib and crypto events in timeline profiler#8042
szegedi merged 10 commits into
masterfrom
profiler/events-zlib-crypto

Conversation

@szegedi

@szegedi szegedi commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Extends the timeline events profiler to observe asynchronous compression (zlib) and encryption (crypto) operations offloaded to the libuv worker thread pool, in addition to the existing DNS, TCP connect, and filesystem events. Along the way, factors the DC-channel protocol shared across these callback-style libuv instrumentations into a single helper and applies it to dns.js as well.

Concretely:

  • Shared helper createCallbackInstrumentor(prefix, { captureResult }) in packages/datadog-instrumentations/src/helpers/callback-instrumentor.js. It owns the end-to-end protocol: bail when no subscribers or no callback, runStores on :start, wrap the callback to publish :error then :finish, publish :error on synchronous throw. The optional captureResult flag writes the callback's first non-error argument back onto the context before :finish, for plugins that tag spans from the call's return value.
  • dns.js migrated to use the helper. Behavior is preserved bit-for-bit: ctx.args is built via buildContext (including the rrtype trailing append for resolve shorthands), and captureResult restores the ctx.result assignment consumed by the DNS lookup plugin. The migration commit doubles as the helper's proof of equivalence on code that's already production-tested.
  • New instrumentation:
    • packages/datadog-instrumentations/src/zlib.js publishing apm:zlib:operation:{start,finish,error} for the callback-based async zlib APIs (deflate{,Raw}, inflate{,Raw}, gzip, gunzip, unzip, brotliCompress, brotliDecompress).
    • packages/datadog-instrumentations/src/crypto.js extended with apm:crypto:operation:{start,finish,error} for pbkdf2, scrypt, random{Bytes,Fill,Int}, generateKey{,Pair}, hkdf, sign, verify, generatePrime, checkPrime. The existing AppSec-oriented datadog:crypto:hashing:start / datadog:crypto:cipher:start channels are preserved unchanged.
  • Two new EventPlugin / decorator pairs (event_plugins/zlib.js, event_plugins/crypto.js) wired into DatadogInstrumentationEventSource so the profiler emits timeline samples with event type zlib / crypto and per-op labels.

The commits follow the above structure for easier review.

Motivation

The events profiler lets us correlate libuv thread-pool contention with specific operations and spans. Today that correlation is only possible for DNS, TCP connect, and filesystem work. Compression and encryption are common sources of pool saturation (HTTP response encoding, PBKDF2 / scrypt password hashing, key generation), but we didn't support observing them before. Part of the reason was that the plugins for the already existing three integrations emit the requisite events in their channels, so it was easy to implement them. In contrast, the zlib and crypto plugins had no instrumentation to emit the events. This is now remedied. With this, we now cover all possible built-in Node.js workloads on the libuv thread and have the best possible visibility (without writing a native profiler for them.)

Additional Notes

  • Scope: only the one-shot callback-based async APIs. Streaming (createGzip, crypto.createCipheriv, etc.) and sync variants are intentionally out of scope for this initial pass — consistent with how fs / net plugins treat streams today. Adding them is a separate follow-up and we can decide per-target.
  • Observer fallback: NodeApiEventSource has no zlib / crypto perf_hooks entry type to piggyback on, so when code hotspots are disabled these events are not captured. This matches the asymmetry the other event sources already have for their sub-events and can be filled in later if needed.
  • Tests: unit tests in packages/dd-trace/test/profiling/profilers/events.spec.js drive the new channels directly and assert on sample labels. Integration fixtures integration-tests/profiler/zlibtest.js and cryptotest.js plus matching cases in profiler.spec.js exercise the full stack end-to-end through a forked tracer process.

Potential future work

As mentioned in additional notes, we aren't instrumenting streaming variants.

Also, some 3rd party libraries also use the libuv worker pool; it might be a future expansion of this work to add support for them, especially for those for which we already have plugins. Some known examples of 3rd party libraries using uv_queue_work are:

  • "bcrypt" and "argon2" (native) for password hashing; can saturate pool under auth load. bcryptjs (pure-JS) doesn't.
  • "sharp" libvips image processing; heavy pool user in media pipelines.
  • "better-sqlite3", "sqlite3", "pg-native", "oracledb", some mysql native clients run queries on the pool
  • "canvas", "puppeteer" (indirectly via native helpers).

Jira: PROF-14316

@github-actions

github-actions Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Overall package size

Self size: 5.67 MB
Deduped: 6.51 MB
No deduping: 6.51 MB

Dependency sizes | name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.0.1 | 82.56 kB | 817.39 kB | | dc-polyfill | 0.1.10 | 26.73 kB | 26.73 kB |

🤖 This report was automatically generated by heaviest-objects-in-the-universe

@codecov

codecov Bot commented Apr 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.15596% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.21%. Comparing base (8fbe16e) to head (92c54c4).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
packages/datadog-instrumentations/src/crypto.js 43.75% 9 Missing ⚠️
...trumentations/src/helpers/callback-instrumentor.js 83.33% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8042      +/-   ##
==========================================
+ Coverage   77.20%   77.21%   +0.01%     
==========================================
  Files         809      813       +4     
  Lines       37562    37636      +74     
==========================================
+ Hits        28998    29062      +64     
- Misses       8564     8574      +10     
Flag Coverage Δ
aiguard-macos 35.82% <55.12%> (+0.02%) ⬆️
aiguard-ubuntu 35.93% <55.12%> (+0.02%) ⬆️
aiguard-windows 35.72% <55.12%> (+0.02%) ⬆️
apm-capabilities-tracing-macos 48.13% <64.04%> (-0.02%) ⬇️
apm-capabilities-tracing-ubuntu-active 48.20% <68.53%> (+0.02%) ⬆️
apm-capabilities-tracing-ubuntu-latest 48.17% <68.53%> (+0.02%) ⬆️
apm-capabilities-tracing-ubuntu-maintenance 48.20% <68.53%> (+0.02%) ⬆️
apm-capabilities-tracing-ubuntu-oldest 48.19% <68.53%> (+0.02%) ⬆️
apm-capabilities-tracing-windows 47.99% <68.53%> (+0.01%) ⬆️
apm-integrations-aerospike-18-gte.5.2.0 34.97% <55.12%> (+0.02%) ⬆️
apm-integrations-aerospike-20-gte.5.5.0 34.99% <55.12%> (+0.02%) ⬆️
apm-integrations-aerospike-22-gte.5.12.1 34.99% <55.12%> (+0.02%) ⬆️
apm-integrations-aerospike-22-gte.6.0.0 34.99% <55.12%> (+0.02%) ⬆️
apm-integrations-aerospike-eol- 34.89% <55.12%> (+0.02%) ⬆️
apm-integrations-child-process 36.15% <55.12%> (+0.02%) ⬆️
apm-integrations-confluentinc-kafka-javascript-18 41.83% <60.25%> (+0.03%) ⬆️
apm-integrations-confluentinc-kafka-javascript-20 41.85% <60.25%> (+0.03%) ⬆️
apm-integrations-confluentinc-kafka-javascript-22 41.85% <60.25%> (+0.03%) ⬆️
apm-integrations-confluentinc-kafka-javascript-24 41.78% <60.25%> (+0.03%) ⬆️
apm-integrations-couchbase-18 35.15% <55.12%> (+0.02%) ⬆️
apm-integrations-couchbase-eol 35.21% <55.12%> (-0.01%) ⬇️
apm-integrations-dns 35.00% <80.76%> (+0.07%) ⬆️
apm-integrations-elasticsearch 35.55% <60.25%> (+0.03%) ⬆️
apm-integrations-http-latest 42.98% <60.25%> (+<0.01%) ⬆️
apm-integrations-http-maintenance 43.04% <60.25%> (+<0.01%) ⬆️
apm-integrations-http-oldest 43.05% <60.25%> (+<0.01%) ⬆️
apm-integrations-http2 40.35% <60.25%> (+0.01%) ⬆️
apm-integrations-kafkajs-latest 41.71% <60.25%> (+0.02%) ⬆️
apm-integrations-kafkajs-oldest 41.77% <60.25%> (+0.03%) ⬆️
apm-integrations-net 35.66% <76.92%> (+0.06%) ⬆️
apm-integrations-next-11.1.4 29.43% <60.25%> (+0.04%) ⬆️
apm-integrations-next-13.2.0 31.28% <60.25%> (+0.04%) ⬆️
apm-integrations-next-gte.10.2.0.and.lt.11 23.33% <ø> (ø)
apm-integrations-next-gte.11.0.0.and.lt.13 31.29% <60.25%> (+0.04%) ⬆️
apm-integrations-next-gte.13.0.0.and.lt.14 31.54% <60.25%> (+0.04%) ⬆️
apm-integrations-next-gte.14.0.0.and.lte.14.2.6 31.36% <60.25%> (+0.04%) ⬆️
apm-integrations-next-gte.14.2.7.and.lt.15 31.36% <60.25%> (+0.04%) ⬆️
apm-integrations-next-gte.15.0.0 31.42% <60.25%> (+0.04%) ⬆️
apm-integrations-prisma-18-gte.6.16.0.and.lt.7.0.0 35.52% <60.25%> (+0.03%) ⬆️
apm-integrations-prisma-latest-all 35.85% <60.25%> (+0.02%) ⬆️
apm-integrations-sharedb 34.57% <55.12%> (+0.02%) ⬆️
apm-integrations-tedious 35.13% <60.25%> (+0.03%) ⬆️
appsec-express 52.78% <60.25%> (-0.05%) ⬇️
appsec-fastify 49.26% <60.25%> (-0.02%) ⬇️
appsec-graphql 49.44% <60.25%> (-0.01%) ⬇️
appsec-kafka 42.07% <60.25%> (+0.02%) ⬆️
appsec-ldapjs 41.30% <60.25%> (+<0.01%) ⬆️
appsec-lodash 41.42% <60.25%> (+<0.01%) ⬆️
appsec-macos 56.79% <60.25%> (-0.04%) ⬇️
appsec-mongodb-core 45.69% <60.25%> (-0.02%) ⬇️
appsec-mongoose 46.57% <60.25%> (-0.01%) ⬇️
appsec-mysql 48.74% <60.25%> (-0.02%) ⬇️
appsec-next-latest-11.1.4 29.60% <55.12%> (+0.04%) ⬆️
appsec-next-latest-13.2.0 31.49% <55.12%> (+0.03%) ⬆️
appsec-next-latest-gte.10.2.0.and.lt.11 31.56% <ø> (ø)
appsec-next-latest-gte.11.0.0.and.lt.13 31.47% <55.12%> (+0.03%) ⬆️
appsec-next-latest-gte.13.0.0.and.lt.14 31.67% <55.12%> (+0.03%) ⬆️
appsec-next-latest-gte.14.0.0.and.lte.14.2.6 31.52% <55.12%> (+0.03%) ⬆️
appsec-next-latest-gte.14.2.7.and.lt.15 31.52% <55.12%> (+0.03%) ⬆️
appsec-next-latest-gte.15.0.0 31.52% <55.12%> (+0.03%) ⬆️
appsec-next-oldest-11.1.4 29.63% <55.12%> (+0.04%) ⬆️
appsec-next-oldest-13.2.0 31.73% <55.12%> (+0.03%) ⬆️
appsec-next-oldest-gte.10.2.0.and.lt.11 31.72% <ø> (ø)
appsec-next-oldest-gte.11.0.0.and.lt.13 31.49% <55.12%> (+0.03%) ⬆️
appsec-next-oldest-gte.13.0.0.and.lt.14 31.92% <55.12%> (+0.03%) ⬆️
appsec-next-oldest-gte.14.0.0.and.lte.14.2.6 31.77% <55.12%> (+0.03%) ⬆️
appsec-next-oldest-gte.14.2.7.and.lt.15 31.77% <55.12%> (+0.03%) ⬆️
appsec-next-oldest-gte.15.0.0 31.77% <55.12%> (+0.03%) ⬆️
appsec-node-serialize 40.60% <60.25%> (+0.01%) ⬆️
appsec-passport 44.59% <60.25%> (-0.01%) ⬇️
appsec-postgres 48.33% <60.25%> (-0.04%) ⬇️
appsec-sourcing 40.09% <60.25%> (+0.01%) ⬆️
appsec-stripe 42.32% <60.25%> (-0.01%) ⬇️
appsec-template 40.76% <60.25%> (+0.01%) ⬆️
appsec-ubuntu 56.87% <60.25%> (-0.04%) ⬇️
appsec-windows 56.66% <60.25%> (-0.04%) ⬇️
debugger-ubuntu-active 62.12% <ø> (-0.33%) ⬇️
debugger-ubuntu-latest 62.02% <ø> (-0.33%) ⬇️
debugger-ubuntu-maintenance 62.12% <ø> (-0.33%) ⬇️
debugger-ubuntu-oldest 62.21% <ø> (-0.33%) ⬇️
instrumentations-instrumentation-bluebird 29.88% <55.12%> (+0.04%) ⬆️
instrumentations-instrumentation-body-parser 37.74% <60.25%> (+0.02%) ⬆️
instrumentations-instrumentation-child_process 35.53% <55.12%> (+0.02%) ⬆️
instrumentations-instrumentation-cookie-parser 31.81% <60.25%> (+0.04%) ⬆️
instrumentations-instrumentation-express 32.03% <60.25%> (+0.04%) ⬆️
instrumentations-instrumentation-express-mongo-sanitize 31.93% <60.25%> (+0.04%) ⬆️
instrumentations-instrumentation-express-session 37.38% <60.25%> (+0.02%) ⬆️
instrumentations-instrumentation-fs 29.56% <55.12%> (+0.04%) ⬆️
instrumentations-instrumentation-generic-pool 30.58% <55.12%> (+0.28%) ⬆️
instrumentations-instrumentation-http 36.99% <60.25%> (+0.02%) ⬆️
instrumentations-instrumentation-knex 29.85% <55.12%> (+0.04%) ⬆️
instrumentations-instrumentation-light-my-request 37.30% <55.12%> (+0.01%) ⬆️
instrumentations-instrumentation-mongoose 30.94% <60.25%> (+0.04%) ⬆️
instrumentations-instrumentation-multer 37.52% <60.25%> (+0.02%) ⬆️
instrumentations-instrumentation-mysql2 35.49% <55.12%> (+0.02%) ⬆️
instrumentations-instrumentation-passport 41.17% <60.25%> (+<0.01%) ⬆️
instrumentations-instrumentation-passport-http 40.95% <60.25%> (+0.01%) ⬆️
instrumentations-instrumentation-passport-local 41.46% <60.25%> (+<0.01%) ⬆️
instrumentations-instrumentation-pg 35.03% <60.25%> (+0.03%) ⬆️
instrumentations-instrumentation-promise 29.82% <55.12%> (+0.04%) ⬆️
instrumentations-instrumentation-promise-js 29.83% <55.12%> (+0.04%) ⬆️
instrumentations-instrumentation-q 29.86% <55.12%> (+0.04%) ⬆️
instrumentations-instrumentation-url 29.83% <55.12%> (+0.04%) ⬆️
instrumentations-instrumentation-when 29.84% <55.12%> (+0.04%) ⬆️
llmobs-ai 38.45% <55.12%> (+0.01%) ⬆️
llmobs-anthropic 37.94% <60.25%> (+0.02%) ⬆️
llmobs-bedrock 37.15% <55.12%> (+0.01%) ⬆️
llmobs-google-genai 37.58% <55.12%> (+0.01%) ⬆️
llmobs-langchain 37.09% <60.25%> (+0.02%) ⬆️
llmobs-openai 41.26% <60.25%> (+<0.01%) ⬆️
llmobs-vertex-ai 37.77% <55.12%> (+0.01%) ⬆️
openfeature-unit-active 50.41% <ø> (-0.52%) ⬇️
openfeature-unit-latest 50.25% <ø> (-0.52%) ⬇️
openfeature-unit-maintenance 50.41% <ø> (-0.52%) ⬇️
openfeature-unit-oldest 50.41% <ø> (-0.52%) ⬇️
platform-core 36.53% <ø> (ø)
platform-esbuild 40.80% <ø> (ø)
platform-instrumentations-misc 31.34% <55.12%> (+0.11%) ⬆️
platform-shimmer 42.11% <ø> (ø)
platform-unit-guardrails 35.88% <ø> (ø)
platform-webpack 20.81% <30.76%> (+0.08%) ⬆️
plugins-azure-durable-functions 25.36% <ø> (ø)
plugins-azure-event-hubs 25.51% <ø> (ø)
plugins-azure-service-bus 24.92% <ø> (ø)
plugins-bullmq 40.68% <60.25%> (-0.08%) ⬇️
plugins-cassandra 35.26% <60.25%> (-0.07%) ⬇️
plugins-cookie 26.47% <ø> (ø)
plugins-cookie-parser 26.28% <ø> (ø)
plugins-crypto 27.32% <ø> (ø)
plugins-dd-trace-api 35.48% <60.25%> (+0.03%) ⬆️
plugins-express-mongo-sanitize 26.42% <ø> (ø)
plugins-express-session 26.24% <ø> (ø)
plugins-fastify 39.38% <78.20%> (+0.04%) ⬆️
plugins-fetch 35.87% <60.25%> (+0.02%) ⬆️
plugins-fs 35.75% <55.12%> (+0.02%) ⬆️
plugins-generic-pool 25.40% <ø> (ø)
plugins-google-cloud-pubsub 43.07% <60.25%> (+<0.01%) ⬆️
plugins-grpc 38.12% <60.25%> (+0.02%) ⬆️
plugins-handlebars 26.46% <ø> (ø)
plugins-hapi 37.36% <60.25%> (+0.02%) ⬆️
plugins-hono 37.61% <60.25%> (+0.02%) ⬆️
plugins-ioredis 35.80% <60.25%> (+0.03%) ⬆️
plugins-knex 26.14% <ø> (ø)
plugins-langgraph 35.14% <55.12%> (+0.02%) ⬆️
plugins-ldapjs 24.02% <ø> (ø)
plugins-light-my-request 25.88% <ø> (ø)
plugins-limitd-client 30.12% <55.12%> (+0.04%) ⬆️
plugins-lodash 25.47% <ø> (ø)
plugins-mariadb 36.67% <60.25%> (-0.02%) ⬇️
plugins-memcached 35.46% <60.25%> (+0.03%) ⬆️
plugins-microgateway-core 36.45% <60.25%> (+0.02%) ⬆️
plugins-modelcontextprotocol-sdk 34.39% <55.12%> (+0.02%) ⬆️
plugins-moleculer 38.14% <60.25%> (+0.02%) ⬆️
plugins-mongodb 36.75% <60.25%> (+0.16%) ⬆️
plugins-mongodb-core 36.27% <60.25%> (+0.05%) ⬆️
plugins-mongoose 36.12% <60.25%> (-0.06%) ⬇️
plugins-multer 26.24% <ø> (ø)
plugins-mysql 36.40% <60.25%> (-0.11%) ⬇️
plugins-mysql2 36.50% <55.12%> (+0.02%) ⬆️
plugins-node-serialize 26.51% <ø> (ø)
plugins-opensearch 35.11% <55.12%> (+0.02%) ⬆️
plugins-passport-http 26.30% <ø> (ø)
plugins-pino 31.91% <55.12%> (+0.03%) ⬆️
plugins-postgres 34.50% <60.25%> (-0.12%) ⬇️
plugins-process 27.32% <ø> (ø)
plugins-pug 26.47% <ø> (ø)
plugins-redis 36.01% <60.25%> (+0.03%) ⬆️
plugins-router 39.78% <60.25%> (+0.01%) ⬆️
plugins-sequelize 25.18% <ø> (ø)
plugins-test-and-upstream-amqp10 35.64% <60.25%> (-0.10%) ⬇️
plugins-test-and-upstream-amqplib 40.92% <60.25%> (+0.01%) ⬆️
plugins-test-and-upstream-apollo 36.61% <60.25%> (+0.02%) ⬆️
plugins-test-and-upstream-avsc 35.10% <55.12%> (+0.02%) ⬆️
plugins-test-and-upstream-bunyan 31.26% <55.12%> (+0.04%) ⬆️
plugins-test-and-upstream-connect 37.95% <60.25%> (+0.02%) ⬆️
plugins-test-and-upstream-graphql 37.29% <55.12%> (+0.01%) ⬆️
plugins-test-and-upstream-koa 37.56% <60.25%> (+0.02%) ⬆️
plugins-test-and-upstream-protobufjs 35.32% <55.12%> (+0.02%) ⬆️
plugins-test-and-upstream-rhea 41.00% <60.25%> (+0.01%) ⬆️
plugins-undici 36.62% <60.25%> (+0.02%) ⬆️
plugins-url 27.32% <ø> (ø)
plugins-valkey 35.49% <60.25%> (+0.03%) ⬆️
plugins-vm 27.32% <ø> (ø)
plugins-winston 31.73% <55.12%> (+0.03%) ⬆️
plugins-ws 39.07% <60.25%> (+0.01%) ⬆️
profiling-macos 40.69% <83.48%> (+0.18%) ⬆️
profiling-ubuntu 40.85% <83.48%> (-0.27%) ⬇️
profiling-windows 40.87% <83.48%> (+0.18%) ⬆️
serverless-azure-functions-client 25.25% <ø> (ø)
serverless-azure-functions-eventhubs 25.25% <ø> (ø)
serverless-azure-functions-servicebus 25.25% <ø> (ø)
serverless-lambda 33.56% <55.12%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Apr 21, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

🎯 Code Coverage (details)
Patch Coverage: 83.64%
Overall Coverage: 71.51% (+0.02%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 92c54c4 | Docs | Datadog PR Page | Give us feedback!

szegedi added 6 commits April 22, 2026 11:51
Introduce createCallbackInstrumentor(prefix, options) in
packages/datadog-instrumentations/src/helpers/callback-instrumentor.js.
It owns the shared protocol for callback-style APIs whose work is
offloaded to the libuv worker thread pool: bail when no subscribers or
no callback, runStores on :start, wrap the callback to publish :error
then :finish, and publish :error on synchronous throw. The per-call
context object is produced by a caller-supplied buildContext function;
the optional captureResult option writes the callback's first
non-error argument back onto the context before :finish for plugins
that tag spans from the return value.

Migrate dns.js to use the helper. Behavior is preserved bit-for-bit:
the existing args capture (including rrtype append for resolve
shorthands) is built via buildContext, and captureResult restores the
ctx.result assignment consumed by the DNS lookup plugin.
Publishes apm:zlib:operation:{start,finish,error} diagnostic channels
for the callback-based async zlib APIs (deflate, deflateRaw, gzip,
gunzip, inflate, inflateRaw, unzip, brotliCompress, brotliDecompress).
These cover the compression operations that are offloaded to the libuv
worker thread pool and will be consumed by the events profiler.

Uses the createCallbackInstrumentor helper, so the instrumentation body
is just the per-method operation name.
Publishes apm:crypto:operation:{start,finish,error} diagnostic channels
for the callback-based async crypto APIs offloaded to the libuv worker
thread pool: pbkdf2, scrypt, randomBytes, randomFill, randomInt,
generateKey, generateKeyPair, hkdf, sign, verify, generatePrime,
checkPrime.

The existing AppSec-oriented datadog:crypto:hashing:start and
datadog:crypto:cipher:start channels are preserved unchanged. Uses the
createCallbackInstrumentor helper; the instrumentation body just
declares per-method argument names to capture on the context.
Subscribes to apm:zlib:operation:{start,finish,error} via a new
ZlibPlugin and emits timeline samples with entry type "zlib" and an
"operation" label carrying the specific compression method name
(gzip, deflate, brotliCompress, etc.). Only the
DatadogInstrumentationEventSource path observes these events; there is
no perf_hooks fallback since Node does not expose a zlib entry type.
Subscribes to apm:crypto:operation:{start,finish,error} via a new
CryptoPlugin and emits timeline samples with entry type "crypto". The
plugin forwards a whitelist of instrumentation-context fields
(operation, algorithm, digest, type, size, keylen, iterations, offset)
as pprof labels; the whitelist mirrors the param names declared in the
crypto instrumentation.
Unit tests publish on the new apm:zlib:operation and
apm:crypto:operation diagnostic channels and assert that the events
profiler produces samples with the expected event type and labels.

Integration tests exercise the full stack: zlibtest.js drives gzip,
gunzip, deflate, and brotliCompress; cryptotest.js drives pbkdf2,
randomBytes, and randomFill. Both are picked up via gatherTimelineEvents
using new ZlibEventProcessor and CryptoEventProcessor helpers that know
the crypto-specific label set.
@szegedi
szegedi force-pushed the profiler/events-zlib-crypto branch from f1f0584 to 6302a23 Compare April 22, 2026 09:52
@szegedi
szegedi marked this pull request as ready for review April 22, 2026 14:19
@szegedi
szegedi requested review from a team as code owners April 22, 2026 14:19
Comment on lines +21 to +32
checkPrime: [],
generateKey: ['type'],
generateKeyPair: ['type'],
generatePrime: ['size'],
hkdf: ['digest', null, null, null, 'keylen'],
pbkdf2: [null, null, 'iterations', 'keylen', 'digest'],
randomBytes: ['size'],
randomFill: [null, 'offset', 'size'],
randomInt: [],
scrypt: [null, null, 'keylen'],
sign: ['algorithm'],
verify: ['algorithm'],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using maps instead so that the key is the arguments index and we do not need to iterate over null entries?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd then still rather use e.g. ['digest',,,,'keylen'] and then an iteration would only include existing indices.

return wrapMethod
}

function buildAsyncContext (operation, paramNames) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will have an overhead compared to writing the code manually. I think the performance wins over the simplification, no?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hardly think an additional level of a function call is a significant enough overhead worth worrying about. We're notoriously bad at intuiting the performance characteristics of code, that's exactly what we use profilers for. If this ever shows up as a significant performance detractor for a customer, I'd consider changing it. Otherwise it's a speculative optimization and I'd rather err towards the side of maintainability in the code.

@szegedi
szegedi force-pushed the profiler/events-zlib-crypto branch 2 times, most recently from e6446d5 to 6302a23 Compare April 24, 2026 10:51
…ames

Replace the null placeholders in asyncParamsByMethod with elided
positions (e.g. hkdf: ['digest', , , , 'keylen']) so unused argument
slots can be skipped at iteration time rather than tested inside the
loop body. buildAsyncContext now walks the populated slots with
for-in, pulls the name inline, and breaks once we pass the callback
position since indices are yielded in ascending numeric order.

for-in is used intentionally here over Object.keys: paramNames is a
plain array literal with no prototype additions, so for-in yields the
same set of keys without the keys-array allocation. Flagged with the
corresponding eslint-disable-line comments on the sparse-array lines
since the repo defaults reject holes.
Comment thread packages/datadog-instrumentations/src/zlib.js
Comment thread packages/datadog-instrumentations/src/crypto.js
Comment thread packages/datadog-instrumentations/src/zlib.js
szegedi added 2 commits April 27, 2026 13:43
Add zstdCompress and zstdDecompress to the asyncMethods list. Available
in Node 22.15+ / 23.8+ / 24+; the existing typeof guard skips them on
older runtimes.
Cover the new apm:zlib:operation and apm:crypto:operation diagnostic
channels at the instrumentation layer, following the pattern in
url.spec.js and child_process.spec.js: subscribe sinon stubs to the
channels, drive the wrapped methods, assert on the published context.

zlib.spec.js exercises every async compress/decompress pair, the
unzip auto-detection path, the on-failure error channel, the
no-callback fast path, and zstd when available on the running Node
version. crypto.spec.js verifies per-method argument extraction
(pbkdf2 iterations/keylen/digest, randomBytes size, randomFill
offset+size, generateKeyPair type, hkdf digest+keylen), confirms that
non-string non-number arguments at captured positions are filtered
out, exercises both the synchronous-throw and asynchronous-callback
error paths, and asserts that the existing AppSec
datadog:crypto:hashing/cipher channels still fire alongside the new
async wrappers.
@szegedi
szegedi requested a review from IlyasShabi April 27, 2026 13:49

@IlyasShabi IlyasShabi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@pr-commenter

pr-commenter Bot commented Apr 29, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-04-29 07:27:32

Comparing candidate commit 92c54c4 in PR branch profiler/events-zlib-crypto with baseline commit 8fbe16e in branch master.

Found 0 performance improvements and 1 performance regressions! Performance is the same for 1345 metrics, 98 unstable metrics.

scenario:runtime-metrics-with-runtime-metrics-22

  • 🟥 max_rss_usage [+5.779MB; +6.792MB] or [+6.943%; +8.160%]

@szegedi
szegedi merged commit 4daebfa into master Apr 29, 2026
876 checks passed
@szegedi
szegedi deleted the profiler/events-zlib-crypto branch April 29, 2026 08:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants