@@ -361,6 +361,11 @@ func ReadMemStats(m *MemStats) {
361
361
startTheWorld (stw )
362
362
}
363
363
364
+ // doubleCheckReadMemStats controls a double-check mode for ReadMemStats that
365
+ // ensures consistency between the values that ReadMemStats is using and the
366
+ // runtime-internal stats.
367
+ var doubleCheckReadMemStats = false
368
+
364
369
// readmemstats_m populates stats for internal runtime values.
365
370
//
366
371
// The world must be stopped.
@@ -435,56 +440,65 @@ func readmemstats_m(stats *MemStats) {
435
440
436
441
heapGoal := gcController .heapGoal ()
437
442
438
- // The world is stopped, so the consistent stats (after aggregation)
439
- // should be identical to some combination of memstats. In particular:
440
- //
441
- // * memstats.heapInUse == inHeap
442
- // * memstats.heapReleased == released
443
- // * memstats.heapInUse + memstats.heapFree == committed - inStacks - inWorkBufs - inPtrScalarBits
444
- // * memstats.totalAlloc == totalAlloc
445
- // * memstats.totalFree == totalFree
446
- //
447
- // Check if that's actually true.
448
- //
449
- // TODO(mknyszek): Maybe don't throw here. It would be bad if a
450
- // bug in otherwise benign accounting caused the whole application
451
- // to crash.
452
- if gcController .heapInUse .load () != uint64 (consStats .inHeap ) {
453
- print ("runtime: heapInUse=" , gcController .heapInUse .load (), "\n " )
454
- print ("runtime: consistent value=" , consStats .inHeap , "\n " )
455
- throw ("heapInUse and consistent stats are not equal" )
456
- }
457
- if gcController .heapReleased .load () != uint64 (consStats .released ) {
458
- print ("runtime: heapReleased=" , gcController .heapReleased .load (), "\n " )
459
- print ("runtime: consistent value=" , consStats .released , "\n " )
460
- throw ("heapReleased and consistent stats are not equal" )
461
- }
462
- heapRetained := gcController .heapInUse .load () + gcController .heapFree .load ()
463
- consRetained := uint64 (consStats .committed - consStats .inStacks - consStats .inWorkBufs - consStats .inPtrScalarBits )
464
- if heapRetained != consRetained {
465
- print ("runtime: global value=" , heapRetained , "\n " )
466
- print ("runtime: consistent value=" , consRetained , "\n " )
467
- throw ("measures of the retained heap are not equal" )
468
- }
469
- if gcController .totalAlloc .Load () != totalAlloc {
470
- print ("runtime: totalAlloc=" , gcController .totalAlloc .Load (), "\n " )
471
- print ("runtime: consistent value=" , totalAlloc , "\n " )
472
- throw ("totalAlloc and consistent stats are not equal" )
473
- }
474
- if gcController .totalFree .Load () != totalFree {
475
- print ("runtime: totalFree=" , gcController .totalFree .Load (), "\n " )
476
- print ("runtime: consistent value=" , totalFree , "\n " )
477
- throw ("totalFree and consistent stats are not equal" )
478
- }
479
- // Also check that mappedReady lines up with totalMapped - released.
480
- // This isn't really the same type of "make sure consistent stats line up" situation,
481
- // but this is an opportune time to check.
482
- if gcController .mappedReady .Load () != totalMapped - uint64 (consStats .released ) {
483
- print ("runtime: mappedReady=" , gcController .mappedReady .Load (), "\n " )
484
- print ("runtime: totalMapped=" , totalMapped , "\n " )
485
- print ("runtime: released=" , uint64 (consStats .released ), "\n " )
486
- print ("runtime: totalMapped-released=" , totalMapped - uint64 (consStats .released ), "\n " )
487
- throw ("mappedReady and other memstats are not equal" )
443
+ if doubleCheckReadMemStats {
444
+ // Only check this if we're debugging. It would be bad to crash an application
445
+ // just because the debugging stats are wrong. We mostly rely on tests to catch
446
+ // these issues, and we enable the double check mode for tests.
447
+ //
448
+ // The world is stopped, so the consistent stats (after aggregation)
449
+ // should be identical to some combination of memstats. In particular:
450
+ //
451
+ // * memstats.heapInUse == inHeap
452
+ // * memstats.heapReleased == released
453
+ // * memstats.heapInUse + memstats.heapFree == committed - inStacks - inWorkBufs - inPtrScalarBits
454
+ // * memstats.totalAlloc == totalAlloc
455
+ // * memstats.totalFree == totalFree
456
+ //
457
+ // Check if that's actually true.
458
+ //
459
+ // Prevent sysmon and the tracer from skewing the stats since they can
460
+ // act without synchronizing with a STW. See #64401.
461
+ lock (& sched .sysmonlock )
462
+ lock (& trace .lock )
463
+ if gcController .heapInUse .load () != uint64 (consStats .inHeap ) {
464
+ print ("runtime: heapInUse=" , gcController .heapInUse .load (), "\n " )
465
+ print ("runtime: consistent value=" , consStats .inHeap , "\n " )
466
+ throw ("heapInUse and consistent stats are not equal" )
467
+ }
468
+ if gcController .heapReleased .load () != uint64 (consStats .released ) {
469
+ print ("runtime: heapReleased=" , gcController .heapReleased .load (), "\n " )
470
+ print ("runtime: consistent value=" , consStats .released , "\n " )
471
+ throw ("heapReleased and consistent stats are not equal" )
472
+ }
473
+ heapRetained := gcController .heapInUse .load () + gcController .heapFree .load ()
474
+ consRetained := uint64 (consStats .committed - consStats .inStacks - consStats .inWorkBufs - consStats .inPtrScalarBits )
475
+ if heapRetained != consRetained {
476
+ print ("runtime: global value=" , heapRetained , "\n " )
477
+ print ("runtime: consistent value=" , consRetained , "\n " )
478
+ throw ("measures of the retained heap are not equal" )
479
+ }
480
+ if gcController .totalAlloc .Load () != totalAlloc {
481
+ print ("runtime: totalAlloc=" , gcController .totalAlloc .Load (), "\n " )
482
+ print ("runtime: consistent value=" , totalAlloc , "\n " )
483
+ throw ("totalAlloc and consistent stats are not equal" )
484
+ }
485
+ if gcController .totalFree .Load () != totalFree {
486
+ print ("runtime: totalFree=" , gcController .totalFree .Load (), "\n " )
487
+ print ("runtime: consistent value=" , totalFree , "\n " )
488
+ throw ("totalFree and consistent stats are not equal" )
489
+ }
490
+ // Also check that mappedReady lines up with totalMapped - released.
491
+ // This isn't really the same type of "make sure consistent stats line up" situation,
492
+ // but this is an opportune time to check.
493
+ if gcController .mappedReady .Load () != totalMapped - uint64 (consStats .released ) {
494
+ print ("runtime: mappedReady=" , gcController .mappedReady .Load (), "\n " )
495
+ print ("runtime: totalMapped=" , totalMapped , "\n " )
496
+ print ("runtime: released=" , uint64 (consStats .released ), "\n " )
497
+ print ("runtime: totalMapped-released=" , totalMapped - uint64 (consStats .released ), "\n " )
498
+ throw ("mappedReady and other memstats are not equal" )
499
+ }
500
+ unlock (& trace .lock )
501
+ unlock (& sched .sysmonlock )
488
502
}
489
503
490
504
// We've calculated all the values we need. Now, populate stats.
0 commit comments