envpprof.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package envpprof
  2. import (
  3. "expvar"
  4. "fmt"
  5. "io/ioutil"
  6. "net"
  7. "net/http"
  8. _ "net/http/pprof"
  9. "os"
  10. "path/filepath"
  11. "runtime"
  12. "runtime/pprof"
  13. "runtime/trace"
  14. "strings"
  15. "github.com/anacrolix/log"
  16. )
  17. var (
  18. pprofDir = filepath.Join(os.Getenv("HOME"), "pprof")
  19. heap bool
  20. )
  21. func writeHeapProfile() {
  22. os.Mkdir(pprofDir, 0750)
  23. f, err := ioutil.TempFile(pprofDir, "heap")
  24. if err != nil {
  25. log.Printf("error creating heap profile file: %s", err)
  26. return
  27. }
  28. defer f.Close()
  29. pprof.WriteHeapProfile(f)
  30. log.Printf("wrote heap profile to %q", f.Name())
  31. }
  32. // Stop ends CPU profiling, waiting for writes to complete. If heap profiling is enabled, it also
  33. // writes the heap profile to a file. Stop should be deferred from main if cpu or heap profiling
  34. // are to be used through envpprof.
  35. func Stop() {
  36. // Should we check if CPU profiling was initiated through this package?
  37. pprof.StopCPUProfile()
  38. trace.Stop()
  39. if heap {
  40. // Can or should we do this concurrently with stopping CPU profiling?
  41. writeHeapProfile()
  42. }
  43. }
  44. func startHTTP(value string) {
  45. var l net.Listener
  46. if value == "" {
  47. for port := uint16(6061); port != 6060; port++ {
  48. var err error
  49. l, err = net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
  50. if err == nil {
  51. break
  52. }
  53. }
  54. if l == nil {
  55. log.Print("unable to create envpprof listener for http")
  56. return
  57. }
  58. } else {
  59. var addr string
  60. _, _, err := net.SplitHostPort(value)
  61. if err == nil {
  62. addr = value
  63. } else {
  64. addr = "localhost:" + value
  65. }
  66. l, err = net.Listen("tcp", addr)
  67. if err != nil {
  68. panic(err)
  69. }
  70. }
  71. log.Printf("(pid=%d) envpprof serving http://%s", os.Getpid(), l.Addr())
  72. go func() {
  73. defer l.Close()
  74. log.Printf("error serving http on envpprof listener: %s", http.Serve(l, nil))
  75. }()
  76. }
  77. func init() {
  78. expvar.Publish("numGoroutine", expvar.Func(func() interface{} { return runtime.NumGoroutine() }))
  79. _var := os.Getenv("GOPPROF")
  80. if _var == "" {
  81. return
  82. }
  83. for _, item := range strings.Split(os.Getenv("GOPPROF"), ",") {
  84. equalsPos := strings.IndexByte(item, '=')
  85. var key, value string
  86. if equalsPos < 0 {
  87. key = item
  88. } else {
  89. key = item[:equalsPos]
  90. value = item[equalsPos+1:]
  91. }
  92. switch key {
  93. case "http":
  94. startHTTP(value)
  95. case "cpu":
  96. os.Mkdir(pprofDir, 0750)
  97. f, err := ioutil.TempFile(pprofDir, "cpu")
  98. if err != nil {
  99. log.Printf("error creating cpu pprof file: %s", err)
  100. break
  101. }
  102. err = pprof.StartCPUProfile(f)
  103. if err != nil {
  104. log.Printf("error starting cpu profiling: %s", err)
  105. break
  106. }
  107. log.Printf("cpu profiling to file %q", f.Name())
  108. case "block":
  109. // Taken from Safe Rate at
  110. // https://github.com/DataDog/go-profiler-notes/blob/main/guide/README.md#go-profilers.
  111. runtime.SetBlockProfileRate(10000)
  112. case "heap":
  113. heap = true
  114. case "mutex":
  115. // Taken from Safe Rate at
  116. // https://github.com/DataDog/go-profiler-notes/blob/main/guide/README.md#go-profilers.
  117. runtime.SetMutexProfileFraction(100)
  118. case "trace":
  119. f, err := ioutil.TempFile(pprofDir, "trace")
  120. if err != nil {
  121. log.Printf("error creating trace file: %v", err)
  122. break
  123. }
  124. err = trace.Start(f)
  125. if err != nil {
  126. log.Printf("error starting tracing: %v", err)
  127. break
  128. }
  129. log.Printf("tracing to file %q", f.Name())
  130. default:
  131. log.Printf("unexpected GOPPROF key %q", key)
  132. }
  133. }
  134. }