Browse code

Windows: Stack dump to file

Signed-off-by: John Howard <jhoward@microsoft.com>

John Howard authored on 2016/06/29 09:12:31
Showing 5 changed files
... ...
@@ -412,7 +412,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
412 412
 
413 413
 	// set up SIGUSR1 handler on Unix-like systems, or a Win32 global event
414 414
 	// on Windows to dump Go routine stacks
415
-	setupDumpStackTrap()
415
+	setupDumpStackTrap(config.Root)
416 416
 
417 417
 	uidMaps, gidMaps, err := setupRemappedRoot(config)
418 418
 	if err != nil {
... ...
@@ -10,12 +10,12 @@ import (
10 10
 	psignal "github.com/docker/docker/pkg/signal"
11 11
 )
12 12
 
13
-func setupDumpStackTrap() {
13
+func setupDumpStackTrap(_ string) {
14 14
 	c := make(chan os.Signal, 1)
15 15
 	signal.Notify(c, syscall.SIGUSR1)
16 16
 	go func() {
17 17
 		for range c {
18
-			psignal.DumpStacks()
18
+			psignal.DumpStacks("")
19 19
 		}
20 20
 	}()
21 21
 }
... ...
@@ -2,6 +2,6 @@
2 2
 
3 3
 package daemon
4 4
 
5
-func setupDumpStackTrap() {
5
+func setupDumpStackTrap(_ string) {
6 6
 	return
7 7
 }
... ...
@@ -10,7 +10,7 @@ import (
10 10
 	"github.com/docker/docker/pkg/system"
11 11
 )
12 12
 
13
-func setupDumpStackTrap() {
13
+func setupDumpStackTrap(root string) {
14 14
 	// Windows does not support signals like *nix systems. So instead of
15 15
 	// trapping on SIGUSR1 to dump stacks, we wait on a Win32 event to be
16 16
 	// signaled.
... ...
@@ -23,7 +23,7 @@ func setupDumpStackTrap() {
23 23
 			logrus.Debugf("Stackdump - waiting signal at %s", ev)
24 24
 			for {
25 25
 				syscall.WaitForSingleObject(h, syscall.INFINITE)
26
-				signal.DumpStacks()
26
+				signal.DumpStacks(root)
27 27
 			}
28 28
 		}
29 29
 	}()
... ...
@@ -3,9 +3,11 @@ package signal
3 3
 import (
4 4
 	"os"
5 5
 	gosignal "os/signal"
6
+	"path/filepath"
6 7
 	"runtime"
7 8
 	"sync/atomic"
8 9
 	"syscall"
10
+	"time"
9 11
 
10 12
 	"github.com/Sirupsen/logrus"
11 13
 )
... ...
@@ -52,7 +54,7 @@ func Trap(cleanup func()) {
52 52
 						logrus.Info("Forcing docker daemon shutdown without cleanup; 3 interrupts received")
53 53
 					}
54 54
 				case syscall.SIGQUIT:
55
-					DumpStacks()
55
+					DumpStacks("")
56 56
 					logrus.Info("Forcing docker daemon shutdown without cleanup on SIGQUIT")
57 57
 				}
58 58
 				//for the SIGINT/TERM, and SIGQUIT non-clean shutdown case, exit with 128 + signal #
... ...
@@ -63,7 +65,7 @@ func Trap(cleanup func()) {
63 63
 }
64 64
 
65 65
 // DumpStacks dumps the runtime stack.
66
-func DumpStacks() {
66
+func DumpStacks(root string) {
67 67
 	var (
68 68
 		buf       []byte
69 69
 		stackSize int
... ...
@@ -77,5 +79,30 @@ func DumpStacks() {
77 77
 	buf = buf[:stackSize]
78 78
 	// Note that if the daemon is started with a less-verbose log-level than "info" (the default), the goroutine
79 79
 	// traces won't show up in the log.
80
-	logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf)
80
+	if root == "" {
81
+		logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf)
82
+	} else {
83
+		// Dumps the stacks to a file in the root directory of the daemon
84
+		// On Windows, this overcomes two issues - one being that if the stack is too big, it doesn't
85
+		// get written to the event log when the Windows daemon is running as a service.
86
+		// Second, using logrus, the tabs and new-lines end up getting written as literal
87
+		// \t and \n's, meaning you need to use something like notepad++ to convert the
88
+		// output into something readable using 'type' from a command line or notepad/notepad++ etc.
89
+		path := filepath.Join(root, "goroutine-stacks.log")
90
+		f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
91
+		if err != nil {
92
+			logrus.Warnf("Could not open %s to write the goroutine stacks: %v", path, err)
93
+			return
94
+		}
95
+		defer f.Close()
96
+		f.WriteString("=== BEGIN goroutine stack dump ===\n")
97
+		f.WriteString(time.Now().String() + "\n")
98
+		if _, err := f.Write(buf); err != nil {
99
+			logrus.Warnf("Could not write goroutine stacks to %s: %v", path, err)
100
+			return
101
+		}
102
+		f.WriteString("=== END goroutine stack dump ===\n")
103
+		f.Sync()
104
+		logrus.Infof("goroutine stacks written to %s", path)
105
+	}
81 106
 }