probes.go (2810B)
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 5 package libc // import "modernc.org/libc" 6 7 import ( 8 "bytes" 9 "fmt" 10 "math" 11 "runtime/debug" 12 "sort" 13 "strings" 14 "sync" 15 "sync/atomic" 16 17 "github.com/dustin/go-humanize" 18 ) 19 20 type PerfCounter struct { 21 a []int32 22 labels []string 23 24 enabled bool 25 } 26 27 func NewPerfCounter(labels []string) *PerfCounter { 28 return &PerfCounter{ 29 a: make([]int32, len(labels)), 30 labels: labels, 31 enabled: true, 32 } 33 } 34 35 func (c *PerfCounter) Disable() { c.enabled = false } 36 func (c *PerfCounter) Enable() { c.enabled = true } 37 38 func (c *PerfCounter) Clear() { 39 for i := range c.a { 40 c.a[i] = 0 41 } 42 } 43 44 func (c *PerfCounter) Inc(n int) { 45 if c.enabled { 46 atomic.AddInt32(&c.a[n], 1) 47 } 48 } 49 50 func (c *PerfCounter) IncN(n, m int) { 51 if c.enabled { 52 atomic.AddInt32(&c.a[n], int32(m)) 53 } 54 } 55 56 func (c *PerfCounter) String() string { 57 sv := c.enabled 58 59 defer func() { c.enabled = sv }() 60 61 c.enabled = false 62 63 var a []string 64 for i, v := range c.a { 65 if v != 0 { 66 a = append(a, fmt.Sprintf("%q: %s", c.labels[i], h(v))) 67 } 68 } 69 return fmt.Sprint(a) 70 } 71 72 func h(v interface{}) string { 73 switch x := v.(type) { 74 case int: 75 return humanize.Comma(int64(x)) 76 case int32: 77 return humanize.Comma(int64(x)) 78 case int64: 79 return humanize.Comma(x) 80 case uint32: 81 return humanize.Comma(int64(x)) 82 case uint64: 83 if x <= math.MaxInt64 { 84 return humanize.Comma(int64(x)) 85 } 86 87 return "-" + humanize.Comma(-int64(x)) 88 } 89 return fmt.Sprint(v) 90 } 91 92 type StackCapture struct { 93 sync.Mutex 94 m map[string]int 95 96 depth int 97 98 enabled bool 99 } 100 101 func NewStackCapture(depth int) *StackCapture { 102 return &StackCapture{ 103 m: map[string]int{}, 104 depth: depth, 105 enabled: true, 106 } 107 } 108 109 func (c *StackCapture) Disable() { c.enabled = false } 110 func (c *StackCapture) Enable() { c.enabled = true } 111 112 func (c *StackCapture) Clear() { 113 c.Lock() 114 115 defer c.Unlock() 116 c.m = map[string]int{} 117 } 118 119 var ( 120 stackCapturePrefix = []byte("\n\t") 121 ) 122 123 func (c *StackCapture) Record() { 124 if !c.enabled { 125 return 126 } 127 128 b := debug.Stack() 129 var s strings.Builder 130 out: 131 for i := 0; len(b) > 0 && i < c.depth+2; i++ { 132 l := bytes.Index(b, stackCapturePrefix) 133 if l < 0 { 134 break out 135 } 136 137 b = b[l+len(stackCapturePrefix):] 138 h := bytes.IndexByte(b, '\n') 139 if h < 0 { 140 h = len(b) 141 } 142 if i > 1 { 143 fmt.Fprintf(&s, "\n\t%s", b[:h]) 144 } 145 b = b[h:] 146 } 147 c.Lock() 148 149 defer c.Unlock() 150 151 c.m[s.String()]++ 152 } 153 154 func (c *StackCapture) String() string { 155 c.Lock() 156 157 defer c.Unlock() 158 159 var b strings.Builder 160 var a []string 161 for k := range c.m { 162 a = append(a, k) 163 } 164 sort.Slice(a, func(i, j int) bool { return c.m[a[i]] < c.m[a[j]] }) 165 for _, k := range a { 166 fmt.Fprintf(&b, "%d%s\n", c.m[k], k) 167 } 168 return b.String() 169 }