Colderleo's blog Colderleo's blog
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)

Colder Leo

热爱代码的打工人
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)
  • 常见程序性能开销cost、latency延迟耗时的经验值
  • 面试常见问题
  • 静态链接-动态链接-elf详解-elfloader
  • 动态库和静态库的依赖问题
  • glibc和ld的编译调试-为某程序单独设置ld
  • dl_iterate_phdr遍历linkmap头、获取so加载地址
  • shell、bash语法和脚本模板
  • so文件查找路径
  • 逻辑地址-线性地址or虚拟地址-物理地址
  • 通过ELF读取当前进程的符号表并获取符号的版本信息
  • 虚拟内存,cache,TLB,页表
  • 用户内存空间分布和mmap
  • numa网卡绑定
  • 隔核绑核、服务器优化
  • popen底层原理和仿照实现-execl
  • tmux用法
  • ASLR机制
  • 程序后台运行、恢复前台nohup
  • 大页内存huge_page
  • 用perf查看page-fault
  • Bash设置显示全部路径
  • 查看socket fd状态,设置nonblock
  • cout输出到屏幕的过程
  • 多进程写同一文件-write原子性-log日志
  • vim用法
  • epoll用法
  • signal信号、软中断、硬中断、alarm
  • 内核模块
  • 读写锁之pthread_rwlock和内核rwlock自旋读写锁
  • systemtap
    • 用户态程序简单使用示例
      • bpftrace 打印执行到指定地址:
      • bpftrace脚本测量fun1到fun2的耗时:
      • systemtap脚本打印函数调用入口或执行到指定地址:
      • systemtap画函数调用图
    • systemtap
      • 安装
      • 下载centos glibc和kernel源码的方法:
      • 使用
      • 参考
      • ppfunc
      • systemtap、bpftrace的相同点和不同点
      • systemtap/bpftrace底层实现
      • 热补丁/gdb调试原理
  • xargs、awk用法
  • openssl libssl.so.10.so缺失问题
  • netstat用法
  • fork函数
  • tcp延迟确认ack
  • 90.centos7上一次std-string编译错误定位
  • docker用法
  • find用法
  • dmesg
  • gcc编译用法
  • avx-sse切换惩罚
  • Centos7防火墙
  • chmod用法
  • kernel-devel安装版本
  • Linux-Centos7系统安装、网络设置、常见报错
  • linux下g++编译c++文件
  • MegaCli 安装及使用
  • mysql
  • mysql忘记密码修改方法
  • set用法
  • crontab
  • ssh传文件scp
  • ssh连接
  • tcpdump、tshark、tcpreplay用法
  • ubantu root登录以及创建新用户
  • ubuntu安装g++和gdb
  • uClibc编译失败解决方法
  • win10安装WSL open-ssh
  • yum升级git
  • 比较so文件版本-md5sum
  • 查看磁盘信息
  • 合并两个硬盘,挂载到一个文件夹下
  • 软件安装目录usr-local-src
  • 下载centos历史版本
  • sh脚本转可执行文件、加密
  • Linux
gaoliu
2022-03-30
目录

systemtap

# 用户态程序简单使用示例

# bpftrace 打印执行到指定地址:

bpftrace -e 'uprobe:/home/zhangsan/sometest/a.out:0x401146 {printf("aa\n");}'
1

# bpftrace脚本测量fun1到fun2的耗时:

# time_cost.bt:
uprobe:/home/a.out:fun1 {
  @start = nsecs;
}
uprobe:/home/a.out:fun2 {
  @cost[nsecs-@start]++;
}
1
2
3
4
5
6
7

运行: bpftrace time_cost.bt

# systemtap脚本打印函数调用入口或执行到指定地址:

#probe process("/home/zhangsan/sometest/a.out").statement(0x401146)
probe process("/home/zhangsan/sometest/a.out").function("*")
{
        # printf ("%s called\n", ppfunc())
        printf ("addr called\n")
}
1
2
3
4
5
6

# systemtap画函数调用图

tmp.stp:

probe process("/home/zhangsan/sometest/a.out").function("*").call
{
      printf ("%s -> %s\n", thread_indent(2), ppfunc())
}

probe process("/home/zhangsan/sometest/a.out").function("*").return
{
        printf ("%s <- %s\n", thread_indent(-2), ppfunc())
}
1
2
3
4
5
6
7
8
9

执行 stap tmp.stp 可得到a.out程序执行时的函数调用图

也可以用它自带的例子,下面有。

# systemtap

# 安装

以centos7.6.1810为例,内核版本为 3.10.0-957.el7.x86_64

更新yum源为国内源 yum install systemtap*

在Index of /7/x86_64 (centos.org) http://debuginfo.centos.org/7/x86_64/ (opens new window) 下载对应内核版本kernel-debuginfo 和 kernel-debuginfo-common rpm包并安装

  • kernel-debuginfo: http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-3.10.0-957.el7.x86_64.rpm (opens new window)
  • kernel-debuginfo-common: http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-common-x86_64-3.10.0-957.el7.x86_64.rpm (opens new window)
  • glibc-debuginfo: http://debuginfo.centos.org/7/x86_64/glibc-debuginfo-2.17-260.el7.x86_64.rpm (opens new window)
  • glibc-debuginfo-common: http://debuginfo.centos.org/7/x86_64/glibc-debuginfo-common-2.17-260.el7.x86_64.rpm (opens new window)

执行stap -v -e 'probe vfs.read {printf("read performed\n"); exit()}' 成功即systemtap安装成功

调试glibc还需要安装glibc调试信息 vim /etc/yum.repos.d/CentOS-Debuginfo.repo enabled=1 yum install yum-utils debug-install glibc* 安装glibc调试信息 或者在这个centos官网下载https://vault.centos.org/7.6.1810/os/x86_64/Packages/ (opens new window)

# 下载centos glibc和kernel源码的方法:

  • 参考: https://blog.csdn.net/u010743406/article/details/121293287 (opens new window)
  • 官网: https://vault.centos.org/7.6.1810/os/Source/SPackages/ (opens new window)
  • glibc: https://vault.centos.org/7.6.1810/os/Source/SPackages/glibc-2.17-260.el7.src.rpm (opens new window)
  • kernel: https://vault.centos.org/7.6.1810/os/Source/SPackages/kernel-3.10.0-957.el7.src.rpm (opens new window)

# 使用

安装后自带的例子,/usr/share/systemtap/examples/general/para-callgraph-verbose.stp

#!/usr/bin/stap

function trace(entry_p, extra) {
  %( $# > 1 %? if (tid() in trace) %)
  printf("%s%s%s %s\n",
         thread_indent (entry_p),
         (entry_p>0?"->":"<-"),
         ppfunc (),
         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) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

使用:

cp /usr/share/systemtap/examples/general/para-callgraph-verbose.stp .

# 内核追踪
stap para-callgraph-verbose.stp 'kernel.function("*@net/socket.c")'

# 程序或so追踪
stap para-callgraph-verbose.stp 'process("/path/to/exe_or_so").function("*")'

# 追踪程序中某个函数调用, 如send
stap para-callgraph-verbose.stp 'process("/path/to/exe_or_so").function("send")'
1
2
3
4
5
6
7
8
9
10

# 参考

网络 - SystemTap 新手指南 - 文江博客 (wenjiangs.com)

# ppfunc

https://www.cnblogs.com/zengkefu/p/6372255.html (opens new window)

# systemtap、bpftrace的相同点和不同点

参考: https://blog.csdn.net/Linux_Everything/article/details/116141384 SystemTap 会将用户提供的脚本翻译成 C 代码,然后作为 module 编译并加载到所运行的 Linux 内核中;bpftrace 则相反,会将脚本转换为 LLVM 中间表示,然后再编译为 BPF 程序。 对于许多应用场景来说,bpftrace 开箱即用,而 SystemTap 通常需要安装额外的依赖关系才能充分利用其所有功能。Bpftrace 的速度一般比较快,而且提供了各种快速汇总和报告的功能,可以说比 SystemTap 提供的类似功能更简单易用。另一方面,SystemTap 提供了一些与众不同的功能,比如:生成用户空间 backtrace 而不需要 frame pointer、通过变量名来访问函数参数和局部变量、以及对任意语句进行 probe 的能力

参考2:https://catbro666.github.io/posts/46dd3f4b/ bpftrace语言特性上没有systemtap丰富,不太能进行复杂的探测操作。想将探针放在函数某个语句上时,可以指定函数名+偏移量(通过汇编查看)。应用程序中的局部变量没有方便的获取方法,可以查看汇编代码,通过寄存器获取。

  • BPF限制

    • 不能调用任意的内核函数:只能调用BPF helper。
    • BPF程序不能做循环,因为必须在规定时间内结束。后续(Linux 5.3)会支持受限的循环。(随着即将添加的循环,您可能会开始怀疑BPF是否将成为图灵完整的。 BPF指令集本身允许创建图灵完备的自动机,但是由于验证器引入的安全性限制,BPF程序不是图灵完整的。)
    • BPF栈大小限制为MAX_BPF_STACK,设置为512,多用几个字符串就用完了。解决方法是使用BPF映射存储替代
    • 指令数限制为4096。Linux5.2大幅增加了这个限制。
  • BPF其他缺点

    • 探针附到函数一定偏移处不是很方便
    • 无法直接获取被探测应用程序中函数局部变量值
    • 结构体的访问非常麻烦,需要将所有依赖的头文件及其路径都包含进来
    • 内核版本要求较高,且在较旧的发行版上安装体验不佳
  • bpftrace优点

    • bpftrace基于内置Linux技术,不用追赶内核版本改动,稳定性更高
    • 脚本执行速度比systemtap快(使用llvm编译成BPF)
  • systemtap缺点

    • 基于内核模块技术,在RHEL以外的系统上都不可靠
    • 不是内置于内核,需要追赶内核版本改动,稳定性不如bpftrace
    • 脚本执行速度比bpftrace慢,且在生产环境需要gcc编译工具链、内核头文件
    • 依赖dwarf,需要安装内核符号,用户层程序则在编译时需要-g
  • systemtap优点

    • 脚本语言支持的特性更全,如函数偏移、循环、函数局部变量、结构体引用
    • 具有成熟的用户态符号自动加载,可以写比较复杂的探针处理程序
    • systemtap中有大量帮助程序(tapset),可用于检测不同的目标
    • systemtap普通模式只读,guru模式可以写,如修改程序中某个变量值
    • systemtap内核版本要求较低,在4.x以前内核上也能使用。

# systemtap/bpftrace底层实现

systemtap/bpftrace底层使用的是kprobe/uprobe,通过编写内核模块(ko)调用内核提供的kprobe/uprobe接口实现探测功能。

kprobe/uprobe可以在任何一条语句上加上探针,当执行到这条语句时将控制流转移到探针的handler上。其实现原理是在module_init时将相应指令的第一个字节替换成0xcc,也就是INT 3。这样执行到这条指令时就会跳转到INT 3的中断处理函数,由其判断出应该跳转到哪个handler。在module_exit中将这些指令还原。

# 热补丁/gdb调试原理

  通过ptrace系统调用修改目标进程。ptrace系统调从名字上看是用于进程跟踪的,它提供了父进程可以观察和控制其子进程执行的能力,并允许父进程检查和替换子进程的内核镜像(包括寄存器)的值。其基本原理是: 当使用了ptrace跟踪后,所有发送给被跟踪的子进程的信号(除了SIGKILL),都会被转发给父进程,而子进程则会被阻塞,这时子进程的状态就会被系统标注为TASK_TRACED。而父进程收到信号后,就可以对停止下来的子进程进行检查和修改,然后让子进程继续运行。 

断点的实现类似于kprobe,将要中断的指令的第一个字节替换为0xcc,继续运行时恢复该字节。

编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54
读写锁之pthread_rwlock和内核rwlock自旋读写锁
xargs、awk用法

← 读写锁之pthread_rwlock和内核rwlock自旋读写锁 xargs、awk用法→

最近更新
01
通过模板实现结构体成员拷贝-按成员名匹配
05-07
02
c++17通过模板获取类成员的个数
05-01
03
avx-sse切换惩罚
04-30
更多文章>
Theme by Vdoing | Copyright © 2019-2023 Colder Leo | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×