mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 09:02:25 -06:00 
			
		
		
		
	
		
			
	
	
		
			137 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			137 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package caller
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"runtime"
							 | 
						||
| 
								 | 
							
									"strings"
							 | 
						||
| 
								 | 
							
									"sync/atomic"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var (
							 | 
						||
| 
								 | 
							
									// callerCache caches PC values to string names.
							 | 
						||
| 
								 | 
							
									// note this may be a little slower than Caller()
							 | 
						||
| 
								 | 
							
									// calls on startup, but after all PCs are cached
							 | 
						||
| 
								 | 
							
									// this should be ~3x faster + less GC overhead.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// see the following benchmark:
							 | 
						||
| 
								 | 
							
									// goos: linux
							 | 
						||
| 
								 | 
							
									// goarch: amd64
							 | 
						||
| 
								 | 
							
									// pkg: codeberg.org/gruf/go-caller
							 | 
						||
| 
								 | 
							
									// cpu: AMD Ryzen 7 7840U w/ Radeon  780M Graphics
							 | 
						||
| 
								 | 
							
									// BenchmarkCallerCache
							 | 
						||
| 
								 | 
							
									// BenchmarkCallerCache-16         16796982                66.19 ns/op           24 B/op          3 allocs/op
							 | 
						||
| 
								 | 
							
									// BenchmarkNoCallerCache
							 | 
						||
| 
								 | 
							
									// BenchmarkNoCallerCache-16        5486168               219.9 ns/op           744 B/op          6 allocs/op
							 | 
						||
| 
								 | 
							
									callerCache atomic.Pointer[map[uintptr]string]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// stringCache caches strings to minimise string memory use
							 | 
						||
| 
								 | 
							
									// by ensuring only 1 instance of the same func name string.
							 | 
						||
| 
								 | 
							
									stringCache atomic.Pointer[map[string]string]
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Clear will empty the global caller PC -> func names cache.
							 | 
						||
| 
								 | 
							
								func Clear() { callerCache.Store(nil); stringCache.Store(nil) }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Name returns the calling function name for given
							 | 
						||
| 
								 | 
							
								// program counter, formatted to be useful for logging.
							 | 
						||
| 
								 | 
							
								func Name(pc uintptr) string {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Get frame iterator for program counter.
							 | 
						||
| 
								 | 
							
									frames := runtime.CallersFrames([]uintptr{pc})
							 | 
						||
| 
								 | 
							
									if frames == nil {
							 | 
						||
| 
								 | 
							
										return "???"
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Get func name from frame.
							 | 
						||
| 
								 | 
							
									frame, _ := frames.Next()
							 | 
						||
| 
								 | 
							
									name := frame.Function
							 | 
						||
| 
								 | 
							
									if name == "" {
							 | 
						||
| 
								 | 
							
										return "???"
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Drop all but package and function name, no path.
							 | 
						||
| 
								 | 
							
									if idx := strings.LastIndex(name, "/"); idx >= 0 {
							 | 
						||
| 
								 | 
							
										name = name[idx+1:]
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const params = `[...]`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Drop any function generic type parameter markers.
							 | 
						||
| 
								 | 
							
									if idx := strings.Index(name, params); idx >= 0 {
							 | 
						||
| 
								 | 
							
										name = name[:idx] + name[idx+len(params):]
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return name
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Get will return calling func information for given PC value,
							 | 
						||
| 
								 | 
							
								// caching func names by their PC values to reduce calls to Caller().
							 | 
						||
| 
								 | 
							
								func Get(pc uintptr) string {
							 | 
						||
| 
								 | 
							
									var cache map[uintptr]string
							 | 
						||
| 
								 | 
							
									for {
							 | 
						||
| 
								 | 
							
										// Load caller cache map.
							 | 
						||
| 
								 | 
							
										ptr := callerCache.Load()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ptr != nil {
							 | 
						||
| 
								 | 
							
											// Look for stored name.
							 | 
						||
| 
								 | 
							
											name, ok := (*ptr)[pc]
							 | 
						||
| 
								 | 
							
											if ok {
							 | 
						||
| 
								 | 
							
												return name
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Make a clone of existing caller cache map.
							 | 
						||
| 
								 | 
							
											cache = make(map[uintptr]string, len(*ptr)+1)
							 | 
						||
| 
								 | 
							
											for key, value := range *ptr {
							 | 
						||
| 
								 | 
							
												cache[key] = value
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											// Allocate new caller cache map.
							 | 
						||
| 
								 | 
							
											cache = make(map[uintptr]string, 1)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Calculate caller
							 | 
						||
| 
								 | 
							
										// name for PC value.
							 | 
						||
| 
								 | 
							
										name := Name(pc)
							 | 
						||
| 
								 | 
							
										name = getString(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Store in map.
							 | 
						||
| 
								 | 
							
										cache[pc] = name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Attempt to update caller cache map pointer.
							 | 
						||
| 
								 | 
							
										if callerCache.CompareAndSwap(ptr, &cache) {
							 | 
						||
| 
								 | 
							
											return name
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func getString(key string) string {
							 | 
						||
| 
								 | 
							
									var cache map[string]string
							 | 
						||
| 
								 | 
							
									for {
							 | 
						||
| 
								 | 
							
										// Load string cache map.
							 | 
						||
| 
								 | 
							
										ptr := stringCache.Load()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ptr != nil {
							 | 
						||
| 
								 | 
							
											// Check for existing string.
							 | 
						||
| 
								 | 
							
											if str, ok := (*ptr)[key]; ok {
							 | 
						||
| 
								 | 
							
												return str
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Make a clone of existing string cache map.
							 | 
						||
| 
								 | 
							
											cache = make(map[string]string, len(*ptr)+1)
							 | 
						||
| 
								 | 
							
											for key, value := range *ptr {
							 | 
						||
| 
								 | 
							
												cache[key] = value
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											// Allocate new string cache map.
							 | 
						||
| 
								 | 
							
											cache = make(map[string]string, 1)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Store this str.
							 | 
						||
| 
								 | 
							
										cache[key] = key
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Attempt to update string cache map pointer.
							 | 
						||
| 
								 | 
							
										if stringCache.CompareAndSwap(ptr, &cache) {
							 | 
						||
| 
								 | 
							
											return key
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |