jesseduffield.lazygit/vendor/github.com/sasha-s/go-deadlock/stacktraces.go
dependabot[bot] 5928e851dd
Bump github.com/sasha-s/go-deadlock from 0.3.6 to 0.3.9
Bumps [github.com/sasha-s/go-deadlock](https://github.com/sasha-s/go-deadlock) from 0.3.6 to 0.3.9.
- [Release notes](https://github.com/sasha-s/go-deadlock/releases)
- [Commits](https://github.com/sasha-s/go-deadlock/compare/v0.3.6...v0.3.9)

---
updated-dependencies:
- dependency-name: github.com/sasha-s/go-deadlock
  dependency-version: 0.3.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-01 09:24:57 +00:00

137 lines
3.1 KiB
Go

package deadlock
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"runtime"
"strings"
"sync"
)
const stackBufSize = 50
var stackBufPool = sync.Pool{
New: func() interface{} {
return new([stackBufSize]uintptr)
},
}
// callers returns a stack trace backed by a pooled buffer. The caller must
// eventually return buf via releaseStackBuf — typically through the
// postLock/postUnlock path which stores it in stackGID.buf.
func callers(skip int) ([]uintptr, *[stackBufSize]uintptr) {
buf := stackBufPool.Get().(*[stackBufSize]uintptr)
n := runtime.Callers(2+skip, buf[:])
return buf[:n], buf
}
// releaseStackBuf returns a pooled stack buffer obtained from callers(). Safe to
// call with nil (e.g. when the buffer was already handed off via stackGID.buf).
func releaseStackBuf(buf *[stackBufSize]uintptr) {
if buf != nil {
stackBufPool.Put(buf)
}
}
// copyStack creates an independent copy of a stack trace. Required when storing
// stacks in long-lived structures (e.g. l.order) because the originals are backed
// by pooled buffers that will be recycled in postUnlock.
func copyStack(s []uintptr) []uintptr {
c := make([]uintptr, len(s))
copy(c, s)
return c
}
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)
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-1, 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)
line -= 2
if line >= len(lines) || line < 0 {
return "???"
}
return "{ " + string(bytes.TrimSpace(lines[line])) + " }"
}
// 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))
}
}