1// run 2 3// Copyright 2015 The Go Authors. All rights reserved. 4// Use of this source code is governed by a BSD-style 5// license that can be found in the LICENSE file. 6 7// Test that tiny allocations with finalizers are correctly profiled. 8// Previously profile special records could have been processed prematurely 9// (while the object is still live). 10 11package main 12 13import ( 14 "runtime" 15 "time" 16 "unsafe" 17) 18 19func main() { 20 runtime.MemProfileRate = 1 21 // Allocate 1M 4-byte objects and set a finalizer for every third object. 22 // Assuming that tiny block size is 16, some objects get finalizers setup 23 // only for middle bytes. The finalizer resurrects that object. 24 // As the result, all allocated memory must stay alive. 25 const ( 26 N = 1 << 20 27 tinyBlockSize = 16 // runtime._TinySize 28 ) 29 hold := make([]*int32, 0, N) 30 for i := 0; i < N; i++ { 31 x := new(int32) 32 if i%3 == 0 { 33 runtime.SetFinalizer(x, func(p *int32) { 34 hold = append(hold, p) 35 }) 36 } 37 } 38 // Finalize as much as possible. 39 // Note: the sleep only increases probability of bug detection, 40 // it cannot lead to false failure. 41 for i := 0; i < 5; i++ { 42 runtime.GC() 43 time.Sleep(10 * time.Millisecond) 44 } 45 // Read memory profile. 46 var prof []runtime.MemProfileRecord 47 for { 48 if n, ok := runtime.MemProfile(prof, false); ok { 49 prof = prof[:n] 50 break 51 } else { 52 prof = make([]runtime.MemProfileRecord, n+10) 53 } 54 } 55 // See how much memory in tiny objects is profiled. 56 var totalBytes int64 57 for _, p := range prof { 58 bytes := p.AllocBytes - p.FreeBytes 59 nobj := p.AllocObjects - p.FreeObjects 60 if nobj == 0 { 61 // There may be a record that has had all of its objects 62 // freed. That's fine. Avoid a divide-by-zero and skip. 63 continue 64 } 65 size := bytes / nobj 66 if size == tinyBlockSize { 67 totalBytes += bytes 68 } 69 } 70 // 2*tinyBlockSize slack is for any boundary effects. 71 if want := N*int64(unsafe.Sizeof(int32(0))) - 2*tinyBlockSize; totalBytes < want { 72 println("got", totalBytes, "want >=", want) 73 panic("some of the tiny objects are not profiled") 74 } 75 // Just to keep hold alive. 76 if len(hold) != 0 && hold[0] == nil { 77 panic("bad") 78 } 79} 80