dqwh1219 2015-10-07 21:28
浏览 29
已采纳

有相当于os.Args()的函数吗?

To help debug GO programs, I want to write two generic functions that will be called on entry and exit, which will print the values of input and output parameters respectively:

printInputParameters(input ...interface{})
printOutputParameters(output ...interface{})

Is there an equivalent of os.Args() for functions? I looked at runtime package and didn't find such functions.

For example lets say I have two functions with different input parameters and output parameters

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

func f2(s string, b []byte) (u uint64, e error) {
     .. some code here
}

I want to be able to do the following

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条回答 默认 最新

  • dsirr48088 2015-10-08 04:00
    关注

    You cannot do this in Go since there is no way you can get the stack frame of the currently active function in the current goroutine. It is not impossible to do this as I'll show further below but the problem is that there is no public API to get this done reliably. That it can be done can be seen in the stack traces printed when a panic is raised: all values on the stack are dumped in that case.

    Should you be interested in how the stack trace is actually generated then have a look at genstacktrace in the runtime package.

    As for a solution to your problem, you can the source code parsing route as already suggested. If you feel adventurous, you can parse the stack trace provided by runtime.Stack. But beware, there are so many drawbacks that you will quickly realize that any solution is better than this one.

    To parse the stack trace, just get the line of the previously called function (from the viewpoint of printInputParameters), get the name of that function and parse the parameter values according to the parameter types provided by reflection. Some examples of stack trace outputs of various function invocations:

    main.Test1(0x2) // Test1(int64(2))
    main.Test1(0xc820043ed5, 0x3, 0x3) // Test1([]byte{'A','B','C'})
    main.Test1(0x513350, 0x4) // Test1("AAAA")
    

    You can see that complex types (those which do not fit into a register) may use more than one 'parameter'. A string for example is a pointer to the data and the length. So you have to use the unsafe package to access these pointers and reflection to create values from this data.

    If you want to try yourself, here's some example code:

    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 == '
    ' {
                line++
            }
            if line == 4 && c == '
    ' {
                start = i + 1
            }
            if line == 5 && c == '
    ' {
                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(")")
    }
    

    Test:

    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)
    }
    

    Output:

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

    A slightly advanced version of this can be found on github:

    go get github.com/githubnemo/pdump
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥50 求解vmware的网络模式问题 别拿AI回答
  • ¥24 EFS加密后,在同一台电脑解密出错,证书界面找不到对应指纹的证书,未备份证书,求在原电脑解密的方法,可行即采纳
  • ¥15 springboot 3.0 实现Security 6.x版本集成
  • ¥15 PHP-8.1 镜像无法用dockerfile里的CMD命令启动 只能进入容器启动,如何解决?(操作系统-ubuntu)
  • ¥30 请帮我解决一下下面六个代码
  • ¥15 关于资源监视工具的e-care有知道的嘛
  • ¥35 MIMO天线稀疏阵列排布问题
  • ¥60 用visual studio编写程序,利用间接平差求解水准网
  • ¥15 Llama如何调用shell或者Python
  • ¥20 谁能帮我挨个解读这个php语言编的代码什么意思?