Linux tracepoint 简介

news/2024/11/25 15:45:00/

文章目录

  • 前言
  • 一、跟踪点的目的
  • 二、跟踪点的使用
  • 三、DECLARE_TRACE
  • 四、sched_switch例程
  • 五、TRACE_EVENT
  • 六、跟踪点的开销
  • 参考资料

前言

跟踪点是放置在内核代码中较重要位置的硬编码检测点。例如,在系统调用、调度程序事件、文件系统操作和磁盘I/O的开始和结束处都有跟踪点。跟踪点基础设施由Mathieu Desnoyers开发,并于2009年在Linux 2.6.32版本中首次提供。跟踪点是一个稳定的API,数量有限。

跟踪点是性能分析的重要资源,因为它们为高级跟踪工具提供了强大的支持,超越了摘要统计,提供了对内核行为的深入了解。虽然基于功能的跟踪可以提供类似的功能(例如,kprobes),但只有跟踪点提供稳定的接口,允许开发健壮的工具。

一、跟踪点的目的

tracepoint是内核预先定义的静态探测点,是一种静态插桩方法,可以用于挂载钩子(hook)函数来做trace,放置在代码中的跟踪点提供了一个钩子(hook)来调用您可以在运行时提供的函数(probe,可以是多个probe,probe函数原型要与跟踪点函数原型一致)。跟踪点可以是“on”(一个 probe连接到跟踪点)或“off”(没有 probe 附加到跟踪点上)。当跟踪点“关闭”时,除了添加一个微小的时间惩罚(检查分支的条件)和空间惩罚(在插入指令的函数末尾为函数调用添加几个字节,并在单独的部分中添加一个数据结构)之外,它没有任何其它影响。当跟踪点为“on”时,每次在调用方的执行上下文中执行跟踪点时都会调用您提供的函数。当提供的函数结束其执行时,它返回到调用者(从 tracepoint site 继续)。

可以在代码中的重要位置放置跟踪点。它们是轻量级钩子,可以传递任意数量的参数,原型在放置在头文件中的跟踪点声明中描述。

当系统执行到 tracepoint 点时,会执行 tracepoint 上的我们注册的 probe 函数(可以注册多个 probe 函数),类似于printk,输出当前 tracepoint 上下文的环境信息,只是不是输出到终端,而是 ring buffer,这样我们在用户层通过debugfs(tracefs)接口就可以读取 ring buffer 中的信息,了解到内核中当前运行的状态。

tracepoints可以用于跟踪和性能计数。跟踪点表示内核中使用的一些重要事件,构建系统内正在发生的事情的“全貌”。跟踪点是内核源代码中的一个标记,当启用它时,可以用来在标记所在的点处挂钩到正在运行的内核。它们可由许多工具用于内核调试和性能问题诊断。

二、跟踪点的使用

跟踪点需要两个元素:

头文件中存放跟踪点语句(为了使用跟踪点,应该包括linux/tracepoint.h。)。
In include/trace/events/subsys.h:

#undef TRACE_SYSTEM
#define TRACE_SYSTEM subsys#if !defined(_TRACE_SUBSYS_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_SUBSYS_H#include <linux/tracepoint.h>//声明跟踪点:subsys_eventname
DECLARE_TRACE(subsys_eventname,TP_PROTO(int firstarg, struct task_struct *p),TP_ARGS(firstarg, p));#endif /* _TRACE_SUBSYS_H *//* This part must be outside protection */
#include <trace/define_trace.h>

c文件中跟踪点的声明。
In subsys/file.c (其中必须添加跟踪语句):

#include <trace/events/subsys.h>#define CREATE_TRACE_POINTS
//定义跟踪点:subsys_eventname
DEFINE_TRACE(subsys_eventname);void somefct(void)
{...trace_subsys_eventname(arg, task);...
}

(1)
subsys_eventname是事件的唯一标识符:
subsys是子系统的名称。
eventname是要跟踪的事件的名称。
会在 tracing/event/目录下产生: tracing/event/subsys/subsys_eventname/ 这样一个跟踪点目录。

(2)
TP_PROTO(int firstarg,struct task_struct*p)是此跟踪点调用的函数的原型。
TP_ARGS(firstarg,p)是与原型中相同的参数名称。

(3)
如果在多个源文件中使用 the header ,#define CREATE_TRACE_POINTS应该只出现在一个源文件中。

通过register_trace_subsys_eventname()为特定跟踪点提供 probe(要调用的函数),可以将函数(probe)连接到跟踪点。通过unregister_trace_subsys_eventname()删除probe;它将移除probe。必须在模块退出函数结束之前调用tracepoint_synchronize_unregister(),以确保没有使用 probe 的调用者。这一点,以及在探测调用周围禁用抢占的事实,确保探测移除和模块卸载是安全的。

跟踪点机制支持插入同一跟踪点的多个实例,但必须在所有内核上对给定的跟踪点名称进行单个定义,以确保不会发生类型冲突。使用原型对跟踪点进行名称修改,以确保类型正确。编译器在注册站点验证探针类型的正确性。跟踪点可以放在内联函数、内联静态函数、展开循环以及常规函数中。

这里建议使用命名方案“subsys_event”作为限制冲突的约定。跟踪点名称对内核是全局的:无论是在核心内核映像中还是在模块中,它们都被认为是相同的。

如果必须在内核模块中使用跟踪点,则可以使用EXPORT_TRACEPOINT_SYMBOL_GPL()或EXPORT_TRACEPOINT_SYMBOL()导出定义的跟踪点。

#define EXPORT_TRACEPOINT_SYMBOL_GPL(name)				\EXPORT_SYMBOL_GPL(__tracepoint_##name)
#define EXPORT_TRACEPOINT_SYMBOL(name)					\EXPORT_SYMBOL(__tracepoint_##name)

如果您需要为跟踪点参数做一些工作,并且该工作仅用于跟踪点,则可以使用以下语句将该工作封装在 If 语句中:

if (trace_foo_bar_enabled()) {int i;int tot = 0;for (i = 0; i < count; i++)tot += calculate_nuggets();trace_foo_bar(tot);
}

所有trace_tracepoint()调用都定义了一个匹配的trace_tracepoint_enabled()函数,如果启用了跟踪点,则返回true,否则返回false。trace_tracepoint()应该始终位于if(trace_trackpoint_enabled())的块内,以防止启用的跟踪点和看到的检查之间的竞争。

使用trace_tracepoint_enabled()的优点是它使用跟踪点的static_key来允许使用跳转标签实现if语句,并避免条件分支。

如果需要从头文件中调用跟踪点,建议不要直接调用跟踪点或使用trace_tracepoint_enabled()函数调用,因为如果头文件中包含的头文件设置了CREATE_TRACE_POINTS ,则头文件中的跟踪点可能会产生副作用,此外,trace_tracepoint()在内联函数中并没有那么小,如果被其他内联函数使用,可能会导致内核膨胀。取而代之的是,包括tracepoint-defs.h并使用tracepoint_Enable()。

In a C file:

void do_trace_foo_bar_wrapper(args)
{trace_foo_bar(args);
}

In the header file:

DECLARE_TRACEPOINT(foo_bar);static inline void some_inline_function()
{[..]if (tracepoint_enabled(foo_bar))do_trace_foo_bar_wrapper(args);[..]
}

三、DECLARE_TRACE

DECLARE_TRACE宏主要用来声明 struct tracepoint 结构体 ,定义register_trace_##name函数,给 tracepoint 点注册 probe 函数。
(struct tracepoint 结构体 在 DEFINE_TRACE宏中定义)

(1)
DECLARE_TRACE()传递“proto”作为跟踪点原型,传递“void*__data,proto”为回调原型。

#define DECLARE_TRACE(name, proto, args)				\__DECLARE_TRACE(name, PARAMS(proto), PARAMS(args), 1,	\PARAMS(void *__data, proto),		\PARAMS(__data, args))

(2)

/** Make sure the alignment of the structure in the __tracepoints section will* not add unwanted padding between the beginning of the section and the* structure. Force alignment to the same alignment as the section start.*/
#define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args) \//声明 struct tracepoint 结构体extern struct tracepoint __tracepoint_##name;			\static inline void trace_##name(proto)				\{								\//判断tracepoint是否使能if (static_key_false(&__tracepoint_##name.key))		\__DO_TRACE(&__tracepoint_##name,		\TP_PROTO(data_proto),			\TP_ARGS(data_args),			\TP_CONDITION(cond),,);			\}								\__DECLARE_TRACE_RCU(name, PARAMS(proto), PARAMS(args),		\PARAMS(cond), PARAMS(data_proto), PARAMS(data_args))	\//通过register_trace_##name()为跟踪点提供 probestatic inline int						\register_trace_##name(void (*probe)(data_proto), void *data)	\{								\return tracepoint_probe_register(#name, (void *)probe,	\data);			\}								\//通过unregister_trace_##name()删除probe,移除probestatic inline int						\unregister_trace_##name(void (*probe)(data_proto), void *data)	\{								\return tracepoint_probe_unregister(#name, (void *)probe, \data);		\}								\static inline void						\check_trace_callback_type_##name(void (*cb)(data_proto))	\{								\}	
// linux-3.10/kernel/tracepoint.c/*** tracepoint_probe_register -  Connect a probe to a tracepoint* @name: tracepoint name* @probe: probe handler** Returns 0 if ok, error value on error.* The probe address must at least be aligned on the architecture pointer size.*/
int tracepoint_probe_register(const char *name, void *probe, void *data)
{struct tracepoint_func *old;mutex_lock(&tracepoints_mutex);old = tracepoint_add_probe(name, probe, data);if (IS_ERR(old)) {mutex_unlock(&tracepoints_mutex);return PTR_ERR(old);}tracepoint_update_probes();		/* may update entry */mutex_unlock(&tracepoints_mutex);release_probes(old);return 0;
}
EXPORT_SYMBOL_GPL(tracepoint_probe_register);/*** tracepoint_probe_unregister -  Disconnect a probe from a tracepoint* @name: tracepoint name* @probe: probe function pointer** We do not need to call a synchronize_sched to make sure the probes have* finished running before doing a module unload, because the module unload* itself uses stop_machine(), which insures that every preempt disabled section* have finished.*/
int tracepoint_probe_unregister(const char *name, void *probe, void *data)
{struct tracepoint_func *old;mutex_lock(&tracepoints_mutex);old = tracepoint_remove_probe(name, probe, data);if (IS_ERR(old)) {mutex_unlock(&tracepoints_mutex);return PTR_ERR(old);}tracepoint_update_probes();		/* may update entry */mutex_unlock(&tracepoints_mutex);release_probes(old);return 0;
}
EXPORT_SYMBOL_GPL(tracepoint_probe_unregister);
struct tracepoint_func {void *func;void *data;
};struct tracepoint {const char *name;		/* Tracepoint name */struct static_key key;  void (*regfunc)(void);void (*unregfunc)(void);struct tracepoint_func __rcu *funcs;
};

每种Tracepoint有一个name、一个key开关、一系列桩函数。

key(static_key来允许使用跳转标签实现if语句,并避免条件分支):tracepoint是否使能的开关trace_tracepoint_enabled(),如果回调函数数组为空则key为disable,如果回调函数数组中有函数指针则key为enable。

regfunc:注册回调函数时用到的hook函数(注意:不是用来注册回调函数的,注册回调函数是register_trace_##name)。

unregfunc:注销回调函数时的的hook函数(注意:不是用来注销回调函数的,注销回调函数是unregister_trace_##name)。

/** Sets the probe callback corresponding to one tracepoint.*/
static void set_tracepoint(struct tracepoint_entry **entry,struct tracepoint *elem, int active)
{WARN_ON(strcmp((*entry)->name, elem->name) != 0);if (elem->regfunc && !static_key_enabled(&elem->key) && active)elem->regfunc();else if (elem->unregfunc && static_key_enabled(&elem->key) && !active)elem->unregfunc();/** rcu_assign_pointer has a smp_wmb() which makes sure that the new* probe callbacks array is consistent before setting a pointer to it.* This array is referenced by __DO_TRACE from* include/linux/tracepoints.h. A matching smp_read_barrier_depends()* is used.*/rcu_assign_pointer(elem->funcs, (*entry)->funcs);if (active && !static_key_enabled(&elem->key))static_key_slow_inc(&elem->key);else if (!active && static_key_enabled(&elem->key))static_key_slow_dec(&elem->key);
}/** Disable a tracepoint and its probe callback.* Note: only waiting an RCU period after setting elem->call to the empty* function insures that the original callback is not used anymore. This insured* by preempt_disable around the call site.*/
static void disable_tracepoint(struct tracepoint *elem)
{if (elem->unregfunc && static_key_enabled(&elem->key))elem->unregfunc();if (static_key_enabled(&elem->key))static_key_slow_dec(&elem->key);rcu_assign_pointer(elem->funcs, NULL);
}

funcs:回调函数数组,tracepoint的作用就是在桩函数被命中时,依次调用回调函数数组中的 probe 函数。
在这里插入图片描述

(3)

#ifdef CONFIG_TRACEPOINTS/** it_func[0] is never NULL because there is at least one element in the array* when the array itself is non NULL.** Note, the proto and args passed in includes "__data" as the first parameter.* The reason for this is to handle the "void" prototype. If a tracepoint* has a "void" prototype, then it is invalid to declare a function* as "(void *, void)". The DECLARE_TRACE_NOARGS() will pass in just* "void *data", where as the DECLARE_TRACE() will pass in "void *data, proto".*/
#define __DO_TRACE(tp, proto, args, cond, prercu, postrcu)		\do {								\struct tracepoint_func *it_func_ptr;			\void *it_func;						\void *__data;						\\if (!(cond))						\return;						\prercu;							\rcu_read_lock_sched_notrace();				\it_func_ptr = rcu_dereference_sched((tp)->funcs);	\if (it_func_ptr) {					\do {						\it_func = (it_func_ptr)->func;		\__data = (it_func_ptr)->data;		\((void(*)(proto))(it_func))(args);	\} while ((++it_func_ptr)->func);		\}							\rcu_read_unlock_sched_notrace();			\postrcu;						\} while (0)

__DO_TRACE宏是用来依次执行 tracepoint 上注册的 probe 函数。
tracepoint提供了统一的框架,用void *指向任何函数,所以各个tracepoint取出桩函数指针后,需要转换成自己的函数指针类型,TP_PROTO(data_proto)传递函数指针类型用于转换。
如果跟踪点使能,在 while 循环中,依次执行回调函数数组 struct tracepoint_func __rcu *funcs 的 回调函数 func ;即依次执行 tracepoint 上的注册的 probe 函数。

四、sched_switch例程

以 tracepoint :TRACE_EVENT(sched_switch)为例,看一下内核中 sched_switch tracepoint 的 probe函数:

/** Tracepoint for task switches, performed by the scheduler:*/
TRACE_EVENT(sched_switch,TP_PROTO(struct task_struct *prev,struct task_struct *next),TP_ARGS(prev, next),TP_STRUCT__entry(__array(	char,	prev_comm,	TASK_COMM_LEN	)__field(	pid_t,	prev_pid			)__field(	int,	prev_prio			)__field(	long,	prev_state			)__array(	char,	next_comm,	TASK_COMM_LEN	)__field(	pid_t,	next_pid			)__field(	int,	next_prio			)),TP_fast_assign(memcpy(__entry->next_comm, next->comm, TASK_COMM_LEN);__entry->prev_pid	= prev->pid;__entry->prev_prio	= prev->prio;__entry->prev_state	= __trace_sched_switch_state(prev);memcpy(__entry->prev_comm, prev->comm, TASK_COMM_LEN);__entry->next_pid	= next->pid;__entry->next_prio	= next->prio;),TP_printk("prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d",__entry->prev_comm, __entry->prev_pid, __entry->prev_prio,__entry->prev_state & (TASK_STATE_MAX-1) ?__print_flags(__entry->prev_state & (TASK_STATE_MAX-1), "|",{ 1, "S"} , { 2, "D" }, { 4, "T" }, { 8, "t" },{ 16, "Z" }, { 32, "X" }, { 64, "x" },{ 128, "K" }, { 256, "W" }, { 512, "P" }) : "R",__entry->prev_state & TASK_STATE_MAX ? "+" : "",__entry->next_comm, __entry->next_pid, __entry->next_prio)
);

register_trace_sched_switch函数将probe函数注册到 sched_switch tracepoint 上。
probe函数要与sched_switch tracepoint的函数原型一致,即:

	TP_PROTO(struct task_struct *prev,struct task_struct *next),TP_ARGS(prev, next),

(1)
将probe函数 ftrace_graph_probe_sched_switch 与 tracepoint:sched_switch 相关联:

// linux-3.10/kernel/trace/ftrace.c#ifdef CONFIG_FUNCTION_GRAPH_TRACERstatic void
ftrace_graph_probe_sched_switch(void *ignore,struct task_struct *prev, struct task_struct *next)
{unsigned long long timestamp;int index;/** Does the user want to count the time a function was asleep.* If so, do not update the time stamps.*/if (trace_flags & TRACE_ITER_SLEEP_TIME)return;timestamp = trace_clock_local();prev->ftrace_timestamp = timestamp;/* only process tasks that we timestamped */if (!next->ftrace_timestamp)return;/** Update all the counters in next to make up for the* time next was sleeping.*/timestamp -= next->ftrace_timestamp;for (index = next->curr_ret_stack; index >= 0; index--)next->ret_stack[index].calltime += timestamp;
}static int start_graph_tracing(void)
{......register_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL)......
}

(2)
将probe函数 probe_sched_switch与 tracepoint:sched_switch 相关联:

// linux-3.10/kernel/trace/trace_sched_switch.cstatic void
probe_sched_switch(void *ignore, struct task_struct *prev, struct task_struct *next)
{struct trace_array_cpu *data;unsigned long flags;int cpu;int pc;if (unlikely(!sched_ref))return;tracing_record_cmdline(prev);tracing_record_cmdline(next);if (!tracer_enabled || sched_stopped)return;pc = preempt_count();local_irq_save(flags);cpu = raw_smp_processor_id();data = per_cpu_ptr(ctx_trace->trace_buffer.data, cpu);if (likely(!atomic_read(&data->disabled)))tracing_sched_switch_trace(ctx_trace, prev, next, flags, pc);local_irq_restore(flags);
}static int tracing_sched_register(void)
{......ret = register_trace_sched_switch(probe_sched_switch, NULL);......
}

五、TRACE_EVENT

我在这三篇文章中描述了 TRACE_EVENT :
Linux 调试之 TRACE_EVENT(一)
Linux 调试之 TRACE_EVENT(二)
Linux 调试之 TRACE_EVENT(三)

(1)
注意这里 TRACE_EVENT 宏展开是 DECLARE_TRACE 宏, DECLARE_TRACE 宏声明 struct tracepoint _tracepoint##name ,注册 tracepoint 的probe 函数,该宏在前面已经说明。

// linux-3.10/include/linux/tracepoint.h#define TRACE_EVENT(name, proto, args, struct, assign, print)	\DECLARE_TRACE(name, PARAMS(proto), PARAMS(args))

(2)
注意这里 TRACE_EVENT 宏展开是 DEFINE_TRACE 宏,DEFINE_TRACE的关键是定义了struct tracepoint __tracepoint_name变量,即定义tracepoint。

初始化static key数据结构为STATIC_KEY_INIT_FALSE:

// linux-3.10/include/linux/jump_label.h#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL)struct static_key {atomic_t enabled;
/* Set lsb bit to 1 if branch is default true, 0 ot */struct jump_entry *entries;
#ifdef CONFIG_MODULESstruct static_key_mod *next;
#endif
};#define STATIC_KEY_INIT_FALSE ((struct static_key) \{ .enabled = ATOMIC_INIT(0), .entries = (void *)0 })
// linux-3.10/include/linux/tracepoint.h/** We have no guarantee that gcc and the linker won't up-align the tracepoint* structures, so we create an array of pointers that will be used for iteration* on the tracepoints.*/
#define DEFINE_TRACE_FN(name, reg, unreg)				 \static const char __tpstrtab_##name[]				 \__attribute__((section("__tracepoints_strings"))) = #name;	 \//定义struct tracepoint 结构体 和 初始化struct static_key(STATIC_KEY_INIT_FALSE)结构体struct tracepoint __tracepoint_##name				 \__attribute__((section("__tracepoints"))) =			 \{ __tpstrtab_##name, STATIC_KEY_INIT_FALSE, reg, unreg, NULL };\static struct tracepoint * const __tracepoint_ptr_##name __used	 \__attribute__((section("__tracepoints_ptrs"))) =		 \&__tracepoint_##name;#define DEFINE_TRACE(name)						\DEFINE_TRACE_FN(name, NULL, NULL);// linux-3.10/include/trace/define_trace.h#undef TRACE_EVENT
#define TRACE_EVENT(name, proto, args, tstruct, assign, print)	\DEFINE_TRACE(name)

定义 tracepoint 执行时对应的probe函数(可以是多个probe函数,可以在probe函数中定义自己的行为),将DEFINE_TRACE定义的 struct tracepoint _tracepoint##name 变量与probe回调进行绑定,这样在执行到 tracepoint 时会触发probe函数。

[root@localhost ]# readelf -S xfs.ko
There are 48 section headers, starting at offset 0x181bf0:Section Headers:[Nr] Name              Type             Address           OffsetSize              EntSize          Flags  Link  Info  Align......[17] __tracepoints_ptr PROGBITS         0000000000000000  000f7f980000000000000bc0  0000000000000000   A       0     0     8[18] .rela__tracepoint RELA             0000000000000000  000f8b580000000000002340  0000000000000018   I      44    17     8[19] __tracepoints_str PROGBITS         0000000000000000  000faea0......[37] __tracepoints     PROGBITS         0000000000000000  001375200000000000005df8  0000000000000000  WA       0     0     32[38] .rela__tracepoint RELA             0000000000000000  0013d3180000000000002340  0000000000000018   I      44    37     8......
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),l (large), p (processor specific)

DECLARE_TRACE 宏 和 DEFINE_TRACE 宏 不一样。

从 TRACE_EVENT 的宏定义可以看到,TRACE_EVENT使用的是 tracepoint 机制。需要静态的定义桩函数,并且在插桩位置显式的调用。

宏TRACE_EVENT提供了定义跟踪点的另一种方法。trace event可以使用 tracepoint 机制,内核其它机制也可以使用 tracepoint ,只是kernel的绝大部分tracepoint都是trace event在使用,trace event也必须向tracepoint注册自己的回调函数,这些回调函数的作用就是在函数被命中时往ring buffer中写入trace信息。内核提前帮我们实现了钩子(hook)函数并挂到tracepoint上,当使能一个 trace event 时,它会输出内容到ftrace ringbuffer中,这样就可以获取到内核运行信息了。

六、跟踪点的开销

当跟踪点被激活时,它们会为每个事件增加少量的CPU开销。当跟踪点“关闭”时,除了添加一个微小的时间惩罚(检查分支的条件)和空间惩罚(在插入指令的函数末尾为函数调用添加几个字节,并在单独的部分中添加一个数据结构)之外,它没有任何其它影响。
禁用的跟踪点变成了少量指令:对于x86_64,它是一个5字节的无操作(NOP)指令。在函数的末尾还添加了一个跟踪点处理程序,这会稍微增加其文本大小。虽然这些开销非常小,但在向内核添加跟踪点时,应该分析和理解它们。

跟踪点往往不是单独使用,跟踪点只是内核中的数据源,还有内核中的跟踪框架和用户态的前端工具也会有一定的开销:
在这里插入图片描述
数据源:内核中提供数据的来源(也有用户态的数据源,比如:uprobe)
Tracing 框架:负责对接数据源,采集解析发送数据,并对用户态提供接口
前端工具/库: 对接Tracing框架,直接与用户交互,负责采集配置和数据分析

跟踪工具(比如:trace-cmd,perf等等)可以增加 post-process events 的CPU开销,以及记录事件的文件系统(debugfs或者tracfs)开销。开销是否高到足以干扰生产应用程序取决于事件的速率和CPU的数量,这是使用跟踪点时需要考虑的问题。

在当今的典型系统(4到128个CPU)上,我发现低于每秒10000个的事件速率的开销可以忽略不计,只有超过100000个的开销才开始变得可测量。作为事件示例,您可能会发现磁盘事件通常小于每秒10000个,但调度程序事件可能远远超过每秒100000个,因此跟踪成本可能很高。

在一些特定的系统中,发现最小的跟踪点开销是96纳秒的CPU时间。在2018年Linux 4.7中添加了一种名为原始跟踪点的新类型的跟踪点,它避免了创建稳定的跟踪点参数的成本,从而减少了这一开销。

参考资料

Linux 3.10.0

Systems.Performance.Enterprise.and.the.Cloud.2nd.Edition

https://static.lwn.net/kerneldoc/trace/tracepoints.html
https://pwl999.blog.csdn.net/article/details/80514271
https://blog.csdn.net/jasonactions/article/details/123470620
https://blog.csdn.net/Rong_Toa/article/details/116602224
https://zhuanlan.zhihu.com/p/547477490


http://www.ppmy.cn/news/6086.html

相关文章

Three JS 调研

0. 结论 three.js是使用WebGL来绘制三维效果的&#xff0c;核心数据是3D对象和三维模型&#xff0c;更多的是关注如何通过webgl更精细而美的渲染数据 three.js相当于封装了webgl&#xff0c;但还是很底层&#xff0c;并不是一个类似于cesium或者mapbox这样的成熟地图框架&…

jQuery 插件开发

文章目录jQuery 插件开发插件概述常用插件文本溢出&#xff1a;dotdotdot.js单行文本省略多行文本省略延迟加载&#xff1a;lazyload.js插件编写方法类插件函数类插件jQuery 插件开发 插件概述 jQuery插件可以理解成是使用jQuery来封装的一个功能或特效。 一般来说&#xff…

Mac 截图工具 iShot Pro - 软件介绍、下载安装详细教程

Mac 截图工具 iShot Pro -软件介绍、下载安装详细教程 iShot -优秀&#xff0c;功能齐全的区域截图&#xff0c;窗口截图&#xff0c;多窗口截图&#xff0c;长屏幕截图&#xff0c;shell截图&#xff0c;时间间隔截图&#xff0c;快速注释&#xff0c;纹理&#xff0c;颜色匹配…

消息中间件Kafka快速入门

前言 Kafka是基于zookeeper管理的&#xff0c;所以要先安装zookeeper&#xff0c;如果是单机模式&#xff0c;zookeeper安装比较简单&#xff0c;本文就介绍一下单机如何搭建kafka&#xff0c;以及基本的java demo。 环境搭建 Zookeeper 安装 http://mirrors.cnnic.cn/apac…

Python 函数用法和底层分析

【无限嚣张&#xff08;菜菜&#xff09;】&#xff1a;hello您好&#xff0c;我是菜菜&#xff0c;很高兴您能来访我的博客&#xff0c;我是一名爱好编程学习研究的菜菜&#xff0c;每天分享自己的学习&#xff0c;想法&#xff0c;博客来源与自己的学习项目以及编程中遇到问题…

详细设计说明书(GB8567——88)基于协同的在线表格forture-sheet

详细设计说明书 1引言 1.1编写目的 该文档在概要设计的基础上&#xff0c;进一步的细化系统结构&#xff0c;展示了软件结构的图标&#xff0c;物理设计、数据结构设计、及算法设计、详细的介绍了系统各个模块是如何实现的&#xff0c;包括涉及到的算法&#xff0c;逻辑流程…

Redis常见面试题(七)

目录 1、什么是缓存预热? 2、什么是缓存热备? 3、什么是缓存雪崩? 4、如何解决缓存雪崩? 5、什么是缓存穿透? 6、如何解决缓存穿透? 7、什么是缓存击穿? 8、如何解决缓存击穿? 9、什么是缓存抖动? 10、如何解决缓存抖动? 11、什么是缓存无底洞? 12、如何…

Vue基础8之Vue组件化编程、非单文件组件与单文件组件

Vue基础8Vue组件化编程对组件的理解一些概念的理解非单文件组件基本使用几个注意点组件的嵌套VueComponent一个重要的内置关系先导篇&#xff1a;原型对象正文&#xff08;可以理解为类的继承&#xff09;单文件组件Vue组件化编程 对组件的理解 传统方式&#xff1a; 使用组…