strace跟踪的原理以及使用

        如果想成为一名合格的工程师,那肯定应该知道如何去分析应用逻辑,对于如何优化应用代码提升系统性能也应该能有自己的一套经验。而今天想要讨论的是,如何拓展自己的边界,让自己能够分析代码之外的模块,以及对我自己而言几乎是黑盒的 Linux 内核。

        在很多情况下,应用的性能问题都需要通过分析内核行为来解决,因此,内核提供了非常多的指标供应用程序参考。当应用出现问题时,可以查看到底是哪些指标出现了异常,然后再做进一步分析。不过,这些内核导出的指标并不能覆盖所有的场景,可能面临的问题可能更加棘手:应用出现性能问题,可是系统中所有的指标都看起来没有异常。相信很多人都会此抓狂。那出现这种情况时,内核到底有没有问题呢,它究竟在搞什么鬼?我们探讨一下。

        对linux应用开发者而言,应用程序的边界就是系统调用,进入到系统调用中就是Linux 内核了,所以,要想拓展分析问题的边界,首先我们需要知道该怎么去分析应用程序使用的系统调用函数。对于内核开发者而言,边界同样是系统调用,系统调用之外是应用程序。如果内核开发者想要拓展分析问题的边界,也需要知道如何利用系统调用去追踪应用程序的逻辑。

        如何拓展你分析问题的边界? 

        不管大家是做linux应用,还是驱动的。对应用逻辑一无所知,或者是黑盒子的linux内核,都可以通过strace来追踪应用逻辑。strace 可以用来分析应用和内核的“边界”——系统调用。借助 strace,我们不仅能够了解应用执行的逻辑,还可以了解内核逻辑,如果作为应用开发者的,就可以借助这个工具来拓展分析应用问题的边界。strace可以跟踪进程的系统调用、特定的系统调用以及系统调用的执行时间。很多时候,我们通过系统调用的执行时间,就能判断出业务延迟发生在哪里。比如我们想要跟踪一个多线程程序的系统调用情况,那就可以这样使用 strace:

strace -T -tt -ff -p pid -o strace.out

         在使用 strace 跟踪进程之前,可以先明白 strace 的工作原理,你不仅要知道怎样使用工具,更要明白工具的原理,这样在出现问题时,就能明白该工具是否适用了。

strace 工具的原理: 

          strace 工具的原理如下图所示。

         对于正在运行的进程而言,strace 可以 attach 到目标进程上,这是通过 ptrace 这个系统调用实现的(gdb 工具也是如此),ptrace是 Linux 系统中一个强大的调试工具,它允许一个进程控制另一个进程,进行系统调用拦截、内存修改、寄存器操作等调试任务,ptrace 的 PTRACE_SYSCALL是 ptrace 中的一个选项,会去追踪目标进程的系统调用,通常用于调试系统调用(即对进程的系统调用进行跟踪和拦截)。目标进程被追踪后,每次进入 syscall,都会产生 SIGTRAP信号并暂停执行;追踪者通过目标进程触发的 SIGTRAP信号,就可以知道目标进程进入了系统调用,然后追踪者会去处理该系统调用,我们用 strace 命令观察到的信息输出就是该处理的结果;追踪者处理完该系统调用后,就会恢复目标进程的执行。被恢复的目标进程会一直执行下去,直到下一个系统调用。

        可以发现,目标进程每执行一次系统调用都会被打断,等 strace 处理完后,目标进程才能继续执行,这就会给目标进程带来比较明显的延迟。

        因此,在生产环境中不建议使用该命令,如果你要使用该命令来追踪生产环境的问题,那就一定要做好预案。假设我们使用 strace 跟踪到,线程延迟抖动是由某一个系统调用耗时长导致的,那么接下来我们该怎么继续追踪呢?这就到了应用开发者和运维人员需要拓展分析边界的时刻了,对内核开发者来说,这才算是分析问题的开始。

strace安装   

如果 strace 没有安装,可以根据你使用的操作系统版本进行安装:

在 Ubuntu/Debian 系统上:

sudo apt update sudo apt install strace

在 CentOS/RHEL 系统上:

sudo yum install strace

在 Fedora 系统上:

sudo dnf install strace

安装完后路径在:

/usr/bin/strace  

strace 工具的使用   

        相关参数:

-c 统计每一系统调用的所执行的时间,次数和出错的次数等. ​

-d 输出strace关于标准错误的调试信息. ​

-f 跟踪由fork调用所产生的子进程. ​

-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号. ​

-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪. ​

-h 输出简要的帮助信息. ​

-i 输出系统调用的入口指针. ​

-q 禁止输出关于脱离的消息. ​

-r 打印出相对时间关于,,每一个系统调用. ​

-t 在输出中的每一行前加上时间信息. ​

-tt 在输出中的每一行前加上时间信息,微秒级. ​

-ttt 微秒级输出,以秒了表示时间. ​

-T 显示每一调用所耗的时间. ​

-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出. ​

-V 输出strace的版本信息. ​

-x 以十六进制形式输出非标准字符串 ​

-xx 所有字符串以十六进制形式输出. ​

-a column 设置返回值的输出位置.默认 为40.

第一种使用方法:

        /usr/bin/strace <command> 要使用结对路径

实验代码:

        通过它启动要跟踪的进程 /usr/bin/strace ./aout

分析打印消息:    主要输出部分及其含义:

        1.启动程序

        execve("./a.out", ["./a.out"], 0x7ffd865038d0 /* 24 vars */) = 0

这行表示 execve 系统调用被调用来执行 ./a.out,并且成功返回(= 0),这表明 a.out 成功启动。

        2.内存分配(brk):

brk(NULL) = 0x55e3ab7ce000
brk 是一个与内存分配相关的系统调用。在这里,brk 返回一个新的内存地址,表示程序为自己的堆分配了内存。

        访问文件(access):

access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)

这表明程序尝试访问 /etc/ld.so.nohwcap 文件,但未找到(ENOENT 表示“没有该文件或目录”)。

后续类似的调用也是在检查一些系统文件,例如 ld.so.cachelibc.so.6,这些文件与程序的动态链接有关。

        打开文件(openat):

openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
程序成功打开了 /etc/ld.so.cache 文件,文件描述符是 3

         读取文件(read):

read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\35\2\0\0\0\0\0"..., 832) = 832
程序正在读取 ld.so.cache 文件的数据,长度为 832 字节。该文件包含了动态链接器的信息。

        内存映射(mmap): 

mmap(NULL, 93075, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f6fe4484000

这是 mmap 系统调用,它映射了一个内存区域(大小为 93075 字节),并从文件描述符 3 中加载数据(即之前打开的 /etc/ld.so.cache 文件)。

其他 mmap 调用也是类似的,它们负责将程序所需的动态库(例如 libc.so.6)映射到进程的内存中。

        关闭文件(close): 

close(3) = 0
关闭文件描述符 3,即 /etc/ld.so.cache 文件。

 出错的分析:

        1.文件打开失败:

        假设文件打开失败,因为文件路径不合法或权限不足。strace 输出可能如下:

$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7fff22a9f1d0 /* 24 vars */) = 0
brk(NULL)                               = 0x55e3ab7ce000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "./text", O_RDWR|O_CREAT|O_APPEND, 0666) = -1 EACCES (Permission denied)
write(2, "Failed to open file\n", 20)   = 20
exit_group(1)                           = ?

  • openat(AT_FDCWD, "./text", O_RDWR|O_CREAT|O_APPEND, 0666) = -1 EACCES (Permission denied):这行表示程序尝试打开 ./text 文件,但由于权限不足,返回 -1,并且错误码是 EACCES(Permission denied)。
  • write(2, "Failed to open file\n", 20):程序尝试向标准错误输出写入 "Failed to open file\n",表示文件打开失败。

        2. 写入失败:

        假设文件系统已满,导致 write 系统调用失败。strace 输出如下:

$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7fff5b4f2cd0 /* 24 vars */) = 0
brk(NULL)                               = 0x55f4c0a5e000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "./text", O_RDWR|O_CREAT|O_APPEND, 0666) = 3
write(3, "小帅", 6)                     = -1 ENOSPC (No space left on device)
write(2, "Write failed\n", 12)           = 12
exit_group(1)                           = ?

  • openat(AT_FDCWD, "./text", O_RDWR|O_CREAT|O_APPEND, 0666) = 3:程序成功打开文件 ./text,文件描述符是 3
  • write(3, "小帅", 6) = -1 ENOSPC (No space left on device):程序在写入 "小帅" 时失败,错误码是 ENOSPC,表示磁盘空间已满。
  • write(2, "Write failed\n", 12):程序向标准错误输出写入 "Write failed\n",提示写入失败。

        程序访问非法内存(崩溃):

        假设程序尝试访问空指针或非法内存,导致崩溃。strace 输出如下:

$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7ffd425fa350 /* 24 vars */) = 0
brk(NULL)                               = 0x55e5d9f9f000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "./text", O_RDWR|O_CREAT|O_APPEND, 0666) = 3
write(3, "小帅", 6)                     = 6
write(3, NULL, 6)                       = -1 EFAULT (Bad address)
write(2, "Write failed\n", 12)           = 12
exit_group(1)                           = ?

  • write(3, "小帅", 6) = 6:程序成功写入 "小帅" 字符串。
  • write(3, NULL, 6) = -1 EFAULT (Bad address):程序尝试写入 NULL 指针,导致 write 返回错误,错误码是 EFAULT(Bad address),这通常是访问非法内存引起的崩溃。
  • write(2, "Write failed\n", 12):程序输出 "Write failed\n"

         访问未映射的内存地址(内存访问违规):

        另一个崩溃的例子是尝试访问未映射的内存地址:

$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7ffd36fcd9b0 /* 24 vars */) = 0
brk(NULL)                               = 0x55c9e48ae000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "./text", O_RDWR|O_CREAT|O_APPEND, 0666) = 3
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4a80000000
write(3, "小帅", 6)                     = 6
write(3, "测试", 6)                     = 6
mmap(0x7f4a80010000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 EINVAL (Invalid argument)
write(2, "Memory mapping failed\n", 22)  = 22
exit_group(1)                           = ?

  • mmap(0x7f4a80010000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 EINVAL (Invalid argument):程序尝试映射一个无效的内存地址(0x7f4a80010000),导致 mmap 返回 EINVAL 错误,表示无效的参数。
  • write(2, "Memory mapping failed\n", 22):程序写入 "Memory mapping failed\n" 到标准错误输出。

分析方法:

  • 检查程序依赖的动态库:从输出中可以看到,程序依赖于 libc.so.6(标准 C 库)。如果某个库加载失败,通常会在 strace 输出中看到 mmapopen 系统调用失败。
  • 文件访问错误:如 /etc/ld.so.nohwcap/etc/ld.so.preload 等文件未找到(ENOENT 错误),可能并不会直接影响程序运行,但如果程序依赖这些文件,可能会导致问题。
  • 内存分配问题brkmmap 显示程序在运行时分配了内存,如果程序内存分配失败或访问越界,strace 输出将帮助你找出问题的根源。

第二种使用方法 

        假设有一个简单的 C 程序 test.c,演示如何使用 ptracePTRACE_SYSCALL 来跟踪系统调用:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    int fp;
    char* buff = "小帅";

    // 打开文件时错误处理
    fp = open("./text", O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
    if (fp < 0)
    {
        perror("Failed to open file");
        exit(-1);
    }

    // 进入循环前做检查
    if (buff == NULL)
    {
        fprintf(stderr, "Buffer is NULL\n");
        close(fp);  // 确保打开的文件会被关闭
        exit(-1);
    }

    // 无限循环写入文件
    while (1)
    {
        ssize_t bytes_written = write(fp, buff, strlen(buff));

        // 处理 write 错误
        if (bytes_written < 0)
        {
            perror("Write failed");
            close(fp);  // 出现写入错误时关闭文件
            exit(-1);
        }
        else
        {
            printf("Written %zd bytes to file\n", bytes_written);
        }

        // 检查是否发生了文件描述符错误
        if (fp < 0)
        {
            perror("File descriptor error");
            exit(-1);
        }

        sleep(3);  // 每 3 秒写入一次,防止 CPU 占用过高
    }

    // 程序结束前关闭文件
    close(fp);
    return 0;
}
 

         通过它启动要跟踪的进程 /usr/bin/strace ./aout 

3. 解释

  • fork():父进程创建子进程。
  • ptrace(PTRACE_SYSCALL, 12345, 0, 0):父进程通过 PTRACE_SYSCALL 跟踪子进程的系统调用,并暂停它。
  • wait4WIFSTOPPED(s):父进程等待子进程停止,表明子进程进入系统调用。
  • SIGSTOPSIGTRAP:当子进程执行系统调用时,父进程接收到 SIGSTOP 信号来暂停它,并在系统调用执行完毕后接收到 SIGTRAP 信号。
5. 注意事项
  • PTRACE_TRACEME:子进程通过此调用告诉内核它将被父进程调试。
  • PTRACE_SYSCALL:父进程通过此调用告诉内核在子进程每次进行系统调用时暂停。
  • waitpid:父进程通过 waitpid 等待子进程的状态变化,可以获取子进程是否执行了系统调用或是否退出。

这只是一个简单的示范,实际的应用中,ptracePTRACE_SYSCALL 组合可以用来进行更复杂的调试和分析任务。

ptracePTRACE_SYSCALL 组合不仅可以用于跟踪系统调用,还可以用于更复杂的调试和分析任务,比如:

  • 获取系统调用的参数和返回值
  • 修改系统调用的参数或返回值
  • 跟踪特定的系统调用
  • 调试进程中的内存内容

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/491462.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Canoe CAPL编程

文章目录 CAPL 简介CAPL的程序结构CAPL的数据类型1. 无符号整数2. 有符号整数3. 有符号整数4. CAN消息类型5. 定时器类型6. 变量定义 on message xxx 中 this相关方法公共方法1. output(msgName) 从程序块输出message&#xff08;形式1&#xff09;或errorframe&#xff08;形式…

详解CompletableFuture

最近一直畅游在RocketMQ的源码中&#xff0c;发现在RocketMQ中很多地方都使用到了CompletableFuture&#xff0c;所以今天就跟大家来聊一聊JDK1.8提供的异步神器CompletableFuture&#xff0c;并且最后会结合RocketMQ源码分析一下CompletableFuture的使用。 Future接口以及它的…

HarmonyOS 非线性容器LightWeightMap 常用的几个方法

LightWeightMap可用于存储具有关联关系的key-value键值对集合&#xff0c;存储元素中key值唯一&#xff0c;每个key对应一个value。 LightWeightMap依据泛型定义&#xff0c;采用轻量级结构&#xff0c;初始默认容量大小为8&#xff0c;每次扩容大小为原始容量的两倍。 集合中k…

三极管功能

1 三极管的结构 2 三极管开关电路设计注意事项 1 三极管进入饱和状态 电机&#xff1a;500毫安 2 判断三级什么状态&#xff1a;电压法 3 判断三级什么状态&#xff1a;电流法 4 求IB的电阻 5 当三极管用作开关时&#xff0c;通常N型三极管控制负载的gnd端&#xff0c;P型…

P6打卡—Pytorch实现人脸识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 1.检查GPU import torch import torch.nn as nn import matplotlib.pyplot as plt import torchvisiondevicetorch.device("cuda" if torch.cuda.is_…

R square 的计算方法和一点思考

模型的性能评价指标有几种方案&#xff1a;RMSE&#xff08;平方根误差&#xff09;、MAE&#xff08;平均绝对误差&#xff09;、MSE(平均平方误差)、R2_score 其中&#xff0c;当量纲不同时&#xff0c;RMSE、MAE、MSE难以衡量模型效果好坏。这就需要用到R2_score&#xff1…

解决并发情况下调用 Instruct-pix2pix 模型推理错误:index out of bounds 问题

解决并发情况下调用 Instruct-pix2pix 模型推理错误&#xff1a;index out of bounds 问题 背景介绍 在对 golang 开发的 图像生成网站 进行并发测试时&#xff0c;调用基于 Instruct-pix2pix 模型和 FastAPI 的图像生成 API 遇到了以下错误&#xff1a; Model inference er…

利用DFT画有限长序列的DTFT

MATLAB中没有DTFT函数&#xff0c;计算机不可能给出连续结果&#xff0c;可以只能利用DFT的fft函数来实现。 %% L 7; x ones(1, L) figure; tiledlayout(2,3,"TileSpacing","tight") nexttile; stem([0:L-1],x) box off title([num2str(L), points rect…

【进程篇】03.进程的概念与基本操作

一、进程的概念与理解 1.1 概念 进程是程序的一个执行实例&#xff0c;即正在执行的程序。 1.2 理解 我们编写代码运行后会在磁盘中会形成一个可执行程序&#xff0c;当我们运行这个可执行程序时&#xff0c;这个程序此时就会被操作系统的调度器加载到内存中&#xff1b;操…

基于MATLAB 的数字图像处理技术总结

大家好&#xff01;欢迎来到本次的总结性的一篇文章&#xff0c;因为咸鱼哥这几个月是真的有点小忙&#xff08;参加了点小比赛&#xff0c;准备考试等等&#xff09;所以&#xff0c;在数字图像学习后&#xff0c;我来写一个总结性的文章&#xff0c;同时帮助大家学习&#xf…

llama2——微调lora,第一次参考教程实践完成包括训练和模型

前言&#xff1a;磕磕绊绊&#xff0c;不过收获很多&#xff0c;最大的收获就是解决报错error的分析方法和解决思路 1、首先&#xff0c;我参考的是这篇博客&#xff1a;怎样训练一个自己的大语言模型&#xff1f;全网最简单易懂的教程&#xff01;_开源模型训练出一个语言模型…

类OCSP靶场-Kioptrix系列-Kioptrix Level 3

一、前情提要 二、实战打靶 1. 信息收集 1.1. 主机发现 1.2. 端口扫描 1.3.目录遍历 1.4. 敏感信息 2.漏洞发现 2.1.登录功能账号密码爆破 2.2.CMS历史漏洞 2.2.1.exp利用 2.2.2.提权 2.3. sql注入getshell 2.3.1.发现注入点 2.3.2. 测试字段和类型 2.3.3.查询字…

WPF实现曲线数据展示【案例:震动数据分析】

wpf实现曲线数据展示&#xff0c;函数曲线展示&#xff0c;实例&#xff1a;震动数据分析为例。 如上图所示&#xff0c;如果你想实现上图中的效果&#xff0c;请详细参考我的内容&#xff0c;创作不易&#xff0c;给个赞吧。 一共有两种方式来实现&#xff0c;一种是使用第三…

PHP代码审计学习(一)--命令注入

1、漏洞原理 参数用户可控&#xff0c;程序将用户可控的恶意参数通过php可执行命令的函数中运行导致。 2、示例代码 <?php echorec-test; $command ping -c 1 .$_GET[ip]; system($command); //system函数特性 执行结果会自动打印 ?> 通过示例代码可知通过system函…

Vivado安装System Generator不支持新版Matlab解决方法

目录 前言&#xff1a; Vivado安装System Generator不支持新版Matlab解决方法 前言&#xff1a; 本文介绍一下Vivado不支持新版Matlab的解决办法&#xff0c;Vivado只支持最近两年3个版本的Matlab&#xff0c;当前最新版vivado 2018.3只支持2017a,2017b,2018a。 Vivado安装Sy…

半导体数据分析(二):徒手玩转STDF格式文件 -- 码农切入半导体系列

一、概述 在上一篇文章中&#xff0c;我们一起学习了STDF格式的文件&#xff0c;知道了这是半导体测试数据的标准格式文件。也解释了为什么码农掌握了STDF文件之后&#xff0c;好比掌握了切入半导体行业的金钥匙。 从今天开始&#xff0c;我们一起来一步步地学习如何解构、熟…

#渗透测试#漏洞挖掘#红蓝攻防#SRC漏洞挖掘02之权限漏洞挖掘技巧

免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章读。 权限相关漏洞 越权、未授权访问、oss、后台暴露、…

IS-IS协议

IS-IS协议介绍 IS-IS&#xff08;Intermediate System to Intermediate System&#xff09;协议是一种链路状态的内部网关协议&#xff08;IGP&#xff09;&#xff0c;用于在同一个自治系统&#xff08;Autonomous System, AS&#xff09;内部的路由器之间交换路由信息。IS-I…

4.7 TCP 的流量控制

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言1 滑动窗口与流量控制2 持续计时器与零窗口探测3 控制TCP发送报文段的时机3.1 控制发送时机的三种机制3.2 糊涂窗口综合症 前言 在网络通信中&#xff0c;流量控制是确保…

不良人系列-复兴数据结构(栈和队列)

个人主页&#xff1a;爱编程的小新☆ 不良人经典语录&#xff1a;“相呴相济 玉汝于成 勿念 心安” 目录 一. 栈(stack) 1. 栈的概念 2. 栈的常见方法 3.栈的模拟实现 ​编辑 二. 队列 1. 队列的概念 2. 队列的使用 2.1 队列的常见方法 2.2 队列的模拟实现 2.3 队列…