Android笔记-Linux Kernel Ftrace (Function Trace)解析
在软体开发时,通常都会面临到系统效能调教的需求,我们希望知道哪些区块的程式码或函式被执行的次数频繁,或是佔据较高的处理器时间,以便藉此优化程式码撰写的行为,或是改善耗CPU时间的算法,以Linux平台来说,OProfile(http://oprofile.sourceforge.net )会是一个大家常推荐的工具,OProfile支持Time-based透过系统Timer中断蒐集当下执行环境资讯,并加以统计,或基于Event-based,以ARM 来说就是Performance Monitor Unit(CP15)硬体支援的Performance控制单元 (更多资讯可以参考:http://infocenter.arm.com/help/topic/com.arm.doc.dai0195b/index.html),ARM PMU提供例如Instruction/Data Cache 使用状况(miss,write-back,write-buffer..etc),Memory/MMU 存取的状况, IRQ/FIQ Latency,Branch预测统计,I/D-TCM Status..etc,基于这机制的Profiling可以在影响系统效能最少的情况下,进行System-wide的性能统计资讯. 另一种选择,则是透过ARM接上JTAG介面,藉由ICE软体的Profiling功能进行分析.
然而,如果我们希望更明确的知道每个Function被执行的次数 (OProfile Time-based的统计时间够长就可得出对应的比例,做为决定的依据),执行的流程,与个别时间成本,或是系统在排程 (Scheduling and Wake-up),中断,Block/Net,或Kernel记忆体配置..等,与Linux Kernel核心物件有关资讯的话,其实Ftrace会是另一个可以辅助的资讯来源,不同于OProfile,Ftrace会透过gcc -pg把每个函式前面都插入呼叫mcount函式的动作,在Branch统计部分,也会把if或是透过likely/unlikely巨集,进行植入是的统计,因此,Ftrace相比OProfile虽然可以提供比较完整的Kernel层级统计资讯,但因为OProfile主要是透过ARM或其他处理器平台的Performance Monitor单元,因此,OProfile可以在影响系统效能较低的情况下进行统计(ㄟ...Time-based Function profiling也是会影响到被测端的效能的.),但总体而言,都比mcount植入每个函式中,对系统效能的影响更算是轻量. 如何决定应用哪个模块进行效能分析,还是要依据当下开发时的目的与所遇到的问题来做决定.
Ftrace最应该参考的文件就是Linux Kernel原始码中位于Documentation/ftrace.txt的文件,参考该文件资讯与Google一下,Ftrace作者为在RedHat服务的 Steven Rostedt,主要目的是为Linux Kernel提供一个系统效能分析的工具,以便用以除错或是改善/优化系统效能,Ftrace为一个以Function Trace为基础的工具,并包含了包括行程Context-Switch,Wake-Up/Ready到执行的时间成本,中断关闭的时间,以及是哪些函式呼叫所触发的,这都有助于在複杂的系统执行下,提供必要资讯以便定位问题.
接下来,我们将介绍GCC对于Ftrace Profiling上,在编译器层级的支援,以及有关的builtin函式,让各位清楚这些机制底层运作的原理,最后,并以Ftrace为主,说明个机制的内容,但本文并不会深入探究到各Ftrace模组机制的实作部分,主要只以笔者自己认为值得说明的区块,来加以说明,对各项细节有兴趣的开发者,建议可以自行探究.
GCC “-pg” Profiling 机制与builtin函式对Ftrace Branch Profiling的支援
Ftrace支援在有加入 “likely/unlikely” 条件判断式位置的Brnch Profiling与对整个核心 if 条件判断式的Brnch Profiling (当然,选择后者对效能影响也比较明显...要做记录的地方变多了.).使用者可以透过Kernel hacking --->Tracers --->Branch Profiling ---> 来选择“No branch profiling”,”Trace likely/unlikely profiler” 或 “Profile all if conditionalss”. 对系统进行Branch Profiling的动作. (Ftrace在 config中有这四个设定跟Branch Profiling有关CONFIG_TRACE_BRANCH_PROFILING,CONFIG_BRANCH_PROFILE_NONE,CONFIG_PROFILE_ANNOTATED_BRANCHES 与 CONFIG_PROFILE_ALL_BRANCHES)
参考include/linux/compiler.h中的实作,如果选择“Profile all if conditionalss”,就会把全部的if条件判断字元,透过gcc precompile定义为巨集 __trace_if,如下所示
#define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
#define __trace_if(cond) \
if (__builtin_constant_p((cond)) ? !!(cond) : \
({ \
int ______r; \
static struct ftrace_branch_data \
__attribute__((__aligned__(4))) \
__attribute__((section("_ftrace_branch"))) \
______f = { \
.func = __func__, \
.file = __FILE__, \
.line = __LINE__, \
}; \
______r = !!(cond); \
______f.miss_hit[______r]++; \
______r; \
}))
如果if 条件式为常数(也就是说编译器可以在编译阶段就决定好if/else路径了),就不纳入统计,反之,就会根据条件式的结果(______r =0 or 1)统计命中的次数,作为if/else条件设计的参考. (其实,透过likely/unlikely优化编译阶段的Branch Predition是很有帮助的).
如果是设定为”Trace likely/unlikely profiler”,就会把 likely与unlikely巨集定义如下所示
/*
* Using __builtin_constant_p(x) to ignore cases where the return
* value is always the same. This idea is taken from a similar patch
* written by Daniel Walker.
*/
# ifndef likely
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
# endif
# ifndef unlikely
# define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0))
# endif
其中__branch_check__定义如下
#define likely_notrace(x) __builtin_expect(!!(x), 1)
#define unlikely_notrace(x) __builtin_expect(!!(x), 0)
#define __branch_check__(x, expect) ({ \
int ______r; \
static struct ftrace_branch_data \
__attribute__((__aligned__(4))) \
__attribute__((section("_ftrace_annotated_branch"))) \
______f = { \
.func = __func__, \
.file = __FILE__, \
.line = __LINE__, \
}; \
______r = likely_notrace(x); \
ftrace_likely_update(&______f, ______r, expect); \
______r; \
})
函式ftrace_likely_update (位置在kernel/trace/trace_branch.c)实作如下所示,
void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect)
{
/*
* I would love to have a trace point here instead, but the
* trace point code is so inundated with unlikely and likely
* conditions that the recursive nightmare that exists is too
* much to try to get working. At least for now.
*/
trace_likely_condition(f, val, expect);
/* FIXME: Make this atomic! */
if (val == expect)
f->correct++;
else
f->incorrect++;
}
- Android笔记-Linux Kernel Ftrace (Function Trace)解析
- Android筆記-Linux Kernel Ftrace (Function Trace)解析(非常强悍的性能分析方法)
- Android筆記-Linux Kernel Ftrace (Function Trace)解析(非常强悍的性能分析方法)
- Android筆記-Linux Kernel Ftrace (Function Trace)解析
- Android筆記-Linux Kernel Ftrace (Function Trace)解析(非常强悍的性能分析方法)
- Android筆記-Linux Kernel Ftrace (Function Trace)解析(非常强悍的性能分析方法)
- Android筆記-Linux Kernel Ftrace (Function Trace)解析
- Android/Linux Kernel 内存管理-入门笔记
- Linux 学习笔记1 --- kernel初始化以及module_init(x)解析
- Android/Linux Kernel 内存管理-入门笔记
- Android/Linux Kernel 内存管理-入门笔记
- MTD系列 - android平台上linux启动时init进程解析init.rc文件分析
- Linux Kernel Development 笔记(二)进程
- "android linux kernel" VS "standard linux kernel"
- Android arm linux kernel启动流程
- MTD系列 - android平台上linux启动时init进程解析init.rc文件分析
- Android 个人学习笔记之---SAX解析XML文件(有一个坑爹的问题)
- Android arm linux kernel启动流程(二)
- Android 筆記-Linux Kernel SMP (Symmetric Multi-Processors) 開機流程解析 Part(4) Linux 多核心啟動流程-kthreadd 與相關的核
- 在Android应用中使用Pull解析XML文件(传智播客视频笔记)