diff options
author | Fabricio Voznika <fvoznika@google.com> | 2019-03-11 11:46:18 -0700 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2019-03-11 11:47:30 -0700 |
commit | bc9b979b9412ad5852872c1a9bee462f73d2455e (patch) | |
tree | ca7fda751568879d4ef08aee53d3a8298e1faf55 /pkg/sentry | |
parent | 71d53382bfb3a6f05e90e31df8f39d22c0131040 (diff) |
Add profiling commands to runsc
Example:
runsc debug --root=<dir> \
--profile-heap=/tmp/heap.prof \
--profile-cpu=/tmp/cpu.prod --profile-delay=30 \
<container ID>
PiperOrigin-RevId: 237848456
Change-Id: Icff3f20c1b157a84d0922599eaea327320dad773
Diffstat (limited to 'pkg/sentry')
-rw-r--r-- | pkg/sentry/control/BUILD | 2 | ||||
-rw-r--r-- | pkg/sentry/control/pprof.go | 124 |
2 files changed, 126 insertions, 0 deletions
diff --git a/pkg/sentry/control/BUILD b/pkg/sentry/control/BUILD index f54e01ee8..5052bcc0d 100644 --- a/pkg/sentry/control/BUILD +++ b/pkg/sentry/control/BUILD @@ -6,6 +6,7 @@ go_library( name = "control", srcs = [ "control.go", + "pprof.go", "proc.go", "state.go", ], @@ -15,6 +16,7 @@ go_library( ], deps = [ "//pkg/abi/linux", + "//pkg/fd", "//pkg/log", "//pkg/sentry/fs", "//pkg/sentry/fs/host", diff --git a/pkg/sentry/control/pprof.go b/pkg/sentry/control/pprof.go new file mode 100644 index 000000000..1af092af3 --- /dev/null +++ b/pkg/sentry/control/pprof.go @@ -0,0 +1,124 @@ +// Copyright 2019 Google LLC +// +// 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 control + +import ( + "errors" + "runtime" + "runtime/pprof" + "sync" + + "gvisor.googlesource.com/gvisor/pkg/fd" + "gvisor.googlesource.com/gvisor/pkg/urpc" +) + +var errNoOutput = errors.New("no output writer provided") + +// ProfileOpts contains options for the StartCPUProfile/Goroutine RPC call. +type ProfileOpts struct { + // File is the filesystem path for the profile. + File string `json:"path"` + + // FilePayload is the destination for the profiling output. + urpc.FilePayload +} + +// Profile includes profile-related RPC stubs. It provides a way to +// control the built-in pprof facility in sentry via sentryctl. +// +// The following options to sentryctl are added: +// +// - collect CPU profile on-demand. +// sentryctl -pid <pid> pprof-cpu-start +// sentryctl -pid <pid> pprof-cpu-stop +// +// - dump out the stack trace of current go routines. +// sentryctl -pid <pid> pprof-goroutine +type Profile struct { + // mu protects the fields below. + mu sync.Mutex + + // cpuFile is the current CPU profile output file. + cpuFile *fd.FD +} + +// StartCPUProfile is an RPC stub which starts recording the CPU profile in a +// file. +func (p *Profile) StartCPUProfile(o *ProfileOpts, _ *struct{}) error { + if len(o.FilePayload.Files) < 1 { + return errNoOutput + } + + output, err := fd.NewFromFile(o.FilePayload.Files[0]) + if err != nil { + return err + } + + p.mu.Lock() + defer p.mu.Unlock() + + // Returns an error if profiling is already started. + if err := pprof.StartCPUProfile(output); err != nil { + output.Close() + return err + } + + p.cpuFile = output + return nil +} + +// StopCPUProfile is an RPC stub which stops the CPU profiling and flush out the +// profile data. It takes no argument. +func (p *Profile) StopCPUProfile(_, _ *struct{}) error { + p.mu.Lock() + defer p.mu.Unlock() + + if p.cpuFile == nil { + return errors.New("CPU profiling not started") + } + + pprof.StopCPUProfile() + p.cpuFile.Close() + p.cpuFile = nil + return nil +} + +// HeapProfile generates a heap profile for the sentry. +func (p *Profile) HeapProfile(o *ProfileOpts, _ *struct{}) error { + if len(o.FilePayload.Files) < 1 { + return errNoOutput + } + output := o.FilePayload.Files[0] + defer output.Close() + runtime.GC() // Get up-to-date statistics. + if err := pprof.WriteHeapProfile(output); err != nil { + return err + } + return nil +} + +// Goroutine is an RPC stub which dumps out the stack trace for all running +// goroutines. +func (p *Profile) Goroutine(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 { + return err + } + return nil +} |