mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2025-01-06 19:12:19 +00:00
231e468e19
git-subtree-dir: erigon-lib git-subtree-mainline:3c8cbda809
git-subtree-split:93d9c9d9fe
108 lines
2.3 KiB
Go
108 lines
2.3 KiB
Go
package dbg
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/ledgerwatch/log/v3"
|
|
)
|
|
|
|
const FileCloseLogLevel = log.LvlTrace
|
|
|
|
// LeakDetector - use it to find which resource was created but not closed (leaked)
|
|
// periodically does print in logs resources which living longer than 1min with their creation stack trace
|
|
// For example db transactions can call Add/Del from Begin/Commit/Rollback methods
|
|
type LeakDetector struct {
|
|
enabled atomic.Bool
|
|
slowThreshold atomic.Pointer[time.Duration]
|
|
autoIncrement atomic.Uint64
|
|
|
|
list map[uint64]LeakDetectorItem
|
|
listLock sync.Mutex
|
|
}
|
|
|
|
type LeakDetectorItem struct {
|
|
stack string
|
|
started time.Time
|
|
}
|
|
|
|
func NewLeakDetector(name string, slowThreshold time.Duration) *LeakDetector {
|
|
enabled := slowThreshold > 0
|
|
if !enabled {
|
|
return nil
|
|
}
|
|
d := &LeakDetector{list: map[uint64]LeakDetectorItem{}}
|
|
d.SetSlowThreshold(slowThreshold)
|
|
|
|
if enabled {
|
|
go func() {
|
|
logEvery := time.NewTicker(60 * time.Second)
|
|
defer logEvery.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-logEvery.C:
|
|
if list := d.slowList(); len(list) > 0 {
|
|
log.Info(fmt.Sprintf("[dbg.%s] long living resources", name), "list", strings.Join(d.slowList(), ", "))
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
return d
|
|
}
|
|
|
|
func (d *LeakDetector) slowList() (res []string) {
|
|
if d == nil || !d.Enabled() {
|
|
return res
|
|
}
|
|
slowThreshold := *d.slowThreshold.Load()
|
|
|
|
d.listLock.Lock()
|
|
defer d.listLock.Unlock()
|
|
i := 0
|
|
for key, value := range d.list {
|
|
living := time.Since(value.started)
|
|
if living > slowThreshold {
|
|
res = append(res, fmt.Sprintf("%d(%s): %s", key, living, value.stack))
|
|
}
|
|
i++
|
|
if i > 10 { // protect logs from too many output
|
|
break
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (d *LeakDetector) Del(id uint64) {
|
|
if d == nil || !d.Enabled() {
|
|
return
|
|
}
|
|
d.listLock.Lock()
|
|
defer d.listLock.Unlock()
|
|
delete(d.list, id)
|
|
}
|
|
func (d *LeakDetector) Add() uint64 {
|
|
if d == nil || !d.Enabled() {
|
|
return 0
|
|
}
|
|
ac := LeakDetectorItem{
|
|
stack: StackSkip(2),
|
|
started: time.Now(),
|
|
}
|
|
id := d.autoIncrement.Add(1)
|
|
d.listLock.Lock()
|
|
defer d.listLock.Unlock()
|
|
d.list[id] = ac
|
|
return id
|
|
}
|
|
|
|
func (d *LeakDetector) Enabled() bool { return d.enabled.Load() }
|
|
func (d *LeakDetector) SetSlowThreshold(t time.Duration) {
|
|
d.slowThreshold.Store(&t)
|
|
d.enabled.Store(t > 0)
|
|
}
|