From b4a642f6a56489ff6b606e017c1b6dfc223b7f57 Mon Sep 17 00:00:00 2001 From: "Justin Terry (VM)" Date: Tue, 27 Nov 2018 15:36:23 -0800 Subject: [PATCH] Add dumpStacks support for containerd on Windows Resolves: #1763 Signed-off-by: Justin Terry (VM) --- cmd/containerd/command/main.go | 16 ++++++++++++ cmd/containerd/command/main_unix.go | 17 ------------- cmd/containerd/command/main_windows.go | 35 +++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/cmd/containerd/command/main.go b/cmd/containerd/command/main.go index 09e0eeb95..4d56f5a8d 100644 --- a/cmd/containerd/command/main.go +++ b/cmd/containerd/command/main.go @@ -24,6 +24,7 @@ import ( "os" "os/signal" "path/filepath" + "runtime" "time" "github.com/containerd/containerd/log" @@ -232,3 +233,18 @@ func setLevel(context *cli.Context, config *srvconfig.Config) error { } return nil } + +func dumpStacks() { + var ( + buf []byte + stackSize int + ) + bufferLen := 16384 + for stackSize == len(buf) { + buf = make([]byte, bufferLen) + stackSize = runtime.Stack(buf, true) + bufferLen *= 2 + } + buf = buf[:stackSize] + logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) +} diff --git a/cmd/containerd/command/main_unix.go b/cmd/containerd/command/main_unix.go index 12c14261b..dc3f11b78 100644 --- a/cmd/containerd/command/main_unix.go +++ b/cmd/containerd/command/main_unix.go @@ -21,11 +21,9 @@ package command import ( "context" "os" - "runtime" "github.com/containerd/containerd/log" "github.com/containerd/containerd/services/server" - "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -66,18 +64,3 @@ func handleSignals(ctx context.Context, signals chan os.Signal, serverC chan *se }() return done } - -func dumpStacks() { - var ( - buf []byte - stackSize int - ) - bufferLen := 16384 - for stackSize == len(buf) { - buf = make([]byte, bufferLen) - stackSize = runtime.Stack(buf, true) - bufferLen *= 2 - } - buf = buf[:stackSize] - logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) -} diff --git a/cmd/containerd/command/main_windows.go b/cmd/containerd/command/main_windows.go index 66df8881a..071958a02 100644 --- a/cmd/containerd/command/main_windows.go +++ b/cmd/containerd/command/main_windows.go @@ -18,12 +18,15 @@ package command import ( "context" + "fmt" "os" "path/filepath" + "unsafe" + winio "github.com/Microsoft/go-winio" "github.com/containerd/containerd/log" "github.com/containerd/containerd/services/server" - + "github.com/sirupsen/logrus" "golang.org/x/sys/windows" ) @@ -54,5 +57,35 @@ func handleSignals(ctx context.Context, signals chan os.Signal, serverC chan *se } } }() + setupDumpStacks() return done } + +func setupDumpStacks() { + // Windows does not support signals like *nix systems. So instead of + // trapping on SIGUSR1 to dump stacks, we wait on a Win32 event to be + // signaled. ACL'd to builtin administrators and local system + event := "Global\\containerd-daemon-" + fmt.Sprint(os.Getpid()) + ev, _ := windows.UTF16PtrFromString(event) + sd, err := winio.SddlToSecurityDescriptor("D:P(A;;GA;;;BA)(A;;GA;;;SY)") + if err != nil { + logrus.Errorf("failed to get security descriptor for debug stackdump event %s: %s", event, err.Error()) + return + } + var sa windows.SecurityAttributes + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0])) + h, err := windows.CreateEvent(&sa, 0, 0, ev) + if h == 0 || err != nil { + logrus.Errorf("failed to create debug stackdump event %s: %s", event, err.Error()) + return + } + go func() { + logrus.Debugf("Stackdump - waiting signal at %s", event) + for { + windows.WaitForSingleObject(h, windows.INFINITE) + dumpStacks() + } + }() +}