用systemtap跟踪打印动态链接库的所有c++函数调用过程

本文介绍用systemtap跟踪打印动态链接库的所有C++函数调用过程。对比了ltrace,指出systemtap无其在elf结构无依赖项时的问题且输出更灵活。还探讨了systemtap解析C++函数问题,可结合c++filt命令转换函数名,最后介绍了函数调用跟踪及用perl脚本转换函数名。

用systemtap跟踪打印动态链接库的所有c++函数调用过程

May 07, 2015 | 1 Minute Read

  • ltrace 的问题
    用ltrace 可以打印所有的so文件调用了。但上次试过如果so是自己用dlopen来加载的。 就是在elf结构里面没有对应的依赖项的情况下,ltrace好像是没有做对应的调用了。 用systemtap的就没有这个问题,打印输出也更灵活一些。

  • systemtap的解析c++ 函数问题
    systemtap的 probe point 指定函数名,是支持c++ 方式指定名字的。 但默认的打印输出却只能输出函数名,不包含c++ 类名。

c++ 的函数符号是经过修饰的,比如用 nm *.so 查看就可以看到 _Z开头一大堆函数名字。

c++filt _ZZN9log4cplus19initializeLog4cplusEvE11initialized

log4cplus::initializeLog4cplus()::initialized

systemtap的 ppfunc () 函数只输出”initialized” systemtap的 probefunc () 函数输出 “_ZZN9log4cplus19initializeLog4cplusEvE11initialized” 其实我们想要的是 “log4cplus::initializeLog4cplus()::initialized” 但看一下文件是没有这个tapset函数的,估计的自己写一个才行。

还好有c++filt 这个命令,可以做c++ 函数名的demangle。 我们就可以简单用probefunc 打印 然后在自己用c++filt 命令来转换一下好了。

  • systemtap的函数调用跟踪
    systemtap的资料就有一个 para-callgraph.stp ,根据自己的要求做修改吧。

我是这样用的,打印所有libTest.so 源码文件 .hpp *.cpp 的所有函数, 也就是只有c++的函数。 其实用来跟踪c函数调用那些用起来更好用吧,都不用做c++ 函数名转换了。根据自己要求来设置跟踪哪些函数吧。 stap para-callgraph.stp ‘process(“/usr/lib/libTest.so”).function(“@*pp”)’ ‘process(“/usr/lib/libTest.so”).function(“Init”)’ > trace_file.txt

#! /usr/bin/env stap

function trace(entry_p, extra) {
  /* %( $# > 1 %? if (tid() in trace) %) */
  %( $# > 1 %? if (2 > 1) %)
  printf("%s%s%s %s\n",
         thread_indent (entry_p),
         (entry_p>0?"->":"<-"),
         /* ppfunc (),  只有函数名,没有包含类名*/
         probefunc (),
         extra)
}


%( $# > 1 %?
global trace
probe $2.call {
  trace[tid()] = 1
}
probe $2.return {
  delete trace[tid()]
}
%)

probe $1.call   { trace(1, $$parms) }
probe $1.return { trace(-1, $$return) }

// 第一个参数是跟踪的打印的函数,第二个参数是触发记录的函数。
// https://sourceware.org/systemtap/langref/Probe_points.html#SECTION00051300000000000000
// probe_point = <function name@filename:line_number>
// stap para-callgraph.stp 'kernel.function("*@fs/*.c")' 'kernel.function("sys_read")':
// stap para-callgraph.stp  'process("/usr/lib/libtest.so").function("*")' 'process("/usr/lib/libtest.so").function("TestInit")'
// stap para-callgraph.stp  'process("/usr/lib/libtest.so").function("*@*cpp")' 'process("/usr/lib/libtest.so").function("TestInit")'
// stap para-callgraph.stp  'process("/usr/lib/libtest.so").function("*@*cpp:*")' 'process("/usr/lib/libtest.so").function("TestInit")'
// stap para-callgraph.stp  'process("/usr/lib/libtest.so").function("*@*cpp:1-200")' 'process("/usr/lib/libtest.so").function("TestInit")'
  • 写个简单的perl脚本来调用c++filt 来做函数名转换
#!/usr/bin/perl -w
use strict;


open(INFILE, "trace_file.txt");
open(OUTFILE, ">trace_file_2.txt");

my $line;
while ($line = <INFILE>){
  if ($line =~ /boost|Trace/) {
    next;
  }
  if ($line =~ /(.*)(_Z\S+)(.*)/) {
    my $function_name = `c++filt $2`;

    chomp($function_name);
    print OUTFILE  $1, $function_name, $3, "\n";
  } else {
    print OUTFILE $line;
  }
}

close INFILE;
close OUTFILE;

原文:https://gmd20.github.io/blog/用systemtap跟踪打印动态链接库的所有c++函数调用过程/

<think>我们正在讨论调试跟踪函数的方法。根据提供的引用,我们可以总结以下几种方法: 1. 使用Python的sys.settrace设置跟踪函数(如引用[1]所示) 2. 在SQL Server中使用SSMS的调试器调试自定义函数和存储过程(引用[2]) 3. 在Linux下使用gdb调试函数(引用[3]) 4. 使用Linux下的其他工具如systemtap、strace等(引用[4]) 下面将分别介绍这些方法,并尽可能提供示例代码或步骤说明。 </think>### 调试跟踪函数的常用方法 以下是几种常用的函数调试跟踪技术,结合引用内容和实际应用场景整理: #### 1. **Python 函数跟踪(`sys.settrace`)** 通过设置系统级跟踪函数捕获函数调用事件: ```python import sys def trace_calls(frame, event, arg): if event == 'call': print(f"调用函数: {frame.f_code.co_name}()") elif event == 'return': print(f"函数返回: {frame.f_code.co_name}()") return trace_calls sys.settrace(trace_calls) # 启用跟踪 # 测试函数调用 def test(): return 42 test() sys.settrace(None) # 停用跟踪 ``` 输出: ``` 调用函数: test() 函数返回: test() ``` **适用场景**:动态分析Python函数调用栈[^1] #### 2. **SQL Server 函数调试(SSMS调试器)** 使用SQL Server Management Studio调试存储过程函数: ```sql -- 设置断点调试自定义函数 DECLARE @result INT SELECT @result = dbo.MyFunction(10) -- 在此行设置断点 PRINT @result -- 调试存储过程 EXEC dbo.MyStoredProcedure @param1 = 'value1' -- 在此行设置断点 ``` **操作步骤**: 1. 在SSMS查询编辑器中编写调用代码 2. 在目标行设置断点(F9) 3. 启动调试(F5或调试菜单) 4. 查看局部变量/监视窗口[^2] #### 3. **Linux GDB 函数调试** 使用GDB动态调用和跟踪函数: ```c #include <stdio.h> int gdata = 0; int fun(void) { return (++gdata); // 目标跟踪函数 } int main() { printf("Initial: %d\n", gdata); return 0; } ``` **调试命令**: ```bash gdb ./program (gdb) break fun # 在函数入口设断点 (gdb) run # 启动程序 (gdb) print fun() # 强制调用函数 (gdb) watch gdata # 监视全局变量变化[^3] ``` #### 4. **Linux 系统级跟踪工具** - **`strace`**:跟踪系统调用 ```bash strace -e trace=open,read -p <PID> # 监控指定进程的文件操作 ``` - **`ltrace`**:跟踪函数调用 ```bash ltrace ./program # 显示所有动态库函数调用 ``` - **`systemtap`**:内核级跟踪 ```bash probe process.function("fun").return { printf("%s returned %d\n", funcname, $return) }[^4] ``` --- ### 选择建议 | 场景 | 推荐工具 | |---------------------|---------------------| | Python应用调试 | `sys.settrace` | | 数据库函数调试 | SSMS调试器 | | C/C++程序函数跟踪 | GDB | | 系统调用/库函数分析 | strace/ltrace | | 内核级函数监控 | systemtap/dtrace | 通过组合使用这些工具,可以实现从用户空间到内核空间的完整函数调用跟踪
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值