148 lignes
3,3 Kio
Go
148 lignes
3,3 Kio
Go
|
// Package sync records the stack when locks are taken, and when locks are
|
||
|
// blocked on and exports them as pprof profiles "lockHolders" and
|
||
|
// "lockBlockers". if "net/http/pprof" is imported, you can view them at
|
||
|
// /debug/pprof/ on the default HTTP muxer.
|
||
|
//
|
||
|
// The API mirrors that of stdlib "sync". The package can be imported in place
|
||
|
// of "sync", and is enabled by setting the envvar PPROF_SYNC non-empty.
|
||
|
//
|
||
|
// Note that currently RWMutex is treated like a Mutex when the package is
|
||
|
// enabled.
|
||
|
package sync
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"runtime"
|
||
|
"runtime/pprof"
|
||
|
"sort"
|
||
|
"sync"
|
||
|
"text/tabwriter"
|
||
|
"time"
|
||
|
|
||
|
"github.com/anacrolix/missinggo"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// Protects initialization and enabling of the package.
|
||
|
enableMu sync.Mutex
|
||
|
// Whether any of this package is to be active.
|
||
|
enabled = false
|
||
|
// Current lock holders.
|
||
|
lockHolders *pprof.Profile
|
||
|
// Those blocked on acquiring a lock.
|
||
|
lockBlockers *pprof.Profile
|
||
|
|
||
|
// Protects lockTimes.
|
||
|
lockTimesMu sync.Mutex
|
||
|
// The longest time the lock is held for any unique stack.
|
||
|
lockTimes map[[32]uintptr]time.Duration
|
||
|
)
|
||
|
|
||
|
type lockTimesSorter struct {
|
||
|
entries []lockTime
|
||
|
}
|
||
|
|
||
|
func (me *lockTimesSorter) Len() int { return len(me.entries) }
|
||
|
func (me *lockTimesSorter) Less(i, j int) bool {
|
||
|
return me.entries[i].held < me.entries[j].held
|
||
|
}
|
||
|
func (me *lockTimesSorter) Swap(i, j int) {
|
||
|
me.entries[i], me.entries[j] = me.entries[j], me.entries[i]
|
||
|
}
|
||
|
|
||
|
type lockTime struct {
|
||
|
stack [32]uintptr
|
||
|
held time.Duration
|
||
|
}
|
||
|
|
||
|
func sortedLockTimes() []lockTime {
|
||
|
var lts lockTimesSorter
|
||
|
lockTimesMu.Lock()
|
||
|
for stack, held := range lockTimes {
|
||
|
lts.entries = append(lts.entries, lockTime{stack, held})
|
||
|
}
|
||
|
lockTimesMu.Unlock()
|
||
|
sort.Sort(sort.Reverse(<s))
|
||
|
return lts.entries
|
||
|
}
|
||
|
|
||
|
func PrintLockTimes(w io.Writer) {
|
||
|
lockTimes := sortedLockTimes()
|
||
|
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
||
|
defer tw.Flush()
|
||
|
w = tw
|
||
|
for _, elem := range lockTimes {
|
||
|
fmt.Fprintf(w, "%s\n", elem.held)
|
||
|
missinggo.WriteStack(w, elem.stack[:])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func Enable() {
|
||
|
enableMu.Lock()
|
||
|
defer enableMu.Unlock()
|
||
|
if enabled {
|
||
|
return
|
||
|
}
|
||
|
lockTimes = make(map[[32]uintptr]time.Duration)
|
||
|
lockHolders = pprof.NewProfile("lockHolders")
|
||
|
lockBlockers = pprof.NewProfile("lockBlockers")
|
||
|
http.DefaultServeMux.HandleFunc("/debug/lockTimes", func(w http.ResponseWriter, r *http.Request) {
|
||
|
PrintLockTimes(w)
|
||
|
})
|
||
|
enabled = true
|
||
|
}
|
||
|
|
||
|
func init() {
|
||
|
if os.Getenv("PPROF_SYNC") != "" {
|
||
|
Enable()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type Mutex struct {
|
||
|
mu sync.Mutex
|
||
|
hold *int // Unique value for passing to pprof.
|
||
|
stack [32]uintptr // The stack for the current holder.
|
||
|
start time.Time // When the lock was obtained.
|
||
|
entries int // Number of entries returned from runtime.Callers.
|
||
|
}
|
||
|
|
||
|
func (m *Mutex) Lock() {
|
||
|
if !enabled {
|
||
|
m.mu.Lock()
|
||
|
return
|
||
|
}
|
||
|
v := new(int)
|
||
|
lockBlockers.Add(v, 0)
|
||
|
m.mu.Lock()
|
||
|
lockBlockers.Remove(v)
|
||
|
m.hold = v
|
||
|
lockHolders.Add(v, 0)
|
||
|
m.entries = runtime.Callers(2, m.stack[:])
|
||
|
m.start = time.Now()
|
||
|
}
|
||
|
func (m *Mutex) Unlock() {
|
||
|
if enabled {
|
||
|
lockHeld := time.Since(m.start)
|
||
|
var key [32]uintptr
|
||
|
copy(key[:], m.stack[:m.entries])
|
||
|
lockTimesMu.Lock()
|
||
|
if lockHeld > lockTimes[key] {
|
||
|
lockTimes[key] = lockHeld
|
||
|
}
|
||
|
lockTimesMu.Unlock()
|
||
|
lockHolders.Remove(m.hold)
|
||
|
}
|
||
|
m.mu.Unlock()
|
||
|
}
|
||
|
|
||
|
type WaitGroup struct {
|
||
|
sync.WaitGroup
|
||
|
}
|
||
|
|
||
|
type Cond struct {
|
||
|
sync.Cond
|
||
|
}
|