github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xruntime/xruntime.go (about) 1 package xruntime 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "log" 8 "net" 9 "os" 10 "path/filepath" 11 "reflect" 12 "runtime" 13 "strconv" 14 "strings" 15 "syscall" 16 "unsafe" 17 ) 18 19 // ================ 20 // function related 21 // ================ 22 23 // NameOfFunction returns given function's name by searching runtime.Func by PC. 24 func NameOfFunction(f interface{}) string { 25 return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() 26 } 27 28 // =================== 29 // trace stack related 30 // =================== 31 32 // RawStack returns the raw debug trace stack of the calling goroutine from runtime.Stack, if all is true, it will also return other 33 // goroutines' trace stack. Also see debug.Stack and runtime.Stack for more information. 34 // 35 // Returned value is just like: 36 // goroutine 19 [running]: 37 // github.com/Aoi-hosizora/ahlib/xruntime.RawStack(0x7?) 38 // .../xruntime/xruntime.go:46 +0x6a 39 // github.com/Aoi-hosizora/ahlib/xruntime.TestRawStack(0x0?) 40 // .../xruntime/xruntime_test.go:65 +0x30 41 // testing.tRunner(0xc000085380, 0xf682b0) 42 // .../src/testing/testing.go:1439 +0x102 43 // created by testing.(*T).Run 44 // .../src/testing/testing.go:1486 +0x35f 45 func RawStack(all bool) []byte { 46 buf := make([]byte, 1024) 47 for { 48 n := runtime.Stack(buf, all) 49 if n < len(buf) { 50 return buf[:n] 51 } 52 buf = make([]byte, 2*len(buf)) 53 } 54 } 55 56 // TraceFrame represents a frame of the runtime trace stack, also see RuntimeTraceStack. 57 type TraceFrame struct { 58 // Index represents the index of the frame in stack, 0 identifying the caller of this xruntime package. 59 Index int 60 61 // FuncPC represents the function's program count. 62 FuncPC uintptr 63 64 // FuncFullName represents the function's fill name, including the package full name and the function name. 65 FuncFullName string 66 67 // FuncName represents the function's name, including the package short name and the function name. 68 FuncName string 69 70 // Filename represents the file's full name. 71 Filename string 72 73 // LineIndex represents the line number in the file, starts from 1. 74 LineIndex int 75 76 // LineText represents the line text in the fileļ¼"?" if the text cannot be got. 77 LineText string 78 } 79 80 // String returns the formatted TraceFrame. 81 // 82 // Returned value is just like: 83 // File: .../xruntime/xruntime_test.go:145 84 // Func: github.com/Aoi-hosizora/ahlib/xruntime.TestTraceStack.func1 85 // stack := RuntimeTraceStack(0) 86 func (t *TraceFrame) String() string { 87 return fmt.Sprintf("File: %s:%d\nFunc: %s\n\t%s", t.Filename, t.LineIndex, t.FuncFullName, t.LineText) 88 } 89 90 // TraceStack represents the runtime trace stack, consists of some TraceFrame, also see RuntimeTraceStack. 91 type TraceStack []*TraceFrame 92 93 // String returns the formatted TraceStack. 94 // 95 // Returned value is just like: 96 // File: .../xruntime/xruntime_test.go:145 97 // Func: github.com/Aoi-hosizora/ahlib/xruntime.TestTraceStack.func1 98 // stack := RuntimeTraceStack(0) 99 // File: .../xruntime/xruntime_test.go:147 100 // Func: github.com/Aoi-hosizora/ahlib/xruntime.TestTraceStack 101 // }() 102 // File: .../src/testing/testing.go:1439 103 // Func: testing.tRunner 104 // fn(t) 105 // File: .../src/runtime/asm_amd64.s:1571 106 // Func: runtime.goexit 107 // BYTE $0x90 // NOP 108 func (t TraceStack) String() string { 109 l := len(t) 110 sb := strings.Builder{} 111 for i, frame := range t { 112 sb.WriteString(frame.String()) 113 if i != l-1 { 114 sb.WriteString("\n") 115 } 116 } 117 return sb.String() 118 } 119 120 // RuntimeTraceStack returns TraceStack (a slice of TraceFrame) from runtime.Caller using given skip (0 identifying the caller of RuntimeTraceStack). 121 func RuntimeTraceStack(skip uint) TraceStack { 122 frames := make([]*TraceFrame, 0) 123 for i := skip + 1; ; i++ { 124 funcPC, filename, lineIndex, ok := runtime.Caller(int(i)) 125 if !ok { 126 break 127 } 128 129 // func 130 funcObj := runtime.FuncForPC(funcPC) 131 funcFullName := funcObj.Name() 132 _, funcName := filepath.Split(funcFullName) 133 134 // file 135 lineText := "?" 136 if filename != "" { 137 if data, err := ioutil.ReadFile(filename); err == nil { 138 lines := bytes.Split(data, []byte{'\n'}) 139 if lineIndex > 0 && lineIndex <= len(lines) { 140 lineText = string(bytes.TrimSpace(lines[lineIndex-1])) 141 } 142 } 143 } 144 145 // out 146 frame := &TraceFrame{Index: int(i), FuncPC: funcPC, FuncFullName: funcFullName, FuncName: funcName, Filename: filename, LineIndex: lineIndex, LineText: lineText} 147 frames = append(frames, frame) 148 } 149 150 return frames 151 } 152 153 // RuntimeTraceStackWithInfo returns TraceStack (a slice of TraceFrame) from runtime.Caller using given skip, with some information of the first TraceFrame's. 154 func RuntimeTraceStackWithInfo(skip uint) (stack TraceStack, filename string, funcName string, lineIndex int, lineText string) { 155 stack = RuntimeTraceStack(skip + 1) 156 if len(stack) == 0 { 157 return []*TraceFrame{}, "", "", 0, "" 158 } 159 top := stack[0] 160 return stack, top.Filename, top.FuncName, top.LineIndex, top.LineText 161 } 162 163 // ======================== 164 // hack hide string related 165 // ======================== 166 167 var ( 168 // _magicBytes represents a slice of bytes, and are used in HackHideString and HackGetHiddenString. 169 _magicBytes = []byte{0, 'h', 'i', 0, 'd', 'e'} 170 171 _magicLength = len(_magicBytes) 172 ) 173 174 // HackHideString hides given hidden string after given data address (in heap space) and returns the new data address. Note that this is an unsafe function. 175 // 176 // Example: 177 // handler := func() {} 178 // funcSize := int(reflect.TypeOf(func() {}).Size()) // which always equals to 8 in x86-64 machine 179 // handlerFn := (*struct{fn uintptr})(unsafe.Pointer(&handler)).fn 180 // hackedFn := HackHideString(handlerFn, funcSize, "handlerName") 181 // hackedHandler := *(*func())(unsafe.Pointer(&struct{fn uintptr}{fn: hackedFn})) 182 // realHandlerName := HackGetHiddenString(hackedFn, funcSize) // got "handlerName" 183 // hackedHandler() // hackedHandler can be invoked normally 184 //go:nosplit 185 func HackHideString(given uintptr, givenLength int, hidden string) (dataAddr uintptr) { 186 hiddenHeader := (*reflect.StringHeader)(unsafe.Pointer(&hidden)) 187 hiddenLen := int32(hiddenHeader.Len) // only store int32 length 188 hiddenBs := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: hiddenHeader.Data, Len: int(hiddenLen), Cap: int(hiddenLen)})) 189 bs := bytes.Buffer{} 190 bs.Grow(givenLength + _magicLength + 4 + int(hiddenLen)) 191 192 // given 193 for i := 0; i < givenLength; i++ { 194 bs.WriteByte(*(*byte)(unsafe.Pointer(given + uintptr(i)))) 195 } 196 bs.Write(_magicBytes) // \x00, 'h', 'i', \x00, 'd', 'e', # = 6 197 198 // hidden 199 bs.Write([]byte{byte(hiddenLen), byte(hiddenLen >> 8), byte(hiddenLen >> 16), byte(hiddenLen >> 24)}) // lo -> hi, # = 4 200 bs.Write(hiddenBs) 201 202 fakeSlice := bs.Bytes() // its Data part will be used to interpret as other types 203 return (*reflect.SliceHeader)(unsafe.Pointer(&fakeSlice)).Data 204 } 205 206 // HackHideStringAfterString hides given hidden string after given string (in heap space) and returns the new string. Note that this is an unsafe function. 207 // 208 // Example: 209 // httpMethod := "GET" 210 // hackedMethod := xruntime.HackHideStringAfterString(httpMethod, "handlerName") 211 // realHandlerName := xruntime.HackGetHiddenStringAfterString(hackedMethod) // got "handlerName" 212 // fmt.Println(hackedMethod) // hackedMethod can be printed normally 213 //go:nosplit 214 func HackHideStringAfterString(given, hidden string) string { 215 givenHeader := (*reflect.StringHeader)(unsafe.Pointer(&given)) 216 fakeData := HackHideString(givenHeader.Data, givenHeader.Len, hidden) 217 fakeHeader := &reflect.StringHeader{Data: fakeData, Len: givenHeader.Len} 218 return *(*string)(unsafe.Pointer(fakeHeader)) 219 } 220 221 // HackGetHiddenString get the hidden string from given unsafe.Pointer, see HackHideString for example. Note that this is an unsafe function. 222 //go:nosplit 223 func HackGetHiddenString(given uintptr, givenLength int) (hidden string) { 224 // check magic 225 for i, ch := range _magicBytes { 226 if *(*byte)(unsafe.Pointer(given + uintptr(givenLength+i))) != ch { 227 return "" 228 } 229 } 230 231 // get hidden data 232 digits := *(*[4]byte)(unsafe.Pointer(given + uintptr(givenLength+_magicLength))) // lo -> hi 233 hiddenData := given + uintptr(givenLength+_magicLength+4) 234 hiddenLen := int32(digits[0]) + int32(digits[1])<<8 + int32(digits[2])<<16 + int32(digits[3])<<24 235 236 // construct string 237 fakeHeader := &reflect.StringHeader{Data: hiddenData, Len: int(hiddenLen)} 238 return *(*string)(unsafe.Pointer(fakeHeader)) 239 } 240 241 // HackGetHiddenStringAfterString get the hidden string from given string, see HackHideStringAfterString for example. Note that this is an unsafe function. 242 //go:nosplit 243 func HackGetHiddenStringAfterString(given string) (hidden string) { 244 givenHeader := (*reflect.StringHeader)(unsafe.Pointer(&given)) 245 return HackGetHiddenString(givenHeader.Data, givenHeader.Len) 246 } 247 248 // ============================ 249 // mass constants and functions 250 // ============================ 251 252 // Pprof profile names, see pprof.Lookup (runtime/pprof) and pprof.Handler (net/http/pprof) for more details. 253 const ( 254 PprofGoroutineProfile = "goroutine" 255 PprofThreadcreateProfile = "threadcreate" 256 PprofHeapProfile = "heap" 257 PprofAllocsProfile = "allocs" 258 PprofBlockProfle = "block" 259 PprofMutexProfile = "mutex" 260 ) 261 262 // signalNames is used by SignalName. 263 var signalNames = [...]string{ 264 1: "SIGHUP", 265 2: "SIGINT", 266 3: "SIGQUIT", 267 4: "SIGILL", 268 5: "SIGTRAP", 269 6: "SIGABRT", 270 7: "SIGBUS", 271 8: "SIGFPE", 272 9: "SIGKILL", 273 10: "SIGUSR1", 274 11: "SIGSEGV", 275 12: "SIGUSR2", 276 13: "SIGPIPE", 277 14: "SIGALRM", 278 15: "SIGTERM", 279 } 280 281 // signalReadableNames is used by SignalReadableName. 282 var signalReadableNames = [...]string{ 283 1: "hangup", 284 2: "interrupt", 285 3: "quit", 286 4: "illegal instruction", 287 5: "trace/breakpoint trap", 288 6: "aborted", 289 7: "bus error", 290 8: "floating point exception", 291 9: "killed", 292 10: "user defined signal 1", 293 11: "segmentation fault", 294 12: "user defined signal 2", 295 13: "broken pipe", 296 14: "alarm clock", 297 15: "terminated", 298 } 299 300 // SignalName returns the SIGXXX string from given syscall.Signal. Note that syscall.Signal.String() and xruntime.SignalReadableName 301 // will return the human-readable string value. 302 func SignalName(sig syscall.Signal) string { 303 if sig >= 1 && int(sig) < len(signalNames) { 304 return signalNames[sig] 305 } 306 return "signal " + strconv.Itoa(int(sig)) 307 } 308 309 // SignalReadableName returns the human-readable name from given syscall.Signal, this function has the same result with syscall.Signal.String(). 310 func SignalReadableName(sig syscall.Signal) string { 311 if sig >= 1 && int(sig) < len(signalReadableNames) { 312 return signalReadableNames[sig] 313 } 314 return "signal " + strconv.Itoa(int(sig)) 315 } 316 317 // ProxyEnv represents some proxy environment variables, returned by GetProxyEnv. 318 type ProxyEnv struct { 319 NoProxy string 320 HttpProxy string 321 HttpsProxy string 322 SocksProxy string 323 } 324 325 // GetProxyEnv lookups and returns four proxy environments, including no_proxy, http_proxy, https_proxy and socks_proxy. 326 func GetProxyEnv() *ProxyEnv { 327 np := strings.TrimSpace(os.Getenv("no_proxy")) 328 hp := strings.TrimSpace(os.Getenv("http_proxy")) 329 hsp := strings.TrimSpace(os.Getenv("https_proxy")) 330 ssp := strings.TrimSpace(os.Getenv("socks_proxy")) 331 return &ProxyEnv{NoProxy: np, HttpProxy: hp, HttpsProxy: hsp, SocksProxy: ssp} 332 } 333 334 // PrintLog prints each proxy variables if it is not empty, using given logFunc (default to log.Println) and prefix (default to empty). 335 // 336 // Example: 337 // proxyEnv := xruntime.GetProxyEnv() 338 // proxyEnv.PrintLog(nil, "[Gin] ") 339 func (p *ProxyEnv) PrintLog(logFunc func(string), prefix string) { 340 if logFunc == nil { 341 logFunc = func(s string) { 342 log.Println(s) 343 } 344 } 345 346 if p.NoProxy != "" { 347 logFunc(fmt.Sprintf("%sUsing no_proxy: %s", prefix, p.NoProxy)) 348 } 349 if p.HttpProxy != "" { 350 logFunc(fmt.Sprintf("%sUsing http_proxy: %s", prefix, p.HttpProxy)) 351 } 352 if p.HttpsProxy != "" { 353 logFunc(fmt.Sprintf("%sUsing https_proxy: %s", prefix, p.HttpsProxy)) 354 } 355 if p.SocksProxy != "" { 356 logFunc(fmt.Sprintf("%sUsing socks_proxy: %s", prefix, p.SocksProxy)) 357 } 358 } 359 360 // NetAddrType describes a concrete address type, including TCPAddrType, UDPAddrType, IPAddrType and UnixAddrType. 361 type NetAddrType string 362 363 const ( 364 // TCPAddrType represents a net.TCPAddr concrete address type. 365 TCPAddrType NetAddrType = "TCPAddr" 366 367 // UDPAddrType represents a net.UDPAddr concrete address type. 368 UDPAddrType NetAddrType = "UDPAddr" 369 370 // IPAddrType represents a net.IPAddr concrete address type. 371 IPAddrType NetAddrType = "IPAddr" 372 373 // UnixAddrType represents a net.UnixAddr concrete address type. 374 UnixAddrType NetAddrType = "UnixAddr" 375 ) 376 377 // String returns the string value of NetAddrType. 378 func (n NetAddrType) String() string { 379 return string(n) 380 } 381 382 // ConcreteNetAddr represents a concrete net.Addr type, which includes implementation of net.TCPAddr, net.UDPAddr, net.IPAddr and net.UnixAddr. 383 type ConcreteNetAddr struct { 384 Type NetAddrType 385 386 TCPAddr *net.TCPAddr 387 UDPAddr *net.UDPAddr 388 IPAddr *net.IPAddr 389 UnixAddr *net.UnixAddr 390 391 // for TCP, UDP, IP 392 IP net.IP 393 Zone string 394 395 // for TCP, UDP 396 Port int 397 398 // for Unix 399 Name string 400 Net string 401 } 402 403 // ParseNetAddr parses given net.Addr value to ConcreteNetAddr, returns error if given address is not TCP, UDP, IP, Unix address. 404 func ParseNetAddr(addr net.Addr) (*ConcreteNetAddr, bool) { 405 if addr == nil { 406 return nil, false 407 } 408 409 var out *ConcreteNetAddr 410 switch addr := addr.(type) { 411 case *net.TCPAddr: 412 out = &ConcreteNetAddr{Type: TCPAddrType, TCPAddr: addr, IP: addr.IP, Zone: addr.Zone, Port: addr.Port} 413 case *net.UDPAddr: 414 out = &ConcreteNetAddr{Type: UDPAddrType, UDPAddr: addr, IP: addr.IP, Zone: addr.Zone, Port: addr.Port} 415 case *net.IPAddr: 416 out = &ConcreteNetAddr{Type: IPAddrType, IPAddr: addr, IP: addr.IP, Zone: addr.Zone} 417 case *net.UnixAddr: 418 out = &ConcreteNetAddr{Type: UnixAddrType, UnixAddr: addr, Name: addr.Name, Net: addr.Net} 419 default: 420 return nil, false 421 } 422 return out, true 423 }