add profile option

This commit is contained in:
moricho 2020-02-25 16:49:08 +09:00
parent 160d5751ab
commit d8ed784311
4 changed files with 152 additions and 19 deletions

View File

@ -117,15 +117,43 @@ func (p *Profile) HeapProfile(o *ProfileOpts, _ *struct{}) error {
return nil return nil
} }
// Goroutine is an RPC stub which dumps out the stack trace for all running // GoroutineProfile is an RPC stub which dumps out the stack trace for all running
// goroutines. // goroutines.
func (p *Profile) Goroutine(o *ProfileOpts, _ *struct{}) error { func (p *Profile) GoroutineProfile(o *ProfileOpts, _ *struct{}) error {
if len(o.FilePayload.Files) < 1 { if len(o.FilePayload.Files) < 1 {
return errNoOutput return errNoOutput
} }
output := o.FilePayload.Files[0] output := o.FilePayload.Files[0]
defer output.Close() defer output.Close()
if err := pprof.Lookup("goroutine").WriteTo(output, 2); err != nil { if err := pprof.Lookup("goroutine").WriteTo(output, 0); err != nil {
return err
}
return nil
}
// BlockProfile is an RPC stub which dumps out the stack trace that led to
// blocking on synchronization primitives.
func (p *Profile) BlockProfile(o *ProfileOpts, _ *struct{}) error {
if len(o.FilePayload.Files) < 1 {
return errNoOutput
}
output := o.FilePayload.Files[0]
defer output.Close()
if err := pprof.Lookup("block").WriteTo(output, 0); err != nil {
return err
}
return nil
}
// MutexProfile is an RPC stub which dumps out the stack trace of holders of
// contended mutexes.
func (p *Profile) MutexProfile(o *ProfileOpts, _ *struct{}) error {
if len(o.FilePayload.Files) < 1 {
return errNoOutput
}
output := o.FilePayload.Files[0]
defer output.Close()
if err := pprof.Lookup("mutex").WriteTo(output, 0); err != nil {
return err return err
} }
return nil return nil

View File

@ -101,11 +101,14 @@ const (
// Profiling related commands (see pprof.go for more details). // Profiling related commands (see pprof.go for more details).
const ( const (
StartCPUProfile = "Profile.StartCPUProfile" StartCPUProfile = "Profile.StartCPUProfile"
StopCPUProfile = "Profile.StopCPUProfile" StopCPUProfile = "Profile.StopCPUProfile"
HeapProfile = "Profile.HeapProfile" HeapProfile = "Profile.HeapProfile"
StartTrace = "Profile.StartTrace" GoroutineProfile = "Profile.GoroutineProfile"
StopTrace = "Profile.StopTrace" BlockProfile = "Profile.BlockProfile"
MutexProfile = "Profile.MutexProfile"
StartTrace = "Profile.StartTrace"
StopTrace = "Profile.StopTrace"
) )
// Logging related commands (see logging.go for more details). // Logging related commands (see logging.go for more details).

View File

@ -32,17 +32,20 @@ import (
// Debug implements subcommands.Command for the "debug" command. // Debug implements subcommands.Command for the "debug" command.
type Debug struct { type Debug struct {
pid int pid int
stacks bool stacks bool
signal int signal int
profileHeap string profileHeap string
profileCPU string profileCPU string
trace string profileGoroutine string
strace string profileBlock string
logLevel string profileMutex string
logPackets string trace string
duration time.Duration strace string
ps bool logLevel string
logPackets string
duration time.Duration
ps bool
} }
// Name implements subcommands.Command. // Name implements subcommands.Command.
@ -66,6 +69,9 @@ func (d *Debug) SetFlags(f *flag.FlagSet) {
f.BoolVar(&d.stacks, "stacks", false, "if true, dumps all sandbox stacks to the log") f.BoolVar(&d.stacks, "stacks", false, "if true, dumps all sandbox stacks to the log")
f.StringVar(&d.profileHeap, "profile-heap", "", "writes heap profile to the given file.") f.StringVar(&d.profileHeap, "profile-heap", "", "writes heap profile to the given file.")
f.StringVar(&d.profileCPU, "profile-cpu", "", "writes CPU profile to the given file.") f.StringVar(&d.profileCPU, "profile-cpu", "", "writes CPU profile to the given file.")
f.StringVar(&d.profileGoroutine, "profile-goroutine", "", "writes goroutine profile to the given file.")
f.StringVar(&d.profileBlock, "profile-block", "", "writes block profile to the given file.")
f.StringVar(&d.profileMutex, "profile-mutex", "", "writes mutex profile to the given file.")
f.DurationVar(&d.duration, "duration", time.Second, "amount of time to wait for CPU and trace profiles") f.DurationVar(&d.duration, "duration", time.Second, "amount of time to wait for CPU and trace profiles")
f.StringVar(&d.trace, "trace", "", "writes an execution trace to the given file.") f.StringVar(&d.trace, "trace", "", "writes an execution trace to the given file.")
f.IntVar(&d.signal, "signal", -1, "sends signal to the sandbox") f.IntVar(&d.signal, "signal", -1, "sends signal to the sandbox")
@ -147,6 +153,42 @@ func (d *Debug) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
} }
log.Infof("Heap profile written to %q", d.profileHeap) log.Infof("Heap profile written to %q", d.profileHeap)
} }
if d.profileGoroutine != "" {
f, err := os.Create(d.profileGoroutine)
if err != nil {
return Errorf(err.Error())
}
defer f.Close()
if err := c.Sandbox.GoroutineProfile(f); err != nil {
return Errorf(err.Error())
}
log.Infof("Goroutine profile written to %q", d.profileGoroutine)
}
if d.profileBlock != "" {
f, err := os.Create(d.profileBlock)
if err != nil {
return Errorf(err.Error())
}
defer f.Close()
if err := c.Sandbox.BlockProfile(f); err != nil {
return Errorf(err.Error())
}
log.Infof("Block profile written to %q", d.profileBlock)
}
if d.profileMutex != "" {
f, err := os.Create(d.profileMutex)
if err != nil {
return Errorf(err.Error())
}
defer f.Close()
if err := c.Sandbox.MutexProfile(f); err != nil {
return Errorf(err.Error())
}
log.Infof("Mutex profile written to %q", d.profileMutex)
}
delay := false delay := false
if d.profileCPU != "" { if d.profileCPU != "" {

View File

@ -972,6 +972,66 @@ func (s *Sandbox) StopCPUProfile() error {
return nil return nil
} }
// GoroutineProfile writes a goroutine profile to the given file.
func (s *Sandbox) GoroutineProfile(f *os.File) error {
log.Debugf("Goroutine profile %q", s.ID)
conn, err := s.sandboxConnect()
if err != nil {
return err
}
defer conn.Close()
opts := control.ProfileOpts{
FilePayload: urpc.FilePayload{
Files: []*os.File{f},
},
}
if err := conn.Call(boot.GoroutineProfile, &opts, nil); err != nil {
return fmt.Errorf("getting sandbox %q goroutine profile: %v", s.ID, err)
}
return nil
}
// BlockProfile writes a block profile to the given file.
func (s *Sandbox) BlockProfile(f *os.File) error {
log.Debugf("Block profile %q", s.ID)
conn, err := s.sandboxConnect()
if err != nil {
return err
}
defer conn.Close()
opts := control.ProfileOpts{
FilePayload: urpc.FilePayload{
Files: []*os.File{f},
},
}
if err := conn.Call(boot.BlockProfile, &opts, nil); err != nil {
return fmt.Errorf("getting sandbox %q block profile: %v", s.ID, err)
}
return nil
}
// MutexProfile writes a mutex profile to the given file.
func (s *Sandbox) MutexProfile(f *os.File) error {
log.Debugf("Mutex profile %q", s.ID)
conn, err := s.sandboxConnect()
if err != nil {
return err
}
defer conn.Close()
opts := control.ProfileOpts{
FilePayload: urpc.FilePayload{
Files: []*os.File{f},
},
}
if err := conn.Call(boot.MutexProfile, &opts, nil); err != nil {
return fmt.Errorf("getting sandbox %q mutex profile: %v", s.ID, err)
}
return nil
}
// StartTrace start trace writing to the given file. // StartTrace start trace writing to the given file.
func (s *Sandbox) StartTrace(f *os.File) error { func (s *Sandbox) StartTrace(f *os.File) error {
log.Debugf("Trace start %q", s.ID) log.Debugf("Trace start %q", s.ID)