易于阅读的Golang汇编输出?

51

我有兴趣检查标准Go编译器的x86汇编输出,以确定我的代码是否被转换成合理高效的汇编代码; 通过分析和检查汇编输出,希望能够了解应该在哪里/如何重写我的Go代码以获得最佳性能。但是当我使用-S标志检查代码时,Go输出了一堆乱七八糟的东西!我想要两件事:

  1. 有没有办法让Go编译器将汇编输出转储到文件中,而不仅仅是在终端上打印出来?

  2. 此外,有没有办法使Go编译器将汇编代码分离成单独的带有标签的函数?我知道一些函数可能会被内联,因此不会出现在汇编代码中。我现在看到的只是一堆难以理解的同质汇编代码块。


6
使用 > 或其他命令 shell 支持的符号将命令的输出重定向到文件中怎么样? - Michael
1
此外,通过查看汇编代码来进行分析的方法可能不是最有效的。您是否已经了解了针对Go代码可用的性能分析工具?(例如http://blog.golang.org/profiling-go-programs) - Michael
1
我同意你的看法,汇编输出需要改进。问题在于Go编译器不会生成实际的汇编代码。它生成了很像汇编的东西,但包含了许多伪指令,这些指令将由链接器扩展。只有在链接完成后,才会产生实际的汇编代码(并立即汇编)。 - fuz
另外,可以查看Plan 9汇编器的工作方式。Go语言的参考实现使用了这个汇编器。 - fuz
PS:我正在寻找类似于GCC命令的东西:-fverbose-asm - Gautam
7个回答

47
  1. 您可以像这样将输出重定向到文件:

 go tool compile -S file.go > file.s
  • 你可以使用-N选项禁用优化:

     go tool compile -S -N file.go
    
  • 或者,您也可以使用gccgo:

    gccgo -S -O0 -masm=intel test.go
    

    这将生成test.s文件。您可以尝试使用-O0/1/2/3选项,以观察不同的优化效果。


    2
    go build -gcflags -S test.go > test.s 也应该可以工作(根据 https://golang.org/doc/asm) - rkusa
    18
    go1.5 起,使用命令 go tool compile -S file.go > file.S 可以将 Go 代码转换为汇编代码并输出到文件 file.S 中。 - Ivan Black
    1
    @IvanBlack 你的评论值得得到一个顶级回复。 - dolmen
    尝试添加原始内容以添加@IvanBlack所说的内容(仅适用于"go tool compile"自go.15以来),但编辑队列已满。也许作者想要这样做。不过没关系,这些版本很快就会过时... - alex gimenez

    24

    我不建议使用-S的输出结果,因为Go链接器可能会对目标代码进行大量更改。但是它确实可以让你了解发生了什么。

    Go汇编器的输出结果也相当非标准化。

    当我想要这样做时,我总是使用objdump,这将给您一个漂亮的标准汇编器输出结果。

    例如对于x86 / amd64处理器:

    objdump -d executable > disassembly
    

    对于ARM(为了让寄存器名称与Go使用的相同)

    objdump -M reg-names-raw -d executable > disassembly
    

    这些示例汇编语法的期望形式是:mov %r11b,0x84(%rsp,%r10,1)。只是说一下。 - undefined

    15

    在生成的可执行文件上运行 go tool objdump

    使用其 -s 选项将输出限制为感兴趣的函数。


    9

    将输出转储到文件中:

    go tool objdump EXECUTABLE_FILE > ASSEMBLY_FILE
    

    如果您想要包含源Go代码(假设您拥有一个可用的golang设置,并且已经自行构建了可执行文件):

    go tool objdump -S EXECUTABLE_FILE
    

    为了使输出更易于查看,我使用了一个小技巧包装器,它产生了以下结果(简而言之,它会给改变控制流的指令着色-蓝色表示跳转,绿色表示调用/返回,红色表示陷阱,紫色表示填充-并在无条件控制流跳转后添加新行):

    example output of go-objdump wrapper

    如果您使用上述包装器,可能希望在管道传输到less时使用-R开关(或通过将其添加到环境中,例如在.bashrc中:export LESS="$LESS -R")。
    go-objdump EXECUTABLE_FILE | less -R
    

    另外,有一个名为godbolt.org的网站,它可能具有最易读的输出,并且允许您轻松切换编译器(gc、gccgo)和版本。


    8
    最近的一种备选方案是loov/lensm,它可以查看汇编和源代码。
    (来自Egon Elbre

    To run the program, provide a regular expression filter for the symbol you want to inspect.
    -watch allows to automatically reload the executable and information when it changes.

    lensm -watch -filter Fibonacci lensm
    

    Note: The program requires a binary that is built on your computer, otherwise the source code for the functions cannot be loaded.

    Result:

    https://github.com/loov/lensm/blob/main/screenshot.png?raw=true


    这可能是对godbolt.org不错的补充。 godbolt.org

    3
    我遇到了一些问题,因为生成的程序提供了比我想要的更多的信息,但仍然没有足够的细节。让我解释一下:它提供了所有由go内部导入的库的汇编代码,并没有提供我的代码所在的行数(我的代码都在文件底部)。
    以下是我从官方文档中找到的内容: $ GOOS=linux GOARCH=amd64 go tool compile -S x.go # 或者:go build -gcflags -S x.go
    文件:
    package main
    
    func main() {
        println(3)
    }
    

    产生:

    --- prog list "main" ---
    0000 (x.go:3) TEXT    main+0(SB),$8-0
    0001 (x.go:3) FUNCDATA $0,gcargs·0+0(SB)
    0002 (x.go:3) FUNCDATA $1,gclocals·0+0(SB)
    0003 (x.go:4) MOVQ    $3,(SP)
    0004 (x.go:4) PCDATA  $0,$8
    0005 (x.go:4) CALL    ,runtime.printint+0(SB)
    0006 (x.go:4) PCDATA  $0,$-1
    0007 (x.go:4) PCDATA  $0,$0
    0008 (x.go:4) CALL    ,runtime.printnl+0(SB)
    0009 (x.go:4) PCDATA  $0,$-1
    0010 (x.go:5) RET     ,
    

    我所做的基本上是: go tool compile -S hello.go > hello.s 这样就得到了我想要的结果!

    1
    您可能会发现Godbolt编译器资源管理器非常有用,因为它可以进行颜色突出显示以将源代码行映射到汇编语言。https://godbolt.org/g/yQ4gbQ中提供了一个简单函数的输出,包括gc1.10.1和gccgo 7.2版本。 - Peter Cordes
    那是一个非常好的工具。 - renno

    2

    对于使用XCode开发工具的Mac用户,我发现最简单的方法是使用otool

    $ otool -tV <executable>
    

    Source


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接