linux tracepoint系列宏定义(TRACE_EVENT,DEFINE_TRACE等)展开过程分析之三 define_trace.h头文件
在linux tracepoint系列宏定义(TRACE_EVENT,DEFINE_TRACE等)展开过程分析之二 文章中,我们知道trace-events-sample.h 文件在包含了tracepoint.h后第一次对TRACE_EVENT(...)等系列宏定义进行了展开,主要是构建tracepoint 调用钩子函数,注册/注销函数。展开的第二阶段,则是包含了define_trace.h头文件,那么在本阶段这些宏定义又会进行怎样的展开呢?
1、包含define_trace.h头文件
在trace-events-sample.h文件的结尾使用#include <trace/define_trace.h> 包含了define_trace.h有文件,意味着在
预编译阶段该头文件会在此位置进行展开。
/*
* TRACE_INCLUDE_FILE is not needed if the filename and TRACE_SYSTEM are equal
*/
#define TRACE_INCLUDE_FILE trace-events-sample
/*
包含define_trace.h头文件,里面对TRACE_EVENT,TRACE_EVENT_CONDITION等宏进行重定义
那么包含trace-events-sample.hwn头文件的文件中后续使用的TRACE_EVENT,TRACE_EVENT_CONDITION等宏定义则来自define_trace.h文件
*/
#include <trace/define_trace.h>
而在define_trace.h中会去包含包含trace-events-sample.h文件。因为define_trace.h中对TRACE_EVENT(...)等系列宏定义进行了#unset 后又#define 重新定义,所以包含trace-events-sample.h后,其中的TRACE_EVENT系列宏定义会被重新展开,本阶段的展开主要TRACE_EVENT调用DEFINE_TRACE定义并初始化struct tracepoint __tracepoint_##name变量。
/* #include "./trace-events-sample.h" */
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
2、define_trace.h 头文件注释:
注意:define_trace.h头文件在每个定义tracepoint的.h头文件中只有第一次的包含是有效的,因为 #undef CREATE_TRACE_POINTS会使define_trace.h再出进入时无效,除非明确定义CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS才有效。这个很重要,否则会给后面的理解造成困惑。
下面张贴出对define_trace.h头文件的详细注释:
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Trace files that want to automate creation of all tracepoints defined
* in their file should include this file. The following are macros that the
* trace file may define:
*
* TRACE_SYSTEM defines the system the tracepoint is for
*
* TRACE_INCLUDE_FILE if the file name is something other than TRACE_SYSTEM.h
* This macro may be defined to tell define_trace.h what file to include.
* Note, leave off the ".h".
*
* TRACE_INCLUDE_PATH if the path is something other than core kernel include/trace
* then this macro can define the path to use. Note, the path is relative to
* define_trace.h, not the file including it. Full path names for out of tree
* modules must be used.
*/
/*在子系统tracepoint所在的.c文件中定义(trace-events-sample.c)*/
#ifdef CREATE_TRACE_POINTS
/* Prevent recursion */
/*意味着define_trace.h文件只能进入一次,因为这里使用#undefa取消其定义,后面就无法再次进入*/
#undef CREATE_TRACE_POINTS
#include <linux/stringify.h>
/*在该阶段,TRACE_EVENT系列宏定义重定义为DEFINE_TRACE系列宏,用于定义并初始化struct tracepoint __tracepoint_##name变量*/
#undef TRACE_EVENT /*取消之前的TRACE_EVENT定义*/
#define TRACE_EVENT(name, proto, args, tstruct, assign, print) \
DEFINE_TRACE(name) /*DEFINE_TRACE在tracepoint.h中定义,内部调用DEFINE_TRACE_FN*/
#undef TRACE_EVENT_CONDITION
#define TRACE_EVENT_CONDITION(name, proto, args, cond, tstruct, assign, print) \
TRACE_EVENT(name, \
PARAMS(proto), \
PARAMS(args), \
PARAMS(tstruct), \
PARAMS(assign), \
PARAMS(print))
#undef TRACE_EVENT_FN
#define TRACE_EVENT_FN(name, proto, args, tstruct, \
assign, print, reg, unreg) \
DEFINE_TRACE_FN(name, reg, unreg)
#undef TRACE_EVENT_FN_COND
#define TRACE_EVENT_FN_COND(name, proto, args, cond, tstruct, \
assign, print, reg, unreg) \
DEFINE_TRACE_FN(name, reg, unreg)
#undef DEFINE_EVENT
#define DEFINE_EVENT(template, name, proto, args) \
DEFINE_TRACE(name)
#undef DEFINE_EVENT_FN
#define DEFINE_EVENT_FN(template, name, proto, args, reg, unreg) \
DEFINE_TRACE_FN(name, reg, unreg)
#undef DEFINE_EVENT_PRINT
#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \
DEFINE_TRACE(name)
#undef DEFINE_EVENT_CONDITION
#define DEFINE_EVENT_CONDITION(template, name, proto, args, cond) \
DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))
#undef DECLARE_TRACE /*在该阶段DECLARE_TRACE重定义为DEFINE_TRACE*/
#define DECLARE_TRACE(name, proto, args) \
DEFINE_TRACE(name)
#undef TRACE_INCLUDE
#undef __TRACE_INCLUDE
/*在定义TRACE_EVNET所在的.h头文件中已定义TRACE_INCLUDE_FILE为trace-events-sample(trace-events-sample.h)*/
#ifndef TRACE_INCLUDE_FILE
/*如果没有定义TRACE_INCLUDE_FILE,就设置其值为TRACE_SYSTEM*/
# define TRACE_INCLUDE_FILE TRACE_SYSTEM
# define UNDEF_TRACE_INCLUDE_FILE
#endif
/*在定义TRACE_EVNET所在的.h头文件中已定义TRACE_INCLUDE_PATH为 . (trace-events-sample.h)*/
#ifndef TRACE_INCLUDE_PATH
# define __TRACE_INCLUDE(system) <trace/events/system.h>
# define UNDEF_TRACE_INCLUDE_PATH
#else
/*./trace-events-sample.h*/
# define __TRACE_INCLUDE(system) __stringify(TRACE_INCLUDE_PATH/system.h)
#endif
/*TRACE_INCLUDE宏用于包含 trace-events-sample.h文件*/
# define TRACE_INCLUDE(system) __TRACE_INCLUDE(system)
/* Let the trace headers be reread */
#define TRACE_HEADER_MULTI_READ
/*
由于上面修改了大量关键宏定义,如TRACE_EVENT由之前tracepoint.h中的调用DECLARE_TRACE来声明实现tracepoint的注册/注销函数,钩子函数调用接口的功能
修改为在该阶段TRACE_EVENT调用DEFINE_TRACE定义并初始化struct tracepoint __tracepoint_##name变量。
所以需要再次包含头文件(trace-events-sample.h)对其中的宏定义重性进行展开,当然定义的作用也不相同。
#include "./trace-events-sample.h"
*/
/* #include "./trace-events-sample.h" */
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
/* Make all open coded DECLARE_TRACE nops */
/*
取消DECLARE_TRACE的定义,定义为空,因为在前面两个阶段已经完成了声明实现tracepoint的注册/注销函数,钩子函数调用接口
以及义并初始化struct tracepoint __tracepoint_##name变量。
因为在tracepoint.h中会判断DECLARE_TRACE是否定义,如果未定义则会重新定义,为了避免再次定义,所以这里设置为空。
*/
#undef DECLARE_TRACE
/*注意,DECLARE_TRACE设置为空*/
#define DECLARE_TRACE(name, proto, args) /*空*/
#ifdef TRACEPOINTS_ENABLED
/*包含trace/trace_events.h头文件,在trace_events.h头文件中会多次包含trace-events-sample.h,并对其进行多次展开*/
#include <trace/trace_events.h>
/*包含trace/perf.h头文件*/
#include <trace/perf.h>
#endif
/*取消下面的宏定义*/
#undef TRACE_EVENT
#undef TRACE_EVENT_FN
#undef TRACE_EVENT_FN_COND
#undef TRACE_EVENT_CONDITION
#undef DECLARE_EVENT_CLASS
#undef DEFINE_EVENT
#undef DEFINE_EVENT_FN
#undef DEFINE_EVENT_PRINT
#undef DEFINE_EVENT_CONDITION
#undef TRACE_HEADER_MULTI_READ
#undef DECLARE_TRACE
/* Only undef what we defined in this file */
#ifdef UNDEF_TRACE_INCLUDE_FILE
# undef TRACE_INCLUDE_FILE
# undef UNDEF_TRACE_INCLUDE_FILE
#endif
#ifdef UNDEF_TRACE_INCLUDE_PATH
# undef TRACE_INCLUDE_PATH
# undef UNDEF_TRACE_INCLUDE_PATH
#endif
/* We may be processing more files */
#define CREATE_TRACE_POINTS
#endif /* CREATE_TRACE_POINTS */
3、define_trace.h文件中包含trace-events-sample.h后,TRACE_EVENT的展开
以trace-events-sample.h中的TRACE_EVENT为例,说明其在本阶段的展开
TRACE_EVENT(foo_bar,
/*trace_foo_bar函数原型*/
TP_PROTO(const char *foo, int bar, const int *lst,
const char *string, const struct cpumask *mask),
/*trace_foo_bar参数列表*/
TP_ARGS(foo, bar, lst, string, mask),
TP_STRUCT__entry(
__array( char, foo, 10 ) /*char foo[10]*/
__field( int, bar ) /*int bar*/
__dynamic_array(int, list, __length_of(lst)) /*int list[]*/
__string( str, string ) /*str的值为string的内容*/
__bitmask( cpus, num_possible_cpus() )
),
/*如何给上述变量赋值*/
TP_fast_assign(
strlcpy(__entry->foo, foo, 10);
__entry->bar = bar;
/*__get_dynamic_array获取list的起始地址*/
memcpy(__get_dynamic_array(list), lst,
__length_of(lst) * sizeof(int));
__assign_str(str, string);
__assign_bitmask(cpus, cpumask_bits(mask), num_possible_cpus());
),
TP_printk("foo %s %d %s %s %s %s (%s)", __entry->foo, __entry->bar,
__print_flags(__entry->bar, "|",
{ 1, "BIT1" },
{ 2, "BIT2" },
{ 4, "BIT3" },
{ 8, "BIT4" }
),
__print_array(__get_dynamic_array(list),
__get_dynamic_array_len(list) / sizeof(int),
sizeof(int)),
__get_str(str), __get_bitmask(cpus))
);
展开后的内容如下:
TRACE_EVENTfoo_bar,宏开始展开,本阶段TRACE_EVENT主要用于 struct tracepoint __tracepoint_##name 结构体定义
static const char __tpstrtab_foo_bar[] __attribute__((section("__tracepoints_strings"))) = "foo_bar";
struct tracepoint __tracepoint_foo_bar __attribute__((section("__tracepoints"))) = { __tpstrtab_foo_bar, { .enabled = { 0 }, { .entries = (void *)0UL } }, ((void *)0), ((void *)0), ((void *)0) };
static struct tracepoint * const __tracepoint_ptr_foo_bar __attribute__((__used__)) __attribute__((section("__tracepoints_ptrs"))) = &__tracepoint_foo_bar;;
4、在define_trace.h中包含trace_events.h头文件
如下:
/*包含trace/trace_events.h头文件,在trace_events.h头文件中会多次包含trace-events-sample.h,并对其进行多次展开*/
#include <trace/trace_events.h>
在trace_events.h头文件中,会再次使用老套路#unset和#define来重定义TRACE_EVENT(...)等一系列宏定义后再包含trace-events-sample.h,这样trace-events-sample.h中的宏定义又被再次展开为不同的内容。其实在trace_events.h中会使用#include来包含trace-events-sample.h 七次且每次的展开对应的目的都是不一样的。所以对trace_events.h的分析将是一个体力活儿和脑力活儿。