2015-10-07 107 views
3

为了帮助调试Go程序,我想写两个通用的功能,将在入口和出口被称为,这将分别打印输入值和输出参数:是否有相当于os.Args()的函数?

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

是否存在的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 
} 

回答

3

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

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

至于你的问题的解决方案,你可以将源代码解析路由为already suggested。如果您觉得冒险,可以解析由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",) 

稍微高级的这个版本可以发现on github

go get github.com/githubnemo/pdump 
+0

非常感谢。我试过这个版本,并在github上。除了字符串参数,它工作正常。 注意: 我必须从1.4.2升级到1.5.1,因为在go1.4.2中不支持reflect.ArraOf() –

+0

@JosephSwaminathan请在github上发布问题或修复问题并打开拉取请求。谢谢。 – nemo

+0

已发布问题。该代码似乎确定无法找到一个修复迄今。 –

0

一般地打印你的函数的自变量两种功能,你可以这样做:

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

printInputParametersa variadic function,input[]interface{}

+0

@RhytmicFistman谢谢。但我不想重新定义函数定义。我只是想能够调试。 (我编辑了我的问题来澄清) –

+0

对不起,有反射,但我只能看到参数的类型,而不是它们的值。您还需要指定函数名称... –

相关问题