debug/profile: move to profiler interface

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
2021-02-14 14:02:51 +03:00
parent e5bf1448f4
commit cc7ebedf22
6 changed files with 0 additions and 123 deletions

78
profiler/http/http.go Normal file
View File

@@ -0,0 +1,78 @@
// Package http enables the http profiler
package http
import (
"context"
"net/http"
"net/http/pprof"
"sync"
"github.com/unistack-org/micro/v3/debug/profile"
)
type httpProfile struct {
sync.Mutex
running bool
server *http.Server
}
var (
DefaultAddress = ":6060"
)
// Start the profiler
func (h *httpProfile) Start() error {
h.Lock()
defer h.Unlock()
if h.running {
return nil
}
go func() {
if err := h.server.ListenAndServe(); err != nil {
h.Lock()
h.running = false
h.Unlock()
}
}()
h.running = true
return nil
}
// Stop the profiler
func (h *httpProfile) Stop() error {
h.Lock()
defer h.Unlock()
if !h.running {
return nil
}
h.running = false
return h.server.Shutdown(context.TODO())
}
func (h *httpProfile) String() string {
return "http"
}
func NewProfile(opts ...profile.Option) profile.Profile {
mux := http.NewServeMux()
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
return &httpProfile{
server: &http.Server{
Addr: DefaultAddress,
Handler: mux,
},
}
}

122
profiler/pprof/pprof.go Normal file
View File

@@ -0,0 +1,122 @@
// Package pprof provides a pprof profiler which writes output to /tmp/[name].{cpu,mem}.pprof
package pprof
import (
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"sync"
"time"
"github.com/unistack-org/micro/v3/debug/profile"
)
type profiler struct {
opts profile.Options
sync.Mutex
running bool
exit chan bool
// where the cpu profile is written
cpuFile *os.File
// where the mem profile is written
memFile *os.File
}
func (p *profiler) writeHeap(f *os.File) {
defer f.Close()
t := time.NewTicker(time.Second * 30)
defer t.Stop()
for {
select {
case <-t.C:
runtime.GC()
pprof.WriteHeapProfile(f)
case <-p.exit:
return
}
}
}
func (p *profiler) Start() error {
p.Lock()
defer p.Unlock()
if p.running {
return nil
}
// create exit channel
p.exit = make(chan bool)
cpuFile := filepath.Join("/tmp", "cpu.pprof")
memFile := filepath.Join("/tmp", "mem.pprof")
if len(p.opts.Name) > 0 {
cpuFile = filepath.Join("/tmp", p.opts.Name+".cpu.pprof")
memFile = filepath.Join("/tmp", p.opts.Name+".mem.pprof")
}
f1, err := os.Create(cpuFile)
if err != nil {
return err
}
f2, err := os.Create(memFile)
if err != nil {
return err
}
// start cpu profiling
if err := pprof.StartCPUProfile(f1); err != nil {
return err
}
// write the heap periodically
go p.writeHeap(f2)
// set cpu file
p.cpuFile = f1
// set mem file
p.memFile = f2
p.running = true
return nil
}
func (p *profiler) Stop() error {
p.Lock()
defer p.Unlock()
select {
case <-p.exit:
return nil
default:
close(p.exit)
pprof.StopCPUProfile()
p.cpuFile.Close()
p.running = false
p.cpuFile = nil
p.memFile = nil
return nil
}
}
func (p *profiler) String() string {
return "pprof"
}
func NewProfile(opts ...profile.Option) profile.Profile {
var options profile.Options
for _, o := range opts {
o(&options)
}
p := new(profiler)
p.opts = options
return p
}

43
profiler/profile.go Normal file
View File

@@ -0,0 +1,43 @@
// Package profile is for profilers
package profile
type Profile interface {
// Start the profiler
Start() error
// Stop the profiler
Stop() error
// Name of the profiler
String() string
}
var (
DefaultProfile Profile = &NoopProfile{}
)
type NoopProfile struct{}
func (p *NoopProfile) Start() error {
return nil
}
func (p *NoopProfile) Stop() error {
return nil
}
func (p *NoopProfile) String() string {
return "noop"
}
type Options struct {
// Name to use for the profile
Name string
}
type Option func(o *Options)
// Name of the profile
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}