@@ -625,81 +625,69 @@ NATIVE_METRICS_VARIANTS.forEach((nativeMetrics) => {
625625 } )
626626
627627 describe ( 'CPU Usage Calculations' , ( ) => {
628- it ( 'should report CPU percentages within valid ranges' , ( ) => {
629- const startCpuUsage = process . cpuUsage ( )
630- const startTime = Date . now ( )
631- const startPerformanceNow = performance . now ( )
628+ it ( 'should report CPU percentages matching real process usage' , ( ) => {
629+ const outerStartCpuUsage = process . cpuUsage ( )
630+ const outerStartTime = performance . now ( )
631+ clock . tick ( 10000 )
632+ client . gauge . resetHistory ( )
633+ const innerStartTime = performance . now ( )
634+ const innerStartCpuUsage = process . cpuUsage ( )
635+
632636 let iterations = 0
633- let ticks = 0
634- while ( Date . now ( ) - startTime < 100 ) {
635- iterations ++
636- if ( iterations % 1000000 === 0 ) {
637- clock . tick ( 1 )
638- ticks ++
637+ let userCpuUsage = 0
638+ while ( userCpuUsage < 100_000 ) {
639+ if ( ++ iterations % 1_000_000 === 0 ) {
640+ userCpuUsage = process . cpuUsage ( innerStartCpuUsage ) . user
639641 }
640642 }
641- const cpuUsage = process . cpuUsage ( )
642- const cpuUsageStub = sinon . stub ( process , 'cpuUsage' ) . returns ( cpuUsage )
643- const performanceNowStub = sinon . stub ( performance , 'now' ) . returns ( startPerformanceNow + 10000 )
644- clock . tick ( 10000 - ticks )
645- performanceNowStub . restore ( )
646- cpuUsageStub . restore ( )
647643
648- const timeDivisor = 100_000 // Microseconds * 100 for percent
644+ const innerEndCpuUsage = process . cpuUsage ( )
645+ const innerEndTime = performance . now ( )
646+ clock . tick ( 10000 )
647+ const outerEndTime = performance . now ( )
648+ const outerEndCpuUsage = process . cpuUsage ( )
649649
650- const cpuMetrics = new Map ( [ [
651- 'runtime.node.cpu.user' ,
652- Number ( ( ( cpuUsage . user - startCpuUsage . user ) / timeDivisor ) . toFixed ( 2 ) ) ,
653- ] , [
650+ const cpuCalls = client . gauge . getCalls ( ) . filter ( call => call . args [ 0 ] . startsWith ( 'runtime.node.cpu.' ) )
651+ const cpuMetrics = new Map ( cpuCalls . map ( call => [ call . args [ 0 ] , call . args [ 1 ] ] ) )
652+ assert . deepStrictEqual ( [ ...cpuMetrics . keys ( ) ] . sort ( ) , [
654653 'runtime.node.cpu.system' ,
655- Number ( ( ( cpuUsage . system - startCpuUsage . system ) / timeDivisor ) . toFixed ( 2 ) ) ,
656- ] , [
657654 'runtime.node.cpu.total' ,
658- Number ( (
659- ( ( cpuUsage . user - startCpuUsage . user ) + ( cpuUsage . system - startCpuUsage . system ) ) / timeDivisor
660- ) . toFixed ( 2 ) ) ,
661- ] ] )
662-
663- let userPercent = 0
664- let systemPercent = 0
665- let totalPercent = 0
666-
667- for ( const call of client . gauge . getCalls ( ) ) {
668- const metric = call . args [ 0 ]
669- const expected = cpuMetrics . get ( metric )
670- cpuMetrics . delete ( metric )
671- if ( expected !== undefined ) {
672- const stringValue = call . args [ 1 ]
673- assert . match ( stringValue , / ^ \d + ( \. \d { 1 , 2 } ) ? $ / )
674- const number = Number ( stringValue )
675- if ( metric === 'runtime.node.cpu.user' ) {
676- assert (
677- number >= 1 ,
678- `${ metric } sanity check failed (increase CPU load above with more ticks): ${ number } `
679- )
680- userPercent = number
681- }
682- if ( metric === 'runtime.node.cpu.system' ) {
683- assert ( number >= 0 && number <= 5 , `${ metric } sanity check failed: ${ number } ` )
684- systemPercent = number
685- }
686- if ( metric === 'runtime.node.cpu.total' ) {
687- assert (
688- // Subtracting 0.1 for time-window/baseline alignment numbers and due to rounding issues.
689- number >= expected - 0.1 && number <= expected + 1 ,
690- `${ metric } sanity check failed (increase CPU load above with more ticks): ${ number } ${ expected } `
691- )
692- totalPercent = number
693- }
694- const epsilon = os . platform ( ) === 'win32' ? 1.5 : 0.5
695- assert ( number - expected < epsilon , `${ metric } sanity check failed: ${ number } ${ expected } ` )
696- }
655+ 'runtime.node.cpu.user' ,
656+ ] )
657+ assert . strictEqual ( cpuCalls . length , cpuMetrics . size , 'CPU metrics should be reported exactly once' )
658+ for ( const value of cpuMetrics . values ( ) ) {
659+ assert . match ( value , / ^ \d + \. \d { 2 } $ / )
697660 }
698661
699- assert . strictEqual ( cpuMetrics . size , 0 , `All CPU metrics should be matched, missing ${ [ ...cpuMetrics . keys ( ) ] } ` )
700-
662+ const userPercent = Number ( cpuMetrics . get ( 'runtime.node.cpu.user' ) )
663+ const systemPercent = Number ( cpuMetrics . get ( 'runtime.node.cpu.system' ) )
664+ const totalPercent = Number ( cpuMetrics . get ( 'runtime.node.cpu.total' ) )
701665 const totalDiff = Math . abs ( totalPercent - userPercent - systemPercent )
702- assert ( totalDiff <= 0.03 , `Total CPU percentage sanity check failed: ${ totalDiff } > 0.03` )
666+ assert ( totalDiff <= 0.02 , `Total CPU percentage sanity check failed: ${ totalDiff } > 0.02` )
667+
668+ // The collector reads its counters between the outer and inner samples on each side.
669+ const minimumElapsedTime = innerEndTime - innerStartTime
670+ const maximumElapsedTime = outerEndTime - outerStartTime
671+ const minimumUserPercent = ( innerEndCpuUsage . user - innerStartCpuUsage . user ) / ( maximumElapsedTime * 10 )
672+ const maximumUserPercent = ( outerEndCpuUsage . user - outerStartCpuUsage . user ) / ( minimumElapsedTime * 10 )
673+ const minimumSystemPercent = ( innerEndCpuUsage . system - innerStartCpuUsage . system ) / ( maximumElapsedTime * 10 )
674+ const maximumSystemPercent = ( outerEndCpuUsage . system - outerStartCpuUsage . system ) / ( minimumElapsedTime * 10 )
675+ const minimumTotalPercent = minimumUserPercent + minimumSystemPercent
676+ const maximumTotalPercent = maximumUserPercent + maximumSystemPercent
677+
678+ assert (
679+ userPercent >= minimumUserPercent - 0.01 && userPercent <= maximumUserPercent + 0.01 ,
680+ `Expected real user CPU percentage ${ minimumUserPercent } <= ${ userPercent } <= ${ maximumUserPercent } `
681+ )
682+ assert (
683+ systemPercent >= minimumSystemPercent - 0.01 && systemPercent <= maximumSystemPercent + 0.01 ,
684+ `Expected real system CPU percentage ${ minimumSystemPercent } <= ${ systemPercent } <= ${ maximumSystemPercent } `
685+ )
686+
687+ assert (
688+ totalPercent >= minimumTotalPercent - 0.01 && totalPercent <= maximumTotalPercent + 0.01 ,
689+ `Expected real total CPU percentage ${ minimumTotalPercent } <= ${ totalPercent } <= ${ maximumTotalPercent } `
690+ )
703691 } )
704692 } )
705693
0 commit comments