profiling: Return goroutines with sleep duration (#12775)

Add a new goroutine file which has another printing format. We need it
to see how much time each goroutine was blocked. Easier to detect stops.

Co-authored-by: Anis Elleuch <anis@min.io>
This commit is contained in:
Anis Elleuch 2021-07-23 21:16:53 +01:00 committed by GitHub
parent 9a31030e74
commit 23ef25b57a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -218,25 +218,27 @@ func contains(slice interface{}, elem interface{}) bool {
// disk since the name of this latter is randomly generated. // disk since the name of this latter is randomly generated.
type profilerWrapper struct { type profilerWrapper struct {
// Profile recorded at start of benchmark. // Profile recorded at start of benchmark.
base []byte records map[string][]byte
stopFn func() ([]byte, error) stopFn func() ([]byte, error)
ext string ext string
} }
// recordBase will record the profile and store it as the base. // record will record the profile and store it as the base.
func (p *profilerWrapper) recordBase(name string, debug int) { func (p *profilerWrapper) record(profileType string, debug int, recordName string) {
var buf bytes.Buffer var buf bytes.Buffer
p.base = nil if p.records == nil {
err := pprof.Lookup(name).WriteTo(&buf, debug) p.records = make(map[string][]byte)
}
err := pprof.Lookup(profileType).WriteTo(&buf, debug)
if err != nil { if err != nil {
return return
} }
p.base = buf.Bytes() p.records[recordName] = buf.Bytes()
} }
// Base returns the recorded base if any. // Records returns the recorded profiling if any.
func (p profilerWrapper) Base() []byte { func (p profilerWrapper) Records() map[string][]byte {
return p.base return p.records
} }
// Stop the currently running benchmark. // Stop the currently running benchmark.
@ -268,9 +270,10 @@ func getProfileData() (map[string][]byte, error) {
if err == nil { if err == nil {
dst[typ+"."+prof.Extension()] = buf dst[typ+"."+prof.Extension()] = buf
} }
buf = prof.Base() for name, buf := range prof.Records() {
if len(buf) > 0 { if len(buf) > 0 {
dst[typ+"-before"+"."+prof.Extension()] = buf dst[typ+"-"+name+"."+prof.Extension()] = buf
}
} }
} }
return dst, nil return dst, nil
@ -314,7 +317,7 @@ func startProfiler(profilerType string) (minioProfiler, error) {
} }
case madmin.ProfilerMEM: case madmin.ProfilerMEM:
runtime.GC() runtime.GC()
prof.recordBase("heap", 0) prof.record("heap", 0, "before")
prof.stopFn = func() ([]byte, error) { prof.stopFn = func() ([]byte, error) {
runtime.GC() runtime.GC()
var buf bytes.Buffer var buf bytes.Buffer
@ -330,7 +333,7 @@ func startProfiler(profilerType string) (minioProfiler, error) {
return buf.Bytes(), err return buf.Bytes(), err
} }
case madmin.ProfilerMutex: case madmin.ProfilerMutex:
prof.recordBase("mutex", 0) prof.record("mutex", 0, "before")
runtime.SetMutexProfileFraction(1) runtime.SetMutexProfileFraction(1)
prof.stopFn = func() ([]byte, error) { prof.stopFn = func() ([]byte, error) {
var buf bytes.Buffer var buf bytes.Buffer
@ -339,7 +342,7 @@ func startProfiler(profilerType string) (minioProfiler, error) {
return buf.Bytes(), err return buf.Bytes(), err
} }
case madmin.ProfilerThreads: case madmin.ProfilerThreads:
prof.recordBase("threadcreate", 0) prof.record("threadcreate", 0, "before")
prof.stopFn = func() ([]byte, error) { prof.stopFn = func() ([]byte, error) {
var buf bytes.Buffer var buf bytes.Buffer
err := pprof.Lookup("threadcreate").WriteTo(&buf, 0) err := pprof.Lookup("threadcreate").WriteTo(&buf, 0)
@ -347,7 +350,8 @@ func startProfiler(profilerType string) (minioProfiler, error) {
} }
case madmin.ProfilerGoroutines: case madmin.ProfilerGoroutines:
prof.ext = "txt" prof.ext = "txt"
prof.recordBase("goroutine", 1) prof.record("goroutine", 1, "before")
prof.record("goroutine", 2, "before,debug=2")
prof.stopFn = func() ([]byte, error) { prof.stopFn = func() ([]byte, error) {
var buf bytes.Buffer var buf bytes.Buffer
err := pprof.Lookup("goroutine").WriteTo(&buf, 1) err := pprof.Lookup("goroutine").WriteTo(&buf, 1)
@ -386,8 +390,8 @@ func startProfiler(profilerType string) (minioProfiler, error) {
// minioProfiler - minio profiler interface. // minioProfiler - minio profiler interface.
type minioProfiler interface { type minioProfiler interface {
// Return base profile. 'nil' if none. // Return recorded profiles, each profile associated with a distinct generic name.
Base() []byte Records() map[string][]byte
// Stop the profiler // Stop the profiler
Stop() ([]byte, error) Stop() ([]byte, error)
// Return extension of profile // Return extension of profile