github.com/zhongdalu/gf@v1.0.0/g/internal/debug/stack.go (about) 1 // Copyright 2019 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 7 // Package debug contains facilities for programs to debug themselves while 8 // they are running. 9 package debug 10 11 import ( 12 "bytes" 13 "fmt" 14 "runtime" 15 "strings" 16 ) 17 18 const ( 19 gMAX_DEPTH = 1000 20 gFILTER_KEY = "/g/internal/debug/stack.go" 21 ) 22 23 var ( 24 // goRootForFilter is used for stack filtering purpose. 25 goRootForFilter = runtime.GOROOT() 26 ) 27 28 func init() { 29 if goRootForFilter != "" { 30 goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1) 31 } 32 } 33 34 // PrintStack prints to standard error the stack trace returned by runtime.Stack. 35 func PrintStack(skip ...int) { 36 fmt.Print(Stack(skip...)) 37 } 38 39 // Stack returns a formatted stack trace of the goroutine that calls it. 40 // It calls runtime.Stack with a large enough buffer to capture the entire trace. 41 func Stack(skip ...int) string { 42 return StackWithFilter("", skip...) 43 } 44 45 // StackWithFilter returns a formatted stack trace of the goroutine that calls it. 46 // It calls runtime.Stack with a large enough buffer to capture the entire trace. 47 // 48 // The parameter <filter> is used to filter the path of the caller. 49 func StackWithFilter(filter string, skip ...int) string { 50 number := 0 51 if len(skip) > 0 { 52 number = skip[0] 53 } 54 name := "" 55 space := " " 56 index := 1 57 buffer := bytes.NewBuffer(nil) 58 for i := callerFromIndex(filter) + number; i < gMAX_DEPTH; i++ { 59 if pc, file, line, ok := runtime.Caller(i); ok { 60 if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { 61 continue 62 } 63 if filter != "" && strings.Contains(file, filter) { 64 continue 65 } 66 if strings.Contains(file, gFILTER_KEY) { 67 continue 68 } 69 if fn := runtime.FuncForPC(pc); fn == nil { 70 name = "unknown" 71 } else { 72 name = fn.Name() 73 } 74 if index > 9 { 75 space = " " 76 } 77 buffer.WriteString(fmt.Sprintf("%d.%s%s\n %s:%d\n", index, space, name, file, line)) 78 index++ 79 } else { 80 break 81 } 82 } 83 return buffer.String() 84 } 85 86 // CallerPath returns the absolute file path along with its line number of the caller. 87 func Caller(skip ...int) string { 88 return CallerWithFilter("", skip...) 89 } 90 91 // CallerPathWithFilter returns the absolute file path along with its line number of the caller. 92 // 93 // The parameter <filter> is used to filter the path of the caller. 94 func CallerWithFilter(filter string, skip ...int) string { 95 number := 0 96 if len(skip) > 0 { 97 number = skip[0] 98 } 99 for i := callerFromIndex(filter) + number; i < gMAX_DEPTH; i++ { 100 if _, file, line, ok := runtime.Caller(i); ok { 101 if filter != "" && strings.Contains(file, filter) { 102 continue 103 } 104 if strings.Contains(file, gFILTER_KEY) { 105 continue 106 } 107 return fmt.Sprintf(`%s:%d`, file, line) 108 } else { 109 break 110 } 111 } 112 return "" 113 } 114 115 // callerFromIndex returns the caller position exclusive of the debug package. 116 func callerFromIndex(filter string) int { 117 for i := 0; i < gMAX_DEPTH; i++ { 118 if _, file, _, ok := runtime.Caller(i); ok { 119 if filter != "" && strings.Contains(file, filter) { 120 continue 121 } 122 if strings.Contains(file, gFILTER_KEY) { 123 continue 124 } 125 // exclude the depth from the function of current package. 126 return i - 1 127 } 128 } 129 return 0 130 }