Linux内核的ftrace调试接口

简介:
为了抓住一个自定义的内核函数是如何被执行的,需要一定的调试手段,其实就需要一种跟踪手段就可以了,理论上不太复杂,可是Linux内核的调试接口太多了,始终找不到一个方便的,直到遇到了ftrace,它简单的使用文件系统作为接口,不需要安装任何用户态程序,和杂乱的发行版毫无关系,这正合我意,相比SystemTap等复杂的前置设置等调试手段,简直棒极了。因为我很讨厌为了做一件理论上很简单的事而去花去大量的时间去做前置工作。
        使用文件系统作为接口的优势自然不必多说,它可以将任意复杂的操作映射到既有的简单的读,写,控制,打开,关闭等简单操作上,ftrace的另一个妙点在于其动态二进制修正技术。其实kprobe也是使用了二进制修正技术,然而它做的很硬,而ftrace则使用了GCC内置的mcount机制,通过重载mcount函数来完成对任意函数调用的统计。
        mcount机制是GCC的一个特性,在任何函数调用时,会纪录关于该函数的一些信息。比如以下的程序:
mcount.c:
#include <stdio.h> void  mcount() {             printf("@@@@\n"); }
gcc -c mcount.c

main.c:
#include <stdlib.h> #include <stdio.h> extern void mcount(void); void b(int i) {         printf("b:%d\n", i); } int a(int i) {         b(i);         return 3; } int main() {         int i = 3;         int k = a(i);         return k; }
gcc -c main.c -pg
gcc mcount.o main.o -o test

执行test,则会发现每个函数调用都会打印出@@@,这说明我们重载mcount成功了。如果能将mcount做成一个只执行ret的stub函数,或者连call mcount一起都执行nop的stub,那么相当于没有这个mcount函数,如果某个时间用户启用了ftrace,则将上述stub替换为真正的trace函数,那不就可以动态开启/关闭trace功能了么?Linux kernel正是这么做的。要想这么做,stub函数要做的足够灵活,以上面的mcount.c/main.c为例,一个比较灵活但不绝对灵活的设计框架如下:
char code[] = {0xc3, 0x90, 0x90...} //0xc3为直接ret  void mcount() {     int (*pf)(void);     pf = &code[0];     pf(); }
如果用户开启了trace,则将code进行替换,替换成call real_func的操作码,而real_func不止一个固定的函数,而是可以register的,那么我们就可以根据自己的爱好来任意替换trace函数进而实现任意的trace风格了。Linux内核的做法比我这个要灵活的多,通过回调func的机制,它甚至可以画出一副函数调用图,十分强大。顺便说一句,trace回调函数的实现利用了内核编译时产生的内核函数位置表,它的条目就是函数名和位置这一对映射,trace回调函数会根据当前的地址查到函数名。
        我上述的框架只是一个框架,如果你真的去编译运行了,会发现出现了恶心的segment fault,这是因为如今大多数的内核都实施了data section不可执行,text section不可写的保护功能,如果你硬要那么做,会出现通用保护异常,因此还要做大量的链接脚本的工作。这个费力的事就不说了,说多了都是泪!
        ftrace的核心在于利用了mcount机制以及文件系统机制,它的使用非常简单,只需要挂载debugfs,你就可以任意调试了:
mount -t debugfs debugfs /debug
然后进入/debug/tracing目录,检查available_tracers,看看你当前的kernel支持的trace功能都有哪些,如果有function,说明你的内核支持函数跟踪功能,ftrace支持过滤功能,比如按照内核函数过滤,按照进程过滤。下面是对于一个长ping的trace结果片断:
 0)               |  sys_socketcall() {
 0)               |    copy_from_user() {
 0)               |      _copy_from_user() {
 0)   0.137 us    |        _cond_resched();
 0)   0.457 us    |      }
 0)   0.806 us    |    }
 0)   0.130 us    |    audit_socketcall();
 0)               |    sys_recvmsg() {
 0)               |      sockfd_lookup_light() {
 0)   0.228 us    |        fget_light();
 0)   0.558 us    |      }
 0)               |      __sys_recvmsg() {
 0)               |        _copy_from_user() {
 0)   0.130 us    |          _cond_resched();
 0)   0.405 us    |        }
 0)               |        verify_iovec() {
 0)               |          _copy_from_user() {
 0)   0.129 us    |            _cond_resched();
 0)   0.429 us    |          }
 0)   0.736 us    |        }
 0)               |        sock_recvmsg() {
 0)               |          security_socket_recvmsg() {
 0)               |            apparmor_socket_recvmsg() {
 0)   0.179 us    |              aa_revalidate_sk();
 0)   0.482 us    |            }
 0)   0.945 us    |          }
 0)   0.210 us    |          sock_update_classid();
 0)               |          inet_recvmsg() {

不光可以绘制出函数调用图,其计时统计信息对于性能分析也是很有参考意义的。

        ftrace很强大,不需要你对系统做任何额外的配置,不需要安装额外的软件,直接使用文件系统接口即可。我十分喜欢这个机制,因为我讨厌需要额外配置的机制,那样促使很多人走偏了路,他们在炫耀敲命令的技巧的同时,给了别人很大的压力,其实行内人士都知道,他们的大部分命令都是为了搭建这个trace环境,而不是解决真正的问题,因此这种命令也包括apt-get,你不懂,你就会觉得他很猛!



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1268890

相关文章
|
2月前
|
安全 Linux 测试技术
Intel Linux 内核测试套件-LKVS介绍 | 龙蜥大讲堂104期
《Intel Linux内核测试套件-LKVS介绍》(龙蜥大讲堂104期)主要介绍了LKVS的定义、使用方法、测试范围、典型案例及其优势。LKVS是轻量级、低耦合且高代码覆盖率的测试工具,涵盖20多个硬件和内核属性,已开源并集成到多个社区CICD系统中。课程详细讲解了如何使用LKVS进行CPU、电源管理和安全特性(如TDX、CET)的测试,并展示了其在实际应用中的价值。
|
2月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
125 15
|
3月前
|
存储 NoSQL Linux
linux之core文件如何查看和调试
通过设置和生成 core 文件,可以在程序崩溃时获取详细的调试信息。结合 GDB 等调试工具,可以深入分析 core 文件,找到程序崩溃的具体原因,并进行相应的修复。掌握这些调试技巧,对于提高程序的稳定性和可靠性具有重要意义。
572 6
|
3月前
|
运维 监控 Linux
BPF及Linux性能调试探索初探
BPF技术从最初的网络数据包过滤发展为强大的系统性能优化工具,无需修改内核代码即可实现实时监控、动态调整和精确分析。本文深入探讨BPF在Linux性能调试中的应用,介绍bpftune和BPF-tools等工具,并通过具体案例展示其优化效果。
123 14
|
3月前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
3月前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
3月前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
3月前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
4月前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
130 4
|
4月前
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
114 17