内核tracepoint的注册回调及添加的方法

一、背景

内核开发时往往需要做一些内核态函数的监测或者内核状态的监测,就需要用一些调试手段来观测。常用的内核态的观测如kprobe和tracepoint,但是kprobe往往受制于一些系统的限制,很多系统并没有打开kprobe选项,这样我们不能通过kprobe来探测。但是tracepoint一般都是打开的,毕竟系统的很多行为默认都是通过ftrace去抓取,ftrace就是基于系统的预埋的tracepoint点去捞系统信息的,所以肯定得打开才行。

关于tracepoint的抓取方法,我们分为改内核方式和不改内核方式:改内核方式,我们可以注册已有的tracepoint点,也可以注册一些新的自定义的tracepoint点,这部分我们会在第二章里展开;不改内核方式,也就是通过编写一个模块来注册,注册的方式稍微有点复杂,但是仍然可以注册现有的一些tracepoint,但是注册不了新的tracepoint,这部分我们会在第三章里展开。

二、修改内核的方式进行tracepoint点的注册和新加tracepoint点

修改内核的方式使用tracepoint的方式分为好几种,我们先介绍使用现有tracepoint点,进行注册回调。

继而介绍如何新增一个tracepoint点,新增一个tracepoint点大致分为两种方式:

1)使用现有的tracepoint的模块比如sched模块,即跟随现有模块进行使能和关闭

2)新增一个tracepoint的模块

tracepoint本身属于一个内核的监测点,我们可以借助这个监测点注册自己的回调来做一些监控的事情。除此以外,内核的大部分tracepoint点,都有默认的ftrace打印。对于我们新增的tracepoint点,我们可以增加打印也可以不增加打印,这在这一章里会介绍。

这一章的最后一节会介绍,如何在内核里基于tracepoint点注册自己的回调函数,模块注册tracepoint回调函数见第三章。

2.1 使用现有的tracepoint模块,新增一个tracepoint点

这一节会先介绍新增一个tracepoint点并附着ftrace打印,意思就是开启所属的tracepoint的模块时,新增的这个tracepoint点的附着的打印也会跟随ftrace的该模块的其他打印一同输出到ftrace里。

然后,再介绍如何不默认附着打印,因为不默认附着打印,对系统的性能损耗是最低的,因为现有的模块有时候会进行调试而打开ftrace,新增的这一条tracepoint也会增加一些性能的损耗,有时候我们已经新增的tracepoint点有了回调处理,其实并不需要再有打印输出,当然,按照需求,我们可以灵活的选择。

2.1.1 现有tracepoint模块新增一个tracepoint点并附着ftrace打印

内核里其实已经有一个现成的新增一个tracepoint的模板供参考,在include/trace/events/sched.h里:

上图中sched_process_template是新增的tracepoint的名字,在代码里要添加的方式如下:

下图就是一个添加例子,比如我们要在kernel/sched/core.c里,在trace_sched_switch的下面加上这个sched_process_template的tracepoint,就可以如下编写

注意,如果要增加tracepoint的.c的文件上面没有引用过tracepoint的相关头文件,就需要学习core.c的添加方法,进行添加相关头文件:

这么添加的话,比如添加到了sched的tracepoint模块里,如果按照如下进行sched的trace的enable的话,新注册的这个tracepoint的日志是会跟随sched其他tracepoint日志一起输出的。

2.1.1.1 一个抓trace的脚本,使能sched的trace,新增到sched的tracepoint附着日志也会跟随输出

下面的是使能trace的方法的例子,下面根据输入的参数作为睡眠的秒数,如果没有输入参数,则睡眠10秒,使能了sched和irq的trace,也是两个常用的trace,其他下面脚本里还有一些注释掉的内容也供参考,经常也会用到

#!/bin/bashif [ -n "$1" ]; thensleeptime=$1
elseecho "no input sleep time"sleeptime=10
fiecho 1 > /sys/kernel/tracing/events/sched/enable
#echo 1 > /sys/kernel/tracing/events/sched/sched_wakeup_new/enable
#echo 1 > /sys/kernel/tracing/events/sched/sched_process_exec/enable
#echo 1 > /sys/kernel/tracing/events/sched/sched_process_fork/enableecho 1 > /sys/kernel/tracing/events/irq/enable
#echo 1 > /sys/kernel/tracing/events/block/enable
#echo 1 > /sys/kernel/tracing/events/writeback/folio_wait_writeback/enableecho 80960 > /sys/kernel/tracing/buffer_size_kb
echo 4096 > /sys/kernel/tracing/buffer_size_kb
echo 1 > /sys/kernel/tracing/options/record-tgid
echo 1280 > /sys/kernel/tracing/saved_cmdlines_sizeecho > /sys/kernel/tracing/traceecho 1 > /sys/kernel/tracing/tracing_onsleep $sleeptime#echo 1 > /sys/kernel/tracing/snapshotecho 0 > /sys/kernel/tracing/tracing_on#cat /sys/kernel/tracing/snapshot > record/snapshot.txtcat /sys/kernel/tracing/trace > trace.txt

2.1.2 现有tracepoint模块新增一个tracepoint点并不附着ftrace打印

不附着打印一般用于新增注册的tracepoint点有自己定义的回调函数,并且考虑到性能不想附着打印影响开trace时的系统效率。

还是用系统已有的sched_process_template作为例子,如下,要用DECLARE_TRACE代替DECLARE_EVENT_CLASS,然后把TP_STRUCT__entry及TP_fast_assign及TP_printk三段删掉即可

2.2 新增一个tracepoint模块,并增加一个tracepoint点

新增一个tracepoint模块,我们可以新增一个include/trace/events/下的一个头文件,头文件如下定义,我们依然还是用现有的sched_process_template来举例:

#undef TRACE_SYSTEM
#define TRACE_SYSTEM xxx#if !defined(_TRACE_XXX_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_XXX_H#include <linux/tracepoint.h>DECLARE_EVENT_CLASS(sched_process_template,TP_PROTO(struct task_struct *p),TP_ARGS(p),TP_STRUCT__entry(__array(	char,	comm,	TASK_COMM_LEN	)__field(	pid_t,	pid			)__field(	int,	prio			)),TP_fast_assign(memcpy(__entry->comm, p->comm, TASK_COMM_LEN);__entry->pid		= p->pid;__entry->prio		= p->prio; /* XXX SCHED_DEADLINE */),TP_printk("comm=%s pid=%d prio=%d",__entry->comm, __entry->pid, __entry->prio)
);#endif /* _TRACE_XXX_H *//* This part must be outside protection */
#include <trace/define_trace.h>

注意,如果新增的tracepoint点在头文件里的函数里用到,则头文件里也得加上相关的定义的头文件,比如新增了一个模块的trace头文件xx.h,在某个.h里函数里有用到,则也在这个.h函数里加上:

#include <trace/events/xx.h>

2.3 注册tracepoint点的自定义回调函数(非内核模块)

分为注册已有的tracepoint点的自定义回调函数和注册新增的tracepoint点的自定义回调函数

其实这两种在注册tracepoint点的自定义回调函数这方便是一样的:

还是以sched_process_template的注册自定义回调为例子

先定义自己的回调函数(注意,tracepoint回调函数的得新增一个void*变量作为第一个参数):

增加这个void*变量通俗的理解就是C里仿C++的this指针的意思

static void cb_sched_process_template(void *i_data, struct task_struct *i_next)
{// to do
}

注册这个自定义回调(注意,register_trace_sched_process_template函数是默认跟着tracepoint函数的定义而自动生成的函数):

register_trace_sched_process_template(cb_sched_process_template, NULL);

不用时进行回调的unregister:

unregister_trace_sched_process_template(cb_sched_process_template, NULL);
tracepoint_synchronize_unregister();

注意上面的tracepoint_synchronize_unregister函数,用于同步等之前的解注册自定义回调函数的逻辑完成,这个在SMP环境下,比如有人还在正在使用它,而不去等这个解注册完成,直接执行后面的清除逻辑,就会发生一种情况,就是在后面的清除逻辑做了一些以后,另外一个核上跑到回调函数里面,又写了一些数据,导致数据上不一致,所以,就需要这个同步等tracepoint都unregister这个动作。

三、使用内核模块进行内核已有tracepoint点的注册

先展示一下,使用第二章里的方法进行模块里的注册遇到的问题:

3.1 模块里不能使用register_trace_xx进行内核里tracepoint点的注册

下面的截图是想在模块里直接使用register_trace_sched_switch进行sched_switch的tracepoint点的注册:

但是编译时遇到错误:

3.2 使用tracepoint_probe_register和tracepoint_probe_unregister进行注册的方式

在模块里不能直接使用register_trace_xx和unregister_trace_xx这个系列的函数,但是可以使用tracepoint_probe_register和tracepoint_probe_unregister这两个函数去根据函数地址直接去注册tracepoint,那么,函数地址怎么来,可以通过for_each_kernel_tracepoint这个内核export的symbol去遍历找指定名字的tracepoint函数,如下查找方式:

上图中红色框出来的函数的定义,这边是通过一个宏来构造,为了可以用一个宏来定义多个tracepoint的查找函数

这么做是因为for_each_kernel_tracepoint这个export的symbol的第一个函数是一个而函数指针:

而传入for_each_kernel_tracepoint函数的第二个参数priv会原封不动的作为入参塞给传入进来的第一个函数指针的函数,所以就有下面的函数名的检查逻辑:

因为tracepoint的结构体里的name也就是tp->name是去掉trace_这个字符串头的后面的部分,比如sched_switch。

3.3 完整的实现代码例子和效果展示

完整的代码如下:

#include <linux/module.h>
#include <linux/capability.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/errno.h>
#include <linux/stddef.h>
#include <linux/lockdep.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/init.h>
#include <asm/atomic.h>
#include <trace/events/workqueue.h>
#include <linux/sched/clock.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/tracepoint.h>
#include <trace/events/sched.h>
#include <trace/events/irq.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhaoxin");
MODULE_DESCRIPTION("module for test tracepoint.");
MODULE_VERSION("1.0");bool blog = false;static void cb_sched_switch(void *i_data, bool i_preempt,struct task_struct *i_prev,struct task_struct *i_next,unsigned int i_prev_state)
{if (!blog) {blog = true;printk("in cb_sched_switch here!\n");}
}struct kern_tracepoint {void *callback;struct tracepoint *ptr;bool bregister;
};static void clear_kern_tracepoint(struct kern_tracepoint *tp)
{if (tp->bregister) {tracepoint_probe_unregister(tp->ptr, tp->callback, NULL);}
}#define INIT_KERN_TRACEPOINT(tracepoint_name) \static struct kern_tracepoint mykern_##tracepoint_name = {.callback = NULL, .ptr = NULL, .bregister = false};#define TRACEPOINT_CHECK_AND_SET(tracepoint_name)                                             \static void tracepoint_name##_tracepoint_check_and_set(struct tracepoint *tp, void *priv) \{                                                                                \if (!strcmp(#tracepoint_name, tp->name))                                     \{                                                                            \((struct kern_tracepoint *)priv)->ptr = tp;                          \return;                                                                  \}                                                                            \}INIT_KERN_TRACEPOINT(sched_switch)
TRACEPOINT_CHECK_AND_SET(sched_switch)static int __init osmon_main_init(void)
{	mykern_sched_switch.callback = cb_sched_switch;for_each_kernel_tracepoint(sched_switch_tracepoint_check_and_set, &mykern_sched_switch);if (!mykern_sched_switch.ptr) {printk("sched_switch register failed!\n");return 0;}else {printk("sched_switch register succeeded!\n");}tracepoint_probe_register(mykern_sched_switch.ptr, mykern_sched_switch.callback, NULL);mykern_sched_switch.bregister = 1;return 0;
}static int __init osmon_sched_module_init(void) {unsigned long flags;unsigned long start_time, end_time;printk(KERN_INFO "osmon_sched_module: init start.\n");osmon_main_init();printk(KERN_INFO "osmon_sched_module: init done.\n");return 0;
}static void __exit osmon_sched_module_exit(void) {clear_kern_tracepoint(&mykern_sched_switch);tracepoint_synchronize_unregister();printk(KERN_INFO "osmon_sched_module: Module unloaded.\n");
}module_init(osmon_sched_module_init);
module_exit(osmon_sched_module_exit);

Makefile:


KERNEL_DIR:=/lib/modules/$(shell uname -r)/build.PHONY: modules modules_cleanmodules:${MAKE} -C $(KERNEL_DIR) M=$(CURDIR) modulesmodules_clean:${MAKE} -C $(KERNEL_DIR) M=$(CURDIR) modules_clean

可以看到是能跑到我注册的tracepoint回调函数的:

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

全网最详细的自动化测试(Jenkins 篇)

学习 Jenkins 自动化测试的系列文章 Robot Framework 概念Robot Framework 安装Pycharm Robot Framework 环境搭建Robot Framework 介绍Jenkins 自动化测试 1. Robot Framework 概念 Robot Framework是一个基于Python的&#xff0c;可扩展的关键字驱动的自动化测试框架。 …

区块链技术在知识产权保护中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 区块链技术在知识产权保护中的应用 区块链技术在知识产权保护中的应用 区块链技术在知识产权保护中的应用 引言 区块链技术概述 …

项目管理中不可或缺的能力

在现代企业中&#xff0c;项目管理是一项至关重要的能力。项目管理需要具备的能力包括&#xff1a;有效的沟通能力、团队协作能力、时间管理能力、风险管理能力、以及问题解决能力。 其中&#xff0c;有效的沟通能力尤为重要&#xff0c;它不仅涉及到信息的传递&#xff0c;还包…

HCIP快速生成树 RSTP

STP&#xff08;Spanning Tree Protocol&#xff0c;生成树协议&#xff09;和RSTP&#xff08;Rapid Spanning Tree Protocol&#xff0c;快速生成树协议&#xff09;都是用于在局域网中消除环路的网络协议。 STP&#xff08;生成树协议&#xff09; 基本概念&#xff1a; ST…

YOLOv11实战PCB电路板缺陷识别

本文采用YOLOv11作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv11以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对PCB电路板缺陷数据集进行训练和优化&#xff0c;该数据集包含丰富的PCB电路板…

把握鸿蒙生态崛起的机遇:开发者视角的探讨

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; 近年来&#xff0c;鸿蒙系统&#xff08;HarmonyOS&#xff09;的发展备受瞩目。随着其在智能手机、智能穿戴、车载系统和智能家居等领域的广泛应用&#xff0c;鸿蒙系统正逐渐形成与安卓、iOS并列的三足鼎立…

丹摩征文活动|FLUX.1+ComfyUI高效部署策略与实践

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀ 丹摩征文 1. FLUX.1简介2. 部署流程创建资源登录实例部署ComfyUI部署FLUX.1 3. 使用流程运行FLUX.1 4. 导入工作流5. 实践感想 前言&#xf…

ChanCMS:牛气的开源cms,帮助我们打造个性化内容管理系统的利器,一款功能强大的开源CMS系统

嗨&#xff0c;大家好&#xff0c;我是小华同学&#xff0c;关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法 ChanCMS是一个基于Java的开源内容管理系统&#xff0c;它采用Spring Boot框架进行开发&#xff0c;具有良好的模块化和扩展性。通过ChanCMS&#xff0…

《怪物猎人:世界》风灵月影修改器功能说明以及使用说明

《怪物猎人&#xff1a;世界》风灵月影修改器能调整游戏数据&#xff0c;如按1键获无限生命&#xff0c;2键开启无敌/无视伤害&#xff0c;3键享无限体力&#xff0c;Ctrl1组合键编辑金钱等&#xff0c;助力玩家轻松通关。为提升您的游戏体验! 修改器安装&#xff1a; https:/…

Python从0到100(七十一):Python OpenCV-OpenCV进行红绿灯识别

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

远程桌面报错-用户账户限制(例如,时间限制)会阻止你登录。

Windows远程时报错 远程桌面报错-用户账户限制&#xff08;例如&#xff0c;时间限制&#xff09;会阻止你登录。 原因是被远程的系统用户密码为空&#xff0c;且默认只允许空白密码的本地账户登录。 登录被远程的系统&#xff0c;WinR输入secpol.msc 然后按照 本地策略-安全选…

Vue CLI 脚手架工程化开发

文章目录 一、生命周期二、工程化开发1. 脚手架介绍2. 使用步骤3. 目录文件介绍4. 组件化开发5. 普通组件的注册5.1 局部注册5.2 全局注册 三、页面开发思路 一、生命周期 Vue 的生命周期&#xff1a;一个 Vue 实例从创建到销毁的整个过程&#xff0c;new Vue() 创建实例&…

Oracle OCP认证考试考点详解082系列18

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 86. 第86题&#xff1a; 题目 解析及答案&#xff1a; 关于自连接&#xff0c;以下哪三个陈述是正确的&#xff1f; A. 它可以是外连接。…

【Android开发】新建虚拟机并运行虚拟机和ADB调试

一、新建虚拟机 在软件首页点击如图所示按钮&#xff1a; 点击添加虚拟机 在左侧分类选择手机&#xff0c;之后选择对应机型。一般选的机型分辨率不用太高&#xff0c;最后点击“Next” 选择虚拟机中的Android系统版本 设置虚拟机名称&#xff0c;注意&#xff0c;此处名称不…

文本批量处理不求人:化繁为简全攻略 (系列一)

在日常工作中&#xff0c;我们经常需要对一些文本文档进行批量操作或者对某一个文档进行繁琐操作&#xff0c;虽然windows系统自带的记事本自带批量替换修改内容的功能&#xff0c;但仅这一个功能往往不够&#xff0c;比如以下一些场景&#xff0c;用记事本就很难实现我们的目标…

智能电动机保护器在提升塑料制品厂电机稳定性中的应用

徐悦 安科瑞电气股份有限公司 在塑料制品生产过程中&#xff0c;电动机的高频启动、长时间连续运行和负载波动增加了电动机故障的风险&#xff0c;对电动机保护提出了高要求。本文从技术角度深入分析了 ARD2F 智能电动机保护器的应用原理&#xff0c;研究其在塑料制品厂不同生…

Unity学习笔记(3):场景绘制和叠层设置 Tilemap

文章目录 前言开发环境规则瓦片绘制拐角 动态瓦片总结 前言 这里学一下后面的场景绘制和叠层技巧。 开发环境 Unity 6windows 11vs studio 2022Unity2022.2 最新教程《勇士传说》入门到进阶&#xff5c;4K:https://www.bilibili.com/video/BV1mL411o77x/?spm_id_from333.10…

mysql利用.ibd文件恢复数据

1、停止原mysql数据库服务&#xff0c;备份原来的.ibd文件。&#xff08;如果本身无法启动的mysql则无需此操作&#xff09; 2、在目标MySQL数据库中创建一张新表&#xff0c;表结构与原表结构一致。确保新表的表结构与.ibd文件中的数据结构一致。--此步骤可以导入之前的备份s…

新手 Vue 项目运行

前言&#xff1a;前面讲了我们已经将spingboot项目运行起来了&#xff0c;现在我们只需将后台管理的Vue项目运行起来即可完成整个项目。 在运行vue项目之前&#xff0c;请先运行springboot项目&#xff0c;运行步骤请看&#xff1a;运行Springboot Vue 项目_springbootvue项目…

Python 如何通过 cron 或 schedule 实现爬虫的自动定时运行

Python 如何通过 cron 或 schedule 实现爬虫的自动定时运行 自动定时运行爬虫是很多数据采集项目的基本需求。例如&#xff0c;每天采集一次新闻数据&#xff0c;或每小时更新股票行情数据等。通过 Python 实现定时任务&#xff0c;可以保证数据采集的高效和持续性。本文将带大…