为了账号安全,请及时绑定邮箱和手机立即绑定

是否有等效于 os.Args() 的函数?

是否有等效于 os.Args() 的函数?

Go
森林海 2021-11-15 15:31:08
为了帮助调试 GO 程序,我想编写两个在进入和退出时调用的通用函数,它们将分别打印输入和输出参数的值:printInputParameters(input ...interface{})printOutputParameters(output ...interface{})是否有等价的os.Args()for 函数?我查看了运行时包并没有找到这样的功能。例如,假设我有两个具有不同输入参数和输出参数的函数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}
查看完整描述

2 回答

?
慕斯王

TA贡献1864条经验 获得超2个赞

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


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


至于您的问题的解决方案,您可以按照已经建议的源代码解析路线。如果你喜欢冒险,你可以解析runtime.Stack. 但要注意,有很多缺点,您很快就会意识到任何解决方案都比这个更好。


要解析堆栈跟踪,只需获取先前调用的函数的行(从 的角度来看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(")")

}

测试:


func Test1(in int, b []byte, in2 int, m string) {

    printInputs(Test1)

}


func main() {

    b := []byte{'A', 'B', 'C'}

    s := "AAAA"

    Test1(2, b, 9, s)

}

输出:


main.Test1(2,[65 66 67],9,"AAAA",)

这个稍微高级的版本可以在 github上找到:


go get github.com/githubnemo/pdump


查看完整回答
反对 回复 2021-11-15
?
ABOUTYOU

TA贡献1812条经验 获得超5个赞

要一般打印函数的参数,您可以执行以下操作:

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

printInputParameters一个可变参数函数input类型为[]interface{}


查看完整回答
反对 回复 2021-11-15
  • 2 回答
  • 0 关注
  • 210 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信