mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 09:02:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			170 lines
		
	
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			170 lines
		
	
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // Copyright 2022 The Libc Authors. All rights reserved. | ||
|  | // Use of this source code is governed by a BSD-style | ||
|  | // license that can be found in the LICENSE file. | ||
|  | 
 | ||
|  | package libc // import "modernc.org/libc" | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"bytes" | ||
|  | 	"fmt" | ||
|  | 	"math" | ||
|  | 	"runtime/debug" | ||
|  | 	"sort" | ||
|  | 	"strings" | ||
|  | 	"sync" | ||
|  | 	"sync/atomic" | ||
|  | 
 | ||
|  | 	"github.com/dustin/go-humanize" | ||
|  | ) | ||
|  | 
 | ||
|  | type PerfCounter struct { | ||
|  | 	a      []int32 | ||
|  | 	labels []string | ||
|  | 
 | ||
|  | 	enabled bool | ||
|  | } | ||
|  | 
 | ||
|  | func NewPerfCounter(labels []string) *PerfCounter { | ||
|  | 	return &PerfCounter{ | ||
|  | 		a:       make([]int32, len(labels)), | ||
|  | 		labels:  labels, | ||
|  | 		enabled: true, | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (c *PerfCounter) Disable() { c.enabled = false } | ||
|  | func (c *PerfCounter) Enable()  { c.enabled = true } | ||
|  | 
 | ||
|  | func (c *PerfCounter) Clear() { | ||
|  | 	for i := range c.a { | ||
|  | 		c.a[i] = 0 | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (c *PerfCounter) Inc(n int) { | ||
|  | 	if c.enabled { | ||
|  | 		atomic.AddInt32(&c.a[n], 1) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (c *PerfCounter) IncN(n, m int) { | ||
|  | 	if c.enabled { | ||
|  | 		atomic.AddInt32(&c.a[n], int32(m)) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (c *PerfCounter) String() string { | ||
|  | 	sv := c.enabled | ||
|  | 
 | ||
|  | 	defer func() { c.enabled = sv }() | ||
|  | 
 | ||
|  | 	c.enabled = false | ||
|  | 
 | ||
|  | 	var a []string | ||
|  | 	for i, v := range c.a { | ||
|  | 		if v != 0 { | ||
|  | 			a = append(a, fmt.Sprintf("%q: %s", c.labels[i], h(v))) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return fmt.Sprint(a) | ||
|  | } | ||
|  | 
 | ||
|  | func h(v interface{}) string { | ||
|  | 	switch x := v.(type) { | ||
|  | 	case int: | ||
|  | 		return humanize.Comma(int64(x)) | ||
|  | 	case int32: | ||
|  | 		return humanize.Comma(int64(x)) | ||
|  | 	case int64: | ||
|  | 		return humanize.Comma(x) | ||
|  | 	case uint32: | ||
|  | 		return humanize.Comma(int64(x)) | ||
|  | 	case uint64: | ||
|  | 		if x <= math.MaxInt64 { | ||
|  | 			return humanize.Comma(int64(x)) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return "-" + humanize.Comma(-int64(x)) | ||
|  | 	} | ||
|  | 	return fmt.Sprint(v) | ||
|  | } | ||
|  | 
 | ||
|  | type StackCapture struct { | ||
|  | 	sync.Mutex | ||
|  | 	m map[string]int | ||
|  | 
 | ||
|  | 	depth int | ||
|  | 
 | ||
|  | 	enabled bool | ||
|  | } | ||
|  | 
 | ||
|  | func NewStackCapture(depth int) *StackCapture { | ||
|  | 	return &StackCapture{ | ||
|  | 		m:       map[string]int{}, | ||
|  | 		depth:   depth, | ||
|  | 		enabled: true, | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (c *StackCapture) Disable() { c.enabled = false } | ||
|  | func (c *StackCapture) Enable()  { c.enabled = true } | ||
|  | 
 | ||
|  | func (c *StackCapture) Clear() { | ||
|  | 	c.Lock() | ||
|  | 
 | ||
|  | 	defer c.Unlock() | ||
|  | 	c.m = map[string]int{} | ||
|  | } | ||
|  | 
 | ||
|  | var ( | ||
|  | 	stackCapturePrefix = []byte("\n\t") | ||
|  | ) | ||
|  | 
 | ||
|  | func (c *StackCapture) Record() { | ||
|  | 	if !c.enabled { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	b := debug.Stack() | ||
|  | 	var s strings.Builder | ||
|  | out: | ||
|  | 	for i := 0; len(b) > 0 && i < c.depth+2; i++ { | ||
|  | 		l := bytes.Index(b, stackCapturePrefix) | ||
|  | 		if l < 0 { | ||
|  | 			break out | ||
|  | 		} | ||
|  | 
 | ||
|  | 		b = b[l+len(stackCapturePrefix):] | ||
|  | 		h := bytes.IndexByte(b, '\n') | ||
|  | 		if h < 0 { | ||
|  | 			h = len(b) | ||
|  | 		} | ||
|  | 		if i > 1 { | ||
|  | 			fmt.Fprintf(&s, "\n\t%s", b[:h]) | ||
|  | 		} | ||
|  | 		b = b[h:] | ||
|  | 	} | ||
|  | 	c.Lock() | ||
|  | 
 | ||
|  | 	defer c.Unlock() | ||
|  | 
 | ||
|  | 	c.m[s.String()]++ | ||
|  | } | ||
|  | 
 | ||
|  | func (c *StackCapture) String() string { | ||
|  | 	c.Lock() | ||
|  | 
 | ||
|  | 	defer c.Unlock() | ||
|  | 
 | ||
|  | 	var b strings.Builder | ||
|  | 	var a []string | ||
|  | 	for k := range c.m { | ||
|  | 		a = append(a, k) | ||
|  | 	} | ||
|  | 	sort.Slice(a, func(i, j int) bool { return c.m[a[i]] < c.m[a[j]] }) | ||
|  | 	for _, k := range a { | ||
|  | 		fmt.Fprintf(&b, "%d%s\n", c.m[k], k) | ||
|  | 	} | ||
|  | 	return b.String() | ||
|  | } |