Skip to content

Commit 616ce46

Browse files
committed
Initial code for code hotspots in wall profiler
* Hide functionality behind DD_PROFILING_EXPERIMENTAL_HOTSPOTS flag * Add a debug message naming the FF to enable
1 parent 3d84f4c commit 616ce46

File tree

4 files changed

+99
-9
lines changed

4 files changed

+99
-9
lines changed

packages/datadog-core/src/storage/async_resource.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { channel } = require('../../../diagnostics_channel')
55

66
const beforeCh = channel('dd-trace:storage:before')
77
const afterCh = channel('dd-trace:storage:after')
8+
const enterCh = channel('dd-trace:storage:enter')
89

910
let PrivateSymbol = Symbol
1011
function makePrivateSymbol () {
@@ -52,6 +53,7 @@ class AsyncResourceStorage {
5253
const resource = this._executionAsyncResource()
5354

5455
resource[this._ddResourceStore] = store
56+
enterCh.publish()
5557
}
5658

5759
run (store, callback, ...args) {
@@ -61,11 +63,13 @@ class AsyncResourceStorage {
6163
const oldStore = resource[this._ddResourceStore]
6264

6365
resource[this._ddResourceStore] = store
66+
enterCh.publish()
6467

6568
try {
6669
return callback(...args)
6770
} finally {
6871
resource[this._ddResourceStore] = oldStore
72+
enterCh.publish()
6973
}
7074
}
7175

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ class Config {
3636
DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED,
3737
DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
3838
DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
39-
DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES
39+
DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES,
40+
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED
4041
} = process.env
4142

4243
const enabled = isTrue(coalesce(options.enabled, DD_PROFILING_ENABLED, true))
@@ -110,6 +111,8 @@ class Config {
110111
const profilers = options.profilers
111112
? options.profilers
112113
: getProfilers({ DD_PROFILING_HEAP_ENABLED, DD_PROFILING_WALLTIME_ENABLED, DD_PROFILING_PROFILERS })
114+
this.codeHotspotsEnabled = isTrue(coalesce(options.codeHotspotsEnabled,
115+
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED, false))
113116

114117
this.profilers = ensureProfilers(profilers, this)
115118
}

packages/dd-trace/src/profiling/profilers/wall.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
'use strict'
22

3+
const { storage } = require('../../../../datadog-core')
4+
5+
const dc = require('../../../../diagnostics_channel')
6+
7+
const beforeCh = dc.channel('dd-trace:storage:before')
8+
const afterCh = dc.channel('dd-trace:storage:after')
9+
const enterCh = dc.channel('dd-trace:storage:enter')
10+
11+
function getActiveSpan () {
12+
const store = storage.getStore()
13+
if (!store) return
14+
return store.span
15+
}
16+
17+
function getStartedSpans (context) {
18+
if (!context) return
19+
return context._trace.started
20+
}
21+
322
class NativeWallProfiler {
423
constructor (options = {}) {
524
this.type = 'wall'
@@ -9,13 +28,26 @@ class NativeWallProfiler {
928
this._mapper = undefined
1029
this._pprof = undefined
1130

31+
// Bind to this so the same value can be used to unsubscribe later
32+
this._enter = this._enter.bind(this)
33+
this._exit = this._exit.bind(this)
1234
this._logger = options.logger
1335
this._started = false
1436
}
1537

38+
codeHotspotsEnabled () {
39+
return this._codeHotspotsEnabled
40+
}
41+
1642
start ({ mapper } = {}) {
1743
if (this._started) return
1844

45+
if (this._hotspots && !this._emittedFFMessage && this._logger) {
46+
this._logger.debug(
47+
`Wall profiler: Enable config_trace_show_breakdown_profiling_for_node feature flag to see code hotspots.`)
48+
this._emittedFFMessage = true
49+
}
50+
1951
this._mapper = mapper
2052
this._pprof = require('@datadog/pprof')
2153

@@ -35,9 +67,45 @@ class NativeWallProfiler {
3567
lineNumbers: false
3668
})
3769

70+
if (this._hotspots) {
71+
beforeCh.subscribe(this._enter)
72+
enterCh.subscribe(this._enter)
73+
afterCh.subscribe(this._exit)
74+
}
75+
3876
this._started = true
3977
}
4078

79+
setLabels (labels) {
80+
this._setLabels(labels)
81+
}
82+
83+
_enter () {
84+
if (!this._started) return
85+
86+
const currentSpan = getActiveSpan() || null
87+
const currentContext = currentSpan ? currentSpan.context() : null
88+
89+
if (!currentContext) return
90+
91+
const startedSpans = getStartedSpans(currentContext)
92+
if (!startedSpans || startedSpans.length === 0) return
93+
const rootContext = startedSpans[0].context()
94+
if (!rootContext) return
95+
96+
const labels = currentContext ? {
97+
'local root span id': rootContext.toSpanId(),
98+
'span id': currentContext.toSpanId()
99+
} : null
100+
101+
this.setLabels(labels)
102+
}
103+
104+
_exit () {
105+
if (!this._started) return
106+
this.setLabels(undefined)
107+
}
108+
41109
profile () {
42110
if (!this._started) return
43111
return this._pprof.time.stop(true)
@@ -51,6 +119,12 @@ class NativeWallProfiler {
51119
if (!this._started) return
52120

53121
const profile = this._pprof.time.stop()
122+
if (this._codeHotspotsEnabled) {
123+
beforeCh.unsubscribe(this._enter)
124+
afterCh.unsubscribe(this._exit)
125+
enterCh.unsubscribe(this._enter)
126+
}
127+
54128
this._started = false
55129
return profile
56130
}

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ describe('config', () => {
4242
expect(config.logger).to.be.an.instanceof(ConsoleLogger)
4343
expect(config.exporters[0]).to.be.an.instanceof(AgentExporter)
4444
expect(config.profilers[0]).to.be.an.instanceof(WallProfiler)
45+
expect(config.profilers[0].codeHotspotsEnabled()).false
4546
expect(config.profilers[1]).to.be.an.instanceof(SpaceProfiler)
4647
})
4748

@@ -57,8 +58,9 @@ describe('config', () => {
5758
error () { }
5859
},
5960
exporters: 'agent,file',
60-
profilers: 'wall',
61-
url: 'http://localhost:1234/'
61+
profilers: 'space,wall',
62+
url: 'http://localhost:1234/',
63+
codeHotspotsEnabled: true
6264
}
6365

6466
const config = new Config(options)
@@ -78,8 +80,10 @@ describe('config', () => {
7880
expect(config.exporters[0]._url.toString()).to.equal(options.url)
7981
expect(config.exporters[1]).to.be.an.instanceof(FileExporter)
8082
expect(config.profilers).to.be.an('array')
81-
expect(config.profilers.length).to.equal(1)
82-
expect(config.profilers[0]).to.be.an.instanceOf(WallProfiler)
83+
expect(config.profilers.length).to.equal(2)
84+
expect(config.profilers[0]).to.be.an.instanceOf(SpaceProfiler)
85+
expect(config.profilers[1]).to.be.an.instanceOf(WallProfiler)
86+
expect(config.profilers[1].codeHotspotsEnabled()).true
8387
})
8488

8589
it('should filter out invalid profilers', () => {
@@ -127,7 +131,8 @@ describe('config', () => {
127131

128132
it('should support profiler config with DD_PROFILING_PROFILERS', () => {
129133
process.env = {
130-
DD_PROFILING_PROFILERS: 'wall'
134+
DD_PROFILING_PROFILERS: 'wall',
135+
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED: '1'
131136
}
132137
const options = {
133138
logger: {
@@ -143,6 +148,7 @@ describe('config', () => {
143148
expect(config.profilers).to.be.an('array')
144149
expect(config.profilers.length).to.equal(1)
145150
expect(config.profilers[0]).to.be.an.instanceOf(WallProfiler)
151+
expect(config.profilers[0].codeHotspotsEnabled()).true
146152
})
147153

148154
it('should support profiler config with DD_PROFILING_XXX_ENABLED', () => {
@@ -190,7 +196,8 @@ describe('config', () => {
190196

191197
it('should prioritize options over env variables', () => {
192198
process.env = {
193-
DD_PROFILING_PROFILERS: 'wall'
199+
DD_PROFILING_PROFILERS: 'space',
200+
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED: '1'
194201
}
195202
const options = {
196203
logger: {
@@ -199,14 +206,16 @@ describe('config', () => {
199206
warn () {},
200207
error () {}
201208
},
202-
profilers: ['space']
209+
profilers: ['wall'],
210+
codeHotspotsEnabled: false
203211
}
204212

205213
const config = new Config(options)
206214

207215
expect(config.profilers).to.be.an('array')
208216
expect(config.profilers.length).to.equal(1)
209-
expect(config.profilers[0]).to.be.an.instanceOf(SpaceProfiler)
217+
expect(config.profilers[0]).to.be.an.instanceOf(WallProfiler)
218+
expect(config.profilers[0].codeHotspotsEnabled()).false
210219
})
211220

212221
it('should support tags', () => {

0 commit comments

Comments
 (0)