sync.go 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. // Package sync is an extension of the stdlib "sync" package. It has extra
  2. // functionality that helps debug the use of synchronization primitives. The
  3. // package should be importable in place of "sync". The extra functionality
  4. // can be enabled by calling Enable() or passing a non-empty PPROF_SYNC
  5. // environment variable to the process.
  6. //
  7. // Several profiles are exposed on the default HTTP muxer (and to
  8. // "/debug/pprof" when "net/http/pprof" is imported by the process).
  9. // "lockHolders" lists the stack traces of goroutines that called Mutex.Lock
  10. // that haven't subsequently been Unlocked. "lockBlockers" contains goroutines
  11. // that are waiting to obtain locks. "/debug/lockTimes" or PrintLockTimes()
  12. // shows the longest time a lock is held for each stack trace.
  13. //
  14. // Note that currently RWMutex is treated like a Mutex when the package is
  15. // enabled.
  16. package sync
  17. import (
  18. "fmt"
  19. "io"
  20. "net/http"
  21. "os"
  22. "runtime/pprof"
  23. "strings"
  24. "sync"
  25. "text/tabwriter"
  26. "github.com/anacrolix/missinggo"
  27. )
  28. var (
  29. // Protects initialization and enabling of the package.
  30. enableMu sync.Mutex
  31. // Whether shared locks must be handled as exclusive locks.
  32. noSharedLocking = false
  33. contentionOn = false
  34. lockTimesOn = false
  35. // Current lock holders.
  36. lockHolders *pprof.Profile
  37. // Those blocked on acquiring a lock.
  38. lockBlockers *pprof.Profile
  39. )
  40. // Writes out the longest time a Mutex remains locked for each stack trace
  41. // that locks a Mutex.
  42. func PrintLockTimes(w io.Writer) {
  43. lockTimes := sortedLockTimes()
  44. tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
  45. defer tw.Flush()
  46. w = tw
  47. for _, elem := range lockTimes {
  48. fmt.Fprintf(w, "%s (%s * %d [%s, %s])\n", elem.Total, elem.MeanTime(), elem.Count, elem.Min, elem.Max)
  49. missinggo.WriteStack(w, elem.stack[:])
  50. }
  51. }
  52. func Enable() {
  53. EnableContention()
  54. EnableLockTimes()
  55. }
  56. func EnableContention() {
  57. lockHolders = pprof.NewProfile("lockHolders")
  58. lockBlockers = pprof.NewProfile("lockBlockers")
  59. noSharedLocking = true
  60. contentionOn = true
  61. }
  62. func EnableLockTimes() {
  63. lockStatsByStack = make(map[lockStackKey]lockStats)
  64. http.DefaultServeMux.HandleFunc("/debug/lockTimes", func(w http.ResponseWriter, r *http.Request) {
  65. PrintLockTimes(w)
  66. })
  67. noSharedLocking = true
  68. lockTimesOn = true
  69. }
  70. func init() {
  71. env := os.Getenv("PPROF_SYNC")
  72. all := true
  73. if strings.Contains(env, "times") {
  74. EnableLockTimes()
  75. all = false
  76. }
  77. if strings.Contains(env, "contention") {
  78. EnableContention()
  79. all = false
  80. }
  81. if all && env != "" {
  82. Enable()
  83. }
  84. }