Golang堆栈在Debug与Build and Run

时间:2019-06-09 03:18:35

标签: go

我有一些代码大多是从https://github.com/go-errors/errors复制而来的,该代码会生成堆栈跟踪

当我在VSCode调试器中运行它时,输出完全符合预期。文件名和函数名正确。

调试输出

C:/work/projects/go/src/errtest/customerrors.go:17 (0x4d149f)
    NewDbError: dbe.stacktrace = NewStackTrace()
C:/work/projects/go/src/errtest/errortest04a.go:6 (0x4d156f)
    errorTest04a: err := NewDbError("This is a db error")
C:/work/projects/go/src/errtest/errortest04.go:4 (0x4d1507)
    errorTest04: errorTest04a()
C:/work/projects/go/src/errtest/main.go:4 (0x4d16b7)
    main: errorTest04()
C:/Go/src/runtime/proc.go:200 (0x431bc1)
    main: fn()
C:/Go/src/runtime/asm_amd64.s:1337 (0x458bc1)
    goexit: BYTE    $0x90   // NOP

当我构建并运行相同的代码时,输​​出不正确。

构建输出

C:/work/projects/go/src/errtest/customerrors.go:17 (0x4a5dbe)
        errorTest04a: dbe.stacktrace = NewStackTrace()
C:/work/projects/go/src/errtest/customerrors.go:17 (0x4a5db9)
        NewDbError: dbe.stacktrace = NewStackTrace()
C:/work/projects/go/src/errtest/errortest04.go:4 (0x4a5e88)
        main: errorTest04a()
C:/work/projects/go/src/errtest/errortest04.go:4 (0x4a5e83)
        errorTest04: errorTest04a()
C:/Go/src/runtime/proc.go:200 (0x42d5ac)
        main: fn()
C:/Go/src/runtime/asm_amd64.s:1337 (0x452911)
        goexit: BYTE    $0x90   // NOP

问题:

为什么代码会根据是在Debug中运行还是在build中生成不同的输出?

“调试输出”中的顶部条目正确显示了最后一个调用方为函数NewDbError和文件dbe.stacktrace = NewStackTrace()中的行customerrors.go:17

Build Output的顶部条目显示相同的详细信息,但函数名称errorTest04a不在文件``customerrors.go but in the file errorTest04a.go`

Build输出的第二行显示Debug输出的第一行

构建输出中的第三行显示了错误的函数名称main

相同的代码生成不同的输出似乎很奇怪。

结束问题

我在想,也许编译会删除文件名,但是它们在那里,只是顺序不正确,而且函数名也乱七八糟。

如果任何人都可以阐明一点,将不胜感激。谢谢

这是我使用的代码

main.go

package main

func main() {
    errorTest04()
}

errortest04.go

package main

func errorTest04() {
    errorTest04a()
}

errortest04a.go     包主

import "fmt"

func errorTest04a() {
    err := NewDbError("This is a db error")

    fmt.Print(string(err.stacktrace.Stack()))
}

customerorrs.go

package main

// DbError is a custom error that holds a dberror
type DbError struct {
    stacktrace *StackTrace

    errmsg string
}

func (e *DbError) Error() string {
    return e.errmsg
}

// NewDbError returns a new DbError
func NewDbError(e string) DbError {
    dbe := DbError{errmsg: e}
    dbe.stacktrace = NewStackTrace()
    return dbe
}

stacktrace.go -从https://github.com/go-errors/errors

提起
package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "runtime"
    "strings"
)

const maxStackDepth = 50

// StackTrace holds the stack data
type StackTrace struct {
    callers    []uintptr
    callerslen int
    frames     []StackFrame
}

// Stack - returns a formatted Stack Trace
func (st *StackTrace) Stack() []byte {
    buf := bytes.Buffer{}

    for _, frame := range st.frames {
        buf.WriteString(frame.String())
    }

    return buf.Bytes()
}

// NewStackTrace creates a new stack object
func NewStackTrace() *StackTrace {
    st := StackTrace{}
    st.callers = make([]uintptr, maxStackDepth)
    st.callerslen = runtime.Callers(2, st.callers[:])

    st.frames = make([]StackFrame, st.callerslen)

    for i := range st.frames {
        st.frames[i] = newStackFrame(st.callers[i])
    }

    return &st
}

// StackFrame - details of a caller. Lifted from https://github.com/go-errors/errors/blob/master/stackframe.go
type StackFrame struct {
    // The path to the file containing this ProgramCounter
    File string
    // The LineNumber in that file
    LineNumber int
    // The Name of the function that contains this ProgramCounter
    FnName string
    // The Package that contains this function
    Package string
    // The underlying ProgramCounter
    ProgramCounter uintptr
}

func newStackFrame(pc uintptr) StackFrame {

    frame := StackFrame{ProgramCounter: pc}
    if frame.Func() == nil {
        return frame
    }

    frame.Package, frame.FnName = packageAndName(frame.Func())

    // pc -1 because the program counters we use are usually return addresses,
    // and we want to show the line that corresponds to the function call
    // frame.File, frame.LineNumber = frame.Func().FileLine(pc)
    frame.File, frame.LineNumber = frame.Func().FileLine(pc - 1)
    return frame
}

// Func returns the function that contained this frame.
func (frame *StackFrame) Func() *runtime.Func {
    if frame.ProgramCounter == 0 {
        return nil
    }
    return runtime.FuncForPC(frame.ProgramCounter)
}

// SourceLine gets the line of code (from File and Line) of the original source if possible.
func (frame *StackFrame) SourceLine() (string, error) {
    data, err := ioutil.ReadFile(frame.File)
    // _ replace with err

    if err != nil {
        return "", err
    }

    lines := bytes.Split(data, []byte{'\n'})
    if frame.LineNumber <= 0 || frame.LineNumber >= len(lines) {
        return "???", nil
    }
    // -1 because line-numbers are 1 based, but our array is 0 based
    return string(bytes.Trim(lines[frame.LineNumber-1], " \t")), nil
}

// String returns the stackframe formatted in the same way as go does
// in runtime/debug.Stack()
func (frame *StackFrame) String() string {
    str := fmt.Sprintf("%s:%d (0x%x)\n", frame.File, frame.LineNumber, frame.ProgramCounter)

    source, err := frame.SourceLine()
    if err != nil {
        return str
    }

    return str + fmt.Sprintf("\t%s: %s\n", frame.FnName, source)
}

func packageAndName(fn *runtime.Func) (string, string) {
    name := fn.Name()
    pkg := ""

    // The name includes the path name to the package, which is unnecessary
    // since the file name is already included.  Plus, it has center dots.
    // That is, we see
    //  runtime/debug.*T·ptrmethod
    // and want
    //  *T.ptrmethod
    // Since the package path might contains dots (e.g. code.google.com/...),
    // we first remove the path prefix if there is one.
    if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 {
        pkg += name[:lastslash] + "/"
        name = name[lastslash+1:]
    }
    if period := strings.Index(name, "."); period >= 0 {
        pkg += name[:period]
        name = name[period+1:]
    }

    name = strings.Replace(name, "·", ".", -1)
    return pkg, name
}

0 个答案:

没有答案