probes.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright 2022 The Libc Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package libc // import "modernc.org/libc"
  5. import (
  6. "bytes"
  7. "fmt"
  8. "math"
  9. "runtime/debug"
  10. "sort"
  11. "strings"
  12. "sync"
  13. "sync/atomic"
  14. "github.com/dustin/go-humanize"
  15. )
  16. type PerfCounter struct {
  17. a []int32
  18. labels []string
  19. enabled bool
  20. }
  21. func NewPerfCounter(labels []string) *PerfCounter {
  22. return &PerfCounter{
  23. a: make([]int32, len(labels)),
  24. labels: labels,
  25. enabled: true,
  26. }
  27. }
  28. func (c *PerfCounter) Disable() { c.enabled = false }
  29. func (c *PerfCounter) Enable() { c.enabled = true }
  30. func (c *PerfCounter) Clear() {
  31. for i := range c.a {
  32. c.a[i] = 0
  33. }
  34. }
  35. func (c *PerfCounter) Inc(n int) {
  36. if c.enabled {
  37. atomic.AddInt32(&c.a[n], 1)
  38. }
  39. }
  40. func (c *PerfCounter) IncN(n, m int) {
  41. if c.enabled {
  42. atomic.AddInt32(&c.a[n], int32(m))
  43. }
  44. }
  45. func (c *PerfCounter) String() string {
  46. sv := c.enabled
  47. defer func() { c.enabled = sv }()
  48. c.enabled = false
  49. var a []string
  50. for i, v := range c.a {
  51. if v != 0 {
  52. a = append(a, fmt.Sprintf("%q: %s", c.labels[i], h(v)))
  53. }
  54. }
  55. return fmt.Sprint(a)
  56. }
  57. func h(v interface{}) string {
  58. switch x := v.(type) {
  59. case int:
  60. return humanize.Comma(int64(x))
  61. case int32:
  62. return humanize.Comma(int64(x))
  63. case int64:
  64. return humanize.Comma(x)
  65. case uint32:
  66. return humanize.Comma(int64(x))
  67. case uint64:
  68. if x <= math.MaxInt64 {
  69. return humanize.Comma(int64(x))
  70. }
  71. return "-" + humanize.Comma(-int64(x))
  72. }
  73. return fmt.Sprint(v)
  74. }
  75. type StackCapture struct {
  76. sync.Mutex
  77. m map[string]int
  78. depth int
  79. enabled bool
  80. }
  81. func NewStackCapture(depth int) *StackCapture {
  82. return &StackCapture{
  83. m: map[string]int{},
  84. depth: depth,
  85. enabled: true,
  86. }
  87. }
  88. func (c *StackCapture) Disable() { c.enabled = false }
  89. func (c *StackCapture) Enable() { c.enabled = true }
  90. func (c *StackCapture) Clear() {
  91. c.Lock()
  92. defer c.Unlock()
  93. c.m = map[string]int{}
  94. }
  95. var (
  96. stackCapturePrefix = []byte("\n\t")
  97. )
  98. func (c *StackCapture) Record() {
  99. if !c.enabled {
  100. return
  101. }
  102. b := debug.Stack()
  103. var s strings.Builder
  104. out:
  105. for i := 0; len(b) > 0 && i < c.depth+2; i++ {
  106. l := bytes.Index(b, stackCapturePrefix)
  107. if l < 0 {
  108. break out
  109. }
  110. b = b[l+len(stackCapturePrefix):]
  111. h := bytes.IndexByte(b, '\n')
  112. if h < 0 {
  113. h = len(b)
  114. }
  115. if i > 1 {
  116. fmt.Fprintf(&s, "\n\t%s", b[:h])
  117. }
  118. b = b[h:]
  119. }
  120. c.Lock()
  121. defer c.Unlock()
  122. c.m[s.String()]++
  123. }
  124. func (c *StackCapture) String() string {
  125. c.Lock()
  126. defer c.Unlock()
  127. var b strings.Builder
  128. var a []string
  129. for k := range c.m {
  130. a = append(a, k)
  131. }
  132. sort.Slice(a, func(i, j int) bool { return c.m[a[i]] < c.m[a[j]] })
  133. for _, k := range a {
  134. fmt.Fprintf(&b, "%d%s\n", c.m[k], k)
  135. }
  136. return b.String()
  137. }