func (c *cache) load(fileName string) {
	if _, ok := c.parsed[fileName]; ok {
		return
	}
	c.parsed[fileName] = nil
	if !strings.HasSuffix(fileName, ".go") {
		c.files[fileName] = nil
		return
	}
	log.Printf("load(%s)", fileName)
	if _, ok := c.files[fileName]; !ok {
		var err error
		if c.files[fileName], err = ioutil.ReadFile(fileName); err != nil {
			log.Printf("Failed to read %s: %s", fileName, err)
			c.files[fileName] = nil
			return
		}
	}
	fset := token.NewFileSet()
	src := c.files[fileName]
	parsed, err := parser.ParseFile(fset, fileName, src, 0)
	if err != nil {
		log.Printf("Failed to parse %s: %s", fileName, err)
		return
	}
	offsets := []int{0, 0}
	start := 0
	for l := 1; start < len(src); l++ {
		start += bytes.IndexByte(src[start:], '\n') + 1
		offsets = append(offsets, start)
	}
	c.parsed[fileName] = &parsedFile{offsets, parsed}
}
Private stuff.
load loads a source file and parses the AST tree. Failures are ignored.
package stack
import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io/ioutil"
	"log"
	"math"
	"strings"
)
type cache struct {
	files  map[string][]byte
	parsed map[string]*parsedFile
}
func Augment(goroutines []Goroutine) {
	c := &cache{}
	for i := range goroutines {
		c.augmentGoroutine(&goroutines[i])
	}
}
func (c *cache) augmentGoroutine(goroutine *Goroutine) {
	if c.files == nil {
		c.files = map[string][]byte{}
	}
	if c.parsed == nil {
		c.parsed = map[string]*parsedFile{}
	}
	for i := range goroutine.Stack.Calls {
		c.load(goroutine.Stack.Calls[i].LocalSourcePath())
	}
	for i := 0; i < len(goroutine.Stack.Calls)-1; i++ {
		if f := c.getFuncAST(&goroutine.Stack.Calls[i]); f != nil {
			processCall(&goroutine.Stack.Calls[i], f)
		}
	}
}