Update: Now covers - as an alternative - to do this in an even easier way, using Dave Cheney's excellent profile library.
In trying to get my head around the code of the very interesting GoFLow library, (for flow-based programming in Go), and the accompanying flow-based bioinformatics library I started hacking on, I needed to get some kind of visualization (like a call graph) ... something like this:
(And in the end, that is what I got ... read on ... ) :)
I then found out about the go tool pprof command, for which the Go team published a blog post on here.
Being a Go newbie, I must admit I had quite a hard time deciphering the blog post though. Maybe it was just a psychological barrier because of all the technological anechdotes, that made it look harder than it actually was. Anyhow, it didn't help that "go run pprof" didn't produce any output if I didn't run processing on a large enough file that it would have time to collect data.
Anyways, with this in mind I wanted to make a slightly easier-to-follow instruction for newbies like me, on how to use "go tool pprof" for profiling and producing call graphs ... but then after I published this post, Dave Cheney pinged me about his excellent profile package, which makes the process even easier, so I went away and updated the blog post to include how to do it both with the profile package, AND with the pprof library itself! :)
Ok, so enough blather, let's get started:
If you have your GOROOT and GOPATH environment variables correctly setup, you should be able to install it with this simple command:
go get github.com/davecheney/profile
... otherwise you'll have to install it manually from http://github.com/davecheney/profile
import ( ... your other imports ... "github.com/davecheney/profile" )
defer profile.Start(profile.CPUProfile).Stop()
... the result should be something like:
func main() { defer profile.Start(profile.CPUProfile).Stop() // ... your main code here ... }
Something like:
go build [your program].go
./[your program]
When running the program in the previous step, you will have seen some output like this:
2013/08/08 16:45:58 profile: cpu profiling enabled, /tmp/profile882806532/cpu.pprof
Copy this file to where you are standing right now, with:
cp /tmp/profile[some number]/cpu.pprof .
Read on from step 4, for the rest of the steps ...
import ( ... your other imports ... "flag" "fmt" "runtime/pprof" )
// Profiling stuff ... from http://blog.golang.org/profiling-go-programs var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
flag.Parse() if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { fmt.Println("Error: ", err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() }
... the result should be something like:
// Profiling stuff ... from http://blog.golang.org/profiling-go-programs var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") func main() { flag.Parse() if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { fmt.Println("Error: ", err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } // ... your main code here ... }
This will add a command line flag "-cpuprofile", which you can later use to specify a filename where to write the profiling data.
Something like:
go build [your program].go
./[your program] -cpuprofile=cpu.pprof
go tool pprof --pdf [my program] cpu.pprof > callgraph.pdf
Let's see what that looks like:
Not too bad, no? (Find the PDF version below as well)
Some other output you might want to do:
go tool pprof --text [my program] cpu.pprof > report.txt
go tool pprof 2>&1|less
(Isn't there a nicer way to get a paginated help screen?)
Now, this might have been an easier start if you are a newbie, but then Dave Cheney's post on profile, and the Go team blog post on runtime/pprof contains info on more advanced use of the pprof tool, so be sure to go back and study it:
Attachment | Size |
---|---|
basecompl_blow_callgraph_201308081408.pdf | 17.57 KB |