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

news2024/12/25 12:25:26

一、背景

内核开发时往往需要做一些内核态函数的监测或者内核状态的监测,就需要用一些调试手段来观测。常用的内核态的观测如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/bash

if [ -n "$1" ]; then
    sleeptime=$1
else
    echo "no input sleep time"
    sleeptime=10
fi

echo 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/enable

echo 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/enable

echo 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_size

echo > /sys/kernel/tracing/trace

echo 1 > /sys/kernel/tracing/tracing_on

sleep $sleeptime

#echo 1 > /sys/kernel/tracing/snapshot

echo 0 > /sys/kernel/tracing/tracing_on

#cat /sys/kernel/tracing/snapshot > record/snapshot.txt

cat /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_clean

modules:
	${MAKE} -C $(KERNEL_DIR) M=$(CURDIR) modules

modules_clean:
	${MAKE} -C $(KERNEL_DIR) M=$(CURDIR) modules_clean

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

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

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

相关文章

React融合css

单纯使用tsx文件生成的页面比较单一&#xff0c;可以考虑结合css进行使用&#xff0c;需要说明的是&#xff0c;本人水平有限&#xff0c;仅对接触过的几种方式进行说明 内联样式 内联样式也有多种写法&#xff0c;此处仅列举两种比较简单的写法 写法一 import React from …

【大数据学习 | kafka高级部分】kafka中的选举机制

controller的选举 首先第一个选举就是借助于zookeeper的controller的选举 第一个就是controller的选举&#xff0c;这个选举是借助于zookeeper的独享锁实现的&#xff0c;先启动的broker会在zookeeper的/contoller节点上面增加一个broker信息&#xff0c;谁创建成功了谁就是主…

【基于PSINS工具箱】以速度为观测量的SINS/GNSS组合导航,UKF滤波

基于【PSINS工具箱】&#xff0c;提供一个MATLAB例程&#xff0c;仅以速度为观测量的SINS/GNSS组合导航&#xff08;滤波方式为UKF&#xff09; 文章目录 工具箱程序简述运行结果 代码程序讲解MATLAB 代码教程&#xff1a;使用UKF进行速度观测1. 引言与基本设置2. 初始设置3. U…

ThinkBook 14+ 2024 Ubuntu 触控板失效 驱动缺失问题解决

首先我的电脑是thinkbook14 2024&#xff0c;从ubuntu18到ubuntu24&#xff0c;笔者整个都试了一遍&#xff0c;触摸板都没反应&#xff0c;确认不是linux系统内核问题&#xff0c;原因为驱动缺失。 解决步骤&#xff1a; &#xff08;1&#xff09;下载驱动&#xff0c;网址如…

如何使用 Web Scraper API 高效采集 Facebook 用户帖子信息

目录 前言一、什么是Web Scraper API二、Web Scraper API 的优势&#xff1a;三、Web Scraper API 适用场景四、实践案例目标需求视频讲解1、选择Web Scraper API2、登录注册3、进入用户控制面板4、选择API5、触发数据收集 API6、获取爬虫结果7、分析爬虫结果&#xff08;1&…

Qt_day3_信号槽

目录 信号槽 1. 概念 2. 函数原型 3. 连接方式 3.1 自带信号 → 自带槽 3.2 自带信号 → 自定义槽 3.3 自定义信号 4. 信号槽传参 5. 对应关系 5.1 一对多 5.2 多对一 信号槽 1. 概念 之前的程序界面只能看&#xff0c;不能交互&#xff0c;信号槽可以让界面进行人机…

Elastic 通用分析:提高性能并降低成本

作者&#xff1a;来自 Elastic Luca Wintergerst•Tim Rhsen 在这篇博客中&#xff0c;我们将介绍我们的一位工程师的一项发现如何帮助我们在 QA 环境中节省数千美元的成本&#xff0c;并且一旦我们将这一变化部署到生产中&#xff0c;还可以节省更多的成本。 在当今的云服务和…

【WRF理论第十一期】检查WPS输出:geogrid和metgrid 的输出nc数据+ungrib输出WPS格式

【WRF理论第十一期】检查WPS输出&#xff1a;geogrid和metgrid输出nc数据ungrib输出WPS格式 检查WPS输出WPS 输出检查的重要性使用 NetCDF 格式查看 geogrid 和 metgrid 的输出检查和可视化数据的工具 ungrib 输出数据的格式使用 plotfmt 工具查看 ungrib 输出 参考 上一篇博客…

万字长文解读深度学习——卷积神经网络CNN

推荐阅读&#xff1a; 卷积神经网络&#xff08;CNN&#xff09;详细介绍及其原理详解 CNN笔记&#xff1a;通俗理解卷积神经网络 文章目录 &#x1f33a;深度学习面试八股汇总&#x1f33a;主要组件输入层卷积层 (Convolutional Layer)批归一化层&#xff08;Batch Normalizat…

Redis生产问题(缓存穿透、击穿、雪崩)——针对实习面试

目录 Redis生产问题什么是缓存穿透&#xff1f;如何解决缓存穿透&#xff1f;什么是缓存击穿&#xff1f;如何解决缓存击穿&#xff1f;缓存穿透和缓存击穿有什么区别&#xff1f;什么是缓存雪崩&#xff1f;如何解决缓存雪崩&#xff1f; Redis生产问题 什么是缓存穿透&#x…

19、centos7优化

优化条目&#xff1a; 优化条目&#xff1a; 1.sudo管理用户授权 &#xff08;不用root管理,以普通用户的名义通过sudo提权&#xff09; 2.更改默认的远程连接SSH服务端口,禁止root用户远程连接,&#xff08;提前建立普通用户&#xff09;&#xff08;甚至更改为只监听内网IP…

河北省内首台心磁图仪正式落户河北梅奥心血管病医院

河北省内首台心磁图仪正式落户河北梅奥心血管病医院。 2024年11月9日&#xff0c;河北梅奥心血管病医院迎来了一场激动人心的历史时刻——河北省首台心磁图仪启用仪式在医院内隆重举行&#xff0c;标志着这一顶尖医疗设备正式入驻&#xff0c;为医院心脏影像诊断技术开启了全新…

【C语言刷力扣】283.移动零

题目&#xff1a; 解题思路&#xff1a; 将不为 0 的元素依次放在数组前面&#xff0c;再在数组末尾补上 0。 时间复杂度&#xff1a; 空间复杂度&#xff1a; void moveZeroes(int* nums, int numsSize) {int i 0, j 0;for (; i < numsSize; i) {if (nums[i]) {nums…

网络初阶——应用层:HTTPS 协议

一、HTTPS & HTTP 的区别 从协议的名字来看&#xff0c;HTTP 比 HTTPS 少了一个 S。而这个 “S”&#xff0c;其实可以理解成 “Safe”&#xff0c;所以不难看出&#xff0c;其实 HTTPS 就是 HTTP 的安全版。就是为了保证客户端 cookie 的传输安全的。 二、相关概念 1、明…

怎么禁止Ubuntu自动更新升级

怎么禁止Ubuntu自动更新升级 笔者在做MIT 6.S081的时候发现他给我的qemu自动更新了又卡住了&#xff0c;故关闭了自动更新 文章目录 怎么禁止Ubuntu自动更新升级一、图形化修改二、基于命令行修改配置文件的方法 一、图形化修改 1.打开设置->软件和更新->更新 2.选择自…

Spring Boot框架:构建符合工程认证的计算机课程

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

机器学习—选择激活函数

可以为神经网络中的不同神经元选择激活函数&#xff0c;我们将从如何为输出层选择它的一些指导开始&#xff0c;事实证明&#xff0c;取决于目标标签或地面真相标签y是什么&#xff0c;对于输出层的激活函数&#xff0c;将有一个相当自然的选择&#xff0c;然后看看激活函数的选…

【学习记录】使用CARLA录制双目摄像头SLAM数据

一、数据录制 数据录制的部分参考了网上的部分代码&#xff0c;代码本身并不复杂&#xff0c;基本都是简单的CARLA语法&#xff0c;关键的一点在于&#xff0c;CARLA内部本身并没有预设的双目摄像头&#xff0c;需要我们添加两个朝向相同的摄像头来组成双目系统&#xff0c;这…

[论文粗读][REALM: Retrieval-Augmented Language Model Pre-Training

引言 今天带来一篇检索增强语言模型预训练论文笔记——REALM: Retrieval-Augmented Language Model Pre-Training。这篇论文是在RAG论文出现之前发表的。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 语言模型预训练…

【人工智能】ChatGPT多模型感知态识别

目录 ChatGPT辅助细化知识增强&#xff01;一、研究背景二、模型结构和代码任务流程一&#xff1a;启发式生成 三、数据集介绍三、性能展示实现过程运行过程训练过程 ChatGPT辅助细化知识增强&#xff01; 多模态命名实体识别&#xff08;MNER&#xff09;最近引起了广泛关注。…