stackdump.go 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. package stack // import "github.com/docker/docker/pkg/stack"
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "runtime"
  7. "strings"
  8. "time"
  9. "github.com/pkg/errors"
  10. )
  11. const stacksLogNameTemplate = "goroutine-stacks-%s.log"
  12. // Dump outputs the runtime stack to os.StdErr.
  13. func Dump() {
  14. _ = dump(os.Stderr)
  15. }
  16. // DumpToFile appends the runtime stack into a file named "goroutine-stacks-<timestamp>.log"
  17. // in dir and returns the full path to that file. If no directory name is
  18. // provided, it outputs to os.Stderr.
  19. func DumpToFile(dir string) (string, error) {
  20. var f *os.File
  21. if dir != "" {
  22. path := filepath.Join(dir, fmt.Sprintf(stacksLogNameTemplate, strings.ReplaceAll(time.Now().Format(time.RFC3339), ":", "")))
  23. var err error
  24. f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
  25. if err != nil {
  26. return "", errors.Wrap(err, "failed to open file to write the goroutine stacks")
  27. }
  28. defer f.Close()
  29. defer f.Sync()
  30. } else {
  31. f = os.Stderr
  32. }
  33. return f.Name(), dump(f)
  34. }
  35. func dump(f *os.File) error {
  36. var (
  37. buf []byte
  38. stackSize int
  39. )
  40. bufferLen := 16384
  41. for stackSize == len(buf) {
  42. buf = make([]byte, bufferLen)
  43. stackSize = runtime.Stack(buf, true)
  44. bufferLen *= 2
  45. }
  46. buf = buf[:stackSize]
  47. if _, err := f.Write(buf); err != nil {
  48. return errors.Wrap(err, "failed to write goroutine stacks")
  49. }
  50. return nil
  51. }