ftrace是了解Linux内核内部工作原理的好方法。操作系统的内核是最难理解的软件之一。自系统启动以来,它一直在后台运行。每个用户虽然不直接与内核交互,但都是在内核的帮助下完成自己的计算任务。与内核的交互发生在调用系统调用或用户日常使用的各种库或应用程序间接调用系统调用时。在上一篇文章中我介绍了如何使用strace来跟踪系统调用。但是,使用strace时您的视图会受到限制。它允许您查看具有特定参数的系统调用。当作业完成时,查看其返回值或状态以指示成功或失败。但是你没有办法知道内核在这段时间发生了什么。除了系统调用之外,内核中还有许多您看不到的其他活动。ftrace简介本文的目的是通过使用称为ftrace的机制来阐明跟踪内核函数。它使任何Linux用户都可以轻松地跟踪内核并了解有关Linux内核内部工作方式的更多信息。默认情况下,ftrace产生的输出往往很大,因为内核总是很忙。为了节省空间,在很多情况下我会截断以提供尽可能小的输出。我正在使用Fedora来演示下面的示例,但它们在其他最新的Linux发行版上应该也同样有效。启用ftraceftrace现在是内核的一部分,您不需要事先安装它。也就是说,如果您使用的是最新的Linux系统,则ftrace已经启用。要验证ftrace是否可用,请运行mount命令并查找tracefs。如果您看到如下所示的输出,则表示ftrace已启用,您可以轻松尝试本文中的以下示例。下面的一些命令需要在root用户下使用(用sudo执行是不行的)。#挂载|greptracefsnoneon/sys/kernel/tracingtypetracefs(rw,relatime,seclabel)要使用ftrace,首先需要进入上面mount命令找到的具体目录,并在该目录下运行文章中的其他命令。#cd/sys/kernel/tracing一般工作流程首先,您需要了解捕获跟踪和获取输出的一般流程。如果直接运行ftrace,则不会运行任何特定的ftrace命令。相反,基本操作是通过标准的Linux命令写入或读取一些文件。一般步骤如下:通过写入某些特定文件来启用/禁用跟踪通过写入某些特定文件来设置/禁用跟踪时的过滤规则从文件中读取基于步骤1和2的跟踪输出早期从文件中清除OutputorbufferNarrow根据您的特定用例(您要跟踪的内核函数),重复步骤1、2、3、4可用的跟踪器类型有许多不同的跟踪器可用。如前所述,在运行任何命令之前,您需要进入一个特定的目录,因为所需的文件都在这些目录中。我在示例中使用了相对路径(而不是绝对路径)。您可以查看available_tracers文件的内容以查看所有可用的跟踪器类型。你可以看到下面列出的一些。无需担心这些:$pwd/sys/kernel/tracing$sudocatavailable_tracershwlatblkmmiotracefunction_graphwakeup_dlwakeup_rtwakeupfunctionnop在所有输出跟踪器中,我将特别关注以下三个:启用跟踪的function和function_graph,和停止跟踪的nop。确保当前跟踪器通常默认设置为nop。即特殊文件中current_tracer中的“no-op”,这意味着跟踪当前处于关闭状态:$pwd/sys/kernel/tracing$sudocatcurrent_tracernop查看跟踪输出在启用任何跟踪功能之前,请查看保存跟踪输出文件。您可以使用cat命令查看名为trace的文件的内容:#cattrace#tracer:nop##entries-in-buffer/entries-written:0/0#P:8##_------=>irqs-off#/_----=>need-resched#|/_---=>硬中断/软中断#||/_--=>抢占深度#|||/delay#TASK-PIDCPU#||||时间戳函数#|||||||||启用功能tracer可以通过将function写入current_tracer文件来启用第一个tracer功能(该文件的原始内容为nop,表示关闭tracer)。将此视为启用跟踪的一种方式:$pwd/sys/kernel/tracing$sudocatcurrent_tracernop$echofunction>current_tracer$$catcurrent_tracerfunction查看函数跟踪器的更新跟踪输出现在您已经启用了跟踪,是时候查看了输出。如果查看trace文件的内容,会看到很多内容是连续写入的。我只传输了文件内容的前20行。根据左侧输出的标题,可以看到某个CPU上运行的任务和进程ID。根据右边的输出,可以看到具体的核函数和它的父函数。中间显示了时间按钮信息:#sudocattrace|head-20#tracer:function##entries-in-buffer/entries-written:409936/4276216#P:8##_----=>irqs-off#/_----=>需要-改期#|/_---=>硬中断/软中断#||/_--=>抢占深度#|||/delay#TASK-PIDCPU#||||时间戳函数#|||||||||-0[000]d...2088.841739:tsc_verify_tsc_adjust<-arch_cpu_idle_enter-0[000]d...2088.841739:local_touch_nmi<-do_idle-0[000]d...2088.841740:rcu_nocb_flush_deferred_wakeup<-do_idle-0[000]d...2088.841740:tick_check_broadcast_expired<-do_idle-0[000]d...2088.841740:cpuidle_get_cpu_driver<-do_idle-0[000]d...2088.841740:cpuidle_not_available<-do_idle-0[000]d...2088.841741:cpuidle_select<-do_idle-0[000]d...2088.841741:menu_select<-do_idle-0[000]d...2088.841741:cpuidle_governor_latency_req<-menu_select请记住,当启用跟踪时,这意味着将连续写入跟踪结果,直到您关闭跟踪关闭跟踪是简单的。您只需要在current_tracer文件中用nop替换函数跟踪器:您可以使用与上述相同的步骤:在current_tracer文件中写入function_graph:$sudoechofunction_graph>current_tracer$sudocatcurrent_tracerfunction_graphfunction_tracerTraceroutput请注意,trace文件的输出格式已更改。现在,您可以看到CPUID和内核函数的执行时间。接下来,大括号表示函数的开始,以及它在内部调用的其他函数:#cattrace|head-20#tracer:function_graph##CPUDURATIONFUNCTIONCALLS#|||||||6)|n_tty_write(){6)|down_read(){6)|__cond_resched(){6)0.341我们|rcu_all_qs();6)1.057|}6)1.807我们|}6)0.402我们|过程回声();6)|add_wait_queue(){6)0.391我们|_raw_spin_lock_irqsave();6)0.359|_raw_spin_unlock_irqrestore();6)1.757|}6)0.350我们|tty_hung_up_p();{6)0.404我们|rcu_all_qs();6)1.067|}启用跟踪设置以增加跟踪深度您可以使用以下步骤调整跟踪器以查看更深层次的函数调用完成后,您可以查看跟踪文件的内容,发现输出变得更加冗长。为了文章的可读性,省略了这个例子的输出:#catmax_graph_depth0#echo1>max_graph_depth##或者:#echo2>max_graph_depth#sudocattrace找到要跟踪的函数上面的步骤足以让你入门追踪。但是它产生的输出是巨大的,当你试图找到你感兴趣的东西时,通常会很困难。通常您更愿意只跟踪某些功能而忽略其他功能。但是,如果您不知道它们的确切名称,您怎么知道要追踪哪些进程呢?有一个文件可以帮助您解决这个问题-available_filter_functions文件提供了一个可以跟踪的函数列表:$sudowc-lavailable_filter_functions63165available_filter_functions查找一般内核函数现在尝试搜索一个您知道的简单内核函数。Userspaceisusedbythemallocfunctiontoallocatememory,whilethekernelisusedbythekmallocfunction,whichprovidessimilarfunctionality.下面是所有与kmalloc相关的函数:$sudogrepkmallocavailable_filter_functionsdebug_kmallocmempool_kmallockmalloc_slabkmalloc_orderkmalloc_order_tracekmalloc_fix_flagskmalloc_large_node__kmalloc__kmalloc_track_caller__kmalloc_node__kmalloc_node_track_caller[...]查找内核模块或者驱动相关函数在available_filter_functions文件的输出中,你可以看到一些以例如内文结尾的行,例如下面的例子[kvm_intel]中。这些函数与当前加载的内核模块kvm_intel有关。你可以运行lsmod命令来验证:$sudogrepkvmavailable_filter_functions|tail__pi_post_block[kvm_intel]vmx_vcpu_pi_load[kvm_intel]vmx_vcpu_pi_put[kvm_intel]pi_pre_block[kvm_intel]pi_post_block[kvm_intel]pi_wakeup_handler[kvm_intel]pi_has_pending_interrupt[kvm_intel]pi_update_irte[kvm_intel]vmx_dump_dtsel[kvm_intel]vmx_dump_sel[kvm_intel]$lsmod|grep-ikvmkvm_intel3358720kvm9871361kvm_intelirqbypass163841kvmonlytracesspecificfunctions为了追踪特定的函数或模式,你可以使用set_ftrace_filter文件来指定你想在上面的输出中追踪哪些函数。该文件还接受*patterns,它可以扩展为包含具有给定模式的其他函数。例如,我在我的机器上使用ext4文件系统。我可以使用以下命令指定要跟踪的ext4特定内核函数:#mount|grephome/dev/mapper/fedora-homeon/hometypeext4(rw,relatime,seclabel)#pwd/sys/kernel/tracing#catset_ftrace_filter####启用所有功能####$$echoext4_*>set_ftrace_filter$$catset_ftrace_filterext4_has_free_clustersext4_validate_block_bitmapext4_get_group_numberext4_get_group_no_and_offsetext4_get_group_desc函数当您只能看到ext4函数的输出时,您现在可以看到带有ext4[…的跟踪,并且您之前已经为其设置了过滤器。忽略所有其他输出:#cattrace|head-20##tracer:function##entries-in-buffer/entries-written:3871/3871#P:8##_------=>irqs-off#/_----=>需要重新安排#|/_---=>硬中断/软中断#||/_--=>抢占深度#|||/delay#TASK-PIDCPU#||||时间戳函数#||||||||cupsd-1066[004]....3308.989545:ext4_file_getattr<-vfs_fstatcupsd-1066[004]....3308.989547:ext4_getattr<-ext4_file_getattrcupsd-1066[004]....3308.989552:extf_stattracing_on#cattracing_on1###运行一些我们希望在此处跟踪的特定命令####echo0>tracing_on#cattracing_on0如果您想跟踪与正在运行的特定进程相关的内容,您可以将PID写入名为set_ftrace_pid的文件并启用跟踪。这样,跟踪仅限于此PID,这在某些情况下非常有用。$sudoecho$PID>set_ftrace_pid总结ftrace是了解Linux内核内部工作原理的好方法。通过一些练习,您可以学习调整ftrace来缩小搜索范围。如需更详细地了解ftrace及其高级用法,请参阅ftrace核心作者StevenRostedt撰写的这些优秀文章。调试Linux内核,第1部分调试Linux内核,第2部分调试Linux内核,第3部分