Source file test/finprofiled.go

     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 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  

View as plain text