SystemTap(stap)架构和原理介绍,以及脚本编写举例

news/2024/9/18 12:34:31/ 标签: linux, systemtap, stap

1 SystemTap简介

SystemTap是一个诊断Linux系统性能或功能问题的开源工具。它允许开发人员和系统管理员深入研究内核甚至用户空间应用程序的行为,以便发现错误状态、性能问题,或者仅仅为了解系统是如何工作的。它使得对运行时的Linux系统进行诊断调式变得更容易、更简单。通过使用SystemTap,可以很容易地总结和可视化与系统工作有关的信息,使您能够轻松地获得处理简单和复杂问题所需的信息

为了诊断系统问题或性能,开发者或调试人员只需要写一些脚本,而且SystemTap本身也提供了很多脚本,称为"tapset"方便开发。通过SystemTap提供的命令行接口就可以对正在运行的内核进行诊断调试,以前需要的修改或插入调试代码、重新编译内核、安装内核和重启动等这些琐碎的工作完全消除

当前该项目的主要开发人员为来自Red Hat, IBM, Intel和Hitachi的工程师。其中Redhat主要负责脚本转换/翻译器和运行时库,IBM负责kprobe和relayfs,Intel负责转换器安全检查以及performance monitor tapset。

2 SystemTap架构

2.1 Kprobes简介

SystemTap用于检查运行的内核的两种方法是 Kprobe和返回探针(return probes)。
Kprobes从 2.6.9 版本开始就添加到主流的 Linux 内核中,并且为探测内核提供一般性服务,最重要的两种服务是 Kprobe 和 Kretprobe。

Kprobe是一个通用钩子,可以插入内核代码中的几乎任何地方。为了允许它探测一条指令,该指令的第一个字节被替换为所使用架构的断点指令。当命中这个断点时,Kprobe接管并执行对应的探针处理程序代码,执行完成之后,接着执行原始的指令(从断点开始)。

Kretprobe(返回探针)有所不同,它操作调用函数的返回结果。因为一个函数可能有多个返回点,它实际使用一种称为 trampoline(蹦床) 的简单技术。它向函数条目添加一小段代码,而不是检查函数中的每个返回点。这段代码使用 trampoline 地址替换堆栈上的返回地址。当函数退出时,它返回到trampoline地址,而不是最初设置它返回的位置,并调用探测的处理程序代码。然后从 Kretprobe 返回到实际的调用方。Kretprobe的工作原理如图2-1所示。
在这里插入图片描述

图2-1 kretprobe工作原理

2.2 SystemTap流程

SystemTap的基本流程主要涉及到 3 个交互实用程序stapstaprun和stapio,以及如下5个阶段:
1. 解析阶段:将stap脚本解析成语法解析树,会执行预处理,以及语义和语法正确性检查。
2. 细化阶段:解析脚本中的符合和引用,导入脚本中引用的脚本库tapsets(目录一般为/usr/share/SystemTap/tapset/)和内核调试信息。
3. 转化阶段:结合细化阶段的输出,将解析树转换成 C 源代码。
4. 构建阶段:将C代码编译成可以在内核中动态加载和执行的内核模块。
5. 执行阶段staprun 和 stapio负责将模块安装到内核中并将输出发送到 stdout。如果在shell中按组合键 Ctrl-C或脚本退出,将执行清除进程,这将导致卸载模块并退出所有相关的实用程序。SystemTa在加载这个模块后,会开启脚本中的探测点。这个功能由systemtap-runtime包中的staprun提供;当某个事件发生时,对应的处理句柄(handler)就会执行;当SystemTap会话终止时,探测就会停止,对应的内核模块也会卸载。
SystemTap的流程如图2-2和图2-3所示。

图2-2 SystemTap流程图

在这里插入图片描述

图2-3 SystemTap逻辑流程图

SystemTap 的一个有趣特性是缓存脚本转换的能力。如果安装后的脚本没有更改,您可以使用现有的模块,而不是重新构建模块。图2-4显示了user-space和kernel-space 元素以及基于 stap的转换流程。
在这里插入图片描述

图2-4 SystemTap用户内核空间

2.3 SystemTap原理

SystemTap的核心思想是定义一个事件(event),以及给出处理该事件的句柄(Handler)。当一个特定的事件发生时,内核运行该处理句柄,就像快速调用一个子函数一样,处理完之后恢复到内核原始状态。这里有两个概念:

  • 事件(Event):SystemTap定义了很多种事件,例如进入或退出某个内核函数、定时器时间到、整个SystemTap会话启动或退出等等。

  • 句柄(Handler):就是一些脚本语句,描述了当事件发生时要完成的工作,通常是从事件的上下文提取数据,将它们存入内部变量中,或者打印出来。

SystemTap工作原理是通过将脚本语句翻译成C语句,编译成内核模块。模块加载之后,将所有探测的事件以钩子的方式挂到内核上,当任何处理器上的某个事件发生时,相应钩子上句柄就会被执行。最后,当SystemTap会话结束之后,钩子从内核上取下,移除模块。整个过程用一个命令 stap 就可以完成。

SystemTap使用了类似于awk和C语言的脚本语言(类似于Dtrace的D语言)。它只使用了三种数据类型:整数(integers)、字符串(strings)和关联数组(associative Arrays)。它有完整的控制结构,包括块(blocks)、条件(conditionals)、循环(loops)和函数(functions)。语句分割符;是可选的,变量不需要声明类型,它们是根据上下文自动推测和检查的,它使用了kprobe提供的接口来实现探测,对于每一个探测,需要定义探测点以及相应的处理函数,探测点就是指kprobe中被探测的函数或指令地址(也被称为内核事件),但在SystemTap中,用户可以指定原文件,原代码的某一行,或者一个异步事件,如周期性的定时器,探测点使用了层次化的命名方式,探测点处理函数能够立刻输出数据,与printk很类似,它也能查看内核数据。脚本被一个翻译器转换成C代码并编译成一个内核模块。探测点根据内核的DWARF调试信息映射到内核的虚地址(因此SystemTap要求用户必须准备好可用的内核调试信息),所有的脚本内容在转换时进行严格的检查,并且在运行时也要检查(如无限循环、内存使用、递归和无效指针等),因此有好的安全性,不会影响正在运行的系统(这对生产系统是非常重要的)。 SystemTap包含了一个黑名单,其中列出的函数不能被SystemTap探测,因为它们会导致无限探测循环、锁重入等问题。

图2-5直观地给出了SystemTap的工作原理:
在这里插入图片描述

图2-5 SystemTap工作原理

SystemTap脚本文件是.stp后缀的文件,使用的脚本语言是前面讲到的SystemTap自己定义的脚本语言,一个SystemTap脚本描述了将要探测的探测点以及定义了相关联的处理函数,每一个探测点对应于一个内核函数或事件或函数内部的某一位置。被关联的处理函数将在内核执行到对应的探测点时被执行。

tapsets是一个脚本库,包含了许多tapset,每一个tapset一般为某一内核子系统或特定的功能块预定义了一套探测点、辅助函数或全局变量供用户脚本或其它的tapset引用,它定义的一些数据能够被每一个探测点处理函数或脚本使用,这些数据通常通过使用处理函数语句块(HSB Handler Statement Block)来出口,HSB语句块中的变量就是被出口的数据。tapset一般由该内核子系统的开发者或对子系统非常了解的开发者编写,既使用了脚本语言,也使用了C语言,并且它已经被测试和验证,可以安全使用。tapsets属于SystemTap发行包的一部分。

SystemTap实现了一个脚本转换器/翻译器,当用户执行一个SystemTap脚本时,SystemTap将首先对它进行分析和一些安全检查,如果它引用了SystemTap预定义的脚本库提供的函数,SystemTap也将读取脚本库得到相应的代码,对于一些内核变量或符号的引用,它必须根据内核调试信息来解析到相应的地址。然后,它被转换成C代码,在这个转换中,SystemTap将根据需要增加必要的锁和安全检查代码。探测点之间共享的变量将被转换成恰当的静态声明并有锁保护,每组本地变量被转换到一个合成的调用帧结构中以避免消耗内核的栈空间。关联到探测点的处理函数被封装成一个接口函数,那调用恰当的kprobe接口函数来注册该探测点。

产生的C代码包含了一些对运行时tapset的引用,运行时tapset库提供了许多SystemTap接口函数,如通用的查询表、受限内存管理、启动、关闭、I/O操作以及其它一些函数。生成的C代码编译链接之后生成一个可加载的内核模块。为了快速得到运行结果,SystemTap使用了relayfs,当加载生成的内核模块后,该模块的初始化函数初始化自身,然后调用kprobe接口函数注册脚本中定义的探测点。当内核运行到注册的探测点时,相应的处理函数被调用,用户在处理函数中的输出语句将调用relayfs接口函数输出结果数据,用户在处理函数也可以调用一些内核的性能测量函数。当用户主动停止或脚本设定的条件满足时,模块将调用退出函数卸载已经注册的探测点并做一些清理处理就卸载模块自身。

SystemTap在运行时启动了一个进程,它专门负责通过relayfs读去模块的输出数据并即时地输出给用户。

3 SystemTap脚本编写

3.1 探针

SystemTap脚本由探针和在触发探针时需要执行的代码块组成。探针有许多预定义模式,表3-1列出了其中的一部分。这个表列举了几种探针类型,包括调用内核函数和从内核函数返回。

探针类型说明
begin在脚本开始时触发
end在脚本结束时触发
kernel.function(“sys_sync”)调用 sys_sync 时触发
kernel.function(“sys_sync”).call同上
kernel.function(“sys_sync”).return返回 sys_sync 时触发
kernel.syscall.*进行任何系统调用时触发
kernel.function(“*@kernel/fork.c:934”)到达 fork.c 的第 934 行时触发
module(“ext3”).function(“ext3_file_write”)调用 ext3 write 函数时触发
timer.jiffies(1000)每隔 1000 个内核 jiffy 触发一次
timer.ms(200).randomize(50)每隔 200 毫秒触发一次,带有线性分布的随机附加时间(-50 到 +50
表3-1 探针

下面这个简单的探针例子,它在调用内核系统调用 sys_sync 时触发。当该探针触发时,您希望计算调用的次数,并发送这个计数以及表示调用进程名字和ID(PID)的信息。

首先,声明一个任何探针都可以使用的全局值(全局名称空间对所有探针都是通用的),然后将它初始化为0。其次,定义您的探针,它是一个探测内核函数 sys_sync的条目。与探针相关联的脚本将递增 count变量,然后发出一条消息,该消息定义调用的次数和当前调用的进程名字和PID。

global count=0
probe kernel.function("sys_sync") {count++printf("sys_sync called %d times, currently by execname:%s pid:%d\n",count, execname(), pid());
}

运行结果如下:
在这里插入图片描述

3.2 元素

SystemTap 允许定义多种类型的变量,但类型是从上下文推断得出的,因此不需要使用类型声明。在 SystemTap 中,您可以找到数字(64 位签名的整数)、整数(64 位)、字符串和字面量(字符串或整数)。您还可以使用关联数组和统计数据。

SystemTap 提供 C 语言中常用的所有必要操作符,并且用法也是一样的。您还可以找到算术操作符、二进制操作符、赋值操作符和指针废弃。您还看到从 C 语言带来的简化,其中包括字符串连接、关联数组元素和合并操作符。

3.3 语句

在探针内部,SystemTap 提供一组类似于C一样易于使用的语句。注意,尽管该语句允许您开发复杂的脚本,但每个探针只能执行 1000 条语句(这个数量是可配置的)。表3-2列出了一小部分语句作为例子。注意,在这里的许多元素和 C 中的一样,尽管有一些附加的东西是特定于SystemTap的。

语句说明
if (exp) {} else {}标准的 if-then-else 语句
for (exp1 ; exp2 ; exp3 ) {}一个 for 循环
while (exp) {}标准的 while 循环
do {} while (exp)一个 do-while 循环
break退出迭代
continue继续迭代
next从探针返回
表3-2 语句

3.4 函数

SystemTap 提供许多内部函数,这些函数提供关于当前上下文的额外信息。例如,您可以使用 caller() 识别当前的调用函数,使用 cpu() 识别当前的处理器号码,以及使用 pid() 返回 PID,对调用堆栈访问等。表3-2列出了一小部分函数

函数名说明
execname()获取当前进程的名称,即可执行文件的名称
pid()获取当前进程的PID
tid()获取当前线程的ID
cpu()获取当前CPU的ID
pp()描述当前被处理的探针点的字符串。例如probe process.syscall,process.end { /* scripts */},在块中调用pp()会返回"process.syscall"和"process.end"。
probefunc()获取当前probe的函数名称。例如probe sys_read函数,在probe块中调用该函数就会返回sys_read。注意这个函数的返回值是从pp()返回的字符串中解析得到的。
ppfunc()获取当前probe的函数名称。在probe指定文件中的函数中时非常有用,可以知道当前的probe位于哪个函数。
gettimeofday_s()自从Epoch开始的秒数,相同的还有ms、us和ns
tz_ctime(sec)根据输入的秒数,转换为date格式的字符串。tz_ctime(gettimeofday_s())
get_cycles()获取处理器周期数
print_backtrace()打印内核调用栈信息
print_ubacktrace()打印用户态调用栈信息
thread_indent()输出当前线程的信息,格式为"相对时间 程序名称(线程id):(空格)",如果当probe的函数执行的次数约到,空格的数量也就越多。这个函数还有一个参数,用来控制空格的数量。如果参数值越大,则空格的数量越多。相对时间是当前的时间(以微秒为单位)减去指定线程第一次执行thread_indent时的时间。
表3-3 函数

下面给出调用这些函数一个脚本示例

probe begin,end {sec = gettimeofday_s()printf("pp() = %s, probefunc() = %s, ppfunc() = %s\n",pp(), probefunc(), ppfunc())printf("execname = %s, pid = %d, tid = %d, cpu = %d\n",execname(), pid(), tid(), cpu())printf("gettimeofday_s() = %ld.\n", sec)printf("tz_ctime(sec) = %s.\n", tz_ctime(sec))printf("get_cycles() = %d.\n", get_cycles())}probe kernel.function("sys_sync") {printf("\n====sys_sync entry====\n")printf("pp() = %s, probefunc() = %s, ppfunc() = %s\n",pp(), probefunc(), ppfunc())printf("execname = %s, pid = %d, tid = %d, cpu = %d\n",execname(), pid(), tid(), cpu())print_backtrace()thread_indent(1)
}probe kernel.function("sys_sync").return {printf("\n====sys_sync return====\n")printf("pp() = %s, probefunc() = %s, ppfunc() = %s\n",pp(), probefunc(), ppfunc())printf("execname = %s, pid = %d, tid = %d, cpu = %d\n",execname(), pid(), tid(), cpu())
}

运行结果如下:
在这里插入图片描述


http://www.ppmy.cn/news/1516522.html

相关文章

Windows安装Tomcat10

1. 下载Tomcat Tomcat官网 https://tomcat.apache.org/download-10.cgi ​下载安装jdk17 :jdk-17_windows-x64_bin.exe 配置JAVA环境变量 JAVA_HOME:C:\Program Files\Java\jdk-17 PATH:%Java_Home%\bin;%Java_Home%\jre\bin; 2. 设置环境变…

【13.3 python中的高级文件操作】

python中的高级文件操作 在Python中,除了基本的文件读写和目录操作外,还有一些高级的文件和目录操作,如删除文件、重命名文件和目录、以及获取文件的基本信息等。这些操作通常通过os模块和pathlib模块来实现。下面我将详细介绍这些操作&#…

电脑换硬盘怎么全盘克隆?轻松实现数据迁移

随着科技的不断发展,电脑硬盘的存储容量和读写速度也在不断提升。为了获得更好的电脑使用体验,许多用户会选择更换更大容量、更高效的硬盘。然而,在更换硬盘的过程中,一个关键的问题摆在了我们面前:如何将旧硬盘中的所…

物联网---ESP32

物联网---ESP32 一、TCP/IP协议(互联网协议)二、MQTT协议(通信协议)2.1 MQTT基本原理2.2 连接MQTT服务端 三、ESP323.1 ESP介绍3.2 ESP32连接云端3.2.1 ESP32连接WIFI/MQTT3.2.2 OneNET云端 一、TCP/IP协议(互联网协议) TCP/IP是一组用于互联网及其他网络中数据传输的通信协议…

hutool工具类JSONUtil无法映射全是大写的单词,如何解决

背景 在解析第三方接口数据时,发现有的字段数据没有映射到对应的字段上,还有对于有的字段有空格或换行,也会一同存入数据库。 示例 实体类: public class Goods { private String id;private String unit;private Integer US…

HexView 刷写文件脚本处理工具-命令行介绍(八)-文件合并(/MO /MT)

介绍 /MO 和 /MT 参数:用于将一个或多个文件合并到程序的内部数据存储中。文件读取:使用第2.2.1.2.1节中描述的自动检测文件类型机制来读取文件。合并操作类型:需要选择合并操作的类型。可以选择透明模式(/MT)或不透明模式(/MO),两者不能混合使用。透明模式(/MT):加载的文…

黑神话悟空无法登录服务器怎么办

黑神话悟空游戏在登录的时候会遇到无法登录服务器的问题,玩家可以采用一些有效的方法进行解决,其中最主要的措施就是优化网络环境和减少网络干扰。Rak小编为您整理黑神话悟空无法登录服务器如何解决的步骤及注意事项。 优化网络环境 1、当游戏无法登录服…

使用notepad++将shell脚本转为UNIX格式方法(主要差别在换行符)

sh文件尽量在linux上改,因windows和linux换行符不同,在windows上改后,在linux上改可能会出现换行符错误。 windows换行符 linux换行符 windows环境改换行符方法 使用notepad点 编辑–》文档格式转换–》转换未unix格式。 注:tx…

搭建ELK-Filebeat采集系统日志

1、解压到/data/elk/filebeat mkdir -p /data/elk/filebeat tar -zxf filebeat-7.17.7-linux-x86_64.tar.gz -C /data/elk/filebeat --strip-components1 #--strip-components选项表示从目录级别上去除指定的前缀,以实现更加控制解压的效果 2、修改配置文件 vi /…

【长文细说】20个ElementPlus核心组件以及使用技巧

Element Plus 是一个基于 Vue 3 和 Vite 的组件库,它提供了一套丰富的 UI 组件,用于构建高质量的网页应用程序。Element Plus 是 Element UI 的 Vue 3 版本,Element UI 是一个广泛使用的 Vue 2 组件库。Element Plus 继承了 Element UI 的设计…

Qt5.14.2 操作PostgreSQL 记录

在Qt5.14.2中操作PostgreSQL数据库. #include <QSqlDatabase> #include <QSqlQuery> #include <QSqlError> #include <QDebug>// 初始化数据库连接QSqlDatabase db QSqlDatabase::addDatabase("QPSQL");//qDebug() << "aaaa&qu…

构建第一个zk

1 必要步骤 视频学习&#xff1a;5. Circcom 中的基本算术电路_哔哩哔哩_bilibili 文字学习&#xff1a;https://hackmd.io/YlNLZS2ESI21OSqdTW_mPw/S1jqN-h80/edit 第五课&#xff0c;circom实践&#xff0c;需要安装 1 vscode 2 rust&#xff1a;Windows安装Rust环境&…

FFmpeg 实现从设备端获取音视频流并通过RTMP推流

使用FFmpeg库&#xff08;版本号为&#xff1a;4.4.2-0ubuntu0.22.04.1&#xff09;实现从摄像头和麦克风获取音视频流并通过RTMP推流。 RTMP服务器使用的是SRS&#xff0c;我这边是跑在Ubuntu上的&#xff0c;最好是关闭掉系统防火墙&#xff0c;不然连接服务器好像会出问题&a…

python手写了个简易的豆瓣影评爬虫

使用python手写了个简易的豆瓣影评爬虫代码。 __author__ wsximport time import requests from bs4 import BeautifulSoup import os import re import uuiddef clean_windows_filename(string_file_name):invalid_chars r[\\/:*?"<>|]return re.sub(invalid_c…

ZooKeeper 的特性及其在分布式系统中的配置中心的应用

以下是配置管理和服务注册的实现方式&#xff1a; 1. 配置管理 配置管理指的是将系统中各个组件的配置信息集中管理&#xff0c;以便动态更新和统一配置。ZooKeeper 可以用来管理配置文件&#xff0c;通过它的节点结构和数据一致性功能&#xff0c;确保所有客户端都能获得最新…

PIL convert(‘RGB‘) 用法

PIL 不提供 BGR转RGB的方法。 1. 图像模式转换 如果图像当前的模式不是 RGB&#xff0c;例如它是灰度&#xff08;L&#xff09;、CMYK 或其他模式时&#xff0c;convert(RGB) 会将图像转换为 RGB 格式。 灰度图像 (L)&#xff1a; 如果图像是灰度图像 (L)&#xff0c;conver…

【机器学习工具库-一-传统机器学习sklearn库】

sklearn库 安装安装顺序 sklearn库的六大功能sklearn中的核心调用流程 sklearn库是用于机器学习一个工具包&#xff0c;有了它&#xff0c;可以帮我们用简单的函数实现传统机器学习中的分类、聚类等任务。 安装 sklearn的官网 http://scikit-learn.org/stable/ sklearn库基于N…

【C++ 面试 - 内存管理】每日 3 题(二)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

Django后端架构开发:Nginx服务优化实践

Django后端架构开发&#xff1a;Nginx服务优化实践 目录 &#x1f31f; Nginx核心概念&#x1f50d; Nginx服务原理&#x1f504; Nginx负载均衡&#x1f517; Nginx反向代理⚙️ Nginx动静分离 &#x1f31f; Nginx核心概念 Nginx作为一款轻量级且功能强大的HTTP服务器&…

分布式性能测试-通篇讲解 Locust 性能测试

分布式性能测试-小试牛刀 Locust 分布式负载生成概述 Locust 支持分布式负载生成,以模拟更高的并发负载。你可以通过以下方式来配置和使用分布式模式: 1. 基本概念 Master 实例:管理整个负载测试,运行 Locust 的 Web 界面,并协调各个 Worker 的任务。Worker 实例:实际…