Skip to content

Commit 031533a

Browse files
szegediclaude
andauthored
profiling: replace sourceMapCount with hasMissingSourceMaps (#7843)
Replace the `sourceMapCount` info field with a boolean `hasMissingSourceMaps` that checks each profile's comments for the `dd:has-missing-map-files` token emitted by @datadog/pprof when source files declare a sourceMappingURL but the map file is missing. Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
1 parent f429a85 commit 031533a

File tree

2 files changed

+32
-37
lines changed

2 files changed

+32
-37
lines changed

packages/dd-trace/src/profiling/profiler.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ function findWebSpan (startedSpans, spanId) {
3232
return false
3333
}
3434

35+
const MISSING_SOURCE_MAPS_TOKEN = 'dd:has-missing-map-files'
36+
37+
function profileHasMissingSourceMaps (profile) {
38+
const strings = profile?.stringTable?.strings
39+
if (!strings) return false
40+
return profile.comment?.some(idx => strings[idx] === MISSING_SOURCE_MAPS_TOKEN) ?? false
41+
}
42+
3543
function processInfo (infos, info, type) {
3644
if (Object.keys(info).length > 0) {
3745
infos[type] = info
@@ -49,7 +57,6 @@ class Profiler extends EventEmitter {
4957
#logger
5058
#profileSeq = 0
5159
#spanFinishListener
52-
#sourceMapCount = 0
5360
#timer
5461

5562
constructor () {
@@ -195,10 +202,10 @@ class Profiler extends EventEmitter {
195202
mapper = new SourceMapper(config.debugSourceMaps)
196203
mapper.loadDirectory(process.cwd())
197204
.then(() => {
198-
this.#sourceMapCount = mapper.infoMap.size
199205
if (config.debugSourceMaps) {
206+
const count = mapper.infoMap.size
200207
this.#logger.debug(() => {
201-
return this.#sourceMapCount === 0
208+
return count === 0
202209
? 'Found no source maps'
203210
: `Found source maps for following files: [${[...mapper.infoMap.keys()].join(', ')}]`
204211
})
@@ -315,7 +322,7 @@ class Profiler extends EventEmitter {
315322
return {
316323
serverless: this.serverless,
317324
settings: this.#config.systemInfoReport,
318-
sourceMapCount: this.#sourceMapCount,
325+
hasMissingSourceMaps: false,
319326
}
320327
}
321328

@@ -362,6 +369,9 @@ class Profiler extends EventEmitter {
362369
? await compressionFn(encoded, this.#compressionOptions)
363370
: encoded
364371
encodedProfiles[profiler.type] = compressed
372+
if (profileHasMissingSourceMaps(profile)) {
373+
infos.hasMissingSourceMaps = true
374+
}
365375
processInfo(infos, info, profiler.type)
366376
this.#logger.debug(() => {
367377
const profileJson = JSON.stringify(profile, (_, value) => {

packages/dd-trace/test/profiling/profiler.spec.js

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -388,61 +388,44 @@ describe('profiler', function () {
388388
assert.strictEqual(infos.serverless, false)
389389
})
390390

391-
it('should include sourceMapCount: 0 when source maps are disabled', async () => {
392-
exporterPromise = new Promise(resolve => {
393-
exporter.export = (exportSpec) => {
394-
resolve(exportSpec)
395-
return Promise.resolve()
396-
}
391+
it('should set hasMissingSourceMaps to true when any profile has the comment token', async () => {
392+
const token = 'dd:has-missing-map-files'
393+
wallProfiler.profile.returns({
394+
comment: [1],
395+
stringTable: { strings: ['', token] },
397396
})
398397

399-
await profiler._start(makeStartOptions({ sourceMap: false }))
400-
401-
clock.tick(interval)
402-
403-
const { infos } = await exporterPromise
404-
405-
assert.strictEqual(infos.sourceMapCount, 0)
406-
})
407-
408-
it('should include sourceMapCount: 0 when no source maps are found', async () => {
409398
exporterPromise = new Promise(resolve => {
410399
exporter.export = (exportSpec) => {
411400
resolve(exportSpec)
412401
return Promise.resolve()
413402
}
414403
})
415404

416-
profiler._start(makeStartOptions({ sourceMap: true }))
417-
await Promise.resolve() // flush .then() callback so #sourceMapCount is set
405+
await profiler._start(makeStartOptions())
418406

419407
clock.tick(interval)
420408

421409
const { infos } = await exporterPromise
422410

423-
assert.strictEqual(infos.sourceMapCount, 0)
411+
assert.strictEqual(infos.hasMissingSourceMaps, true)
424412
})
425413

426-
it('should include sourceMapCount with the number of loaded source maps', async () => {
427-
mapperInstance.infoMap.set('file1.js', {})
428-
mapperInstance.infoMap.set('file2.js', {})
429-
mapperInstance.infoMap.set('file3.js', {})
430-
414+
it('should set hasMissingSourceMaps to false when no profile has the comment token', async () => {
431415
exporterPromise = new Promise(resolve => {
432416
exporter.export = (exportSpec) => {
433417
resolve(exportSpec)
434418
return Promise.resolve()
435419
}
436420
})
437421

438-
profiler._start(makeStartOptions({ sourceMap: true }))
439-
await Promise.resolve() // flush .then() callback so #sourceMapCount is set
422+
await profiler._start(makeStartOptions())
440423

441424
clock.tick(interval)
442425

443426
const { infos } = await exporterPromise
444427

445-
assert.strictEqual(infos.sourceMapCount, 3)
428+
assert.strictEqual(infos.hasMissingSourceMaps, false)
446429
})
447430
})
448431

@@ -520,9 +503,12 @@ describe('profiler', function () {
520503
assert.strictEqual(infos.serverless, true)
521504
})
522505

523-
it('should include sourceMapCount in export infos', async () => {
524-
mapperInstance.infoMap.set('file1.js', {})
525-
mapperInstance.infoMap.set('file2.js', {})
506+
it('should set hasMissingSourceMaps to true when any profile has the comment token', async () => {
507+
const token = 'dd:has-missing-map-files'
508+
wallProfiler.profile.returns({
509+
comment: [1],
510+
stringTable: { strings: ['', token] },
511+
})
526512

527513
exporterPromise = new Promise(resolve => {
528514
exporter.export = (exportSpec) => {
@@ -531,8 +517,7 @@ describe('profiler', function () {
531517
}
532518
})
533519

534-
profiler._start(makeStartOptions({ sourceMap: true }))
535-
await Promise.resolve() // flush .then() callback so #sourceMapCount is set
520+
profiler._start(makeStartOptions())
536521

537522
// flushAfterIntervals + 1 because it flushes after last interval
538523
for (let i = 0; i < flushAfterIntervals + 1; i++) {
@@ -541,7 +526,7 @@ describe('profiler', function () {
541526

542527
const { infos } = await exporterPromise
543528

544-
assert.strictEqual(infos.sourceMapCount, 2)
529+
assert.strictEqual(infos.hasMissingSourceMaps, true)
545530
})
546531
})
547532
})

0 commit comments

Comments
 (0)