/go-dump

Quick hack for dumping memory stats to stderr for Go

Primary LanguageGoApache License 2.0Apache-2.0

go-dump

Utility for printing memory allocations info using printf. This allows instrumentation of code for memory allocation analysis with very little effort.

Collecting and printing memory stats and profile:

  • MemStats: Uses runtime.ReadMemStats. The numbers shown include "garbage" not yet collected by GC according to Go docs.

  • MemProf: Uses code from pprof package to show stats as would be shown by pprof tool. The pprof itself uses some probabilistic approach. Hopefully this is more accurate than MemStats and do not include "garbage".

  • WriteHeapDump(): Write pprof heap profiles at any point in code.

Whatever the metric, it will be only an approximation, it will NOT be 100% accurate.

Sample usage:

func Example() []int64 {
	// Write pprof heap profile at the start and end of function
	dump.WriteHeapDump(fmt.Sprintf("heap-example-before"))
	defer dump.WriteHeapDump(fmt.Sprintf("heap-example-after"))

	// Take a snapshot at the start of a function
	// Capture and print deltas at the end
	memProf := dump.NewMemProf("example")
	defer memProf.PrintDiff()

	// Similar for memStats
	memStats := dump.NewMemStats("example")
	defer memStats.PrintDiff()

	// allocate memory
	allocateMem := func () []int64 {
		return make([]int64, 100000)
	}

	var result []int64
	for i := 0; i < 1000; i++{
		result = append(result, allocateMem()...)
	}

	return result
}

This will produce results similar to this:

WRITTEN HEAP DUMP TO /Users/philip/thanos/github.com/ppanyukov/go-dump/heap-example-before.pb.gz
MEM STATS DIFF:   	example 	example - AFTER 	-> Delta
	HeapAlloc  : 	219.76K 	963.52M 		-> 963.30M
	HeapObjects: 	203 		408 			-> 205

MEM PROF DIFF:    	example 	example - AFTER 	-> Delta
	InUseBytes  : 	0 		816.39M 		-> 816.39M
	InUseObjects: 	0 		1 			-> 1
	AllocBytes  : 	1.26M 		4.69G 			-> 4.69G
	AllocObjects: 	6 		808 			-> 802

WRITTEN HEAP DUMP TO /Users/philip/thanos/github.com/ppanyukov/go-dump/heap-example-after.pb.gz

The size of 1,000,000 int64 values is 800MB, and can be seen MemProf reports this quite close to this number. The MemStats are slightly off with its 963.30M as this number includes objects not yet collected by GC.

To use pprof tool to see allocations use this command:

go tool pprof \
	-sample_index=inuse_space \
	-edgefraction=0 \
	-functions \
	-http=:8081 \
	-base /Users/philip/thanos/github.com/ppanyukov/go-dump/heap-example-before.pb.gz \
	/Users/philip/thanos/github.com/ppanyukov/go-dump/heap-example-after.pb.gz

This would report figures like so:

Showing nodes accounting for 778.57MB, 100% of 778.57MB total

Also close to 800MB.

Using pprof allows us to look where in code we allocate those bytes, and predictably enough it shows our array allocation:

28            .          .           	var result []int64 
29            .          .           	for i := 0; i < 1000; i++{ 
30     778.57MB   778.57MB           		result = append(result, allocateMem()...) 
31            .          .           	}