99 ArrayPrototypePush,
1010 ArrayPrototypeSlice,
1111 ArrayPrototypeSort,
12+ Error,
1213 ObjectDefineProperties,
1314 ObjectFreeze,
1415 ObjectKeys,
@@ -32,6 +33,7 @@ const {
3233const {
3334 InternalPerformanceEntry,
3435 isPerformanceEntry,
36+ kBufferNext,
3537} = require ( 'internal/perf/performance_entry' ) ;
3638
3739const {
@@ -83,12 +85,17 @@ const kSupportedEntryTypes = ObjectFreeze([
8385 'mark' ,
8486 'measure' ,
8587] ) ;
86- const kTimelineEntryTypes = ObjectFreeze ( [
87- 'mark' ,
88- 'measure' ,
89- ] ) ;
9088
91- const kPerformanceEntryBuffer = new SafeMap ( ) ;
89+ // Performance timeline entry Buffers
90+ const markEntryBuffer = createBuffer ( ) ;
91+ const measureEntryBuffer = createBuffer ( ) ;
92+ const kMaxPerformanceEntryBuffers = 1e6 ;
93+ const kClearPerformanceEntryBuffers = ObjectFreeze ( {
94+ 'mark' : 'performance.clearMarks' ,
95+ 'measure' : 'performance.clearMeasures' ,
96+ } ) ;
97+ const kWarnedEntryTypes = new SafeMap ( ) ;
98+
9299const kObservers = new SafeSet ( ) ;
93100const kPending = new SafeSet ( ) ;
94101let isPending = false ;
@@ -238,7 +245,7 @@ class PerformanceObserver {
238245 maybeIncrementObserverCount ( type ) ;
239246 if ( buffered ) {
240247 const entries = filterBufferMapByNameAndType ( undefined , type ) ;
241- this [ kBuffer ] . push ( ...entries ) ;
248+ ArrayPrototypePush ( this [ kBuffer ] , ...entries ) ;
242249 kPending . add ( this ) ;
243250 if ( kPending . size )
244251 queuePending ( ) ;
@@ -307,50 +314,97 @@ function enqueue(entry) {
307314 }
308315
309316 const entryType = entry . entryType ;
310- if ( ! kTimelineEntryTypes . includes ( entryType ) ) {
317+ let buffer ;
318+ if ( entryType === 'mark' ) {
319+ buffer = markEntryBuffer ;
320+ } else if ( entryType === 'measure' ) {
321+ buffer = measureEntryBuffer ;
322+ } else {
311323 return ;
312324 }
313- const buffer = getEntryBuffer ( entryType ) ;
314- buffer . push ( entry ) ;
325+
326+ const count = buffer . count + 1 ;
327+ buffer . count = count ;
328+ if ( count === 1 ) {
329+ buffer . head = entry ;
330+ buffer . tail = entry ;
331+ return ;
332+ }
333+ buffer . tail [ kBufferNext ] = entry ;
334+ buffer . tail = entry ;
335+
336+ if ( count > kMaxPerformanceEntryBuffers &&
337+ ! kWarnedEntryTypes . has ( entryType ) ) {
338+ kWarnedEntryTypes . set ( entryType , true ) ;
339+ // No error code for this since it is a Warning
340+ // eslint-disable-next-line no-restricted-syntax
341+ const w = new Error ( 'Possible perf_hooks memory leak detected. ' +
342+ `${ count } ${ entryType } entries added to the global ` +
343+ 'performance entry buffer. Use ' +
344+ `${ kClearPerformanceEntryBuffers [ entryType ] } to ` +
345+ 'clear the buffer.' ) ;
346+ w . name = 'MaxPerformanceEntryBufferExceededWarning' ;
347+ w . entryType = entryType ;
348+ w . count = count ;
349+ process . emitWarning ( w ) ;
350+ }
315351}
316352
317353function clearEntriesFromBuffer ( type , name ) {
354+ let buffer ;
355+ if ( type === 'mark' ) {
356+ buffer = markEntryBuffer ;
357+ } else if ( type === 'measure' ) {
358+ buffer = measureEntryBuffer ;
359+ } else {
360+ return ;
361+ }
318362 if ( name === undefined ) {
319- kPerformanceEntryBuffer . delete ( type ) ;
363+ resetBuffer ( buffer ) ;
320364 return ;
321365 }
322- let buffer = getEntryBuffer ( type ) ;
323- buffer = ArrayPrototypeFilter (
324- buffer ,
325- ( entry ) => entry . name !== name ) ;
326- kPerformanceEntryBuffer . set ( type , buffer ) ;
327- }
328366
329- function getEntryBuffer ( type ) {
330- let buffer = kPerformanceEntryBuffer . get ( type ) ;
331- if ( buffer === undefined ) {
332- buffer = [ ] ;
333- kPerformanceEntryBuffer . set ( type , buffer ) ;
367+ let head = null ;
368+ let tail = null ;
369+ for ( let entry = buffer . head ; entry !== null ; entry = entry [ kBufferNext ] ) {
370+ if ( entry . name !== name ) {
371+ head = head ?? entry ;
372+ tail = entry ;
373+ continue ;
374+ }
375+ if ( tail === null ) {
376+ continue ;
377+ }
378+ tail [ kBufferNext ] = entry [ kBufferNext ] ;
334379 }
335- return buffer ;
380+ buffer . head = head ;
381+ buffer . tail = tail ;
336382}
337383
338384function filterBufferMapByNameAndType ( name , type ) {
339385 let bufferList ;
340- if ( type !== undefined ) {
341- bufferList = kPerformanceEntryBuffer . get ( type ) ?? [ ] ;
386+ if ( type === 'mark' ) {
387+ bufferList = [ markEntryBuffer ] ;
388+ } else if ( type === 'measure' ) {
389+ bufferList = [ measureEntryBuffer ] ;
390+ } else if ( type !== undefined ) {
391+ // Unrecognized type;
392+ return [ ] ;
342393 } else {
343- bufferList = ArrayFrom ( kPerformanceEntryBuffer . values ( ) ) ;
394+ bufferList = [ markEntryBuffer , measureEntryBuffer ] ;
344395 }
345396 return ArrayPrototypeFlatMap ( bufferList ,
346397 ( buffer ) => filterBufferByName ( buffer , name ) ) ;
347398}
348399
349400function filterBufferByName ( buffer , name ) {
350- if ( name === undefined ) {
351- return buffer ;
401+ const arr = [ ] ;
402+ for ( let entry = buffer . head ; entry !== null ; entry = entry [ kBufferNext ] ) {
403+ if ( name === undefined || entry . name === name ) {
404+ ArrayPrototypePush ( arr , entry ) ;
405+ }
352406 }
353- return ArrayPrototypeFilter ( buffer , ( it ) => it . name === name ) ;
407+ return arr ;
354408}
355409
356410function observerCallback ( name , type , startTime , duration , details ) {
@@ -398,6 +452,20 @@ function hasObserver(type) {
398452 return observerCounts [ observerType ] > 0 ;
399453}
400454
455+ function createBuffer ( ) {
456+ return {
457+ head : null ,
458+ tail : null ,
459+ count : 0 ,
460+ } ;
461+ }
462+
463+ function resetBuffer ( buffer ) {
464+ buffer . head = null ;
465+ buffer . tail = null ;
466+ buffer . count = 0 ;
467+ }
468+
401469module . exports = {
402470 PerformanceObserver,
403471 enqueue,
0 commit comments