猿问

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

为了帮助调试 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

}


森林海
浏览 211回答 2
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 (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "math"&nbsp; &nbsp; "reflect"&nbsp; &nbsp; "runtime"&nbsp; &nbsp; "strconv"&nbsp; &nbsp; "strings"&nbsp; &nbsp; "unsafe")// Parses the second call's parameters in a stack trace of the form://// goroutine 1 [running]:// main.printInputs(0x4c4c60, 0x539038)//&nbsp; /.../go/src/debug/main.go:16 +0xe0// main.Test1(0x2)//&nbsp; /.../go/src/debug/main.go:23//func parseParams(st string) (string, []uintptr) {&nbsp; &nbsp; line := 1&nbsp; &nbsp; start, stop := 0, 0&nbsp; &nbsp; for i, c := range st {&nbsp; &nbsp; &nbsp; &nbsp; if c == '\n' {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; line++&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if line == 4 && c == '\n' {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; start = i + 1&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if line == 5 && c == '\n' {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stop = i&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; call := st[start:stop]&nbsp; &nbsp; fname := call[0:strings.IndexByte(call, '(')]&nbsp; &nbsp; param := call[strings.IndexByte(call, '(')+1 : strings.IndexByte(call, ')')]&nbsp; &nbsp; params := strings.Split(param, ", ")&nbsp; &nbsp; parsedParams := make([]uintptr, len(params))&nbsp; &nbsp; for i := range params {&nbsp; &nbsp; &nbsp; &nbsp; iv, err := strconv.ParseInt(params[i], 0, 64)&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; panic(err.Error())&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; parsedParams[i] = uintptr(iv)&nbsp; &nbsp; }&nbsp; &nbsp; return fname, parsedParams}func fromAddress(t reflect.Type, addr uintptr) reflect.Value {&nbsp; &nbsp; return reflect.NewAt(t, unsafe.Pointer(&addr)).Elem()}func printInputs(fn interface{}) {&nbsp; &nbsp; v := reflect.ValueOf(fn)&nbsp; &nbsp; vt := v.Type()&nbsp; &nbsp; b := make([]byte, 500)&nbsp; &nbsp; if v.Kind() != reflect.Func {&nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; }&nbsp; &nbsp; runtime.Stack(b, false)&nbsp; &nbsp; name, params := parseParams(string(b))&nbsp; &nbsp; pidx := 0&nbsp; &nbsp; fmt.Print(name + "(")&nbsp; &nbsp; for i := 0; i < vt.NumIn(); i++ {&nbsp; &nbsp; &nbsp; &nbsp; t := vt.In(i)&nbsp; &nbsp; &nbsp; &nbsp; switch t.Kind() {&nbsp; &nbsp; &nbsp; &nbsp; case reflect.Int64:&nbsp; &nbsp; &nbsp; &nbsp; case reflect.Int:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Just use the value from the stack&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Print(params[pidx], ",")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pidx++&nbsp; &nbsp; &nbsp; &nbsp; case reflect.Float64:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Print(math.Float64frombits(uint64(params[pidx])), ",")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pidx++&nbsp; &nbsp; &nbsp; &nbsp; case reflect.Slice:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // create []T pointing to slice content&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data := reflect.ArrayOf(int(params[pidx+2]), t.Elem())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; svp := reflect.NewAt(data, unsafe.Pointer(params[pidx]))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("%v,", svp.Elem())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pidx += 3&nbsp; &nbsp; &nbsp; &nbsp; case reflect.String:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sv := fromAddress(t, params[pidx])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("%v,", sv)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pidx += 2&nbsp; &nbsp; &nbsp; &nbsp; case reflect.Map:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // points to hmap struct&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mv := fromAddress(t,params[pidx])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("%v,", mv)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pidx++&nbsp; &nbsp; &nbsp; &nbsp; } /* switch */&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println(")")}测试:func Test1(in int, b []byte, in2 int, m string) {&nbsp; &nbsp; printInputs(Test1)}func main() {&nbsp; &nbsp; b := []byte{'A', 'B', 'C'}&nbsp; &nbsp; s := "AAAA"&nbsp; &nbsp; Test1(2, b, 9, s)}输出:main.Test1(2,[65 66 67],9,"AAAA",)这个稍微高级的版本可以在 github上找到:go get github.com/githubnemo/pdump

ABOUTYOU

要一般打印函数的参数,您可以执行以下操作:func&nbsp;printInputParameters(input&nbsp;...interface{})&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;fmt.Printf("Args:&nbsp;%v",&nbsp;input)}printInputParameters是一个可变参数函数,input类型为[]interface{}。
随时随地看视频慕课网APP

相关分类

Go
我要回答