Skip to content

Commit b1be664

Browse files
mknyszekheschi
authored andcommitted
[release-branch.go1.17] runtime: store consistent total allocation stats as uint64
Currently the consistent total allocation stats are managed as uintptrs, which means they can easily overflow on 32-bit systems. Fix this by storing these stats as uint64s. This will cause some minor performance degradation on 32-bit systems, but there really isn't a way around this, and it affects the correctness of the metrics we export. For #52680. Fixes #52688. Change-Id: I8b1926116e899ae9f03d58e0320bcb9264945b3e Reviewed-on: https://go-review.googlesource.com/c/go/+/411496 Run-TryBot: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Michael Pratt <mpratt@google.com>
1 parent 77cc1c0 commit b1be664

File tree

4 files changed

+39
-37
lines changed

4 files changed

+39
-37
lines changed

src/runtime/mcache.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,11 @@ func (c *mcache) refill(spc spanClass) {
175175
// Assume all objects from this span will be allocated in the
176176
// mcache. If it gets uncached, we'll adjust this.
177177
stats := memstats.heapStats.acquire()
178-
atomic.Xadduintptr(&stats.smallAllocCount[spc.sizeclass()], uintptr(s.nelems)-uintptr(s.allocCount))
178+
atomic.Xadd64(&stats.smallAllocCount[spc.sizeclass()], int64(s.nelems)-int64(s.allocCount))
179179

180180
// Flush tinyAllocs.
181181
if spc == tinySpanClass {
182-
atomic.Xadduintptr(&stats.tinyAllocCount, c.tinyAllocs)
182+
atomic.Xadd64(&stats.tinyAllocCount, int64(c.tinyAllocs))
183183
c.tinyAllocs = 0
184184
}
185185
memstats.heapStats.release()
@@ -229,8 +229,8 @@ func (c *mcache) allocLarge(size uintptr, needzero bool, noscan bool) (*mspan, b
229229
throw("out of memory")
230230
}
231231
stats := memstats.heapStats.acquire()
232-
atomic.Xadduintptr(&stats.largeAlloc, npages*pageSize)
233-
atomic.Xadduintptr(&stats.largeAllocCount, 1)
232+
atomic.Xadd64(&stats.largeAlloc, int64(npages*pageSize))
233+
atomic.Xadd64(&stats.largeAllocCount, 1)
234234
memstats.heapStats.release()
235235

236236
// Update gcController.heapLive and revise pacing if needed.
@@ -261,9 +261,9 @@ func (c *mcache) releaseAll() {
261261
s := c.alloc[i]
262262
if s != &emptymspan {
263263
// Adjust nsmallalloc in case the span wasn't fully allocated.
264-
n := uintptr(s.nelems) - uintptr(s.allocCount)
264+
n := int64(s.nelems) - int64(s.allocCount)
265265
stats := memstats.heapStats.acquire()
266-
atomic.Xadduintptr(&stats.smallAllocCount[spanClass(i).sizeclass()], -n)
266+
atomic.Xadd64(&stats.smallAllocCount[spanClass(i).sizeclass()], -n)
267267
memstats.heapStats.release()
268268
if s.sweepgen != sg+1 {
269269
// refill conservatively counted unallocated slots in gcController.heapLive.
@@ -273,7 +273,7 @@ func (c *mcache) releaseAll() {
273273
// gcController.heapLive was totally recomputed since
274274
// caching this span, so we don't do this for
275275
// stale spans.
276-
atomic.Xadd64(&gcController.heapLive, -int64(n)*int64(s.elemsize))
276+
atomic.Xadd64(&gcController.heapLive, -n*int64(s.elemsize))
277277
}
278278
// Release the span to the mcentral.
279279
mheap_.central[i].mcentral.uncacheSpan(s)
@@ -286,7 +286,7 @@ func (c *mcache) releaseAll() {
286286

287287
// Flush tinyAllocs.
288288
stats := memstats.heapStats.acquire()
289-
atomic.Xadduintptr(&stats.tinyAllocCount, c.tinyAllocs)
289+
atomic.Xadd64(&stats.tinyAllocCount, int64(c.tinyAllocs))
290290
c.tinyAllocs = 0
291291
memstats.heapStats.release()
292292

src/runtime/metrics.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -388,13 +388,13 @@ func (a *heapStatsAggregate) compute() {
388388
memstats.heapStats.read(&a.heapStatsDelta)
389389

390390
// Calculate derived stats.
391-
a.totalAllocs = uint64(a.largeAllocCount)
392-
a.totalFrees = uint64(a.largeFreeCount)
393-
a.totalAllocated = uint64(a.largeAlloc)
394-
a.totalFreed = uint64(a.largeFree)
391+
a.totalAllocs = a.largeAllocCount
392+
a.totalFrees = a.largeFreeCount
393+
a.totalAllocated = a.largeAlloc
394+
a.totalFreed = a.largeFree
395395
for i := range a.smallAllocCount {
396-
na := uint64(a.smallAllocCount[i])
397-
nf := uint64(a.smallFreeCount[i])
396+
na := a.smallAllocCount[i]
397+
nf := a.smallFreeCount[i]
398398
a.totalAllocs += na
399399
a.totalFrees += nf
400400
a.totalAllocated += na * uint64(class_to_size[i])

src/runtime/mgcsweep.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
581581
// free slots zeroed.
582582
s.needzero = 1
583583
stats := memstats.heapStats.acquire()
584-
atomic.Xadduintptr(&stats.smallFreeCount[spc.sizeclass()], uintptr(nfreed))
584+
atomic.Xadd64(&stats.smallFreeCount[spc.sizeclass()], int64(nfreed))
585585
memstats.heapStats.release()
586586
}
587587
if !preserve {
@@ -628,8 +628,8 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
628628
mheap_.freeSpan(s)
629629
}
630630
stats := memstats.heapStats.acquire()
631-
atomic.Xadduintptr(&stats.largeFreeCount, 1)
632-
atomic.Xadduintptr(&stats.largeFree, size)
631+
atomic.Xadd64(&stats.largeFreeCount, 1)
632+
atomic.Xadd64(&stats.largeFree, int64(size))
633633
memstats.heapStats.release()
634634
return true
635635
}

src/runtime/mstats.go

+22-20
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package runtime
88

99
import (
1010
"runtime/internal/atomic"
11-
"runtime/internal/sys"
1211
"unsafe"
1312
)
1413

@@ -565,29 +564,29 @@ func updatememstats() {
565564
memstats.heapStats.unsafeRead(&consStats)
566565

567566
// Collect large allocation stats.
568-
totalAlloc := uint64(consStats.largeAlloc)
569-
memstats.nmalloc += uint64(consStats.largeAllocCount)
570-
totalFree := uint64(consStats.largeFree)
571-
memstats.nfree += uint64(consStats.largeFreeCount)
567+
totalAlloc := consStats.largeAlloc
568+
memstats.nmalloc += consStats.largeAllocCount
569+
totalFree := consStats.largeFree
570+
memstats.nfree += consStats.largeFreeCount
572571

573572
// Collect per-sizeclass stats.
574573
for i := 0; i < _NumSizeClasses; i++ {
575574
// Malloc stats.
576-
a := uint64(consStats.smallAllocCount[i])
575+
a := consStats.smallAllocCount[i]
577576
totalAlloc += a * uint64(class_to_size[i])
578577
memstats.nmalloc += a
579578
memstats.by_size[i].nmalloc = a
580579

581580
// Free stats.
582-
f := uint64(consStats.smallFreeCount[i])
581+
f := consStats.smallFreeCount[i]
583582
totalFree += f * uint64(class_to_size[i])
584583
memstats.nfree += f
585584
memstats.by_size[i].nfree = f
586585
}
587586

588587
// Account for tiny allocations.
589-
memstats.nfree += uint64(consStats.tinyAllocCount)
590-
memstats.nmalloc += uint64(consStats.tinyAllocCount)
588+
memstats.nfree += consStats.tinyAllocCount
589+
memstats.nmalloc += consStats.tinyAllocCount
591590

592591
// Calculate derived stats.
593592
memstats.total_alloc = totalAlloc
@@ -703,17 +702,20 @@ type heapStatsDelta struct {
703702
inPtrScalarBits int64 // byte delta of memory reserved for unrolled GC prog bits
704703

705704
// Allocator stats.
706-
tinyAllocCount uintptr // number of tiny allocations
707-
largeAlloc uintptr // bytes allocated for large objects
708-
largeAllocCount uintptr // number of large object allocations
709-
smallAllocCount [_NumSizeClasses]uintptr // number of allocs for small objects
710-
largeFree uintptr // bytes freed for large objects (>maxSmallSize)
711-
largeFreeCount uintptr // number of frees for large objects (>maxSmallSize)
712-
smallFreeCount [_NumSizeClasses]uintptr // number of frees for small objects (<=maxSmallSize)
713-
714-
// Add a uint32 to ensure this struct is a multiple of 8 bytes in size.
715-
// Only necessary on 32-bit platforms.
716-
_ [(sys.PtrSize / 4) % 2]uint32
705+
//
706+
// These are all uint64 because they're cumulative, and could quickly wrap
707+
// around otherwise.
708+
tinyAllocCount uint64 // number of tiny allocations
709+
largeAlloc uint64 // bytes allocated for large objects
710+
largeAllocCount uint64 // number of large object allocations
711+
smallAllocCount [_NumSizeClasses]uint64 // number of allocs for small objects
712+
largeFree uint64 // bytes freed for large objects (>maxSmallSize)
713+
largeFreeCount uint64 // number of frees for large objects (>maxSmallSize)
714+
smallFreeCount [_NumSizeClasses]uint64 // number of frees for small objects (<=maxSmallSize)
715+
716+
// NOTE: This struct must be a multiple of 8 bytes in size because it
717+
// is stored in an array. If it's not, atomic accesses to the above
718+
// fields may be unaligned and fail on 32-bit platforms.
717719
}
718720

719721
// merge adds in the deltas from b into a.

0 commit comments

Comments
 (0)