syncthing/vendor/github.com/sasha-s/go-deadlock/stacktraces.go

108 lines
2.2 KiB
Go

package deadlock
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"runtime"
"strings"
"sync"
)
func callers(skip int) []uintptr {
s := make([]uintptr, 50) // Most relevant context seem to appear near the top of the stack.
return s[:runtime.Callers(2+skip, s)]
}
func printStack(w io.Writer, stack []uintptr) {
home := os.Getenv("HOME")
usr, err := user.Current()
if err == nil {
home = usr.HomeDir
}
cwd, _ := os.Getwd()
for i, pc := range stack {
f := runtime.FuncForPC(pc)
name := f.Name()
pkg := ""
if pos := strings.LastIndex(name, "/"); pos >= 0 {
name = name[pos+1:]
}
if pos := strings.Index(name, "."); pos >= 0 {
pkg = name[:pos]
name = name[pos+1:]
}
file, line := f.FileLine(pc - 1)
if (pkg == "runtime" && name == "goexit") || (pkg == "testing" && name == "tRunner") {
fmt.Fprintln(w)
return
}
tail := ""
if i == 0 {
tail = " <<<<<" // Make the line performing a lock prominent.
}
// Shorten the file name.
clean := file
if cwd != "" {
cl, err := filepath.Rel(cwd, file)
if err == nil {
clean = cl
}
}
if home != "" {
s2 := strings.Replace(file, home, "~", 1)
if len(clean) > len(s2) {
clean = s2
}
}
fmt.Fprintf(w, "%s:%d %s.%s %s%s\n", clean, line, pkg, name, code(file, line), tail)
}
fmt.Fprintln(w)
}
var fileSources struct {
sync.Mutex
lines map[string][][]byte
}
// Reads souce file lines from disk if not cached already.
func getSourceLines(file string) [][]byte {
fileSources.Lock()
defer fileSources.Unlock()
if fileSources.lines == nil {
fileSources.lines = map[string][][]byte{}
}
if lines, ok := fileSources.lines[file]; ok {
return lines
}
text, _ := ioutil.ReadFile(file)
fileSources.lines[file] = bytes.Split(text, []byte{'\n'})
return fileSources.lines[file]
}
func code(file string, line int) string {
lines := getSourceLines(file)
// lines are 1 based.
if line >= len(lines) || line <= 0 {
return "???"
}
return "{ " + string(bytes.TrimSpace(lines[line-1])) + " }"
}
// Stacktraces for all goroutines.
func stacks() []byte {
buf := make([]byte, 1024*16)
for {
n := runtime.Stack(buf, true)
if n < len(buf) {
return buf[:n]
}
buf = make([]byte, 2*len(buf))
}
}