您的位置 首页 golang

bpftrace动态追踪golang应用-函数内联问题

问题

在 的golang代码中,函数add的上一行,增加了一条注释语句: //go:noinline 。在bpftrace追踪时,是否可以去掉?有什么作用?

现象

为了说明该问题,设计一个例子。

golang代码中,有两个求和函数。其中,add1加上 //go:noinline ,另一个add2不加。代码如下:

 # cat inline.go
package main

import "fmt"

//go:noinline
func add1(a, b int) int {
  return a + b
}

func add2(a, b int) int {
  return a + b
}

func main() {
  a := 1
  r1 := add1(a, 2)
  fmt.Println(r1)
  r2 := add2(a, 3)
  fmt.Println(r2)
}

# ./inline
3
4  

bpftrace程序分别对函数add1和add2的输入参数、返回值进行追踪,代码如下:

 # cat inline.bt
#!/usr/bin/bpftrace
uprobe:./inline:main.add1
{
    printf("add1 arg1:%d\n", sarg0);
    printf("add1 arg2:%d\n", sarg1);
}

ur:./inline:main.add1
{
    printf("add1 retval:%d\n", retval);
}

uprobe:./inline:main.add2
{
    printf("add2 arg1:%d\n", sarg0);
    printf("add2 arg2:%d\n", sarg1);
}

ur:./inline:main.add2
{
    printf("add2 retval:%d\n", retval);
}

# bpftrace ./inline.bt
Attaching 2 probes...
add1 arg1:1
add1 arg2:2
add1 retval:3  

执行程序后,可以看到bpftrace程序能够正常追踪到函数add1,但是无法追踪到函数add2。

分析

通过上文中的示例代码,可以看到,没有加 //go:noinline 的函数无法被bpftrace程序追踪到。通过查阅golang相关文档,可以知道, //go:noinline 表示该函数在编译时,不会被内联。

使用 objump -S 生成golang程序的汇编代码如下:

     a := 1
    r1 := add1(a, 2)
  498e41:   48 c7 04 24 01 00 00    movq   $0x1,(%rsp)
  498e48:   00
  498e49:   48 c7 44 24 08 02 00    movq   $0x2,0x8(%rsp)
  498e50:   00 00
  498e52:   e8 a9 ff ff ff          callq  498e00 <main.add1>
  498e57:   48 8b 44 24 10          mov    0x10(%rsp),%rax
    fmt.Println(r1)
-----省略无关代码-----
  498ead:   48 c7 44 24 20 01 00    movq   $0x1,0x20(%rsp)
  498eb4:   00 00
  498eb6:   e8 a5 9a ff ff          callq  492960 <fmt.Fprintln>
    r2 := add2(a, 3)
    fmt.Println(r2)
  498ebb:   48 c7 04 24 04 00 00    movq   $0x4,(%rsp)
  498ec2:   00
  498ec3:   e8 d8 11 f7 ff          callq  40a0a0 < runtime .convT64>
  498ec8:   48 8b 44 24 08          mov    0x8(%rsp),%rax
  498ecd:   0f 57 c0                xorps  %xmm0,%xmm0
  498ed0:   0f 11 44 24 40          movups %xmm0,0x40(%rsp)
  498ed5:   48 8d 0d a4 a3 00 00    lea    0xa3a4(%rip),%rcx        # 4a3280 <type.*+0xa280>  

通过汇编代码,我们可以看到,主函数中,地址 0x498e52 callq 498e00 <main.add1> 调用了add1函数,地址 0x498ebb movq $0x4,(%rsp) 直接计算求值。

因此,golang编译器在编译代码时,会对代码进行分析,并按照内联规则,将某些函数生成内联代码。一旦函数被内联,bpftrace将无法追踪到对应函数。也就是,上文中函数 add2 无法被追踪到。

解决方法

针对golang程序中编译器内联的问题,可以通过禁止内联的方式来解决。禁止内联的方式有:

  • 全局禁用:设定编译参数为 go build -gcflags=”-l” ,其缺点是,所有函数都不会进行内联编译,对程序的运行性能可能会有一定的影响。
  • 函数禁用:在函数定义的前一行,加上 //go:noinline ,golang编译器,对该函数不会进行内联。

在实践中,可以通过 go build -gcflags=”-m -m” 来查看,哪些函数会在编译时执行内联,如:

 # go build -gcflags="-m -m" inline.go
# command-line-arguments
./inline.go:6:6: cannot inline add1: marked go:noinline
./inline.go:10:6: can inline add2 with cost 4 as: func(int, int) int { return a + b }
./inline.go:14:6: cannot inline main: function too complex: cost 234 exceeds budget 80
./inline.go:17:13: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = <N>; var fmt..autotmp_4 error; fmt..autotmp_4 = <N>; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }
./inline.go:18:12: inlining call to add2 func(int, int) int { return a + b }
./inline.go:19:13: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = <N>; var fmt..autotmp_4 error; fmt..autotmp_4 = <N>; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }  

从输出中,可以看到:

  • 函数add1被标记为不进行内联: cannot inline add1: marked go:noinline
  • 函数add2内联代价小,进行内联: can inline add2 with cost 4 as: func(int, int) int { return a + b }
  • main函数太大,内联代价超过80,不进行内联: cannot inline main: function too complex: cost 234 exceeds budget 80

关于golang编译器进行内联的场景,可以参考golang源码:。

总结

由于golang编译器内联优化,bpftrace可能无法正常追踪golang程序。在编写bpftrace脚本时,可以先使用 nm 命令查看一下可执行程序,是否存在需要追踪的函数的符号信息。如果没有则bpftrace将不能对其进行追踪。

前面的示例中,都是对 int 类型的参数进行追踪,那对于 string 类型的参数,是否也可以用同样的方式进行追踪?将在下一篇中进行讨论。

文章来源:智云一二三科技

文章标题:bpftrace动态追踪golang应用-函数内联问题

文章地址:https://www.zhihuclub.com/97618.shtml

关于作者: 智云科技

热门文章

网站地图