a41f938cec
As we have seen, dependencies version can prevent the build. We should user lock versions on dependencies that we know work: * Packages are vendored * Add Godep support * Added addtional install step in readme * Fix travis build error
147 lignes
3,3 Kio
Go
147 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
|
|
}
|