Skip to content

Commit

Permalink
support print stack
Browse files Browse the repository at this point in the history
Signed-off-by: ningmingxiao <[email protected]>
  • Loading branch information
ningmingxiao committed Jun 9, 2022
1 parent b702a25 commit 8f8e69f
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 0 deletions.
35 changes: 35 additions & 0 deletions cmd/nerdctl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
package main

import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"path/filepath"
"runtime"
"strings"

Expand Down Expand Up @@ -71,11 +74,16 @@ func main() {
}

func xmain() error {
signals := make(chan os.Signal, 2048)
ctx, cancel := context.WithCancel(context.Background())
handleSignals(ctx, signals, cancel)
signal.Notify(signals, handledSignals...)
if len(os.Args) == 3 && os.Args[1] == logging.MagicArgv1 {
// containerd runtime v2 logging plugin mode.
// "binary://BIN?KEY=VALUE" URI is parsed into Args {BIN, KEY, VALUE}.
return logging.Main(os.Args[2])
}

// nerdctl CLI mode
app, err := newApp()
if err != nil {
Expand Down Expand Up @@ -450,3 +458,30 @@ func AddPersistentStringArrayFlag(cmd *cobra.Command, name string, aliases, nonP
}
}
}

func dumpStacks(writeToFile bool) {
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.Debugf("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf)

if writeToFile {
// Also write to file to aid gathering diagnostics
name := filepath.Join(os.TempDir(), fmt.Sprintf("nerdctl.%d.stacks.log", os.Getpid()))
f, err := os.Create(name)
if err != nil {
return
}
defer f.Close()
f.WriteString(string(buf))
logrus.Debugf("goroutine stack dump written to %s", name)
}
}
40 changes: 40 additions & 0 deletions cmd/nerdctl/main_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,21 @@
package main

import (
"context"
"os"

"github.com/containerd/containerd/log"
"github.com/spf13/cobra"
"golang.org/x/sys/unix"
)

var handledSignals = []os.Signal{
unix.SIGTERM,
unix.SIGINT,
unix.SIGUSR1,
unix.SIGPIPE,
}

func appNeedsRootlessParentMain(cmd *cobra.Command, args []string) bool {
return false
}
Expand All @@ -35,3 +47,31 @@ func addApparmorCommand(rootCmd *cobra.Command) {
func addCpCommand(rootCmd *cobra.Command) {
// NOP
}

func handleSignals(ctx context.Context, signals chan os.Signal, cancel func()) chan struct{} {
done := make(chan struct{}, 1)
go func() {
for {
select {
case s := <-signals:

// Do not print message when dealing with SIGPIPE, which may cause
// nested signals and consume lots of cpu bandwidth.
if s == unix.SIGPIPE {
continue
}

log.G(ctx).WithField("signal", s).Debug("received signal")
switch s {
case unix.SIGUSR1:
dumpStacks(true)
default:
cancel()
close(done)
return
}
}
}
}()
return done
}
40 changes: 40 additions & 0 deletions cmd/nerdctl/main_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,24 @@
package main

import (
"context"
"os"

"github.com/containerd/containerd/log"
ncdefaults "github.com/containerd/nerdctl/pkg/defaults"
"github.com/containerd/nerdctl/pkg/rootlessutil"
"github.com/containerd/nerdctl/pkg/strutil"
"github.com/spf13/cobra"
"golang.org/x/sys/unix"
)

var handledSignals = []os.Signal{
unix.SIGTERM,
unix.SIGINT,
unix.SIGUSR1,
unix.SIGPIPE,
}

func appNeedsRootlessParentMain(cmd *cobra.Command, args []string) bool {
commands := []string{}
for tcmd := cmd; tcmd != nil; tcmd = tcmd.Parent() {
Expand Down Expand Up @@ -72,3 +84,31 @@ func addApparmorCommand(rootCmd *cobra.Command) {
func addCpCommand(rootCmd *cobra.Command) {
rootCmd.AddCommand(newCpCommand())
}

func handleSignals(ctx context.Context, signals chan os.Signal, cancel func()) chan struct{} {
done := make(chan struct{}, 1)
go func() {
for {
select {
case s := <-signals:

// Do not print message when dealing with SIGPIPE, which may cause
// nested signals and consume lots of cpu bandwidth.
if s == unix.SIGPIPE {
continue
}

log.G(ctx).WithField("signal", s).Debug("received signal")
switch s {
case unix.SIGUSR1:
dumpStacks(true)
default:
cancel()
close(done)
return
}
}
}
}()
return done
}
61 changes: 61 additions & 0 deletions cmd/nerdctl/main_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,22 @@
package main

import (
"context"
"fmt"
"os"
"unsafe"

"github.com/containerd/containerd/log"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/sys/windows"
)

var (
handledSignals = []os.Signal{
windows.SIGTERM,
windows.SIGINT,
}
)

func appNeedsRootlessParentMain(cmd *cobra.Command, args []string) bool {
Expand All @@ -43,3 +58,49 @@ func addApparmorCommand(rootCmd *cobra.Command) {
func addCpCommand(rootCmd *cobra.Command) {
// NOP
}

func handleSignals(ctx context.Context, signals chan os.Signal, cancel func()) chan struct{} {
done := make(chan struct{})
go func() {
for {
select {
case s := <-signals:
log.G(ctx).WithField("signal", s).Debug("received signal")
cancel()
close(done)
return
}
}
}()
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\\stackdump-" + fmt.Sprint(os.Getpid())
ev, _ := windows.UTF16PtrFromString(event)
sd, err := windows.SecurityDescriptorFromString("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 = sd
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(true)
}
}()
}

0 comments on commit 8f8e69f

Please sign in to comment.