@platformatic/wattpm-pprof-capture 3.21.0 → 3.23.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
@@ -2,6 +2,7 @@ import { time, heap, SourceMapper } from '@datadog/pprof'
2
2
  import { performance } from 'node:perf_hooks'
3
3
  import { workerData } from 'node:worker_threads'
4
4
  import { NoProfileAvailableError, NotEnoughELUError, ProfilingAlreadyStartedError, ProfilingNotStartedError } from './lib/errors.js'
5
+ import { SourceMapperWrapper } from './lib/source-mapper-wrapper.js'
5
6
 
6
7
  const kITC = Symbol.for('plt.runtime.itc')
7
8
 
@@ -9,55 +10,6 @@ const kITC = Symbol.for('plt.runtime.itc')
9
10
  let sourceMapper = null
10
11
  let sourceMapperInitialized = false
11
12
 
12
- /**
13
- * Wrapper around SourceMapper that fixes Windows path normalization issues.
14
- *
15
- * On Windows, V8 profiler returns paths like `file:///D:/path/to/file.js`.
16
- * The @datadog/pprof library removes `file://` leaving `/D:/path/to/file.js`,
17
- * but SourceMapper stores paths as `D:\path\to\file.js`.
18
- *
19
- * This wrapper normalizes Windows paths to match the internal format.
20
- */
21
- class SourceMapperWrapper {
22
- constructor (innerMapper) {
23
- this.innerMapper = innerMapper
24
- this.debug = innerMapper.debug
25
- }
26
-
27
- /**
28
- * Normalize Windows-style paths from V8 profiler to match SourceMapper format.
29
- * Handles paths like `/D:/path/to/file.js` -> `D:\path\to\file.js`
30
- */
31
- normalizePath (filePath) {
32
- if (process.platform !== 'win32') {
33
- return filePath
34
- }
35
-
36
- // Handle paths like /D:/path/to/file -> D:\path\to\file
37
- // This happens because pprof removes 'file://' from 'file:///D:/path/to/file'
38
- if (filePath.startsWith('/') && filePath.length > 2 && filePath[2] === ':') {
39
- // Remove leading slash and convert forward slashes to backslashes
40
- return filePath.slice(1).replace(/\//g, '\\')
41
- }
42
-
43
- // Also convert any forward slashes to backslashes on Windows
44
- return filePath.replace(/\//g, '\\')
45
- }
46
-
47
- hasMappingInfo (inputPath) {
48
- const normalized = this.normalizePath(inputPath)
49
- return this.innerMapper.hasMappingInfo(normalized)
50
- }
51
-
52
- mappingInfo (location) {
53
- const normalized = {
54
- ...location,
55
- file: this.normalizePath(location.file)
56
- }
57
- return this.innerMapper.mappingInfo(normalized)
58
- }
59
- }
60
-
61
13
  // Track ELU globally (shared across all profiler types)
62
14
  let lastELU = null
63
15
  let previousELU = performance.eventLoopUtilization()
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Wrapper around SourceMapper that fixes Windows path normalization issues.
3
+ *
4
+ * On Windows, V8 profiler returns paths like `file:///D:/path/to/file.js`.
5
+ * The @datadog/pprof library removes `file://` leaving `/D:/path/to/file.js`,
6
+ * but SourceMapper stores paths as `D:\path\to\file.js`.
7
+ *
8
+ * This wrapper normalizes Windows paths to match the internal format.
9
+ */
10
+ export class SourceMapperWrapper {
11
+ constructor (innerMapper) {
12
+ this.innerMapper = innerMapper
13
+ this.debug = innerMapper.debug
14
+ }
15
+
16
+ /**
17
+ * Normalize Windows-style paths from V8 profiler to match SourceMapper format.
18
+ * Handles paths like `/D:/path/to/file.js` -> `D:\path\to\file.js`
19
+ */
20
+ normalizePath (filePath) {
21
+ if (process.platform !== 'win32') {
22
+ return filePath
23
+ }
24
+
25
+ // Handle paths like /D:/path/to/file -> D:\path\to\file
26
+ // This happens because pprof removes 'file://' from 'file:///D:/path/to/file'
27
+ if (filePath.startsWith('/') && filePath.length > 2 && filePath[2] === ':') {
28
+ // Remove leading slash and convert forward slashes to backslashes
29
+ return filePath.slice(1).replace(/\//g, '\\')
30
+ }
31
+
32
+ // Also convert any forward slashes to backslashes on Windows
33
+ return filePath.replace(/\//g, '\\')
34
+ }
35
+
36
+ hasMappingInfo (inputPath) {
37
+ const normalized = this.normalizePath(inputPath)
38
+ return this.innerMapper.hasMappingInfo(normalized)
39
+ }
40
+
41
+ mappingInfo (location) {
42
+ const normalized = {
43
+ ...location,
44
+ file: this.normalizePath(location.file)
45
+ }
46
+
47
+ const protocols = ['webpack:']
48
+ const mappedInfo = this.innerMapper.mappingInfo(normalized)
49
+ // The @datadog/pprof SourceMapper uses path.resolve() which treats webpack: URLs
50
+ // as relative paths, creating malformed paths like:
51
+ // /path/to/.next/server/app/api/heavy/webpack:/next/src/app/api/heavy/route.js
52
+ // We need to extract just the webpack: URL part
53
+
54
+ for (const protocol of protocols) {
55
+ if (!mappedInfo.file) continue
56
+
57
+ const webpackIndex = mappedInfo.file.indexOf(protocol)
58
+ if (webpackIndex > 0) {
59
+ // Extract just the webpack: URL
60
+ mappedInfo.file = mappedInfo.file.substring(webpackIndex)
61
+ }
62
+ }
63
+ return mappedInfo
64
+ }
65
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/wattpm-pprof-capture",
3
- "version": "3.21.0",
3
+ "version": "3.23.0",
4
4
  "description": "pprof profiling capture for wattpm",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -26,8 +26,8 @@
26
26
  "neostandard": "^0.12.0",
27
27
  "pprof-format": "^2.1.0",
28
28
  "typescript": "^5.0.0",
29
- "@platformatic/foundation": "3.21.0",
30
- "@platformatic/service": "3.21.0"
29
+ "@platformatic/foundation": "3.23.0",
30
+ "@platformatic/service": "3.23.0"
31
31
  },
32
32
  "engines": {
33
33
  "node": ">=22.19.0"
@@ -0,0 +1,43 @@
1
+ import { test } from 'node:test'
2
+ import { deepStrictEqual } from 'node:assert'
3
+ import { SourceMapperWrapper } from '../lib/source-mapper-wrapper.js'
4
+
5
+ class MockSourceMapper {
6
+ constructor (mapper) {
7
+ this.mapper = mapper
8
+ }
9
+
10
+ mappingInfo (location) {
11
+ return this.mapper(location)
12
+ }
13
+ }
14
+
15
+ test('should correctly map webpack paths', async () => {
16
+ const appPath = '/Users/ivan-tymoshenko/projects/platformatic/leads-demo/web/next'
17
+ const info = {
18
+ file: `${appPath}/.next/server/app/api/heavy/route.js`,
19
+ line: 1,
20
+ column: 42,
21
+ name: 'a'
22
+ }
23
+
24
+ const mapper = () => {
25
+ return {
26
+ file: `${appPath}/.next/server/app/api/heavy/webpack:/next/src/app/api/heavy/route.js`,
27
+ name: 'fibonacci',
28
+ line: 6,
29
+ column: 12
30
+ }
31
+ }
32
+
33
+ const innerMapper = new MockSourceMapper(mapper)
34
+ const sourceMapper = new SourceMapperWrapper(innerMapper)
35
+
36
+ const mappedInfo = sourceMapper.mappingInfo(info)
37
+ deepStrictEqual(mappedInfo, {
38
+ file: 'webpack:/next/src/app/api/heavy/route.js',
39
+ name: 'fibonacci',
40
+ line: 6,
41
+ column: 12
42
+ })
43
+ })