Home | History | Annotate | Download | only in test
      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 
     11 package main
     12 
     13 import (
     14 	"runtime"
     15 	"time"
     16 	"unsafe"
     17 )
     18 
     19 func 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 probility 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