Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DI] Improve sampling tests #4999

Merged
merged 3 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions integration-tests/debugger/basic.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Dynamic Instrumentation', function () {
it('base case: target app should work as expected if no test probe has been added', async function () {
const response = await t.axios.get(t.breakpoint.url)
assert.strictEqual(response.status, 200)
assert.deepStrictEqual(response.data, { hello: 'foo' })
assert.deepStrictEqual(response.data, { hello: 'bar' })
})

describe('diagnostics messages', function () {
Expand Down Expand Up @@ -54,7 +54,7 @@ describe('Dynamic Instrumentation', function () {
t.axios.get(t.breakpoint.url)
.then((response) => {
assert.strictEqual(response.status, 200)
assert.deepStrictEqual(response.data, { hello: 'foo' })
assert.deepStrictEqual(response.data, { hello: 'bar' })
})
.catch(done)
} else {
Expand Down Expand Up @@ -245,7 +245,7 @@ describe('Dynamic Instrumentation', function () {
message: 'Hello World!',
logger: {
name: t.breakpoint.file,
method: 'handler',
method: 'fooHandler',
version,
thread_name: 'MainThread'
},
Expand Down Expand Up @@ -279,7 +279,7 @@ describe('Dynamic Instrumentation', function () {
const topFrame = payload['debugger.snapshot'].stack[0]
// path seems to be prefeixed with `/private` on Mac
assert.match(topFrame.fileName, new RegExp(`${t.appFile}$`))
assert.strictEqual(topFrame.function, 'handler')
assert.strictEqual(topFrame.function, 'fooHandler')
assert.strictEqual(topFrame.lineNumber, t.breakpoint.line)
assert.strictEqual(topFrame.columnNumber, 3)

Expand Down Expand Up @@ -375,6 +375,61 @@ describe('Dynamic Instrumentation', function () {

t.agent.addRemoteConfig(rcConfig)
})

it('should adhere to individual probes sample rate', function (done) {
const rcConfig1 = t.breakpoints[0].generateRemoteConfig({ sampling: { snapshotsPerSecond: 1 } })
const rcConfig2 = t.breakpoints[1].generateRemoteConfig({ sampling: { snapshotsPerSecond: 1 } })
const state = {
[rcConfig1.config.id]: {
payloadsReceived: 0,
tiggerBreakpointContinuously () {
t.axios.get(t.breakpoints[0].url).catch(done)
this.timer = setTimeout(this.tiggerBreakpointContinuously.bind(this), 10)
}
},
[rcConfig2.config.id]: {
payloadsReceived: 0,
tiggerBreakpointContinuously () {
t.axios.get(t.breakpoints[1].url).catch(done)
this.timer = setTimeout(this.tiggerBreakpointContinuously.bind(this), 10)
}
}
}

t.agent.on('debugger-diagnostics', ({ payload }) => {
const { probeId, status } = payload.debugger.diagnostics
if (status === 'INSTALLED') state[probeId].tiggerBreakpointContinuously()
})

t.agent.on('debugger-input', ({ payload }) => {
const _state = state[payload['debugger.snapshot'].probe.id]
_state.payloadsReceived++
if (_state.payloadsReceived === 1) {
_state.start = Date.now()
} else if (_state.payloadsReceived === 2) {
const duration = Date.now() - _state.start
clearTimeout(_state.timer)

// Allow for a variance of -5/+50ms (time will tell if this is enough)
assert.isAbove(duration, 995)
assert.isBelow(duration, 1050)

// Wait at least a full sampling period, to see if we get any more payloads
_state.timer = setTimeout(doneWhenCalledTwice, 1250)
} else {
clearTimeout(_state.timer)
done(new Error('Too many payloads received!'))
}
})

t.agent.addRemoteConfig(rcConfig1)
t.agent.addRemoteConfig(rcConfig2)

function doneWhenCalledTwice () {
if (doneWhenCalledTwice.calledOnce) return done()
doneWhenCalledTwice.calledOnce = true
}
})
})

describe('race conditions', function () {
Expand Down
8 changes: 6 additions & 2 deletions integration-tests/debugger/target-app/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ const Fastify = require('fastify')

const fastify = Fastify()

fastify.get('/:name', function handler (request) {
return { hello: request.params.name } // BREAKPOINT: /foo
fastify.get('/foo/:name', function fooHandler (request) {
return { hello: request.params.name } // BREAKPOINT: /foo/bar
})

fastify.get('/bar/:name', function barHandler (request) {
return { hello: request.params.name } // BREAKPOINT: /bar/baz
})

fastify.listen({ port: process.env.APP_PORT }, (err) => {
Expand Down
48 changes: 35 additions & 13 deletions integration-tests/debugger/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,43 @@ module.exports = {

function setup () {
let sandbox, cwd, appPort
const breakpoint = getBreakpointInfo(1) // `1` to disregard the `setup` function
const breakpoints = getBreakpointInfo(1) // `1` to disregard the `setup` function
const t = {
breakpoint,
breakpoint: breakpoints[0],
breakpoints,

axios: null,
appFile: null,
agent: null,

// Default to the first breakpoint in the file (normally there's only one)
rcConfig: null,
triggerBreakpoint,
generateRemoteConfig,
generateProbeConfig
triggerBreakpoint: triggerBreakpoint.bind(null, breakpoints[0].url),
generateRemoteConfig: generateRemoteConfig.bind(null, breakpoints[0]),
generateProbeConfig: generateProbeConfig.bind(null, breakpoints[0])
}

function triggerBreakpoint () {
// Allow specific access to each breakpoint
for (let i = 0; i < breakpoints.length; i++) {
t.breakpoints[i] = {
rcConfig: null,
triggerBreakpoint: triggerBreakpoint.bind(null, breakpoints[i].url),
generateRemoteConfig: generateRemoteConfig.bind(null, breakpoints[i]),
generateProbeConfig: generateProbeConfig.bind(null, breakpoints[i]),
...breakpoints[i]
}
}

function triggerBreakpoint (url) {
// Trigger the breakpoint once probe is successfully installed
t.agent.on('debugger-diagnostics', ({ payload }) => {
if (payload.debugger.diagnostics.status === 'INSTALLED') {
t.axios.get(breakpoint.url)
t.axios.get(url)
}
})
}

function generateRemoteConfig (overrides = {}) {
function generateRemoteConfig (breakpoint, overrides = {}) {
overrides.id = overrides.id || randomUUID()
return {
product: 'LIVE_DEBUGGING',
Expand All @@ -54,15 +69,19 @@ function setup () {
sandbox = await createSandbox(['fastify']) // TODO: Make this dynamic
cwd = sandbox.folder
// The sandbox uses the `integration-tests` folder as its root
t.appFile = join(cwd, 'debugger', breakpoint.file)
t.appFile = join(cwd, 'debugger', breakpoints[0].file)
})

after(async function () {
await sandbox.remove()
})

beforeEach(async function () {
t.rcConfig = generateRemoteConfig(breakpoint)
// Default to the first breakpoint in the file (normally there's only one)
t.rcConfig = generateRemoteConfig(breakpoints[0])
// Allow specific access to each breakpoint
t.breakpoints.forEach((breakpoint) => { breakpoint.rcConfig = generateRemoteConfig(breakpoint) })

appPort = await getPort()
t.agent = await new FakeAgent().start()
t.proc = await spawnProc(t.appFile, {
Expand Down Expand Up @@ -96,16 +115,19 @@ function getBreakpointInfo (stackIndex = 0) {
.slice(0, -1)
.split(':')[0]

// Then, find the corresponding file in which the breakpoint exists
// Then, find the corresponding file in which the breakpoint(s) exists
const file = join('target-app', basename(testFile).replace('.spec', ''))

// Finally, find the line number of the breakpoint
// Finally, find the line number(s) of the breakpoint(s)
const lines = readFileSync(join(__dirname, file), 'utf8').split('\n')
const result = []
for (let i = 0; i < lines.length; i++) {
const index = lines[i].indexOf(BREAKPOINT_TOKEN)
if (index !== -1) {
const url = lines[i].slice(index + BREAKPOINT_TOKEN.length + 1).trim()
return { file, line: i + 1, url }
result.push({ file, line: i + 1, url })
}
}

return result
}
Loading