/usr/share/gocode/src/gopkg.in/inconshreveable/log15.v2/stack/stack.go is in golang-gopkg-inconshreveable-log15.v2-dev 2.11+git20150921.0.b105bd3-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | // Package stack implements utilities to capture, manipulate, and format call
// stacks.
package stack
import (
"fmt"
"path/filepath"
"runtime"
"strings"
)
// Call records a single function invocation from a goroutine stack. It is a
// wrapper for the program counter values returned by runtime.Caller and
// runtime.Callers and consumed by runtime.FuncForPC.
type Call uintptr
// Format implements fmt.Formatter with support for the following verbs.
//
// %s source file
// %d line number
// %n function name
// %v equivalent to %s:%d
//
// It accepts the '+' and '#' flags for most of the verbs as follows.
//
// %+s path of source file relative to the compile time GOPATH
// %#s full path of source file
// %+n import path qualified function name
// %+v equivalent to %+s:%d
// %#v equivalent to %#s:%d
func (pc Call) Format(s fmt.State, c rune) {
// BUG(ChrisHines): Subtracting one from pc is a work around for
// https://code.google.com/p/go/issues/detail?id=7690. The idea for this
// work around comes from rsc's initial patch at
// https://codereview.appspot.com/84100043/#ps20001, but as noted in the
// issue discussion, it is not a complete fix since it doesn't handle some
// cases involving signals. Just the same, it handles all of the other
// cases I have tested.
pcFix := uintptr(pc) - 1
fn := runtime.FuncForPC(pcFix)
if fn == nil {
fmt.Fprintf(s, "%%!%c(NOFUNC)", c)
return
}
switch c {
case 's', 'v':
file, line := fn.FileLine(pcFix)
switch {
case s.Flag('#'):
// done
case s.Flag('+'):
// Here we want to get the source file path relative to the
// compile time GOPATH. As of Go 1.3.x there is no direct way to
// know the compiled GOPATH at runtime, but we can infer the
// number of path segments in the GOPATH. We note that fn.Name()
// returns the function name qualified by the import path, which
// does not include the GOPATH. Thus we can trim segments from the
// beginning of the file path until the number of path separators
// remaining is one more than the number of path separators in the
// function name. For example, given:
//
// GOPATH /home/user
// file /home/user/src/pkg/sub/file.go
// fn.Name() pkg/sub.Type.Method
//
// We want to produce:
//
// pkg/sub/file.go
//
// From this we can easily see that fn.Name() has one less path
// separator than our desired output.
const sep = "/"
impCnt := strings.Count(fn.Name(), sep) + 1
pathCnt := strings.Count(file, sep)
for pathCnt > impCnt {
i := strings.Index(file, sep)
if i == -1 {
break
}
file = file[i+len(sep):]
pathCnt--
}
default:
const sep = "/"
if i := strings.LastIndex(file, sep); i != -1 {
file = file[i+len(sep):]
}
}
fmt.Fprint(s, file)
if c == 'v' {
fmt.Fprint(s, ":", line)
}
case 'd':
_, line := fn.FileLine(pcFix)
fmt.Fprint(s, line)
case 'n':
name := fn.Name()
if !s.Flag('+') {
const pathSep = "/"
if i := strings.LastIndex(name, pathSep); i != -1 {
name = name[i+len(pathSep):]
}
const pkgSep = "."
if i := strings.Index(name, pkgSep); i != -1 {
name = name[i+len(pkgSep):]
}
}
fmt.Fprint(s, name)
}
}
// Callers returns a Trace for the current goroutine with element 0
// identifying the calling function.
func Callers() Trace {
pcs := poolBuf()
pcs = pcs[:cap(pcs)]
n := runtime.Callers(2, pcs)
cs := make([]Call, n)
for i, pc := range pcs[:n] {
cs[i] = Call(pc)
}
putPoolBuf(pcs)
return cs
}
// name returns the import path qualified name of the function containing the
// call.
func (pc Call) name() string {
pcFix := uintptr(pc) - 1 // work around for go issue #7690
fn := runtime.FuncForPC(pcFix)
if fn == nil {
return "???"
}
return fn.Name()
}
func (pc Call) file() string {
pcFix := uintptr(pc) - 1 // work around for go issue #7690
fn := runtime.FuncForPC(pcFix)
if fn == nil {
return "???"
}
file, _ := fn.FileLine(pcFix)
return file
}
// Trace records a sequence of function invocations from a goroutine stack.
type Trace []Call
// Format implements fmt.Formatter by printing the Trace as square brackes ([,
// ]) surrounding a space separated list of Calls each formatted with the
// supplied verb and options.
func (pcs Trace) Format(s fmt.State, c rune) {
s.Write([]byte("["))
for i, pc := range pcs {
if i > 0 {
s.Write([]byte(" "))
}
pc.Format(s, c)
}
s.Write([]byte("]"))
}
// TrimBelow returns a slice of the Trace with all entries below pc removed.
func (pcs Trace) TrimBelow(pc Call) Trace {
for len(pcs) > 0 && pcs[0] != pc {
pcs = pcs[1:]
}
return pcs
}
// TrimAbove returns a slice of the Trace with all entries above pc removed.
func (pcs Trace) TrimAbove(pc Call) Trace {
for len(pcs) > 0 && pcs[len(pcs)-1] != pc {
pcs = pcs[:len(pcs)-1]
}
return pcs
}
// TrimBelowName returns a slice of the Trace with all entries below the
// lowest with function name name removed.
func (pcs Trace) TrimBelowName(name string) Trace {
for len(pcs) > 0 && pcs[0].name() != name {
pcs = pcs[1:]
}
return pcs
}
// TrimAboveName returns a slice of the Trace with all entries above the
// highest with function name name removed.
func (pcs Trace) TrimAboveName(name string) Trace {
for len(pcs) > 0 && pcs[len(pcs)-1].name() != name {
pcs = pcs[:len(pcs)-1]
}
return pcs
}
var goroot string
func init() {
goroot = filepath.ToSlash(runtime.GOROOT())
if runtime.GOOS == "windows" {
goroot = strings.ToLower(goroot)
}
}
func inGoroot(path string) bool {
if runtime.GOOS == "windows" {
path = strings.ToLower(path)
}
return strings.HasPrefix(path, goroot)
}
// TrimRuntime returns a slice of the Trace with the topmost entries from the
// go runtime removed. It considers any calls originating from files under
// GOROOT as part of the runtime.
func (pcs Trace) TrimRuntime() Trace {
for len(pcs) > 0 && inGoroot(pcs[len(pcs)-1].file()) {
pcs = pcs[:len(pcs)-1]
}
return pcs
}
|