DumpHandler provides a custom SIGQUIT and SIGTRAP handler that dumps the stacktrace of all goroutines to stderr and a well-known file in the home directory. This is useful for debugging deadlock issues that may occur in production in workspaces, since the default Go runtime will only dump to stderr
(ctx context.Context, name string)
| 1190 | // |
| 1191 | // On Windows this immediately returns. |
| 1192 | func DumpHandler(ctx context.Context, name string) { |
| 1193 | if runtime.GOOS == "windows" { |
| 1194 | // free up the goroutine since it'll be permanently blocked anyways |
| 1195 | return |
| 1196 | } |
| 1197 | |
| 1198 | listenSignals := []os.Signal{syscall.SIGTRAP} |
| 1199 | if os.Getenv("GOTRACEBACK") != "crash" { |
| 1200 | listenSignals = append(listenSignals, syscall.SIGQUIT) |
| 1201 | } |
| 1202 | |
| 1203 | sigs := make(chan os.Signal, 1) |
| 1204 | signal.Notify(sigs, listenSignals...) |
| 1205 | defer signal.Stop(sigs) |
| 1206 | |
| 1207 | for { |
| 1208 | sigStr := "" |
| 1209 | select { |
| 1210 | case <-ctx.Done(): |
| 1211 | return |
| 1212 | case sig := <-sigs: |
| 1213 | switch sig { |
| 1214 | case syscall.SIGQUIT: |
| 1215 | sigStr = "SIGQUIT" |
| 1216 | case syscall.SIGTRAP: |
| 1217 | sigStr = "SIGTRAP" |
| 1218 | } |
| 1219 | } |
| 1220 | |
| 1221 | // Start with a 1MB buffer and keep doubling it until we can fit the |
| 1222 | // entire stacktrace, stopping early once we reach 64MB. |
| 1223 | buf := make([]byte, 1_000_000) |
| 1224 | stacklen := 0 |
| 1225 | for { |
| 1226 | stacklen = runtime.Stack(buf, true) |
| 1227 | if stacklen < len(buf) { |
| 1228 | break |
| 1229 | } |
| 1230 | if 2*len(buf) > 64_000_000 { |
| 1231 | // Write a message to the end of the buffer saying that it was |
| 1232 | // truncated. |
| 1233 | const truncatedMsg = "\n\n\nstack trace truncated due to size\n" |
| 1234 | copy(buf[len(buf)-len(truncatedMsg):], truncatedMsg) |
| 1235 | break |
| 1236 | } |
| 1237 | buf = make([]byte, 2*len(buf)) |
| 1238 | } |
| 1239 | |
| 1240 | _, _ = fmt.Fprintf(os.Stderr, "%s:\n%s\n", sigStr, buf[:stacklen]) |
| 1241 | |
| 1242 | // Write to a well-known file. |
| 1243 | dir, err := os.UserHomeDir() |
| 1244 | if err != nil { |
| 1245 | dir = os.TempDir() |
| 1246 | } |
| 1247 | // Make the time filesystem-safe, for example ":" is not |
| 1248 | // permitted on many filesystems. Note that Z here only appends |
| 1249 | // Z to the string, it does not actually change the time zone. |