浅析Linux追踪技术之ftrace:Tracepoint

文章目录

    • 概述
    • Tracepoint使用
      • 定义Tracepoint
      • 添加Tracepoint调用
    • Tracepoint数据结构
    • TRACE_EVENT实现
      • DECLARE_TRACE
      • __DECLARE_TRACE
    • trace_xxx函数
    • 相关参考

概述

Tracepoint(跟踪点)是添加到代码流程中的调用点,并且允许开发者注册自定义的回调函数执行。默认情况下,跟踪点是关闭的状态,不会对原代码逻辑造成影响;当跟踪点为开启状态时,每次运行到跟踪点,都会调用开发者注册的回调函数。

Tracepoint使用

开发者使用Tracepoint需要进行两个步骤:

  1. 定义Tracepoint;
  2. 在代码流程中添加对跟踪点的调用。

定义Tracepoint

Linux内核使用TRACE_EVENT宏来定义以及向系统中添加一个Tracepoint,使用方式如下:

TRACE_EVENT(block_rq_complete,TP_PROTO(struct request *rq, int error, unsigned int nr_bytes),TP_ARGS(rq, error, nr_bytes),TP_STRUCT__entry(__field(  dev_t,	dev			)__field(  sector_t,	sector			)__field(  unsigned int,	nr_sector		)__field(  int,		error			)__array(  char,		rwbs,	RWBS_LEN	)__dynamic_array( char,	cmd,	1		)),TP_fast_assign(__entry->dev	   = rq->rq_disk ? disk_devt(rq->rq_disk) : 0;__entry->sector    = blk_rq_pos(rq);__entry->nr_sector = nr_bytes >> 9;__entry->error     = error;blk_fill_rwbs(__entry->rwbs, rq->cmd_flags, nr_bytes);__get_str(cmd)[0] = '\0';),TP_printk("%d,%d %s (%s) %llu + %u [%d]",MAJOR(__entry->dev), MINOR(__entry->dev),__entry->rwbs, __get_str(cmd),(unsigned long long)__entry->sector,__entry->nr_sector, __entry->error)
);

其中:

  • block_rq_complete为跟踪点名称;
  • TP_PROTO部分定义了跟踪点回调函数原型;
  • TP_ARGS部分定义了回调函数的参数;
  • TP_STRUCT__entry部分定义了跟踪程序可以使用的数据结构,
  • TP_fast_assign部分描述了传递数据的方式;
  • TP_printk部分定义了打印数据结构的方法。

添加Tracepoint调用

bool blk_update_request(struct request *req, blk_status_t error,unsigned int nr_bytes)
{int total_bytes;trace_block_rq_complete(req, blk_status_to_errno(error), nr_bytes);if (!req->bio)return false;...

Tracepoint数据结构

Tracepoing机制的核心数据为tracepoint和tracepoint_func结构,tracepoint结构对应于一个跟踪点的概念,包含了跟踪点的名称、开关配置以及自定义的回调函数;而tracepoint_func结构描述了开发者注册的回调函数信息。
在这里插入图片描述

TRACE_EVENT实现

TRACE_EVENT宏定义如下:

#define TRACE_EVENT(name, proto, args, struct, assign, print)	\DECLARE_TRACE(name, PARAMS(proto), PARAMS(args))
  • name:要创建的跟踪点的名称。
  • prototype:跟踪点回调的原型
  • args:与原型匹配的参数。
  • struct:跟踪程序可以使用(但不需要)存储传递到跟踪点的数据的结构。
  • assign:已类似于 C-like 的方式 将数据分配给结构。
  • print:以可读的ASCII格式输出结构的方法。

DECLARE_TRACE

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

__DECLARE_TRACE

__DECLARE_TRACE宏定义了一系列内联函数(其中xxx为定义的tracepoint名称):

  • trace_xxx:代码中调用trace_xxx函数记录tracepoint运行信息;
  • register_trace_xxx:向tracepoint注册回调函数,在tracepoint使能时会调用;
  • register_trace_prio_xxx:与register_trace_xxx相似,支持调用优先级;
  • unregister_trace_xxx:向tracepoint去注册回调函数;
  • trace_xxx_enabled:tracepoint使能接口
#define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args) \extern struct tracepoint __tracepoint_##name;			\static inline void trace_##name(proto)		// 定义trace_xxx函数,以上文示例即为trace_block_rq_complete		\{								\if (static_key_false(&__tracepoint_##name.key))		\__DO_TRACE(&__tracepoint_##name,		\TP_PROTO(data_proto),			\TP_ARGS(data_args),			\TP_CONDITION(cond), 0);			\if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) {		\WARN_ON_ONCE(!rcu_is_watching());		\}							\}								\__DECLARE_TRACE_RCU(name, PARAMS(proto), PARAMS(args),		\PARAMS(cond), PARAMS(data_proto), PARAMS(data_args))	\static inline int						\register_trace_##name(void (*probe)(data_proto), void *data)   \   // 定义register_trace_xxx函数,以上文示例即为register_trace_rq_complete{								\return tracepoint_probe_register(&__tracepoint_##name,	\(void *)probe, data);	\}								\static inline int						\register_trace_prio_##name(void (*probe)(data_proto), void *data,\     // 定义register_trace_prio_xxx函数,以上文示例即为register_trace_prio_block_rq_completeint prio)				\{								\return tracepoint_probe_register_prio(&__tracepoint_##name, \(void *)probe, data, prio); \}								\static inline int						\unregister_trace_##name(void (*probe)(data_proto), void *data)     \   //  定义unregister_trace_xxx函数,以上文示例即为unregister_trace_prio_block_rq_complete{								\return tracepoint_probe_unregister(&__tracepoint_##name,\(void *)probe, data);	\}								\static inline void						\check_trace_callback_type_##name(void (*cb)(data_proto))   \   // 定义check_trace_callback_type_xxx函数,以上文示例即为check_trace_callback_type_block_rq_complete{								\}								\static inline bool						\trace_##name##_enabled(void)				\  // 定义trace_xxx_enabled函数,以上文示例即为trace_block_rq_complete_enabled	{								\return static_key_false(&__tracepoint_##name.key);	\}

trace_xxx函数

trace_xxx函数内部主要是调用__DO_TRACE宏来完成实际的跟踪点信息处理,__DO_TRACE的核心流程是遍历tracepoint结构体里面的一个函数数组,执行对应的回调函数,这些回调函数由register_trace_xxx接口进行注册。

#define __DO_TRACE(tp, proto, args, cond, rcuidle)			\do {								\struct tracepoint_func *it_func_ptr;			\void *it_func;						\void *__data;						\int __maybe_unused __idx = 0;				\\if (!(cond))						\return;						\\/* srcu can't be used from NMI */			\WARN_ON_ONCE(rcuidle && in_nmi());			\\/* keep srcu and sched-rcu usage consistent */		\preempt_disable_notrace();				\\/*							\* For rcuidle callers, use srcu since sched-rcu	\* doesn't work from the idle path.			\*/							\if (rcuidle) {						\__idx = srcu_read_lock_notrace(&tracepoint_srcu);\rcu_irq_enter_irqson();				\}							\\it_func_ptr = rcu_dereference_raw((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);		\}							\\if (rcuidle) {						\rcu_irq_exit_irqson();				\srcu_read_unlock_notrace(&tracepoint_srcu, __idx);\}							\\preempt_enable_notrace();				\} while (0)

相关参考

  • Linux tracepoint 简介

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/468931.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TCP高频知识点

本篇文章主要讲述一下在面试过程中TCP的高频知识点 1.TCP三次握手流程图: 客户端发送一个SYN(同步)报文段给服务器,选择一个初始序列号,并设置SYN标志位为1。服务器接收到客户端的SYN报文段后,回复一个ACK&#xff08…

webgis后端安卓系统部署攻略,超详细Termux攻略

目录 前言 一、将后端项目编译ARM64 二、安卓手机安装termux 1.更换为国内源 2.安装ssh远程访问 3.安装文件远程访问 三、安装postgis数据库 1.安装数据库 2.数据库配置 3.数据导入 四、后端项目部署 五、自启动设置 总结 前言 因为之前一直做的H5APP开发&#xf…

如何利用SpringSecurity进行认证与授权

一、SpringSecurity简介 Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多&#x…

【排序】归并排序

归并排序 动图演示: 基本思想:分治思想 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子…

深入理解C语言(4):文件操作详解

文章主题:文件操作详解🌏所属专栏:深入理解C语言📔作者简介:更新有关深入理解C语言知识的博主一枚,记录分享自己对C语言的深入解读。😆个人主页:[₽]的个人主页🏄&#x…

Linux中alarm/setitimer函数(信号函数)

alarm函数 函数原型: unsigned int alarm(unsigned int seconds); 函数描述:设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送 14)SIGALRM信号。进程收到该信号,默认动作终止。每个进程…

JAVA-多进程开发-创建等待进程

前言 在项目中,为了实现“并发编程”(同时执行多个任务),就引入了“多进程编程”,把一个很大的任务,拆分成若干个很小的任务,创建多个进程,每个进程分别负责其中的一部分任务。 这也…

【数据结构】计算节点个数和二叉树高度(C语言版)

数据结构——计算节点个数、二叉树高度 一、计算各种节点 (1)计算总节点:(2)计算单分支节点:(3)计算双分支节点: 二、计算二叉树高度 代码实现: 一、计算各种…

NSSCTF Round#18 RE GenshinWishSimulator WP

恶搞原神抽卡模拟器 看到软件的界面,大致有三种思路: 修改石头数量一直抽,如果概率正常肯定能抽到(但是估计设置的概率是0)在源码里找flag的数据把抽卡概率改成100%直接抽出来 Unity逆向,根据经验应该dnsp…

MySQL数据库⑩_视图+MySQL用户管理(增删查改)

目录 1. 视图的概念和规则限制 2. 视图的基本使用 2.1 创建视图 2.2 修改视图影响基表 2.3 修改基表影响视图 2.4 删除视图 3. MySQL用户管理 3.1 用户信息 3.2 创建用户 3.3 修改用户密码 3.4 删除用户 4. 用户权限 4.1 MySQL权限 4.2 给用户授权 4.3 回收权限…

Vue-自定义属性和插槽(五)

目录 自定义指令 基本语法 (全局&局部注册) 指令的值 练习:v-loading 指令封装 总结: 插槽(slot) 默认插槽 插槽 - 后备内容(默认值) 具名插槽 具名插槽基本语法: 具名插槽简化语法: 作…

单片机学习路线(简单介绍)

学习单片机对于电子爱好者和未来的嵌入式系统工程师来说是一段激动人心的旅程。单片机因其强大的功能、灵活性以及在各种智能设备中的广泛应用,成为了电子和计算机科学领域一个不可或缺的组成部分。如果你对如何开始这段旅程感到好奇,那么你来对地方了。…