diff options
Diffstat (limited to 'runsc/cmd/debug.go')
-rw-r--r-- | runsc/cmd/debug.go | 182 |
1 files changed, 106 insertions, 76 deletions
diff --git a/runsc/cmd/debug.go b/runsc/cmd/debug.go index 609e8231c..843dce01d 100644 --- a/runsc/cmd/debug.go +++ b/runsc/cmd/debug.go @@ -19,6 +19,7 @@ import ( "os" "strconv" "strings" + "sync" "syscall" "time" @@ -70,10 +71,10 @@ func (d *Debug) SetFlags(f *flag.FlagSet) { f.StringVar(&d.profileCPU, "profile-cpu", "", "writes CPU 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.IntVar(&d.signal, "signal", -1, "sends signal to the sandbox") - f.StringVar(&d.strace, "strace", "", `A comma separated list of syscalls to trace. "all" enables all traces, "off" disables all`) + f.StringVar(&d.strace, "strace", "", `A comma separated list of syscalls to trace. "all" enables all traces, "off" disables all.`) f.StringVar(&d.logLevel, "log-level", "", "The log level to set: warning (0), info (1), or debug (2).") f.StringVar(&d.logPackets, "log-packets", "", "A boolean value to enable or disable packet logging: true or false.") f.BoolVar(&d.ps, "ps", false, "lists processes") @@ -90,8 +91,10 @@ func (d *Debug) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) f.Usage() return subcommands.ExitUsageError } + id := f.Arg(0) + var err error - c, err = container.LoadAndCheck(conf.RootDir, f.Arg(0)) + c, err = container.Load(conf.RootDir, container.FullID{ContainerID: id}, container.LoadOpts{}) if err != nil { return Errorf("loading container %q: %v", f.Arg(0), err) } @@ -106,9 +109,10 @@ func (d *Debug) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) return Errorf("listing containers: %v", err) } for _, id := range ids { - candidate, err := container.LoadAndCheck(conf.RootDir, id) + candidate, err := container.Load(conf.RootDir, id, container.LoadOpts{Exact: true, SkipCheck: true}) if err != nil { - return Errorf("loading container %q: %v", id, err) + log.Warningf("Skipping container %q: %v", id, err) + continue } if candidate.SandboxPid() == d.pid { c = candidate @@ -120,11 +124,12 @@ func (d *Debug) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) } } - if c.Sandbox == nil || !c.Sandbox.IsRunning() { + if !c.IsSandboxRunning() { return Errorf("container sandbox is not running") } log.Infof("Found sandbox %q, PID: %d", c.Sandbox.ID, c.Sandbox.Pid) + // Perform synchronous actions. if d.signal > 0 { log.Infof("Sending signal %d to process: %d", d.signal, c.Sandbox.Pid) if err := syscall.Kill(c.Sandbox.Pid, syscall.Signal(d.signal)); err != nil { @@ -140,80 +145,15 @@ func (d *Debug) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) log.Infof(" *** Stack dump ***\n%s", stacks) } if d.profileHeap != "" { - f, err := os.Create(d.profileHeap) + f, err := os.OpenFile(d.profileHeap, os.O_CREATE|os.O_TRUNC, 0644) if err != nil { - return Errorf(err.Error()) + return Errorf("error opening heap profile output: %v", err) } defer f.Close() - if err := c.Sandbox.HeapProfile(f); err != nil { - return Errorf(err.Error()) - } - log.Infof("Heap profile written to %q", d.profileHeap) - } - 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()) + return Errorf("error collecting heap profile: %v", err) } - log.Infof("Mutex profile written to %q", d.profileMutex) } - - delay := false - if d.profileCPU != "" { - delay = true - f, err := os.Create(d.profileCPU) - if err != nil { - return Errorf(err.Error()) - } - defer func() { - f.Close() - if err := c.Sandbox.StopCPUProfile(); err != nil { - Fatalf(err.Error()) - } - log.Infof("CPU profile written to %q", d.profileCPU) - }() - if err := c.Sandbox.StartCPUProfile(f); err != nil { - return Errorf(err.Error()) - } - log.Infof("CPU profile started for %v, writing to %q", d.duration, d.profileCPU) - } - if d.trace != "" { - delay = true - f, err := os.Create(d.trace) - if err != nil { - return Errorf(err.Error()) - } - defer func() { - f.Close() - if err := c.Sandbox.StopTrace(); err != nil { - Fatalf(err.Error()) - } - log.Infof("Trace written to %q", d.trace) - }() - if err := c.Sandbox.StartTrace(f); err != nil { - return Errorf(err.Error()) - } - log.Infof("Tracing started for %v, writing to %q", d.duration, d.trace) - } - if d.strace != "" || len(d.logLevel) != 0 || len(d.logPackets) != 0 { args := control.LoggingArgs{} switch strings.ToLower(d.strace) { @@ -282,8 +222,98 @@ func (d *Debug) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) log.Infof(o) } - if delay { - time.Sleep(d.duration) + // Open profiling files. + var ( + cpuFile *os.File + traceFile *os.File + blockFile *os.File + mutexFile *os.File + ) + if d.profileCPU != "" { + f, err := os.OpenFile(d.profileCPU, os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return Errorf("error opening cpu profile output: %v", err) + } + defer f.Close() + cpuFile = f + } + if d.trace != "" { + f, err := os.OpenFile(d.trace, os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return Errorf("error opening trace profile output: %v", err) + } + traceFile = f + } + if d.profileBlock != "" { + f, err := os.OpenFile(d.profileBlock, os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return Errorf("error opening blocking profile output: %v", err) + } + defer f.Close() + blockFile = f + } + if d.profileMutex != "" { + f, err := os.OpenFile(d.profileMutex, os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return Errorf("error opening mutex profile output: %v", err) + } + defer f.Close() + mutexFile = f + } + + // Collect profiles. + var ( + wg sync.WaitGroup + cpuErr error + traceErr error + blockErr error + mutexErr error + ) + if cpuFile != nil { + wg.Add(1) + go func() { + defer wg.Done() + cpuErr = c.Sandbox.CPUProfile(cpuFile, d.duration) + }() + } + if traceFile != nil { + wg.Add(1) + go func() { + defer wg.Done() + traceErr = c.Sandbox.Trace(traceFile, d.duration) + }() + } + if blockFile != nil { + wg.Add(1) + go func() { + defer wg.Done() + blockErr = c.Sandbox.BlockProfile(blockFile, d.duration) + }() + } + if mutexFile != nil { + wg.Add(1) + go func() { + defer wg.Done() + mutexErr = c.Sandbox.MutexProfile(mutexFile, d.duration) + }() + } + + wg.Wait() + errorCount := 0 + if cpuErr != nil { + log.Infof("error collecting cpu profile: %v", cpuErr) + } + if traceErr != nil { + log.Infof("error collecting trace profile: %v", traceErr) + } + if blockErr != nil { + log.Infof("error collecting block profile: %v", blockErr) + } + if mutexErr != nil { + log.Infof("error collecting mutex profile: %v", mutexErr) + } + if errorCount > 0 { + return subcommands.ExitFailure } return subcommands.ExitSuccess |