Golang中gccgo是否支持-finstrument-functions选项或有其他替代方案?

Golang中gccgo是否支持-finstrument-functions选项或有其他替代方案? 你好!

我是 GitHub 上 Corelight cwrap 的作者,这是一个实验性工具,通过 gcc 的 -finstrument-functions 选项自动对 Intel 平台上的 C/C++ 程序进行插桩。即使是超过 100,000 个函数的大型 C++ 程序也能处理。我想知道如何对 Golang 程序实现类似的功能?

我注意到存在 gccgo 作为另一种 Golang 编译器,它甚至可能支持 gcc 的 -finstrument-functions 选项,有人在[1]这篇帖子中暗示过尝试这样做。

有谁知道 gccgo 是否确实支持 -finstrument-functions 选项吗?

另外,gccgo 似乎在 macOS 上不可用[2]?所以根据这个帖子的回复,我可能得切换到 Linux 系统来进一步实验 🙂

最后,撇开 gccgo 不谈,在 Golang 生态系统中,是否还有其他技术和/或机制可以在不使用 gccgo 的 -finstrument-functions 命令行选项的情况下,实现类似的结果?

先谢谢了!

[1] https://stackoverflow.com/questions/52125955/how-to-build-hyperledger-fabric-with-gccgo [2] https://github.com/golang/go/issues/463


更多关于Golang中gccgo是否支持-finstrument-functions选项或有其他替代方案?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

后续问题:

如果 gccgo 支持 -finstrument-functions 命令行选项,它是否能与 goroutine 良好协作,或者如何使其与 goroutine 良好协作?

更多关于Golang中gccgo是否支持-finstrument-functions选项或有其他替代方案?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


gccgo确实支持-finstrument-functions选项。你可以通过以下方式验证:

# 查看gccgo支持的instrumentation选项
gccgo -finstrument-functions --help=instrumentation

使用示例:

// main.go
package main

import "fmt"

// 这些函数会被gcc自动调用
//go:linkname __cyg_profile_func_enter __cyg_profile_func_enter
func __cyg_profile_func_enter(this_fn, call_site uintptr)

//go:linkname __cyg_profile_func_exit __cyg_profile_func_exit
func __cyg_profile_func_exit(this_fn, call_site uintptr)

func foo() {
    fmt.Println("foo")
}

func bar() {
    fmt.Println("bar")
    foo()
}

func main() {
    bar()
}

编译命令:

# 使用gccgo编译并启用插桩
gccgo -finstrument-functions -o program main.go

运行时你会看到每个函数的进入和退出信息。不过需要注意几点:

  1. macOS支持问题:确实如你所说,gccgo在macOS上不可用。官方Go团队在2015年就移除了对Darwin系统的gccgo支持,你需要使用Linux环境。

  2. 标准Go编译器替代方案:如果不使用gccgo,Go生态中有这些替代方案:

使用runtime/trace包

package main

import (
    "os"
    "runtime/trace"
)

func main() {
    f, _ := os.Create("trace.out")
    trace.Start(f)
    defer trace.Stop()
    
    // 你的代码
}

使用pprof进行CPU分析

import _ "net/http/pprof"

func main() {
    go func() {
        http.ListenAndServe("localhost:6060", nil)
    }()
    // 你的代码
}

使用go tool trace可视化

go run -trace=trace.out main.go
go tool trace trace.out

基于AST的代码插桩:可以使用go/ast和go/parser包编写工具:

package main

import (
    "go/ast"
    "go/parser"
    "go/token"
)

func instrumentFile(filename string) {
    fset := token.NewFileSet()
    node, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
    if err != nil {
        panic(err)
    }
    
    ast.Inspect(node, func(n ast.Node) bool {
        if fn, ok := n.(*ast.FuncDecl); ok {
            // 在这里插入你的插桩代码
            fmt.Printf("Found function: %s\n", fn.Name.Name)
        }
        return true
    })
}

使用eBPF进行动态追踪(Linux):

# 使用bcc工具
sudo funccount 'go:main.*'

使用debug/gosym进行符号解析

package main

import (
    "debug/gosym"
    "fmt"
)

func main() {
    // 解析Go二进制文件的符号表
    table, err := gosym.NewTable(nil, nil)
    if err == nil {
        for _, f := range table.Funcs {
            fmt.Printf("Function: %s at %x\n", f.Name, f.Entry)
        }
    }
}

对于大型Go程序,我建议结合使用pprof和trace,它们能提供函数级别的执行信息,并且是Go标准库的一部分,兼容性最好。如果你需要更底层的控制,基于AST的代码插桩工具可以提供最大的灵活性。

回到顶部