2 回答

TA貢獻(xiàn)1864條經(jīng)驗(yàn) 獲得超2個(gè)贊
您無法在 Go 中執(zhí)行此操作,因?yàn)槟鸁o法在當(dāng)前 goroutine 中獲取當(dāng)前活動(dòng)函數(shù)的堆棧幀。這樣做并非不可能,因?yàn)槲覍⒃谙旅孢M(jìn)一步展示,但問題是沒有公共 API 可以可靠地完成這項(xiàng)工作。可以在 apanic引發(fā)時(shí)打印的堆棧跟蹤中看到它可以完成:在這種情況下,堆棧中的所有值都被轉(zhuǎn)儲(chǔ)。
如果您對堆棧跟蹤的實(shí)際生成方式感興趣,請查看genstacktrace運(yùn)行時(shí)包。
至于您的問題的解決方案,您可以按照已經(jīng)建議的源代碼解析路線。如果你喜歡冒險(xiǎn),你可以解析runtime.Stack. 但要注意,有很多缺點(diǎn),您很快就會(huì)意識到任何解決方案都比這個(gè)更好。
要解析堆棧跟蹤,只需獲取先前調(diào)用的函數(shù)的行(從 的角度來看printInputParameters),獲取該函數(shù)的名稱并根據(jù)反射提供的參數(shù)類型解析參數(shù)值。各種函數(shù)調(diào)用的堆棧跟蹤輸出的一些示例:
main.Test1(0x2) // Test1(int64(2))
main.Test1(0xc820043ed5, 0x3, 0x3) // Test1([]byte{'A','B','C'})
main.Test1(0x513350, 0x4) // Test1("AAAA")
您可以看到復(fù)雜類型(那些不適合寄存器的類型)可能使用多個(gè)“參數(shù)”。例如,字符串是指向數(shù)據(jù)和長度的指針。所以你必須使用unsafe包來訪問這些指針和反射來從這些數(shù)據(jù)中創(chuàng)建值。
如果你想自己嘗試,這里有一些示例代碼:
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",)
這個(gè)稍微高級的版本可以在 github上找到:
go get github.com/githubnemo/pdump

TA貢獻(xiàn)1812條經(jīng)驗(yàn) 獲得超5個(gè)贊
要一般打印函數(shù)的參數(shù),您可以執(zhí)行以下操作:
func printInputParameters(input ...interface{}) { fmt.Printf("Args: %v", input)}
printInputParameters
是一個(gè)可變參數(shù)函數(shù),input
類型為[]interface{}
。
- 2 回答
- 0 關(guān)注
- 250 瀏覽
添加回答
舉報(bào)