mirror of
https://github.com/syncthing/syncthing.git
synced 2024-11-16 18:41:59 -07:00
108 lines
2.2 KiB
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))
|
|
}
|
|
}
|