Go 函数是否有os.Args()的等价物?

Go 函数是否有os.Args()的等价物?,go,Go,为了帮助调试GO程序,我想编写两个在进入和退出时调用的通用函数,分别打印输入和输出参数的值: printInputParameters(input ...interface{}) printOutputParameters(output ...interface{}) 函数是否有与os.Args()等效的os.Args()?我查看了运行时包,没有找到这样的函数 例如,假设我有两个具有不同输入参数和输出参数的函数 func f1(int i, float f) (e error) { .

为了帮助调试GO程序,我想编写两个在进入和退出时调用的通用函数,分别打印输入和输出参数的值:

printInputParameters(input ...interface{})
printOutputParameters(output ...interface{})
函数是否有与os.Args()等效的
os.Args()
?我查看了运行时包,没有找到这样的函数

例如,假设我有两个具有不同输入参数和输出参数的函数

func f1(int i, float f) (e error) {
    ... some code here
}

func f2(s string, b []byte) (u uint64, e error) {
     .. some code here
}
我希望能够做到以下几点

func f1(int i, float f) (e error) {
     printInputparameters( ? )
     defer func() {
          printOutputParameters( ? )
     }()

     ... some code here
}

func f2(s string, b []byte) (u uint64, e error) {
     printInputparameters( ? )
     defer func() {
          printOutputParameters( ? )
     }()

     ... some code here
}

要常规打印函数的参数,可以执行以下操作:

func printInputParameters(input ...interface{}) {
    fmt.Printf("Args: %v", input)
}

printInputParameters
是,而
input
是类型
[]接口{}

您不能在Go中执行此操作,因为您无法在当前goroutine中获取当前活动函数的堆栈帧。这不是不可能的,我将在下面进一步说明,但问题是没有公共API来可靠地完成这项工作。在引发
panic
时打印的堆栈跟踪中可以看到这一点:在这种情况下,堆栈上的所有值都被转储

如果您对堆栈跟踪的实际生成方式感兴趣,请查看运行时包中的内容

至于问题的解决方案,您可以使用源代码解析路由。如果您觉得有冒险精神,可以解析由提供的堆栈跟踪。但是要小心,有太多的缺点,你会很快意识到任何解决方案都比这个好

要解析堆栈跟踪,只需获取前面调用的函数的行(从
printInputParameters
的角度),获取该函数的名称,并根据反射提供的参数类型解析参数值。各种函数调用的堆栈跟踪输出的一些示例:

main.Test1(0x2) // Test1(int64(2))
main.Test1(0xc820043ed5, 0x3, 0x3) // Test1([]byte{'A','B','C'})
main.Test1(0x513350, 0x4) // Test1("AAAA")
您可以看到,复杂类型(不适合寄存器的类型)可能会使用多个“参数”。例如,字符串是指向数据和长度的指针。因此,您必须使用
unsafe
包来访问这些指针和反射,以便从这些数据创建值

如果您想亲自尝试,下面是一些示例代码:

import (
    "fmt"
    "math"
    "reflect"
    "runtime"
    "strconv"
    "strings"
    "unsafe"
)

// Parses the second call's parameters in a stack trace of the form:
//
// goroutine 1 [running]:
// main.printInputs(0x4c4c60, 0x539038)
//  /.../go/src/debug/main.go:16 +0xe0
// main.Test1(0x2)
//  /.../go/src/debug/main.go:23
//
func parseParams(st string) (string, []uintptr) {

    line := 1
    start, stop := 0, 0
    for i, c := range st {
        if c == '\n' {
            line++
        }
        if line == 4 && c == '\n' {
            start = i + 1
        }
        if line == 5 && c == '\n' {
            stop = i
        }
    }

    call := st[start:stop]
    fname := call[0:strings.IndexByte(call, '(')]
    param := call[strings.IndexByte(call, '(')+1 : strings.IndexByte(call, ')')]
    params := strings.Split(param, ", ")
    parsedParams := make([]uintptr, len(params))

    for i := range params {
        iv, err := strconv.ParseInt(params[i], 0, 64)

        if err != nil {
            panic(err.Error())
        }

        parsedParams[i] = uintptr(iv)
    }

    return fname, parsedParams
}

func fromAddress(t reflect.Type, addr uintptr) reflect.Value {
    return reflect.NewAt(t, unsafe.Pointer(&addr)).Elem()
}

func printInputs(fn interface{}) {
    v := reflect.ValueOf(fn)
    vt := v.Type()
    b := make([]byte, 500)

    if v.Kind() != reflect.Func {
        return
    }

    runtime.Stack(b, false)

    name, params := parseParams(string(b))
    pidx := 0

    fmt.Print(name + "(")
    for i := 0; i < vt.NumIn(); i++ {
        t := vt.In(i)
        switch t.Kind() {
        case reflect.Int64:
        case reflect.Int:
            // Just use the value from the stack
            fmt.Print(params[pidx], ",")
            pidx++
        case reflect.Float64:
            fmt.Print(math.Float64frombits(uint64(params[pidx])), ",")
            pidx++
        case reflect.Slice:
            // create []T pointing to slice content
            data := reflect.ArrayOf(int(params[pidx+2]), t.Elem())
            svp := reflect.NewAt(data, unsafe.Pointer(params[pidx]))
            fmt.Printf("%v,", svp.Elem())
            pidx += 3
        case reflect.String:
            sv := fromAddress(t, params[pidx])
            fmt.Printf("%v,", sv)
            pidx += 2
        case reflect.Map:
            // points to hmap struct
            mv := fromAddress(t,params[pidx])
            fmt.Printf("%v,", mv)
            pidx++
        } /* switch */
    }
    fmt.Println(")")
}
输出:

main.Test1(2,[65 66 67],9,"AAAA",)
您可以在以下位置找到此功能的稍高级版本:


@雷特米克费斯曼,谢谢。但我不想重新定义函数定义。我只想能够调试。(我编辑了我的问题以澄清)对不起,这里有反映,但我只能看到参数的类型,而不能看到它们的值。您还需要指定函数的名称…非常感谢。我在github上尝试了这个版本和一个版本。除了字符串参数外,它工作正常。注意:我必须将go从1.4.2升级到1.5.1,因为go1.4不支持reflect.ArraOf()。2@JosephSwaminathan请在github上发布一个问题,或者修复它并打开一个请求。谢谢。发布了一个问题。代码似乎没有问题,到目前为止找不到修复程序。@尼莫打开了一个拉取请求
main.Test1(2,[65 66 67],9,"AAAA",)
go get github.com/githubnemo/pdump