From aa53a56f957d80316a6c91a89ecff183dc869983 Mon Sep 17 00:00:00 2001 From: Richard Liu Date: Mon, 25 Jun 2018 11:49:59 -0700 Subject: [PATCH] removed go-stack temporarily --- log/handler.go | 2 +- log/logger.go | 2 +- stack/stack.go | 324 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 326 insertions(+), 2 deletions(-) create mode 100644 stack/stack.go diff --git a/log/handler.go b/log/handler.go index 3c99114dc..768e7ac40 100644 --- a/log/handler.go +++ b/log/handler.go @@ -8,7 +8,7 @@ import ( "reflect" "sync" - "github.com/go-stack/stack" + "harmony-benchmark/stack" ) // Handler defines where and how log records are written. diff --git a/log/logger.go b/log/logger.go index 5e5924a3d..798df4c24 100644 --- a/log/logger.go +++ b/log/logger.go @@ -5,7 +5,7 @@ import ( "os" "time" - "github.com/go-stack/stack" + "harmony-benchmark/stack" ) const timeKey = "t" diff --git a/stack/stack.go b/stack/stack.go new file mode 100644 index 000000000..1bc6971d8 --- /dev/null +++ b/stack/stack.go @@ -0,0 +1,324 @@ +// +build go1.7 + +// Package stack implements utilities to capture, manipulate, and format call +// stacks. It provides a simpler API than package runtime. +// +// The implementation takes care of the minutia and special cases of +// interpreting the program counter (pc) values returned by runtime.Callers. +// +// Package stack's types implement fmt.Formatter, which provides a simple and +// flexible way to declaratively configure formatting when used with logging +// or error tracking packages. +package stack + +import ( + "bytes" + "errors" + "fmt" + "io" + "runtime" + "strconv" + "strings" +) + +// Call records a single function invocation from a goroutine stack. +type Call struct { + frame runtime.Frame +} + +// Caller returns a Call from the stack of the current goroutine. The argument +// skip is the number of stack frames to ascend, with 0 identifying the +// calling function. +func Caller(skip int) Call { + // As of Go 1.9 we need room for up to three PC entries. + // + // 0. An entry for the stack frame prior to the target to check for + // special handling needed if that prior entry is runtime.sigpanic. + // 1. A possible second entry to hold metadata about skipped inlined + // functions. If inline functions were not skipped the target frame + // PC will be here. + // 2. A third entry for the target frame PC when the second entry + // is used for skipped inline functions. + var pcs [3]uintptr + n := runtime.Callers(skip+1, pcs[:]) + frames := runtime.CallersFrames(pcs[:n]) + frame, _ := frames.Next() + frame, _ = frames.Next() + + return Call{ + frame: frame, + } +} + +// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c). +func (c Call) String() string { + return fmt.Sprint(c) +} + +// MarshalText implements encoding.TextMarshaler. It formats the Call the same +// as fmt.Sprintf("%v", c). +func (c Call) MarshalText() ([]byte, error) { + if c.frame == (runtime.Frame{}) { + return nil, ErrNoFunc + } + + buf := bytes.Buffer{} + fmt.Fprint(&buf, c) + return buf.Bytes(), nil +} + +// ErrNoFunc means that the Call has a nil *runtime.Func. The most likely +// cause is a Call with the zero value. +var ErrNoFunc = errors.New("no call stack information") + +// Format implements fmt.Formatter with support for the following verbs. +// +// %s source file +// %d line number +// %n function name +// %k last segment of the package path +// %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 +// %+k full package path +// %+v equivalent to %+s:%d +// %#v equivalent to %#s:%d +func (c Call) Format(s fmt.State, verb rune) { + if c.frame == (runtime.Frame{}) { + fmt.Fprintf(s, "%%!%c(NOFUNC)", verb) + return + } + + switch verb { + case 's', 'v': + file := c.frame.File + switch { + case s.Flag('#'): + // done + case s.Flag('+'): + file = file[pkgIndex(file, c.frame.Function):] + default: + const sep = "/" + if i := strings.LastIndex(file, sep); i != -1 { + file = file[i+len(sep):] + } + } + io.WriteString(s, file) + if verb == 'v' { + buf := [7]byte{':'} + s.Write(strconv.AppendInt(buf[:1], int64(c.frame.Line), 10)) + } + + case 'd': + buf := [6]byte{} + s.Write(strconv.AppendInt(buf[:0], int64(c.frame.Line), 10)) + + case 'k': + name := c.frame.Function + const pathSep = "/" + start, end := 0, len(name) + if i := strings.LastIndex(name, pathSep); i != -1 { + start = i + len(pathSep) + } + const pkgSep = "." + if i := strings.Index(name[start:], pkgSep); i != -1 { + end = start + i + } + if s.Flag('+') { + start = 0 + } + io.WriteString(s, name[start:end]) + + case 'n': + name := c.frame.Function + 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):] + } + } + io.WriteString(s, name) + } +} + +// Frame returns the call frame infomation for the Call. +func (c Call) Frame() runtime.Frame { + return c.frame +} + +// PC returns the program counter for this call frame; multiple frames may +// have the same PC value. +// +// Deprecated: Use Call.Frame instead. +func (c Call) PC() uintptr { + return c.frame.PC +} + +// CallStack records a sequence of function invocations from a goroutine +// stack. +type CallStack []Call + +// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", cs). +func (cs CallStack) String() string { + return fmt.Sprint(cs) +} + +var ( + openBracketBytes = []byte("[") + closeBracketBytes = []byte("]") + spaceBytes = []byte(" ") +) + +// MarshalText implements encoding.TextMarshaler. It formats the CallStack the +// same as fmt.Sprintf("%v", cs). +func (cs CallStack) MarshalText() ([]byte, error) { + buf := bytes.Buffer{} + buf.Write(openBracketBytes) + for i, pc := range cs { + if i > 0 { + buf.Write(spaceBytes) + } + fmt.Fprint(&buf, pc) + } + buf.Write(closeBracketBytes) + return buf.Bytes(), nil +} + +// Format implements fmt.Formatter by printing the CallStack as square brackets +// ([, ]) surrounding a space separated list of Calls each formatted with the +// supplied verb and options. +func (cs CallStack) Format(s fmt.State, verb rune) { + s.Write(openBracketBytes) + for i, pc := range cs { + if i > 0 { + s.Write(spaceBytes) + } + pc.Format(s, verb) + } + s.Write(closeBracketBytes) +} + +// Trace returns a CallStack for the current goroutine with element 0 +// identifying the calling function. +func Trace() CallStack { + var pcs [512]uintptr + n := runtime.Callers(1, pcs[:]) + + frames := runtime.CallersFrames(pcs[:n]) + cs := make(CallStack, 0, n) + + // Skip extra frame retrieved just to make sure the runtime.sigpanic + // special case is handled. + frame, more := frames.Next() + + for more { + frame, more = frames.Next() + cs = append(cs, Call{frame: frame}) + } + + return cs +} + +// TrimBelow returns a slice of the CallStack with all entries below c +// removed. +func (cs CallStack) TrimBelow(c Call) CallStack { + for len(cs) > 0 && cs[0] != c { + cs = cs[1:] + } + return cs +} + +// TrimAbove returns a slice of the CallStack with all entries above c +// removed. +func (cs CallStack) TrimAbove(c Call) CallStack { + for len(cs) > 0 && cs[len(cs)-1] != c { + cs = cs[:len(cs)-1] + } + return cs +} + +// pkgIndex returns the index that results in file[index:] being the path of +// file relative to the compile time GOPATH, and file[:index] being the +// $GOPATH/src/ portion of file. funcName must be the name of a function in +// file as returned by runtime.Func.Name. +func pkgIndex(file, funcName string) int { + // As of Go 1.6.2 there is no direct way to know the compile time GOPATH + // at runtime, but we can infer the number of path segments in the GOPATH. + // We note that runtime.Func.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: + // + // file[:idx] == /home/user/src/ + // file[idx:] == pkg/sub/file.go + // + // From this we can easily see that fn.Name() has one less path separator + // than our desired result for file[idx:]. We count separators from the + // end of the file path until it finds two more than in the function name + // and then move one character forward to preserve the initial path + // segment without a leading separator. + const sep = "/" + i := len(file) + for n := strings.Count(funcName, sep) + 2; n > 0; n-- { + i = strings.LastIndex(file[:i], sep) + if i == -1 { + i = -len(sep) + break + } + } + // get back to 0 or trim the leading separator + return i + len(sep) +} + +var runtimePath string + +func init() { + var pcs [3]uintptr + runtime.Callers(0, pcs[:]) + frames := runtime.CallersFrames(pcs[:]) + frame, _ := frames.Next() + file := frame.File + + idx := pkgIndex(frame.File, frame.Function) + + runtimePath = file[:idx] + if runtime.GOOS == "windows" { + runtimePath = strings.ToLower(runtimePath) + } +} + +func inGoroot(c Call) bool { + file := c.frame.File + if len(file) == 0 || file[0] == '?' { + return true + } + if runtime.GOOS == "windows" { + file = strings.ToLower(file) + } + return strings.HasPrefix(file, runtimePath) || strings.HasSuffix(file, "/_testmain.go") +} + +// TrimRuntime returns a slice of the CallStack with the topmost entries from +// the go runtime removed. It considers any calls originating from unknown +// files, files under GOROOT, or _testmain.go as part of the runtime. +func (cs CallStack) TrimRuntime() CallStack { + for len(cs) > 0 && inGoroot(cs[len(cs)-1]) { + cs = cs[:len(cs)-1] + } + return cs +}