2015-10-07 116 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

對不起,有反射,但我只能看到參數的類型,而不是它們的值。您還需要指定函數名稱... –

相關問題