您的位置:首页 > 运维架构 > Linux

Android笔记-Linux Kernel Ftrace (Function Trace)解析

2011-05-24 00:00 2691 查看

在软体开发时,通常都会面临到系统效能调教的需求,我们希望知道哪些区块的程式码或函式被执行的次数频繁,或是佔据较高的处理器时间,以便藉此优化程式码撰写的行为,或是改善耗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