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		size := bytes / nobj
61		if size == tinyBlockSize {
62			totalBytes += bytes
63		}
64	}
65	// 2*tinyBlockSize slack is for any boundary effects.
66	if want := N*int64(unsafe.Sizeof(int32(0))) - 2*tinyBlockSize; totalBytes < want {
67		println("got", totalBytes, "want >=", want)
68		panic("some of the tiny objects are not profiled")
69	}
70	// Just to keep hold alive.
71	if len(hold) != 0 && hold[0] == nil {
72		panic("bad")
73	}
74}
75