@platformatic/wattpm-pprof-capture 3.54.0 → 3.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,10 +1,24 @@
1
- import { time, heap, SourceMapper } from '@datadog/pprof'
1
+ import { heap, SourceMapper, time } from '@datadog/pprof'
2
+ import { getITC, getLogger } from '@platformatic/globals'
2
3
  import { performance } from 'node:perf_hooks'
3
4
  import { workerData } from 'node:worker_threads'
4
5
  import { NoProfileAvailableError, NotEnoughELUError, ProfilingAlreadyStartedError, ProfilingNotStartedError } from './lib/errors.js'
5
6
  import { SourceMapperWrapper } from './lib/source-mapper-wrapper.js'
6
7
 
7
- const kITC = Symbol.for('plt.runtime.itc')
8
+ // @datadog/pprof >= 5.14.2 introduced a regression in the legacy time profiler
9
+ // `stop()`: it now clears the internal source mapper (via handleStopNoRestart)
10
+ // *before* serializing the profile, so transpiled frames (e.g. TypeScript) are
11
+ // no longer mapped back to their original `.ts` sources. The `stopV2()` entry
12
+ // point serializes the profile while the source mapper is still set — the
13
+ // behaviour `stop()` had in earlier versions. It is not re-exported on the
14
+ // public `time` facade, so resolve it from the internal module when available
15
+ // and fall back to the public `stop()` otherwise (e.g. older versions).
16
+ let stopTimeProfilerV2 = null
17
+ try {
18
+ ;({ stopV2: stopTimeProfilerV2 } = await import('@datadog/pprof/out/src/time-profiler.js'))
19
+ } catch {
20
+ // Internal module path not available; fall back to the public stop().
21
+ }
8
22
 
9
23
  // SourceMapper for resolving transpiled code locations back to original source
10
24
  let sourceMapper = null
@@ -55,14 +69,15 @@ const profilingState = {
55
69
  }
56
70
 
57
71
  // Keep trying until ITC is available. This is needed because preloads run
58
- // before the app thread initialization, so globalThis.platformatic.messaging
59
- // and ITC don't exist yet.
72
+ // before the app thread initialization, so messaging and ITC don't exist yet.
60
73
  const registerInterval = setInterval(() => {
61
- if (globalThis[kITC]) {
62
- globalThis[kITC].handle('getLastProfile', getLastProfile)
63
- globalThis[kITC].handle('startProfiling', startProfiling)
64
- globalThis[kITC].handle('stopProfiling', stopProfiling)
65
- globalThis[kITC].handle('getProfilingState', getProfilingState)
74
+ const itc = getITC({ throwOnMissing: false })
75
+
76
+ if (itc) {
77
+ itc.handle('getLastProfile', getLastProfile)
78
+ itc.handle('startProfiling', startProfiling)
79
+ itc.handle('stopProfiling', stopProfiling)
80
+ itc.handle('getProfilingState', getProfilingState)
66
81
  clearInterval(registerInterval)
67
82
  }
68
83
  }, 10)
@@ -159,9 +174,11 @@ function stopProfiler (type, state) {
159
174
  state.latestProfile = (state.sourceMapsEnabled && sourceMapper) ? profiler.profile(undefined, sourceMapper) : profiler.profile()
160
175
  profiler.stop()
161
176
  } else {
162
- // CPU time profiler returns the profile when stopping
163
- // sourceMapper was already passed to start(), so it's applied automatically
164
- state.latestProfile = profiler.stop()
177
+ // CPU time profiler returns the profile when stopping.
178
+ // sourceMapper was already passed to start(), so it's applied automatically.
179
+ // Use stopV2() when available so the source mapper is honoured during
180
+ // serialization (see the note next to stopTimeProfilerV2 above).
181
+ state.latestProfile = stopTimeProfilerV2 ? stopTimeProfilerV2() : profiler.stop()
165
182
  }
166
183
  }
167
184
 
@@ -211,18 +228,19 @@ function startIfOverThreshold (type, state, wasRunning = state.profilerStarted)
211
228
  // Was not running: only start if ELU rises above start threshold
212
229
  : isAboveThreshold(state)
213
230
 
231
+ const logger = getLogger({ throwOnMissing: false })
214
232
  if (shouldRun) {
215
233
  // ELU is high enough, start/restart profiling
216
- if (!wasRunning && !state.profilerStarted && globalThis.platformatic?.logger) {
217
- globalThis.platformatic.logger.debug(
234
+ if (!wasRunning && !state.profilerStarted && logger) {
235
+ logger.debug(
218
236
  { type, eluThreshold: state.eluThreshold, currentELU },
219
237
  'Starting profiler due to ELU threshold exceeded'
220
238
  )
221
239
  }
222
240
  startProfiler(type, state, state.options)
223
- } else if (!shouldRun && wasRunning && globalThis.platformatic?.logger && state.eluThreshold != null) {
241
+ } else if (!shouldRun && wasRunning && state.eluThreshold != null && logger) {
224
242
  // Log when deciding not to restart after stopping (only in rotation context)
225
- globalThis.platformatic.logger.debug(
243
+ logger.debug(
226
244
  { type, eluThreshold: state.eluThreshold, currentELU },
227
245
  'Pausing profiler due to ELU below threshold'
228
246
  )
@@ -240,8 +258,9 @@ async function initializeSourceMapper (options = {}) {
240
258
  // Get the application directory from workerData
241
259
  const appPath = workerData?.applicationConfig?.path
242
260
  if (!appPath) {
243
- if (globalThis.platformatic?.logger) {
244
- globalThis.platformatic.logger.debug('No application path available for sourcemap resolution')
261
+ const logger = getLogger({ throwOnMissing: false })
262
+ if (logger) {
263
+ logger.debug('No application path available for sourcemap resolution')
245
264
  }
246
265
  return
247
266
  }
@@ -267,16 +286,18 @@ async function initializeSourceMapper (options = {}) {
267
286
  // Wrap the SourceMapper to fix Windows path normalization
268
287
  sourceMapper = new SourceMapperWrapper(innerMapper)
269
288
 
270
- if (globalThis.platformatic?.logger) {
289
+ const logger = getLogger({ throwOnMissing: false })
290
+ if (logger) {
271
291
  const hasMappings = sourceMapper && typeof sourceMapper.hasMappingInfo === 'function'
272
- globalThis.platformatic.logger.info(
292
+ logger.info(
273
293
  { appPath, hasSourceMapper: !!sourceMapper, hasMappingInfo: hasMappings },
274
294
  'SourceMapper initialized for profiling'
275
295
  )
276
296
  }
277
297
  } catch (err) {
278
- if (globalThis.platformatic?.logger) {
279
- globalThis.platformatic.logger.warn(
298
+ const logger = getLogger({ throwOnMissing: false })
299
+ if (logger) {
300
+ logger.warn(
280
301
  { err: err.message, stack: err.stack },
281
302
  'Failed to initialize SourceMapper'
282
303
  )
@@ -1,3 +1,4 @@
1
+ import { getLogger } from '@platformatic/globals'
1
2
  import { promises as fs } from 'node:fs'
2
3
  import path from 'node:path'
3
4
  import { createRequire } from 'node:module'
@@ -184,7 +185,7 @@ function resolveModulePath (appPath, moduleName) {
184
185
  */
185
186
  export async function loadNodeModulesSourceMaps (appPath, moduleNames, debug = false) {
186
187
  const entries = new Map()
187
- const logger = globalThis.platformatic?.logger
188
+ const logger = getLogger({ throwOnMissing: false })
188
189
 
189
190
  if (debug && logger) {
190
191
  logger.debug({ appPath, moduleNames }, 'Loading source maps from node_modules')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/wattpm-pprof-capture",
3
- "version": "3.54.0",
3
+ "version": "3.56.0",
4
4
  "description": "pprof profiling capture for wattpm",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -17,7 +17,8 @@
17
17
  "dependencies": {
18
18
  "@datadog/pprof": "^5.3.0",
19
19
  "@fastify/error": "^4.0.0",
20
- "undici": "^7.0.0"
20
+ "undici": "^7.27.2",
21
+ "@platformatic/globals": "3.56.0"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@types/node": "^22.0.0",
@@ -26,8 +27,8 @@
26
27
  "neostandard": "^0.12.0",
27
28
  "pprof-format": "^2.1.0",
28
29
  "typescript": "^5.0.0",
29
- "@platformatic/service": "3.54.0",
30
- "@platformatic/foundation": "3.54.0"
30
+ "@platformatic/service": "3.56.0",
31
+ "@platformatic/foundation": "3.56.0"
31
32
  },
32
33
  "engines": {
33
34
  "node": ">=22.19.0"