From 3e8b38d08bbe200a11909bc268e66e4ee1d27d79 Mon Sep 17 00:00:00 2001 From: Adin Scannell Date: Mon, 10 Feb 2020 13:56:17 -0800 Subject: Add flag package to limit visibility. PiperOrigin-RevId: 294297004 --- runsc/cmd/BUILD | 1 + runsc/cmd/boot.go | 2 +- runsc/cmd/checkpoint.go | 2 +- runsc/cmd/create.go | 2 +- runsc/cmd/debug.go | 2 +- runsc/cmd/delete.go | 2 +- runsc/cmd/do.go | 2 +- runsc/cmd/events.go | 2 +- runsc/cmd/exec.go | 2 +- runsc/cmd/gofer.go | 2 +- runsc/cmd/help.go | 2 +- runsc/cmd/install.go | 2 +- runsc/cmd/kill.go | 2 +- runsc/cmd/list.go | 2 +- runsc/cmd/pause.go | 2 +- runsc/cmd/ps.go | 2 +- runsc/cmd/restore.go | 2 +- runsc/cmd/resume.go | 2 +- runsc/cmd/run.go | 2 +- runsc/cmd/spec.go | 2 +- runsc/cmd/start.go | 2 +- runsc/cmd/state.go | 2 +- runsc/cmd/syscalls.go | 2 +- runsc/cmd/wait.go | 2 +- 24 files changed, 24 insertions(+), 23 deletions(-) (limited to 'runsc/cmd') diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index 09aa46434..2a88b85a9 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -50,6 +50,7 @@ go_library( "//runsc/boot/platforms", "//runsc/console", "//runsc/container", + "//runsc/flag", "//runsc/fsgofer", "//runsc/fsgofer/filter", "//runsc/specutils", diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index b40fded5b..0f3da69a0 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -21,12 +21,12 @@ import ( "strings" "syscall" - "flag" "github.com/google/subcommands" specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/boot/platforms" + "gvisor.dev/gvisor/runsc/flag" "gvisor.dev/gvisor/runsc/specutils" ) diff --git a/runsc/cmd/checkpoint.go b/runsc/cmd/checkpoint.go index d8b3a8573..8a29e521e 100644 --- a/runsc/cmd/checkpoint.go +++ b/runsc/cmd/checkpoint.go @@ -20,11 +20,11 @@ import ( "path/filepath" "syscall" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" "gvisor.dev/gvisor/runsc/specutils" ) diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index 1815c93b9..910e97577 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -17,10 +17,10 @@ package cmd import ( "context" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" "gvisor.dev/gvisor/runsc/specutils" ) diff --git a/runsc/cmd/debug.go b/runsc/cmd/debug.go index f37415810..79965460e 100644 --- a/runsc/cmd/debug.go +++ b/runsc/cmd/debug.go @@ -22,12 +22,12 @@ import ( "syscall" "time" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/control" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) // Debug implements subcommands.Command for the "debug" command. diff --git a/runsc/cmd/delete.go b/runsc/cmd/delete.go index 30d8164b1..0e4863f50 100644 --- a/runsc/cmd/delete.go +++ b/runsc/cmd/delete.go @@ -19,11 +19,11 @@ import ( "fmt" "os" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) // Delete implements subcommands.Command for the "delete" command. diff --git a/runsc/cmd/do.go b/runsc/cmd/do.go index 9a8a49054..b184bd402 100644 --- a/runsc/cmd/do.go +++ b/runsc/cmd/do.go @@ -27,12 +27,12 @@ import ( "strings" "syscall" - "flag" "github.com/google/subcommands" specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" "gvisor.dev/gvisor/runsc/specutils" ) diff --git a/runsc/cmd/events.go b/runsc/cmd/events.go index 3972e9224..51f6a98ed 100644 --- a/runsc/cmd/events.go +++ b/runsc/cmd/events.go @@ -20,11 +20,11 @@ import ( "os" "time" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) // Events implements subcommands.Command for the "events" command. diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go index d1e99243b..d9a94903e 100644 --- a/runsc/cmd/exec.go +++ b/runsc/cmd/exec.go @@ -27,7 +27,6 @@ import ( "syscall" "time" - "flag" "github.com/google/subcommands" specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.dev/gvisor/pkg/log" @@ -37,6 +36,7 @@ import ( "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/console" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" "gvisor.dev/gvisor/runsc/specutils" ) diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index 7df7995f0..6e06f3c0f 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -23,7 +23,6 @@ import ( "strings" "syscall" - "flag" "github.com/google/subcommands" specs "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/sys/unix" @@ -32,6 +31,7 @@ import ( "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/unet" "gvisor.dev/gvisor/runsc/boot" + "gvisor.dev/gvisor/runsc/flag" "gvisor.dev/gvisor/runsc/fsgofer" "gvisor.dev/gvisor/runsc/fsgofer/filter" "gvisor.dev/gvisor/runsc/specutils" diff --git a/runsc/cmd/help.go b/runsc/cmd/help.go index 930e8454f..c7d210140 100644 --- a/runsc/cmd/help.go +++ b/runsc/cmd/help.go @@ -18,8 +18,8 @@ import ( "context" "fmt" - "flag" "github.com/google/subcommands" + "gvisor.dev/gvisor/runsc/flag" ) // NewHelp returns a help command for the given commander. diff --git a/runsc/cmd/install.go b/runsc/cmd/install.go index 441c1db0d..2e223e3be 100644 --- a/runsc/cmd/install.go +++ b/runsc/cmd/install.go @@ -23,8 +23,8 @@ import ( "os" "path" - "flag" "github.com/google/subcommands" + "gvisor.dev/gvisor/runsc/flag" ) // Install implements subcommands.Command. diff --git a/runsc/cmd/kill.go b/runsc/cmd/kill.go index 6c1f197a6..8282ea0e0 100644 --- a/runsc/cmd/kill.go +++ b/runsc/cmd/kill.go @@ -21,11 +21,11 @@ import ( "strings" "syscall" - "flag" "github.com/google/subcommands" "golang.org/x/sys/unix" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) // Kill implements subcommands.Command for the "kill" command. diff --git a/runsc/cmd/list.go b/runsc/cmd/list.go index dd2d99a6b..d8d906fe3 100644 --- a/runsc/cmd/list.go +++ b/runsc/cmd/list.go @@ -22,11 +22,11 @@ import ( "text/tabwriter" "time" - "flag" "github.com/google/subcommands" specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) // List implements subcommands.Command for the "list" command for the "list" command. diff --git a/runsc/cmd/pause.go b/runsc/cmd/pause.go index 9c0e92001..6f95a9837 100644 --- a/runsc/cmd/pause.go +++ b/runsc/cmd/pause.go @@ -17,10 +17,10 @@ package cmd import ( "context" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) // Pause implements subcommands.Command for the "pause" command. diff --git a/runsc/cmd/ps.go b/runsc/cmd/ps.go index 45c644f3f..7fb8041af 100644 --- a/runsc/cmd/ps.go +++ b/runsc/cmd/ps.go @@ -18,11 +18,11 @@ import ( "context" "fmt" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/pkg/sentry/control" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) // PS implements subcommands.Command for the "ps" command. diff --git a/runsc/cmd/restore.go b/runsc/cmd/restore.go index 7be60cd7d..72584b326 100644 --- a/runsc/cmd/restore.go +++ b/runsc/cmd/restore.go @@ -19,10 +19,10 @@ import ( "path/filepath" "syscall" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" "gvisor.dev/gvisor/runsc/specutils" ) diff --git a/runsc/cmd/resume.go b/runsc/cmd/resume.go index b2df5c640..61a55a554 100644 --- a/runsc/cmd/resume.go +++ b/runsc/cmd/resume.go @@ -17,10 +17,10 @@ package cmd import ( "context" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) // Resume implements subcommands.Command for the "resume" command. diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index 33f4bc12b..cf41581ad 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -18,10 +18,10 @@ import ( "context" "syscall" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" "gvisor.dev/gvisor/runsc/specutils" ) diff --git a/runsc/cmd/spec.go b/runsc/cmd/spec.go index 344da13ba..8e2b36e85 100644 --- a/runsc/cmd/spec.go +++ b/runsc/cmd/spec.go @@ -20,8 +20,8 @@ import ( "os" "path/filepath" - "flag" "github.com/google/subcommands" + "gvisor.dev/gvisor/runsc/flag" ) var specTemplate = []byte(`{ diff --git a/runsc/cmd/start.go b/runsc/cmd/start.go index 5e9bc53ab..0205fd9f7 100644 --- a/runsc/cmd/start.go +++ b/runsc/cmd/start.go @@ -17,10 +17,10 @@ package cmd import ( "context" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) // Start implements subcommands.Command for the "start" command. diff --git a/runsc/cmd/state.go b/runsc/cmd/state.go index e9f41cbd8..cf2413deb 100644 --- a/runsc/cmd/state.go +++ b/runsc/cmd/state.go @@ -19,11 +19,11 @@ import ( "encoding/json" "os" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) // State implements subcommands.Command for the "state" command. diff --git a/runsc/cmd/syscalls.go b/runsc/cmd/syscalls.go index fb6c1ab29..7072547be 100644 --- a/runsc/cmd/syscalls.go +++ b/runsc/cmd/syscalls.go @@ -25,9 +25,9 @@ import ( "strconv" "text/tabwriter" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/pkg/sentry/kernel" + "gvisor.dev/gvisor/runsc/flag" ) // Syscalls implements subcommands.Command for the "syscalls" command. diff --git a/runsc/cmd/wait.go b/runsc/cmd/wait.go index 046489687..29c0a15f0 100644 --- a/runsc/cmd/wait.go +++ b/runsc/cmd/wait.go @@ -20,10 +20,10 @@ import ( "os" "syscall" - "flag" "github.com/google/subcommands" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/container" + "gvisor.dev/gvisor/runsc/flag" ) const ( -- cgit v1.2.3 From ec5630527bc4473081048d2d13d1dcfadc6c7cdd Mon Sep 17 00:00:00 2001 From: Adin Scannell Date: Wed, 19 Feb 2020 18:27:48 -0800 Subject: Add statefile command to runsc. PiperOrigin-RevId: 296105337 --- runsc/cmd/BUILD | 3 ++ runsc/cmd/statefile.go | 143 +++++++++++++++++++++++++++++++++++++++++++++++++ runsc/main.go | 3 +- 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 runsc/cmd/statefile.go (limited to 'runsc/cmd') diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index 2a88b85a9..d0bb4613a 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -31,6 +31,7 @@ go_library( "spec.go", "start.go", "state.go", + "statefile.go", "syscalls.go", "wait.go", ], @@ -43,6 +44,8 @@ go_library( "//pkg/sentry/control", "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", + "//pkg/state", + "//pkg/state/statefile", "//pkg/sync", "//pkg/unet", "//pkg/urpc", diff --git a/runsc/cmd/statefile.go b/runsc/cmd/statefile.go new file mode 100644 index 000000000..e6f1907da --- /dev/null +++ b/runsc/cmd/statefile.go @@ -0,0 +1,143 @@ +// Copyright 2020 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/google/subcommands" + "gvisor.dev/gvisor/pkg/state" + "gvisor.dev/gvisor/pkg/state/statefile" + "gvisor.dev/gvisor/runsc/flag" +) + +// Statefile implements subcommands.Command for the "statefile" command. +type Statefile struct { + list bool + get string + key string + output string + html bool +} + +// Name implements subcommands.Command. +func (*Statefile) Name() string { + return "state" +} + +// Synopsis implements subcommands.Command. +func (*Statefile) Synopsis() string { + return "shows information about a statefile" +} + +// Usage implements subcommands.Command. +func (*Statefile) Usage() string { + return `statefile [flags] ` +} + +// SetFlags implements subcommands.Command. +func (s *Statefile) SetFlags(f *flag.FlagSet) { + f.BoolVar(&s.list, "list", false, "lists the metdata in the statefile.") + f.StringVar(&s.get, "get", "", "extracts the given metadata key.") + f.StringVar(&s.key, "key", "", "the integrity key for the file.") + f.StringVar(&s.output, "output", "", "target to write the result.") + f.BoolVar(&s.html, "html", false, "outputs in HTML format.") +} + +// Execute implements subcommands.Command.Execute. +func (s *Statefile) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + // Check arguments. + if s.list && s.get != "" { + Fatalf("error: can't specify -list and -get simultaneously.") + } + + // Setup output. + var output = os.Stdout // Default. + if s.output != "" { + f, err := os.OpenFile(s.output, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) + if err != nil { + Fatalf("error opening output: %v", err) + } + defer func() { + if err := f.Close(); err != nil { + Fatalf("error flushing output: %v", err) + } + }() + output = f + } + + // Open the file. + if f.NArg() != 1 { + f.Usage() + return subcommands.ExitUsageError + } + input, err := os.Open(f.Arg(0)) + if err != nil { + Fatalf("error opening input: %v\n", err) + } + + if s.html { + fmt.Fprintf(output, "\n") + defer fmt.Fprintf(output, "\n") + } + + // Dump the full file? + if !s.list && s.get == "" { + var key []byte + if s.key != "" { + key = []byte(s.key) + } + rc, _, err := statefile.NewReader(input, key) + if err != nil { + Fatalf("error parsing statefile: %v", err) + } + if err := state.PrettyPrint(output, rc, s.html); err != nil { + Fatalf("error printing state: %v", err) + } + return subcommands.ExitSuccess + } + + // Load just the metadata. + metadata, err := statefile.MetadataUnsafe(input) + if err != nil { + Fatalf("error reading metadata: %v", err) + } + + // Is it a single key? + if s.get != "" { + val, ok := metadata[s.get] + if !ok { + Fatalf("metadata key %s: not found", s.get) + } + fmt.Fprintf(output, "%s\n", val) + return subcommands.ExitSuccess + } + + // List all keys. + if s.html { + fmt.Fprintf(output, "
    \n") + defer fmt.Fprintf(output, "
\n") + } + for key := range metadata { + if s.html { + fmt.Fprintf(output, "
  • %s
  • \n", key) + } else { + fmt.Fprintf(output, "%s\n", key) + } + } + return subcommands.ExitSuccess +} diff --git a/runsc/main.go b/runsc/main.go index 762b0f801..af73bed97 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -116,8 +116,8 @@ func main() { subcommands.Register(new(cmd.Resume), "") subcommands.Register(new(cmd.Run), "") subcommands.Register(new(cmd.Spec), "") - subcommands.Register(new(cmd.Start), "") subcommands.Register(new(cmd.State), "") + subcommands.Register(new(cmd.Start), "") subcommands.Register(new(cmd.Wait), "") // Register internal commands with the internal group name. This causes @@ -127,6 +127,7 @@ func main() { subcommands.Register(new(cmd.Boot), internalGroup) subcommands.Register(new(cmd.Debug), internalGroup) subcommands.Register(new(cmd.Gofer), internalGroup) + subcommands.Register(new(cmd.Statefile), internalGroup) // All subcommands must be registered before flag parsing. flag.Parse() -- cgit v1.2.3 From d8ed78431162fcaed0b31b54d939c8a54d4736e7 Mon Sep 17 00:00:00 2001 From: moricho Date: Tue, 25 Feb 2020 16:49:08 +0900 Subject: add profile option --- pkg/sentry/control/pprof.go | 34 +++++++++++++++++++++--- runsc/boot/controller.go | 13 +++++---- runsc/cmd/debug.go | 64 +++++++++++++++++++++++++++++++++++++-------- runsc/sandbox/sandbox.go | 60 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 19 deletions(-) (limited to 'runsc/cmd') diff --git a/pkg/sentry/control/pprof.go b/pkg/sentry/control/pprof.go index 151808911..5d1907c0e 100644 --- a/pkg/sentry/control/pprof.go +++ b/pkg/sentry/control/pprof.go @@ -117,15 +117,43 @@ func (p *Profile) HeapProfile(o *ProfileOpts, _ *struct{}) error { 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. -func (p *Profile) Goroutine(o *ProfileOpts, _ *struct{}) error { +func (p *Profile) GoroutineProfile(o *ProfileOpts, _ *struct{}) error { if len(o.FilePayload.Files) < 1 { return errNoOutput } output := o.FilePayload.Files[0] 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 nil diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index 17e774e0c..8125d5061 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -101,11 +101,14 @@ const ( // Profiling related commands (see pprof.go for more details). const ( - StartCPUProfile = "Profile.StartCPUProfile" - StopCPUProfile = "Profile.StopCPUProfile" - HeapProfile = "Profile.HeapProfile" - StartTrace = "Profile.StartTrace" - StopTrace = "Profile.StopTrace" + StartCPUProfile = "Profile.StartCPUProfile" + StopCPUProfile = "Profile.StopCPUProfile" + HeapProfile = "Profile.HeapProfile" + GoroutineProfile = "Profile.GoroutineProfile" + BlockProfile = "Profile.BlockProfile" + MutexProfile = "Profile.MutexProfile" + StartTrace = "Profile.StartTrace" + StopTrace = "Profile.StopTrace" ) // Logging related commands (see logging.go for more details). diff --git a/runsc/cmd/debug.go b/runsc/cmd/debug.go index 79965460e..b5de2588b 100644 --- a/runsc/cmd/debug.go +++ b/runsc/cmd/debug.go @@ -32,17 +32,20 @@ import ( // Debug implements subcommands.Command for the "debug" command. type Debug struct { - pid int - stacks bool - signal int - profileHeap string - profileCPU string - trace string - strace string - logLevel string - logPackets string - duration time.Duration - ps bool + pid int + stacks bool + signal int + profileHeap string + profileCPU string + profileGoroutine string + profileBlock string + profileMutex string + trace string + strace string + logLevel string + logPackets string + duration time.Duration + ps bool } // 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.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.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.StringVar(&d.trace, "trace", "", "writes an execution trace to the given file.") 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) } + 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 if d.profileCPU != "" { diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index ec72bdbfd..2e0e2fd66 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -972,6 +972,66 @@ func (s *Sandbox) StopCPUProfile() error { 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. func (s *Sandbox) StartTrace(f *os.File) error { log.Debugf("Trace start %q", s.ID) -- cgit v1.2.3