@@ -5,6 +5,7 @@ const { getGeneratedPosition } = require('./source-maps')
55const session = require ( './session' )
66const { compile, compileSegments, templateRequiresEvaluation } = require ( './condition' )
77const { MAX_SNAPSHOTS_PER_SECOND_PER_PROBE , MAX_NON_SNAPSHOTS_PER_SECOND_PER_PROBE } = require ( './defaults' )
8+ const { compileBreakpointCondition, getRemoveProbeExpression } = require ( './probe_sampler' )
89const {
910 DEFAULT_MAX_REFERENCE_DEPTH ,
1011 DEFAULT_MAX_COLLECTION_SIZE ,
@@ -17,6 +18,7 @@ const {
1718 locationToBreakpoint,
1819 breakpointToProbes,
1920 probeToLocation,
21+ samplingIndexToProbe,
2022} = require ( './state' )
2123const log = require ( './log' )
2224
@@ -26,6 +28,7 @@ const log = require('./log')
2628
2729let sessionStarted = false
2830const probes = new Map ( )
31+ let nextSamplingIndex = 0
2932let scriptLoadingStabilizedResolve
3033const scriptLoadingStabilized = new Promise ( ( resolve ) => { scriptLoadingStabilizedResolve = resolve } )
3134
@@ -55,6 +58,8 @@ async function addBreakpoint (probe) {
5558 if ( ! sessionStarted ) await start ( )
5659
5760 probes . set ( probe . id , probe )
61+ probe . samplingIndex = nextSamplingIndex ++
62+ samplingIndexToProbe . set ( probe . samplingIndex , probe )
5863
5964 const file = probe . where . sourceFile
6065 let lineNumber = Number ( probe . where . lines [ 0 ] ) // Tracer doesn't support multiple-line breakpoints
@@ -75,8 +80,6 @@ async function addBreakpoint (probe) {
7580 ? MAX_SNAPSHOTS_PER_SECOND_PER_PROBE
7681 : MAX_NON_SNAPSHOTS_PER_SECOND_PER_PROBE )
7782 probe . nsBetweenSampling = BigInt ( Math . trunc ( 1 / snapshotsPerSecond * 1e9 ) )
78- // Initialize to a large negative value to ensure first probe hit is always captured
79- probe . lastCaptureNs = BigInt ( Number . MIN_SAFE_INTEGER )
8083
8184 // Warning: The code below relies on undocumented behavior of the inspector!
8285 // It expects that `await session.post('Debugger.enable')` will wait for all loaded scripts to be emitted as
@@ -174,7 +177,7 @@ async function addBreakpoint (probe) {
174177 try {
175178 result = /** @type {SetBreakpointResponse } */ ( await session . post ( 'Debugger.setBreakpoint' , {
176179 location,
177- condition : probe . condition ,
180+ condition : compileBreakpointCondition ( [ probe ] ) ,
178181 } ) )
179182 } catch ( err ) {
180183 throw new Error ( `Error setting breakpoint for probe ${ probe . id } (version: ${ probe . version } )` , { cause : err } )
@@ -195,11 +198,14 @@ async function removeBreakpoint ({ id }) {
195198 }
196199
197200 probes . delete ( id )
201+ await removeProbeFromSampler ( id )
198202
199203 const locationKey = probeToLocation . get ( id )
200204 const breakpoint = locationToBreakpoint . get ( locationKey )
201205 const probesAtLocation = breakpointToProbes . get ( breakpoint . id )
206+ const probe = probesAtLocation . get ( id )
202207
208+ samplingIndexToProbe . delete ( probe . samplingIndex )
203209 probesAtLocation . delete ( id )
204210 probeToLocation . delete ( id )
205211
@@ -229,36 +235,37 @@ async function modifyBreakpoint (probe) {
229235
230236async function updateBreakpointInternal ( breakpoint , probe ) {
231237 const probesAtLocation = breakpointToProbes . get ( breakpoint . id )
232- const conditionBeforeNewProbe = compileCompoundCondition ( [ ...probesAtLocation . values ( ) ] )
233238
234- // If a probe is provided, add it to the breakpoint. If not, it's because we're removing a probe, but potentially
235- // need to update the condition of the breakpoint .
239+ // If a probe is provided, add it to the breakpoint. If not, it's because we're removing a probe. In both cases the
240+ // breakpoint condition must be rebuilt to match the remaining probes at the location .
236241 if ( probe ) {
237242 probesAtLocation . set ( probe . id , probe )
238243 probeToLocation . set ( probe . id , breakpoint . locationKey )
239244 }
240245
241- const condition = compileCompoundCondition ( [ ... probesAtLocation . values ( ) ] )
242-
243- if ( condition || conditionBeforeNewProbe !== condition ) {
244- try {
245- await session . post ( 'Debugger.removeBreakpoint' , { breakpointId : breakpoint . id } )
246- } catch ( err ) {
247- throw new Error ( `Error removing breakpoint for probe ${ probe . id } ` , { cause : err } )
248- }
249- breakpointToProbes . delete ( breakpoint . id )
250- let result
251- try {
252- result = /** @type {SetBreakpointResponse } */ ( await session . post ( 'Debugger.setBreakpoint' , {
253- location : breakpoint . location ,
254- condition,
255- } ) )
256- } catch ( err ) {
257- throw new Error ( `Error setting breakpoint for probe ${ probe . id } (version: ${ probe . version } )` , { cause : err } )
258- }
259- breakpoint . id = result . breakpointId
260- breakpointToProbes . set ( result . breakpointId , probesAtLocation )
246+ try {
247+ await session . post ( 'Debugger.removeBreakpoint' , { breakpointId : breakpoint . id } )
248+ } catch ( err ) {
249+ const message = probe
250+ ? `Error replacing breakpoint while adding probe ${ probe . id } (version: ${ probe . version } )`
251+ : `Error replacing breakpoint after removing probe from ${ breakpoint . locationKey } `
252+ throw new Error ( message , { cause : err } )
253+ }
254+ breakpointToProbes . delete ( breakpoint . id )
255+ let result
256+ try {
257+ result = /** @type {SetBreakpointResponse } */ ( await session . post ( 'Debugger.setBreakpoint' , {
258+ location : breakpoint . location ,
259+ condition : compileBreakpointCondition ( [ ... probesAtLocation . values ( ) ] ) ,
260+ } ) )
261+ } catch ( err ) {
262+ const message = probe
263+ ? `Error setting breakpoint while adding probe ${ probe . id } (version: ${ probe . version } )`
264+ : `Error setting breakpoint after removing probe from ${ breakpoint . locationKey } `
265+ throw new Error ( message , { cause : err } )
261266 }
267+ breakpoint . id = result . breakpointId
268+ breakpointToProbes . set ( result . breakpointId , probesAtLocation )
262269}
263270
264271async function reEvaluateProbe ( probe ) {
@@ -306,16 +313,20 @@ function lock (fn) {
306313 }
307314}
308315
309- // Only if all probes have a condition can we use a compound condition.
310- // Otherwise, we need to evaluate each probe individually once the breakpoint is hit.
311- function compileCompoundCondition ( probes ) {
312- if ( probes . length === 1 ) return probes [ 0 ] . condition
313-
314- return probes . every ( p => p . condition )
315- ? probes
316- . map ( ( p ) => `(() => { try { return ${ p . condition } } catch { return false } })()` )
317- . join ( ' || ' )
318- : undefined
316+ /**
317+ * Remove cached sampling state for a probe from the runtime sampler.
318+ *
319+ * @param {string } id - The probe id.
320+ * @returns {Promise<void> }
321+ */
322+ async function removeProbeFromSampler ( id ) {
323+ try {
324+ await session . post ( 'Runtime.evaluate' , {
325+ expression : getRemoveProbeExpression ( id ) ,
326+ } )
327+ } catch ( err ) {
328+ log . error ( '[debugger:devtools_client] Error removing probe %s from sampler' , id , err )
329+ }
319330}
320331
321332function generateLocationKey ( scriptId , lineNumber , columnNumber ) {
0 commit comments