bpftrace动态追踪golang应用-string参数

在前面文章的例子中,函数参数都是整数类型,对于string数据类型,bpftrace的追踪方式也是一样的吗?

例子

golang程序中定义了一个函数,实现两个字符串的拼接。其参数数据类型为string。代码如下:

bpftrace动态追踪golang应用-string参数

# cat string.gopackage mainimport (  "fmt")//go:noinlinefunc join(s1, s2 string) string {  return s1 + s2}func main() {  s1 := "world"  ss := join("hello,", s1)  fmt.Println(ss)}# ./stringhello,world

使用bpftrace追踪join函数,代码如下:

#!/usr/bin/bpftraceuprobe:./string:main.join{    printf("arg1:%s\n", str(sarg0));    printf("arg2:%s\n", str(sarg1));}

执行bpftrace代码后,可以看到,输出结果为:

# bpftrace string.btAttaching 1 probe...arg1:hello,objectpopcntselectstringstructsweep sysmontimersuint16uinarg2:

从输出结果来看,显然bpftrace追踪代码的实现是不对的。因此,对golang程序进一步分析。

分析

使用gdb调试golang程序,查看join函数的参数传递。在ss := join("hello,", s1)处设置断点,运行后,单步跟踪至函数调用处,查看对应汇编代码。

(gdb) b 13(gdb) r(gdb) si 6(gdb) disassembleDump of assembler code for function main.main:   0x0000000000498e80 <+0>: mov    %fs:0xfffffffffffffff8,%rcx   0x0000000000498e89 <+9>: cmp    0x10(%rcx),%rsp   0x0000000000498e8d <+13>:  jbe    0x498f47 <main.main+199>   0x0000000000498e93 <+19>:  sub    $0x58,%rsp   0x0000000000498e97 <+23>:  mov    %rbp,0x50(%rsp)   0x0000000000498e9c <+28>:  lea    0x50(%rsp),%rbp   0x0000000000498ea1 <+33>:  lea    0x2309d(%rip),%rax        # 0x4bbf45   0x0000000000498ea8 <+40>:  mov    %rax,(%rsp)   0x0000000000498eac <+44>:  movq   $0x6,0x8(%rsp)   0x0000000000498eb5 <+53>:  lea    0x22f35(%rip),%rax        # 0x4bbdf1   0x0000000000498ebc <+60>:  mov    %rax,0x10(%rsp)   0x0000000000498ec1 <+65>:  movq   $0x5,0x18(%rsp)=> 0x0000000000498eca <+74>:  callq  0x498e00 <main.join>   0x0000000000498ecf <+79>:  mov    0x20(%rsp),%rax   0x0000000000498ed4 <+84>:  mov    0x28(%rsp),%rcx   0x0000000000498ed9 <+89>:  mov    %rax,(%rsp)   0x0000000000498edd <+93>:  mov    %rcx,0x8(%rsp)   0x0000000000498ee2 <+98>:  callq  0x40a120 <runtime.convTstring>   0x0000000000498ee7 <+103>: mov    0x10(%rsp),%rax   0x0000000000498eec <+108>: xorps  %xmm0,%xmm0   0x0000000000498eef <+111>: movups %xmm0,0x40(%rsp)   0x0000000000498ef4 <+116>: lea    0xaa05(%rip),%rcx        # 0x4a3900

结合如下join函数的汇编代码,

   0x0000000000498ea8 <+40>:  mov    %rax,(%rsp)   0x0000000000498eac <+44>:  movq   $0x6,0x8(%rsp)   0x0000000000498eb5 <+53>:  lea    0x22f35(%rip),%rax        # 0x4bbdf1   0x0000000000498ebc <+60>:  mov    %rax,0x10(%rsp)   0x0000000000498ec1 <+65>:  movq   $0x5,0x18(%rsp)

可以得出:

  • join函数的参数是通过栈进行传递的。因此,可以通过sargN变量访问到函数的参数。(argN和sargN是有区别的。)
  • 在main函数中,一共向join函数传递了4个参数,在栈中的位置分别为:$rsp、$rsp+0x8、$rsp+0x10和$rsp+0x18。

为了进一步验证,查看栈中保存的内容:

(gdb) p ($rsp)$4 = (void *) 0xc000064f28(gdb) x/10c *0xc000064f280x4bbf45: 104 'h' 101 'e' 108 'l' 108 'l' 111 'o' 44 ','  111 'o' 98 'b'0x4bbf4d: 106 'j' 101 'e'(gdb) p *($rsp+0x10)Attempt to dereference a generic pointer.(gdb) p ($rsp+0x10)$3 = (void *) 0xc000064f38(gdb) x/10c *0xc000064f380x4bbdf1: 119 'w' 111 'o' 114 'r' 108 'l' 100 'd' 119 'w' 114 'r' 105 'i'0x4bbdf9: 116 't' 101 'e

同时,$rsp+0x8为字符串"hello,"的长度6,$rsp+0x18为字符串"world"的长度5。

验证

通过前面的分析,将bpftrace代码修改为:

#!/usr/bin/bpftraceuprobe:./string:main.join{    printf("arg1[%d]:%s\n", sarg1, str(sarg0, sarg1));    printf("arg2[%d]:%s\n", sarg3, str(sarg2, sarg3));}

运行之后,可以看到结果:

# bpftrace string.btAttaching 1 probe...arg1[6]:hello,arg2[5]:world

指针参数

上文中,函数join的两个参数均为string类型,现将其定义为string指针,对应函数调用的汇编代码也发生了变化,如下所示:

//go:noinlinefunc join(s1, s2 *string) string {  return *s1 + *s2}ss1 := join(&s1, &s1)对应的汇编代码:498fd0:   48 89 04 24             mov    %rax,(%rsp)498fd4:   48 89 44 24 08          mov    %rax,0x8(%rsp)498fd9:   e8 a2 fe ff ff          callq  498e80 <main.join>

可以看到,传递给join函数的参数只有两个,即s1的地址。因此,在uprobe中sargN存储的内容为字符串的地址。此时,需要将该地址转换为string结构体才能可视化输出。

查阅golang中string结构的存储结构,在bpftrace程序中定义一个GoString的结构体:

struct GoString {    char* str;    int len;};

uprobe的代码可以写成:

uprobe:./string:main.join{    $p1 = (struct GoString*) sarg0;    printf("arg1[%d]:%s\n", $p1->len, str($p1->str, $p1->len));    $p2 = (struct GoString*) sarg1;    printf("arg2[%d]:%s\n", $p2->len, str($p2->str, $p2->len));}

通过这种方式,有效地解决了地址参数的可视化输出的问题。同样的原理,如果参数为结构体或其他数据类型,也可以通过同样的方式进行解决。

总结

使用bpftrace分析应用程序的输入参数,重点在于,需要弄清楚:

  • 参数的传递方式:是栈进行传递?还是寄存器进行传递?这决定了在uprobe中,使用sargN还是argN来获取参数。
  • 参数的存储结构:如果参数是一个地址或结构体,需要将该地址强转到对应的数据结构,才能正常解析。

版权声明

1 本文地址:https://www.sunlonger.com/jijin/1237389.html 转载请注明出处。
2 本站内容除隼龙儿财经签约编辑原创以外,部分来源网络由互联网用户自发投稿及AIGC生成仅供学习参考。
3 文章观点仅代表原作者本人不代表本站立场,并不完全代表本站赞同其观点和对其真实性负责。
4 文章版权归原作者所有,部分转载文章仅为传播更多信息服务用户,如信息标记有误请联系管理员。
5 本站禁止以任何方式发布转载违法违规相关信息,如发现本站有涉嫌侵权/违规及任何不妥内容,请第一时间联系我们申诉反馈,经核实立即修正或删除。


本站仅提供信息存储空间服务,部分内容不拥有所有权,不承担相关法律责任。
上一篇 2024年06月18日
下一篇 2024年06月18日

相关推荐

  • 以下是几种构建指数基金组合的常用方法: 一、核心

    以下是几种构建指数基金组合的常用方法:一、核心 - 卫星策略1.核心资产配置●选择宽基指数基金作为核心资产,如沪深300指数基金。沪深300涵盖了沪深两市规模大、流动性好的300只股票,能代表A股市场的整体表现。这部分资产通常占组合的较大比例,比如60% - 70%。其作用是获取

    2024-10-28 22:02:12
    0 0
  • A500投资锦囊,来了

    风险提示:基金有风险,投资须谨慎。本观点仅代表当时观点,今后可能发生改变,仅供参考,不构成投资建议或保证,亦不作为任何法律文件。基金过往业绩并不预示其未来表现,基金管理人管理的其他基金的业绩并不构成基金业绩表现的保证。我国基金运作时间较短,不能反映股市发展

    2024-10-28 22:02:07
    0 0
  • 李稻葵谈理财:假如有10万元,会先买1万元保险,再买8万元基金

    近日,清化大学中国经济思想与实践研究院院长李稻葵做客央视财经频道《对话》栏目,对当前宏观经济运行情况发表最新见解。现场,李稻葵被问假如有10万元,该如何分配。李稻葵表示:“首先有个小前提,就是这10万元钱我5年之内不需要急用。首先,我想至少拿出1万元钱买保险,我

    2024-10-28 22:01:57
    0 0
  • 指数基金的三种配置方法

    1976年6月,先锋领航发行了旗下第一款指数基金产品,甚至其名称都叫做First Index Investment Trust(现已改名为Vanguard 500 Index Fund ETF——标普500ETF先锋领航)。这只产品的“第一”,不仅预示着先锋领航指数基金业务的开端,同时也标志着全球指数基金产品0的突破。投

    2024-10-28 21:23:17
    0 0
  • 基金投资:稳健获利的策略选择

    在当今的金融市场中,基金投资已成为众多投资者的热门选择。然而,如何进行有效的基金投资却是一门需要深入研究的学问。基金投资的风险、流动性和收益性一直是投资者关注的焦点。过高的风险可能导致本金的大幅损失,而缺乏流动性则可能在急需资金时陷入困境,当然,追求高收益

    2024-10-28 21:23:13
    0 0
  • 权益理财三季度跑输主流指数和权益型基金,施罗德交银理财欲推新品布局红利策略丨机警理财日报

    南财理财通课题组 黄桂煊榜单排名来自理财通AI全自动化实时排名,如您对数据有疑问,请在文末联系助理进一步核实。权益类理财三季度涨8.98%,跑输主流指数和权益型基金2024年三季度,A股出现V型反转,上证指数在第二季度末跌破3000点之后持续下行,9月20日最低至2700点,9月底

    2024-10-28 21:23:09
    0 0

发表回复

8206

评论列表(0条)

    暂无评论