Linux 新的调试接口utrace简介
- 格式:pdf
- 大小:361.92 KB
- 文档页数:14
Linux版utorrent的使用1、首先上utorrent官网下载对应linux版本的utserver的压缩包,linux版本没有GUI只有WebUI,所以提供的只是utserver而不是utorrent。
2、解压缩utserver,得到一个文件夹,里面是utserver的主程序和WebUI的压缩包,这两个基本上就是程序的主体,WebUI就是之后你在网页中能显示出来的界面。
此外还有一个doc的文件夹里面有utserver的使用说明。
英文好的朋友可以自己看看。
3、这里有个简单的方法使用linux版utorrent。
直接打开终端,cd到utserver这个执行文件所在的文件夹,利用nohup ./utserver >/dev/null 2>&1 &的命令让utserver后台运行,再打开浏览器输入localhost:8080/gui/,初始用户名为admin,密码为空,即可登录。
之后就可以开始下载之旅了。
这个简单方法唯一缺点就是必须在命令行中打开,而且必须cd到utserver所在的目录。
例如将压缩包解压缩到/opt/utorrent/ 则每次启动必须用终端输入cd /opt/utorrentnohup ./utserver >/dev/null 2>&1 &虽然简单但是这种方式最保险。
需要注意的是utserver存放的位置会对其下载的文件权限和utserver的程序数据造成影响。
如上面存放在/opt中会是utserver下载的文件权限属于root,因为/opt本来就属于root的。
这样的话可以使用sudo chown -hR 你的用户名/opt/utorrent和sudo chgrp -hR 你的用户名/opt/utorrent 这样两条命令改变utserver的权限归属。
不过最简单的还是将utserver的包直接解压到你的用户文件夹下,虽然使用的时候会多出好多程序数据文件,不过用起来最方便。
linux trace 解读
LinuxTrace解读是关于如何使用LinuxTrace工具进行系统跟踪和分析的指南。
本文将介绍LinuxTrace工具的基本知识和用法,包括如何使用 ftrace、perf 和 SystemTap 等工具进行系统跟踪和分析。
首先,本文将介绍 ftrace 工具,它是 Linux 内核提供的一个跟踪框架,可以用于跟踪内核函数、进程和系统调用等信息。
本文将详细介绍 ftrace 的用法和其它相关工具。
其次,本文将介绍 perf 工具,它是一个基于硬件性能计数器的工具,可以用于跟踪系统的硬件性能,如 CPU 使用率、缓存命中率和内存带宽等信息。
本文将详细介绍 perf 工具的用法和其它相关工具。
最后,本文将介绍 SystemTap 工具,它是一个基于内核动态追踪技术的工具,可以用于跟踪内核函数、系统调用和进程等信息。
本文将详细介绍 SystemTap 工具的用法和其它相关工具。
通过本文的介绍,读者将能够了解 Linux Trace 工具的基本知识和用法,掌握如何使用 ftrace、perf 和 SystemTap 等工具进行系统跟踪和分析,为系统调优和性能优化提供有力的支持。
- 1 -。
linux truncate函数的原理在Linux 系统中,`truncate` 函数用于修改文件大小。
它的主要目的是截断或扩展文件的大小到指定的长度。
以下是`truncate` 函数的原理:```c#include <unistd.h>int truncate(const char *path, off_t length);```- `path`:文件路径名。
- `length`:指定的新文件长度。
`truncate` 函数的原理如下:1. 打开文件:首先,`truncate` 函数会尝试打开指定路径的文件。
如果文件不存在,则会创建一个空文件,然后再截断为指定的长度。
如果文件已存在,则直接打开。
2. 截断文件:一旦文件被成功打开,`truncate` 函数会将文件截断或扩展到指定的长度。
如果文件当前的大小大于指定长度,那么文件将被截断到指定长度。
如果文件当前的大小小于指定长度,那么文件将会被扩展,新增的部分会用零字节填充。
3. 关闭文件:操作完成后,`truncate` 函数会关闭文件。
这个函数对于一些场景很有用,比如在文件内容被截断之前,你可能想要备份或者读取文件的内容。
以下是一个使用`truncate` 函数的简单示例:```c#include <unistd.h>#include <fcntl.h>#include <stdio.h>int main() {const char *path = "example.txt";off_t new_length = 1000;int fd = open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);if (fd == -1) {perror("Error opening file");return 1;}if (truncate(path, new_length) == -1) {perror("Error truncating file");close(fd);return 1;}close(fd);printf("File truncated successfully.\n");return 0;}```这个例子中,程序尝试打开一个文件(如果不存在则创建),然后使用`truncate` 函数将文件截断或扩展到指定的长度。
Linux下的调试工具ltrace与straceltrace能够跟踪进程的库函数调用,它会显现出哪个库函数被调用strace则是跟踪程序的每个系统调用.下面是一个ltrace与strace的对比1)系统调用的输出对比我们用输出hello world的程序做如下测试:#include <stdio.h>int main(){printf("Hello World!\n");return 0;}gcc hello.c -o hello用ltrace跟踪hello程序,如下:ltrace ./hello__libc_start_main(0x80483b4, 1, 0xbfc780a4, 0x80483e0, 0x80483d0<unfinished ...>puts("Hello World!"Hello World!) = 13+++ exited (status 0) +++注:我们看到程序调用了puts();库函数做了输出.用strace跟踪hello程序,如下:strace ./helloexecve("./hello", ["./hello"], [/* 45 vars */]) = 0brk(0) = 0x98e8000access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7837000access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3fstat64(3, {st_mode=S_IFREG|0644, st_size=83009, ...}) = 0mmap2(NULL, 83009, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7822000close(3) = 0access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)open("/lib/libc.so.6", O_RDONLY) = 3read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@n\1\0004\0\0\0"..., 512) = 512fstat64(3, {st_mode=S_IFREG|0755, st_size=1421892, ...}) = 0mmap2(NULL, 1427880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x73a000mmap2(0x891000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED| MAP_DENYWRITE, 3, 0x157) = 0x891000mmap2(0x894000, 10664, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED| MAP_ANONYMOUS, -1, 0) = 0x894000close(3) = 0mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7821000set_thread_area({entry_number:-1 -> 6, base_addr:0xb78216c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0mprotect(0x891000, 8192, PROT_READ) = 0mprotect(0x8049000, 4096, PROT_READ) = 0mprotect(0x8c9000, 4096, PROT_READ) = 0munmap(0xb7822000, 83009) = 0fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7836000write(1, "Hello World!\n", 13Hello World!) = 13exit_group(0) = ?注:我们看到程序调用write()系统调用做了输出,同时strace还把hello程序运行时所做的系统调用都打印出来了.同样的ltrace也可以把系统调用都打印出来,如下:ltrace -S ./helloSYS_execve(NULL, NULL, NULL) = 0xffffffdaSYS_brk(NULL) = 0x09597000SYS_access("/etc/ld.so.nohwcap", 00) = -2SYS_mmap2(0, 8192, 3, 34, -1) = 0xb780a000SYS_access("/etc/ld.so.preload", 04) = -2SYS_open("/etc/ld.so.cache", 0, 00) = 3SYS_fstat64(3, 0xbf86b0e4, 0x55eff4, 0x55f8a0, 3) = 0SYS_mmap2(0, 83009, 1, 2, 3) = 0xb77f5000SYS_close(3) = 0SYS_access("/etc/ld.so.nohwcap", 00) = -2SYS_open("/lib/libc.so.6", 0, 00) = 3SYS_read(3, "\177ELF\001\001\001", 512) = 512SYS_fstat64(3, 0xbf86b138, 0x55eff4, 0xb7806b86, 0x804820c) = 0SYS_mmap2(0, 0x15c9a8, 5, 2050, 3) = 0x1b4000SYS_mmap2(0x30b000, 12288, 3, 2066, 3) = 0x30b000SYS_mmap2(0x30e000, 10664, 3, 50, -1) = 0x30e000SYS_close(3) = 0SYS_mmap2(0, 4096, 3, 34, -1) = 0xb77f4000SYS_set_thread_area(0xbf86b5cc, 0x55eff4, 0xb77f46c0, 1, 0) = 0SYS_mprotect(0x0030b000, 8192, 1) = 0SYS_mprotect(0x08049000, 4096, 1) = 0SYS_mprotect(0x0055e000, 4096, 1) = 0SYS_munmap(0xb77f5000, 83009) = 0__libc_start_main(0x80483b4, 1, 0xbf86b7c4, 0x80483e0, 0x80483d0<unfinished ...>puts("Hello World!" <unfinished ...>SYS_fstat64(1, 0xbf86b5f0, 0x30cff4, 0x30d4e0, 0x8048490) = 0SYS_mmap2(0, 4096, 3, 34, -1) = 0xb7809000SYS_write(1, "Hello World!\n", 13Hello World!) = 13<... puts resumed> ) = 13SYS_exit_group(0 <no return ...>+++ exited (status 0) +++注:我们看到它实际是用SYS_write系统调用来做打印输出,其实write()函数是SYS_write的封装,SYS_write是真正的系统调用.二)ltrace/strace的耗时ltrace -c dd if=/dev/urandom of=/dev/null count=1000记录了1000+0 的读入记录了1000+0 的写出512000字节(512 kB)已复制,0.645496 秒,793 kB/秒% time seconds usecs/call calls function------ ----------- ----------- --------- --------------------91.18 2.293684 2293 1000 read4.96 0.124725 124 1000 write2.87 0.072140 72 1000 memcpy0.79 0.019871 9935 2 dcgettext0.02 0.000594 594 1 setlocale0.02 0.000478 119 4 close0.02 0.000461 115 4 sigaction0.02 0.000418 139 3 __fprintf_chk0.01 0.000353 88 4 __errno_location0.01 0.000301 50 6 strlen0.01 0.000259 129 2 open640.01 0.000245 122 2 clock_gettime0.01 0.000243 121 2 dup20.01 0.000191 63 3 strchr0.01 0.000156 78 2 malloc0.01 0.000128 64 2 sigismember0.01 0.000127 63 2 sigaddset0.00 0.000120 60 2 __sprintf_chk0.00 0.000112 112 1 lseek640.00 0.000107 53 2 localeconv0.00 0.000085 85 1 fclose0.00 0.000081 81 1 __strtoull_internal 0.00 0.000078 78 1 bindtextdomain0.00 0.000078 78 1 getenv0.00 0.000076 76 1 sigemptyset0.00 0.000074 74 1 textdomain0.00 0.000072 72 1 getopt_long0.00 0.000070 70 1 __cxa_atexit0.00 0.000065 65 1 getpagesize0.00 0.000064 64 1 __ctype_b_loc0.00 0.000064 64 1 strrchr0.00 0.000064 64 1 dcngettext0.00 0.000050 50 1 memmove0.00 0.000049 49 1 __fpending------ ----------- ----------- --------- --------------------100.00 2.515683 3058 total注:使用-c选项,ltrace输出由进程创建的库调用,输出结果以调用过程的时间为准进行排序,因为是从urandom设备上读,这是一种产生随机数的设备,完成后,写入null设备.所以读过程花费了较多的时间.使用ltrace去捕获运行时函数,就好像在进程上系上了一个调试工具,它占据了ltrace大量的时间,这里ltrace一共消耗了2.5秒我们再来看一下strace所花费的时间,如下:strace -c dd if=/dev/urandom of=/dev/null count=1000记录了1000+0 的读入记录了1000+0 的写出512000字节(512 kB)已复制,0.305115 秒,1.7 MB/秒% time seconds usecs/call calls errors syscall------ ----------- ----------- --------- --------- ----------------99.66 0.009417 9 1005 read0.34 0.000032 0 1003 write0.00 0.000000 0 12 2 open0.00 0.000000 0 13 close0.00 0.000000 0 1 execve0.00 0.000000 0 5 5 access0.00 0.000000 0 3 brk0.00 0.000000 0 2 dup20.00 0.000000 0 2 munmap0.00 0.000000 0 1 uname0.00 0.000000 0 6 mprotect0.00 0.000000 0 1 _llseek0.00 0.000000 0 6 rt_sigaction0.00 0.000000 0 1 rt_sigprocmask0.00 0.000000 0 1 getrlimit0.00 0.000000 0 16 mmap20.00 0.000000 0 8 fstat640.00 0.000000 0 3 1 futex0.00 0.000000 0 1 set_thread_area0.00 0.000000 0 1 set_tid_address0.00 0.000000 0 2 clock_gettime0.00 0.000000 0 1 set_robust_list------ ----------- ----------- --------- --------- ----------------100.00 0.009449 2094 8 total注:strace一共消耗了0.0095秒,strace把性能提升了260倍,这主要是strace在跟踪系统调用的时候不需要动态库,而ltrace是根据动态库来分析程序运行的.所以ltrace也只能跟踪动态库,不能跟踪静态库.事实上我们用ltrace和strace都可以发现程序在哪个系统调用时发生了性能瓶径.ltrace用-T,而strace也用-T.三)ltrace与strace的相同点ltrace与strace都可以指定PID,即对运行中的程序进行跟踪.ltrace -p PID与strace -p PIDltrace与strace都可以跟踪程序fork或clone子进程.ltrace是用-f参数,而strace是用-f(fork/clone)和-F(vfork).——EmbedXJ——2011年7月30日。
uretprobe 原理-回复uretprobe是Linux系统中的一种动态追踪技术,用于在运行时跟踪系统中的函数调用。
本文将介绍uretprobe的原理,包括其工作流程、具体实现方法以及应用实例等。
一、uretprobe的概述uretprobe是Linux系统中的一个动态追踪技术,它可以在函数返回时执行用户指定的代码。
它的原理是利用内核提供的ftrace功能,在函数返回时设置一个断点,并在断点处执行用户指定的代码。
uretprobe通过在目标函数返回时插入额外的代码,可以实现函数调用的跟踪和分析。
二、uretprobe的工作流程uretprobe的工作流程可以总结为以下几个步骤:1. 选择目标函数:首先,用户需要选择一个目标函数进行跟踪。
目标函数是用户希望跟踪的函数,在函数返回时将执行用户指定的代码。
2. 编写钩子函数:用户需要编写一个钩子函数,用于在目标函数返回时执行。
钩子函数可以是一段C代码或者一个脚本,它在目标函数返回时被调用。
3. 注册uretprobe:使用系统提供的工具,将用户编写的钩子函数注册到目标函数上。
注册的过程中需要指定目标函数的地址和钩子函数的地址。
4. 执行跟踪:一旦uretprobe被注册成功,每当目标函数返回时,钩子函数就会被调用。
用户可以在钩子函数中执行任意代码,并对函数的返回值进行分析。
三、uretprobe的具体实现方法uretprobe的具体实现方法主要依赖于Linux内核提供的ftrace功能。
下面将阐述uretprobe的实现方法:1. ftrace的背景介绍:Linux内核提供了一种叫做ftrace的功能,用于对内核的函数调用进行跟踪。
ftrace可以通过在函数的入口和出口设置断点,记录函数调用的信息。
2. uretprobe的原理:uretprobe是在ftrace的基础上扩展而来的。
在注册uretprobe时,用户需要指定目标函数的地址,并提供一个钩子函数的地址。
linux uart 流控机制Linux系统中,UART(Universal AsynchronousReceiver/Transmitter)是一种常见的串行通信接口,用于在嵌入式系统和计算机之间进行数据传输。
流控机制是一种控制数据流动的方法,可以确保数据在通信过程中不丢失或损坏。
在Linux系统中,UART的流控机制通常涉及硬件流控和软件流控两种方式。
硬件流控通常使用RTS(Ready To Send)和CTS(Clear To Send)信号线来控制数据流动。
当接收端准备好接收数据时,会通过CTS信号通知发送端可以发送数据,而当接收端缓冲区已满时,会通过CTS信号通知发送端停止发送数据,以防止数据丢失。
类似地,发送端通过RTS信号通知接收端自己是否准备好发送数据。
在Linux系统中,可以通过设置串口的参数来启用硬件流控。
另一种流控方式是软件流控,它通过发送特定的控制字符来告知对方停止发送数据或准备好接收数据。
在Linux系统中,可以使用termios结构体中的c_iflag和c_oflag成员来设置软件流控。
除了上述基本的流控机制外,Linux系统还提供了更高级的流控功能,如XON/XOFF流控。
这种流控方式通过发送特定的控制字符(XON和XOFF)来控制数据流动,当接收端缓冲区快满时发送XOFF,发送端收到XOFF后停止发送数据,当接收端缓冲区有足够的空间时发送XON,发送端收到XON后继续发送数据。
这种流控方式在处理较慢的终端设备或串口通信时非常有用。
总的来说,在Linux系统中,串口的流控机制可以通过设置串口参数来实现硬件流控和软件流控,同时还提供了更高级的流控功能来满足不同场景下的需求。
对于不同的应用场景,可以根据需要选择合适的流控方式来保证数据的可靠传输。
linux 调试手段在Linux系统中,调试是开发人员必备的技能,它可以帮助我们分析和解决软件开发过程中的各种问题。
下面将介绍一些常用的Linux 调试工具和技术,以帮助开发人员更好地进行调试工作。
一、调试工具1. GDB(GNU调试器):GDB是一个功能强大的调试器,可以用于C,C++等语言的调试。
它可以帮助我们跟踪程序的执行过程,查看变量的值,定位错误等。
我们可以使用命令"gdb命令"来启动GDB。
2. strace:strace可以用来跟踪进程的系统调用和信号传递。
它可以帮助我们分析程序的行为,找到可能的问题。
我们可以使用命令"strace命令"来启动strace。
3. ltrace:ltrace可以用来跟踪进程的库函数调用。
它可以帮助我们查看程序调用了哪些库函数,以及这些函数的参数和返回值。
我们可以使用命令"ltrace命令"来启动ltrace。
4. valgrind:valgrind是一个内存调试和性能分析工具。
它可以帮助我们检测程序中可能存在的内存泄漏,访问越界等问题。
我们可以使用命令"valgrind命令"来启动valgrind。
5. gdbserver:gdbserver是GDB的一个辅助工具,它允许我们在远程机器上调试程序。
我们可以在远程机器上运行gdbserver,然后在本地机器上使用GDB连接到远程机器上进行调试。
二、调试技术1.断点调试:断点是调试过程中常用的技术之一。
我们可以在代码中设置断点,在程序执行到断点的位置时停下来,以便我们可以查看变量的值,调试程序。
在GDB中,我们可以使用命令"break行号"来设置断点。
2.日志调试:日志调试是调试过程中常用的技术之一。
我们可以在程序中插入日志打印语句,以便在程序执行过程中查看变量的值。
通过分析日志,我们可以了解程序的执行流程,找到可能存在的问题。
1简介ETM trace 是一种高速Trace,并提供强大的调试模式,可帮助您解决最困难的问题。
本文档旨在介绍如何为i.MXRT10xx 芯片启用ETM Trace 以及使用uTrace 调试器的基本步骤。
2安装软件用户可以从https:///frames.html?download_overview.html 找到TRACE32安装软件包,将TRACE32_201909.7z 下载到计算机上并进行安装。
注意以下两点:1.由于安装包比较大,可以根据目标处理器安装软件组件,以节省硬盘空间;2.您可以在C:\T32\bin\windows64\drivers 文件夹中找到安装的驱动程序。
3连接硬件TRACE32调试器硬件包括:•通用调试器硬件•特定于处理器体系结构的调试电缆图 1 是硬件连接示意图。
目录1 简介........................................................12 安装软件................................................13 连接硬件................................................14 使用软件 (4)5 加载应用................................................56 创建脚本................................................67 加载应用................................................88 Trace 调试.............................................89 其他.. (9)AN12877如何在iMXRT10xx 系列上使能ETM_TraceRev. 0 — June 2020Application Note1. 以 i.MX RT1010 验证板(RAM)为例,图 2 显示了 i.MX RT1010 验证板硬件连接图。
Linux终端中的进程调试掌握strace和gdb命令Linux终端中的进程调试——掌握strace和gdb命令在Linux开发中,进程调试是非常重要的一环。
有时候我们需要对程序进行故障排除,查找bug,或者优化程序性能。
为了实现这些目标,掌握Linux终端中的进程调试工具是必不可少的。
本文将介绍两个常用的进程调试工具:strace和gdb命令。
一、strace命令strace命令可以追踪和记录程序的系统调用和信号。
它能够帮助我们了解程序在运行过程中系统调用的情况,从而分析程序的行为和定位问题。
使用strace命令非常简单,只需要在终端中输入以下命令:```strace -p <pid>```其中,`<pid>`是需要调试的进程的PID(进程ID)。
通过这个命令,strace就会开始追踪指定PID的进程的系统调用和信号。
strace命令输出的信息非常详细,包括每个系统调用的名称、参数和返回值等。
通过分析这些信息,我们可以定位到程序的问题所在。
例如,当程序出现段错误时,我们可以通过strace命令追踪到问题发生的位置。
二、gdb命令gdb是GNU Debugger的缩写,是一款功能强大的调试器。
它可以帮助我们在Linux终端中对程序进行源代码级别的调试,实时监控程序的状态和变量的值。
使用gdb命令调试程序的步骤如下:1. 编译程序时,需要加上-g选项,以保留源代码级别的调试信息。
例如:```gcc -g program.c -o program```2. 在终端中使用以下命令启动gdb调试器:```gdb ./program```其中,`./program`是需要调试的程序的可执行文件。
3. 在gdb调试器中,我们可以使用一系列命令来控制程序的执行,如下所示:- `break <line>`:在指定行设置断点,程序会在该行暂停执行。
- `run`:运行程序,直到遇到断点或者程序结束。
讨论区搜索Linux红薯发表于Utrace 运行在 ptrace 下层,ptrace 通过请求 utrace 来完成用户所要求的行为。
那些原先依赖于 ptrace 的应用程序,比如 gdb,不需要做任何改变。
相对于原有的实现,采用 utrace 重写的 ptrace 结构更加合理,容易维护和扩展;不过,utrace 的出现并不仅仅是为了重新实现 ptrace,它提供了比 ptrace 更强大的基础功能,为未来更智能的 debugger 做好了准备。
简单说来,utrace 主要有三种功能:Thread Event Reporting:跟踪目标进程的各种事件,当这些事件发生时,utrace 调用对应的 callback 函数进行处理。
这些事件比如:接收到信号,系统调用,进程退出等。
Core thread control:Debugger 最基本的要求是能够控制被调试程序,比如让目标进程暂停或者单步执行。
Utrace 提供这种进程控制能力。
Thread machine state access:很多调试器都需要修改目标进程的地址空间,比如设置断点时,debugger 需要将目标进程的目标代码替换为 trap 指令。
因此作为 trace/debug 基础构架,utrace 也必须提供对目标进程地址空间的访问能力。
有了这三种能力,便可以完成很多跟踪和调试的任务。
发明 utrace 的动机凡事都有因有果,有些事情人们不必去深究其缘由,但 utrace 这件事,却很值得去研究其出现的动机和意义。
Ptrace 的问题在 Linux 中,调试应用程序的传统接口是 ptrace。
Gdb, strace 等 debugger 都是ptrace 的具体应用。
但 ptrace 有很多问题。
Roland McGraph 是 Linux 社区中 ptrace 的维护者,他在长期的维护工作中总结出了很多 ptrace 的问题,也正是他开发了 utrace,可以称他为 utrace 之父。
下面列举 Roland 所发现的问题。
Dtrace EnvyLinux 社区中会提到一个名词叫作 Dtrace Envy。
Dtrace 是一种让 SUN 工程师具有优越感的技术。
Linux 开发者在 Dtrace 面前总会表露些羡慕之意。
于是 Linux 社区出现了 systemTap, systemTap 利用了内核的 Kprobe 机制,完成了Dtrace 类似的功能,IBM Developerworks 网站上曾有文章介绍了 systemTap,非常值得一读。
要和 Dtrace 相提并论,systemTap 还必须要有能力调试用户态进程,否则只能说达到了 Dtrace 部分的功能。
目前 systemTap 正是依赖 utrace 来调试用户态进程的。
Ptrace 的落后,赶超 Dtrace 的强烈渴望,我想这足以说明 utrace 出现的意义了。
Utrace 的使用简介这一节介绍 utrace 的编程方法和概念。
utrace 编程的基础概念如果 trace 是一篇文章,那么一定是形如“几点几分发生了什么事情”这样的一个流水帐。
记录这个流水账最简便的方法就是当事情发生时,产生一个通知,当我们接到通知时便立即将事件记录下来。
Utrace 的编程思想就是这样的,您告诉 utrace 当哪些事件发生时需要产生通知,然后告诉 utrace 这些事件发生时应该调用哪些 callback 函数来处理。
调试目标进程的第一件事情是 attach 它。
您可以通过utrace_attach_task()或utrace_attach_pid()完成 attach。
Attach 成功将返回一个 trace engine 数据结构,从此以后程序便通过 trace engine 对目标进程进行控制。
清单 1. trace engine 数据结构struct utrace_engine {const struct utrace_engine_ops *ops;void *data;unsigned long flags;};使用 utrace 的第二步是告诉 utrace 您对哪些事件感兴趣。
比如目标进程执行了 exec,或者收到了一个信号等等。
Table 1 列出了 utrace 支持的所有事件。
函数utrace_set_events_pid 用来通知 utrace 您所感兴趣的事件。
表 1. utrace 事件UTRACE_EVENT_QUIESCE Thread is available for examination UTRACE_EVENT_REAP Zombie reaped, no more tracing possible UTRACE_EVENT_CLONE Successful clone/fork/vfork just doneUTRACE_EVENT_EXEC Successful execve just completedUTRACE_EVENT_EXIT Thread exit in progressUTRACE_EVENT_DEATH Thread has diedUTRACE_EVENT_SYSCALL_ENTRYUser entered kernel for system callUTRACE_EVENT_SYSCALL_EXIT Returning to user after system callUTRACE_EVENT_SIGNAL Signal delivery will run a user handler UTRACE_EVENT_SIGNAL_IGN No-op signal to be deliveredUTRACE_EVENT_SIGNAL_STOP Signal delivery will suspendUTRACE_EVENT_SIGNAL_TERM Signal delivery will terminateUTRACE_EVENT_SIGNAL_CORE Signal delivery will dump coreUTRACE_EVENT_JCTL Job control stop or continue completed接下来,您必须告诉 utrace,当这些事件发生时您想做什么。
这是通过注册 callback 函数实现的。
在调用utrace_attach_task()时您可以传入一个 utrace_engine_ops 数据结构完成callback 的注册。
至此,基本的准备工作就都完成了。
当目标进程发生事件时,callback 便被调用。
您现在可以在 callback 中做任何想做的事情。
如果我们只需静静地观察目标进程,那么上面的基本工作便足够了。
但有时候我们还想干涉目标进程的执行。
比如 debugger 需要控制进程,实现诸如单步执行,或者暂停目标进程等功能。
这可以通过 utrace_control 完成。
Table 2 列出了您能够通过 utrace_control 完成的控制动作。
表 2. utrace control 动作ACTION描述UTRACE_STOP Stay quiescent after callbacksUTRACE_INTERRUPT Make report_signal() callback soonUTRACE_REPORT Make some callback soonUTRACE_SINGLESTEPResume in user mode for one instructionUTRACE_BLOCKSTEP Resume in user mode until next branchUTRACE_RESUME Resume normally in user modeUTRACE_DETACH Detach my engine这些 action 不仅可以作为 utrace_control 的入口参数来控制目标进程,也可以出现在每个callback 函数的返回值中。
这样当 callback 返回时,utrace 便执行相应的动作。
比如暂停或者单步执行等。
在 Table 2 中值得一提的 action 是 UTRACE_INTERRUPT。
有些情况下,目标进程并没有发生任何我们所感兴趣的事件,但我们想做些事情,比如发送一个 fake 信号给进程,或者将目标进程暂停下来。
这种时候,可以通过调用 utrace_control(UTRACE_INTERRUPT)来打断目标进程,其结果是 signal callback 被立即触发。
这时候,您可以做一些想要做的工作,一个例子是本文最后给出的 code inject 例子程序,利用能够 UTRACE_REPORT 在module 初始化时主动触发一个 fake 的信号,从而使得 signal callback 被调用。
Utrace 的代码实例演示对于程序员来说,说再多的也不如写几个例子程序更来劲。
在这一节中,我将用 utrace 开发一些小程序,希望能使您对 utrace 有更直观的了解。
学习 ptrace 的人或许都读过 Pradeep Padala 写的 <playing with ptrace>,如果没有那么我推荐您去读一下。
在该文中,Pradeep 写了几个小程序来一步步地演示 ptrace 的使用,效果非常好。
下面我也将开发一些程序,实现大侠 Pradeep 在 playing with ptrace 中的所有示范代码。
只是这一次,我们用 utrace 来实现。
在写作本文的时候,utrace 还没有能够进入 Linux upstream,所以首先我们必须对kernel 打些补丁。
本文中的例子程序都是在 2.6.32+utrace patch 下调试通过的。
您可以在/roland/utrace/下载 utrace 的补丁,在 2.6.32 下,补丁的顺序如下:>cd /src/linux26>patch – p1 <tracehook.patch>patch – p1 <utrace.patch>patch – p1 <utrace-ptrace.patch打好补丁之后,重新编译内核,注意需要选中 utrace 选项。
获取 syscall 参数第一个例子程序打印出目标进程在运行时所调用的系统调用信息,功能类似 strace。
作为演示,我将研究 ls 命令在执行过程中调用了哪些系统调用,以及这些系统调用的参数。
在本文的下载附件中,您可以找到所有在这里罗列出的源代码。