当前位置: 首页 > news >正文

Strace:深入系统调用的强大调试工具

#作者:stackofumbrella

文章目录

  • 简介
  • 系统调用
  • 定位共享内存异常
  • 识别性能瓶颈
  • 追踪子进程
  • 追踪网络操作
  • 改变系统调用的行为
  • 最后总结

简介

strace常用来跟踪进程执行时的系统调用和所接收的信号。 在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。

strace命令是一个集诊断、调试、统计于一体的工具,使用strace对应用的系统调用和信号传递的跟踪结果来对应用进行分析,以达到解决问题或是了解应用工作过程的目的。当然strace与专业的调试工具比如说gdb之类的是没法相比的,因为它不是一个专业的调试器。 strace的最简单的用法就是执行一个指定的命令,在指定的命令结束之后它也就退出了。在命令执行的过程中,strace会记录和解析命令进程的所有系统调用以及这个进程所接收到的所有的信号值。strace底层使用内核的ptrace特性来实现其功能。

系统调用

既然strace是用来跟踪用户空间进程的系统调用和信号的,那么先理解什么是系统调用。
在计算机中,系统调用(英语:system call)又称为系统呼叫,指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。
系统调用提供用户程序与操作系统之间的接口。操作系统的进程空间分为用户空间和内核空间。

操作系统内核直接运行在硬件上,提供设备管理、内存管理、任务调度等功能。
用户空间通过API请求内核空间的服务来完成其功能。内核提供给用户空间的这些API,就是系统调用。
在Linux系统上,应用代码通过glibc库封装的函数,间接使用系统调用。

Linux内核目前有300多个系统调用,详细的列表可以通过syscalls手册页查看。这些系统调用主要分为几类:
文件和设备访问类,比如open/close/read/write/chmod等

进程管理类,fork/clone/execve/exit/getpid等

信号类,signal/sigaction/kill等

内存管理,brk/mmap/mlock等

进程间通信IPC,shmget/semget * 信号量,共享内存,消息队列等

网络通信,socket/connect/sendto/sendmsg等

安装方法

CentOS
# yum -y install strace
Ubuntu
# apt -y install strace

常用参数
通过演示跟踪ls命令的调用过程作为参考
-c:统计每一个系统调用所执行的时间,次数和出错的次数等
$ strace -c ls
在这里插入图片描述
-tt:在每行输出的前面,显示微秒级别的时间
$ strace -tt ls
在这里插入图片描述
-T:显示每次系统调用所花费的时间
$ strace -T ls
在这里插入图片描述
-v:对于某些相关调用,把完整的环境变量,文件stat结构等打出来
$ strace -v ls
在这里插入图片描述
-f:跟踪目标进程,以及目标进程创建的所有子进程
$ strace -f ls
在这里插入图片描述
-o:把strace的输出单独写到指定的文件
$ strace -f ls -o 1.txt
在这里插入图片描述
在这里插入图片描述
-p:指定要跟踪的进程pid,要同时跟踪多个pid,重复多次-p选项即可
$ sudo strace -p 3927400
在这里插入图片描述
通用完整用法
跟踪28979进程的所有系统调用(-e trace=all),并统计系统调用的花费时间,以及开始时间(并以可视化的时分秒格式显示),最后将记录存在output.txt文件里面
$ strace -o output.txt -T -tt -e trace=all -p 28979
进程正常退出会输出的信息,这里有一个退出程序,代码如下:

$ vim abc.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {exit(1);
}
$ sudo strace -tt -e trace=process -f gcc abc.c

在这里插入图片描述
可以看出进程自己退出时(调用exit函数,或从main函数返回),最终调用的是exit_group系统调用,并且strace会输出exited with X(X为退出码)。
可能会疑惑代码里面明明调用的是exit,怎么显示为exit_group?
这是因为这里的exit函数不是系统调用,而是glibc库提供的一个函数,exit函数的调用最终会转化为exit_group系统调用,它会退出当前进程的所有线程。实际上,有一个叫做_exit()的系统调用(注意exit前面的下划线),线程退出时最终会调用它。

定位共享内存异常

有个服务启动时报错:

shmget 267264 30097568: Invalid argument

Can not get shm…exit!

错误日志显示获取共享内存出错,通过strace看下:

$ strace -tt -f -e trace=ipc ./a_mon_svr …/conf/a_mon_svr.conf

22:46:36.351798 shmget(0x5feb, 12000, 0666) = 0

22:46:36.351939 shmat(0, 0, 0) = ?

Process 21406 attached

22:46:36.355439 shmget(0x41400, 30097568, 0666) = -1 EINVAL (Invalid argument)

shmget 267264 30097568: Invalid argument

Can not get shm…exit!
这里通过-e trace=ipc选项让strace只跟踪和进程通信相关的系统调用。从strace输出看到shmget系统调用出错了,error是EINVAL。同样查询下shmget手册页,搜索EINVAL的错误码说明:
要创建的共享内存段比SHMMIN小 (一般是1个字节)

要创建的共享内存段比SHMMAX大 (内核参数kernel.shmmax配置)

指定key的共享内存段已存在,其大小和调用shmget时传递的值不同。
从strace输出看,要连的共享内存key 0x41400,指定的大小是30097568字节,明显与第1、2种情况不匹配。那只剩下第三种情况。使用ipcs看下是否真的是大小不匹配:

ipcs  -m | grep 41400
key        shmid      owner      perms      bytes      nattch     status    
0x00041400 1015822    root       666        30095516   1

可以看到0x41400这个key已经存在,并且其大小为30095516字节,和调用参数中的30097568不匹配,于是产生了这个错误。
在这个案例里面,导致共享内存大小不一致的原因是一组程序中,其中一个编译为32位,另外一个编译为64位,代码里面使用了long这个变长int数据类型,把两个程序都编译为64位解决了这个问题。

要跟踪某个具体的系统调用使用-e trace=xxx即可。但有时候要跟踪一类系统调用,比如所有和文件名有关的调用、所有和内存分配有关的调用。
如果人工输入每一个具体的系统调用名称,可能容易遗漏。于是strace提供了几类常用的系统调用组合名字。
-e:控制要跟踪的事件和跟踪行为,比如指定要跟踪的系统调用名称。指定一个表达式,用来控制如何跟踪,格式:[qualifier=][!]value1[,value2]…

qualifier只能是trace,abbrev,verbose,raw,signal,read,write其中之一。value是用来限定的符号或数字,默认的qualifier是trace。感叹号是否定符号。例如:

-e open等价于-e trace=open,表示只跟踪open调用,而-e trace!=open表示跟踪除了open以外的其他调用,有两个特殊的符号all和none。
注意有些shell使用!来执行历史记录里的命令,所以要使用\。

-e trace=open

只跟踪指定的系统调用。例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用,默认的为set=all

-e trace=file

只跟踪有关文件操作的系统调用

-e trace=process

只跟踪有关进程控制的系统调用

-e trace=network

跟踪与网络有关的所有系统调用

-e strace=signal

跟踪所有与系统信号有关的系统调用

-e trace=ipc

跟踪所有与进程通讯有关的系统调用

识别性能瓶颈

strace可以帮助开发者识别程序执行中的性能瓶颈。例如,如果程序频繁进行文件操作或者 I/O 请求,可能会影响性能。通过使用-e trace=open,read,write 选项,开发者可以查看程序进行文件操作时的详细信息,从而优化相关代码。
$ sudo strace -e trace=read -f ls
在这里插入图片描述

追踪子进程

有时需要追踪一个进程启动的所有子进程。使用 -f选项,strace将追踪主进程及其所有子进程的系统调用

$ strace -f ./my_program
在这里插入图片描述

追踪网络操作

strace也支持追踪与网络相关的系统调用,如socket、connect和sendto等。使用-e trace=network选项可以只显示与网络相关的操作

$ sudo strace -e trace=network -f ip addr
在这里插入图片描述

改变系统调用的行为

strace支持模拟一些系统调用的行为。例如,可以使用-e inject选项模拟某个系统调用返回错误,以测试程序在错误情况下的表现。

最后总结

当发现进程或服务异常时,可以通过strace来跟踪其系统调用,进而找到异常的原因。熟悉常用系统调用,能够更好地理解和使用strace。
当然,万能的strace也不是真正的万能。当目标进程卡死在用户态时,strace就没有输出了。
这个时候需要其他的跟踪手段,比如gdb/perf/SystemTap等。

1、perf:原生kernel支持

2、ftrace:kernel支持可编程

3、systemtap:功能强大,对用户态,内核态逻辑都能探查,使用范围更广

http://www.xdnf.cn/news/588367.html

相关文章:

  • 手机打电话时由对方DTMF响应切换多级IVR语音菜单(完结)
  • PyTorch中diag_embed和transpose函数使用详解
  • 小白的进阶之路系列之三----人工智能从初步到精通pytorch计算机视觉详解上
  • vue2使用pdfmake
  • Qt无边框界面添加鼠标事件
  • 吃透 Golang 基础:数据结构之切片
  • 实现了TCP的单向通信
  • 【数据库】-2 mysql基础语句(上)
  • 旋转编码器计次 红外对射传感器计次小实验及其相关库函数详解 (江协科技)
  • 第四章:YOLOv11 实战应用与开发指南
  • LeetCode 404.左叶子之和的迭代求解:栈结构与父节点定位的深度解析
  • 力扣.H指数力扣.字母异位词力扣.289生命游戏力扣452.用最小数量的箭引爆气球力扣.86分隔链表力扣.轮转数组
  • 高等数学-常微分方程
  • 国产三维CAD皇冠CAD(CrownCAD)建模教程:交流发电机
  • 推荐一个Excel与实体映射导入导出的C#开源库
  • 手写简单的tomcat
  • (泛函分析)线性算子连续必有界的证明
  • GraphRAG使用
  • 动态规划(七)——子数组系列(求和问题)
  • labview实现将百分制分数转换为等级制分数
  • Vue 3 官方 Hooks 的用法与实现原理
  • ai外呼平台:AnKo打造高效多模型服务体验!
  • labview实现LED流水灯的第二种方法
  • 每日算法刷题计划day13 5.22:leetcode不定长滑动窗口最短/最小1道题+求子数组个数越长越合法2道题,用时1h
  • 学习vue3:跨组件通信(provide+inject)
  • vscode include总是报错
  • Ubuntu24.04 LTS安装java8、mysql8.0
  • 【VScode】python初学者的有力工具
  • Labview使用报表工具
  • linux二进制安装mysql: