debug: 死锁监控+DebugMutex+ChSend+GoStart 调试工具

This commit is contained in:
2026-06-28 13:01:54 +08:00
parent 537e4ed550
commit dc410c1cff
2 changed files with 123 additions and 0 deletions
@@ -0,0 +1,98 @@
package crashlog
import (
"fmt"
"log"
"runtime"
"strings"
"sync"
"time"
)
// ── Mutex with debug tracking ──
// DebugMutex wraps a sync.Mutex with lock-holder tracking.
type DebugMutex struct {
mu sync.Mutex
holder string
lockedAt time.Time
}
// Lock acquires the mutex with caller tracking.
func (m *DebugMutex) Lock() {
m.mu.Lock()
_, file, line, _ := runtime.Caller(1)
if idx := strings.LastIndex(file, "Cyrene/"); idx >= 0 {
file = file[idx+len("Cyrene/"):]
}
m.holder = fmt.Sprintf("%s:%d", file, line)
m.lockedAt = time.Now()
}
// Unlock releases the mutex.
func (m *DebugMutex) Unlock() {
m.holder = ""
m.mu.Unlock()
}
// TryLock attempts to acquire the lock with a timeout. Returns true if acquired.
func (m *DebugMutex) TryLock(timeout time.Duration) bool {
done := make(chan struct{})
go func() {
m.mu.Lock()
m.mu.Unlock()
close(done)
}()
select {
case <-done:
return true
case <-time.After(timeout):
return false
}
}
// Holder returns who holds the lock.
func (m *DebugMutex) Holder() (string, time.Time) {
return m.holder, m.lockedAt
}
// ── Channel debug helpers ──
// ChSend sends to a channel with a timeout and logs if it blocks.
func ChSend[T any](name string, ch chan<- T, val T, timeout time.Duration) bool {
select {
case ch <- val:
return true
case <-time.After(timeout):
log.Printf("[debug] ChSend timeout: %s after %v", name, timeout)
return false
}
}
// ── Goroutine lifecycle ──
// GoStart logs goroutine start and wraps fn with panic recovery.
func GoStart(name string, fn func()) {
log.Printf("[debug] goroutine START: %s", name)
go func() {
defer func() {
if r := recover(); r != nil {
stack := make([]byte, 4096)
n := runtime.Stack(stack, false)
log.Printf("[debug] goroutine PANIC: %s panic=%v\n%s", name, r, stack[:n])
}
log.Printf("[debug] goroutine EXIT: %s", name)
}()
fn()
}()
}
// ── Timing ──
// Since logs if a duration exceeds a threshold.
func Since(name string, start time.Time, threshold time.Duration) {
elapsed := time.Since(start)
if elapsed > threshold {
log.Printf("[debug] SLOW: %s took %v (threshold=%v)", name, elapsed.Round(time.Millisecond), threshold)
}
}