diff options
Diffstat (limited to 'src/runtime/mstats.go')
-rw-r--r-- | src/runtime/mstats.go | 114 |
1 files changed, 64 insertions, 50 deletions
diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go index 3a5273f361..8424946e77 100644 --- a/src/runtime/mstats.go +++ b/src/runtime/mstats.go @@ -356,6 +356,11 @@ func ReadMemStats(m *MemStats) { startTheWorld() } +// doubleCheckReadMemStats controls a double-check mode for ReadMemStats that +// ensures consistency between the values that ReadMemStats is using and the +// runtime-internal stats. +var doubleCheckReadMemStats = false + // readmemstats_m populates stats for internal runtime values. // // The world must be stopped. @@ -430,56 +435,65 @@ func readmemstats_m(stats *MemStats) { heapGoal := gcController.heapGoal() - // The world is stopped, so the consistent stats (after aggregation) - // should be identical to some combination of memstats. In particular: - // - // * memstats.heapInUse == inHeap - // * memstats.heapReleased == released - // * memstats.heapInUse + memstats.heapFree == committed - inStacks - inWorkBufs - inPtrScalarBits - // * memstats.totalAlloc == totalAlloc - // * memstats.totalFree == totalFree - // - // Check if that's actually true. - // - // TODO(mknyszek): Maybe don't throw here. It would be bad if a - // bug in otherwise benign accounting caused the whole application - // to crash. - if gcController.heapInUse.load() != uint64(consStats.inHeap) { - print("runtime: heapInUse=", gcController.heapInUse.load(), "\n") - print("runtime: consistent value=", consStats.inHeap, "\n") - throw("heapInUse and consistent stats are not equal") - } - if gcController.heapReleased.load() != uint64(consStats.released) { - print("runtime: heapReleased=", gcController.heapReleased.load(), "\n") - print("runtime: consistent value=", consStats.released, "\n") - throw("heapReleased and consistent stats are not equal") - } - heapRetained := gcController.heapInUse.load() + gcController.heapFree.load() - consRetained := uint64(consStats.committed - consStats.inStacks - consStats.inWorkBufs - consStats.inPtrScalarBits) - if heapRetained != consRetained { - print("runtime: global value=", heapRetained, "\n") - print("runtime: consistent value=", consRetained, "\n") - throw("measures of the retained heap are not equal") - } - if gcController.totalAlloc.Load() != totalAlloc { - print("runtime: totalAlloc=", gcController.totalAlloc.Load(), "\n") - print("runtime: consistent value=", totalAlloc, "\n") - throw("totalAlloc and consistent stats are not equal") - } - if gcController.totalFree.Load() != totalFree { - print("runtime: totalFree=", gcController.totalFree.Load(), "\n") - print("runtime: consistent value=", totalFree, "\n") - throw("totalFree and consistent stats are not equal") - } - // Also check that mappedReady lines up with totalMapped - released. - // This isn't really the same type of "make sure consistent stats line up" situation, - // but this is an opportune time to check. - if gcController.mappedReady.Load() != totalMapped-uint64(consStats.released) { - print("runtime: mappedReady=", gcController.mappedReady.Load(), "\n") - print("runtime: totalMapped=", totalMapped, "\n") - print("runtime: released=", uint64(consStats.released), "\n") - print("runtime: totalMapped-released=", totalMapped-uint64(consStats.released), "\n") - throw("mappedReady and other memstats are not equal") + if doubleCheckReadMemStats { + // Only check this if we're debugging. It would be bad to crash an application + // just because the debugging stats are wrong. We mostly rely on tests to catch + // these issues, and we enable the double check mode for tests. + // + // The world is stopped, so the consistent stats (after aggregation) + // should be identical to some combination of memstats. In particular: + // + // * memstats.heapInUse == inHeap + // * memstats.heapReleased == released + // * memstats.heapInUse + memstats.heapFree == committed - inStacks - inWorkBufs - inPtrScalarBits + // * memstats.totalAlloc == totalAlloc + // * memstats.totalFree == totalFree + // + // Check if that's actually true. + // + // Prevent sysmon and the tracer from skewing the stats since they can + // act without synchronizing with a STW. See #64401. + lock(&sched.sysmonlock) + lock(&trace.lock) + if gcController.heapInUse.load() != uint64(consStats.inHeap) { + print("runtime: heapInUse=", gcController.heapInUse.load(), "\n") + print("runtime: consistent value=", consStats.inHeap, "\n") + throw("heapInUse and consistent stats are not equal") + } + if gcController.heapReleased.load() != uint64(consStats.released) { + print("runtime: heapReleased=", gcController.heapReleased.load(), "\n") + print("runtime: consistent value=", consStats.released, "\n") + throw("heapReleased and consistent stats are not equal") + } + heapRetained := gcController.heapInUse.load() + gcController.heapFree.load() + consRetained := uint64(consStats.committed - consStats.inStacks - consStats.inWorkBufs - consStats.inPtrScalarBits) + if heapRetained != consRetained { + print("runtime: global value=", heapRetained, "\n") + print("runtime: consistent value=", consRetained, "\n") + throw("measures of the retained heap are not equal") + } + if gcController.totalAlloc.Load() != totalAlloc { + print("runtime: totalAlloc=", gcController.totalAlloc.Load(), "\n") + print("runtime: consistent value=", totalAlloc, "\n") + throw("totalAlloc and consistent stats are not equal") + } + if gcController.totalFree.Load() != totalFree { + print("runtime: totalFree=", gcController.totalFree.Load(), "\n") + print("runtime: consistent value=", totalFree, "\n") + throw("totalFree and consistent stats are not equal") + } + // Also check that mappedReady lines up with totalMapped - released. + // This isn't really the same type of "make sure consistent stats line up" situation, + // but this is an opportune time to check. + if gcController.mappedReady.Load() != totalMapped-uint64(consStats.released) { + print("runtime: mappedReady=", gcController.mappedReady.Load(), "\n") + print("runtime: totalMapped=", totalMapped, "\n") + print("runtime: released=", uint64(consStats.released), "\n") + print("runtime: totalMapped-released=", totalMapped-uint64(consStats.released), "\n") + throw("mappedReady and other memstats are not equal") + } + unlock(&trace.lock) + unlock(&sched.sysmonlock) } // We've calculated all the values we need. Now, populate stats. |