OpenHarmony开发者论坛

标题: OpenHarmony TracePoint Demo实现机制 [打印本页]

作者: 润开鸿_闻飞    时间: 2024-6-3 13:50
标题: OpenHarmony TracePoint Demo实现机制
[md][itopen组织](https://gitee.com/itopen)
1、提供OpenHarmony优雅实用的小工具
2、手把手适配riscv + qemu + linux的三方库移植
3、未来计划riscv + qemu + ohos的三方库移植 + 小程序开发
4、一切拥抱开源,拥抱国产化

## 一、实现原理机制介绍

内核利用TracePoint机制实现内核和芯片特性功能解耦,根据TracePoint的代码实现来看,其主要原理是在内核代码中插入一个桩点,芯片厂家自己功能代码实现,然后注册到内核中即可,具体的流程可见下图:

1、在内核中定义一个桩点的宏,该宏的展开是产生三个函数:**钩子执行函数**、钩子注册函数和**钩子注销函数**

2、芯片厂家需要做的事是在**功能实现代码函数**中将需要完成的功能写进去,然后调用上一步的**钩子注册函数**将实现代码函数注册进去,最后将该模块加载到内核中

3、内核在特定的调用点加入插桩函数即步骤一中的**钩子执行函数**

4、测试用例:想办法调用**内核接口函数**,验证运行插桩函数是否执行了芯片厂家的**功能实现代码的函数**

![img](data:image/png;base64,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)

## 二、宏定义

宏定义的展开过程

```c
#define DECLARE_TRACE(name, proto, args) \
        __DECLARE_TRACE(name, PARAMS(proto), PARAMS(args), \
                cpu_online(raw_smp_processor_id()), \
                PARAMS(void *__data, proto))
#define DECLARE_HOOK DECLARE_TRACE
#define PARAMS(args...) args
#define TP_PROTO(args...)   args
#define TP_ARGS(args...)    args

DECLARE_HOOK(vendor_tracepoint_demo,
    TP_PROTO(void *arg),
    TP_ARGS(arg));
    |||||
    |||||
    |||||
    \|||/
     \|/
      |
__DECLARE_TRACE(vendor_tracepoint_demo, \
    PARAMS(void *arg), \
    PARAMS(arg), \
        cpu_online(raw_smp_processor_id()), \
        PARAMS(void *__data, void *arg))
```

进一步解析

```c
#define raw_smp_processor_id() (current_thread_info()->cpu) // 获取运行当前线程的CPUID
#define cpu_online(cpu)     cpumask_test_cpu((cpu), cpu_online_mask) // 测试 CPU 掩码中的 CPU
```

```c
#define __DECLARE_TRACE(name, proto, args, cond, data_proto)            \
    extern int __traceiter_##name(data_proto);                          \
    DECLARE_STATIC_CALL(tp_func_##name, __traceiter_##name);            \
    extern struct tracepoint __tracepoint_##name;                       \
    static inline void trace_##name(proto)                              \
    {                                                                   \
        if (static_key_false(&__tracepoint_##name.key))                 \
            __DO_TRACE(name,                                            \
                TP_ARGS(args),                                          \
                TP_CONDITION(cond), 0);                                 \
        if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) {                     \
            rcu_read_lock_sched_notrace();                              \
            rcu_dereference_sched(__tracepoint_##name.funcs);           \
            rcu_read_unlock_sched_notrace();                            \
        }                                                               \
    }                                                                   \
    __DECLARE_TRACE_RCU(name, PARAMS(proto), PARAMS(args),              \
                PARAMS(cond))                                           \
    static inline int                                                   \
    register_trace_##name(void (*probe)(data_proto), void *data)        \
    {                                                                   \
        return tracepoint_probe_register(&__tracepoint_##name,          \
                        (void *)probe, data);                           \
    }                                                                   \
    static inline int                                                   \
    register_trace_prio_##name(void (*probe)(data_proto), void *data,   \
                   int prio)                                            \
    {                                                                   \
        return tracepoint_probe_register_prio(&__tracepoint_##name,     \
                          (void *)probe, data, prio);                   \
    }                                                                   \
    static inline int                                                   \
    unregister_trace_##name(void (*probe)(data_proto), void *data)      \
    {                                                                   \
        return tracepoint_probe_unregister(&__tracepoint_##name,        \
                        (void *)probe, data);                           \
    }                                                                   \
    static inline void                                                  \
    check_trace_callback_type_##name(void (*cb)(data_proto))            \
    {                                                                   \
    }                                                                   \
    static inline bool                                                  \
    trace_##name##_enabled(void)                                        \
    {                                                                   \
        return static_key_false(&__tracepoint_##name.key);              \
    }
```

最后展开结果

```c
__DECLARE_TRACE( \
    vendor_tracepoint_demo, \
    PARAMS(void *arg), \
    PARAMS(arg), \
        cpu_online(raw_smp_processor_id()), \
        PARAMS(void *__data, void *arg) \
)
    |||||
    |||||
    |||||
    \|||/
     \|/
      |
extern int __traceiter_vendor_tracepoint_demo(
    void *__data, void *arg);
DECLARE_STATIC_CALL(tp_func_vendor_tracepoint_demo, __traceiter_vendor_tracepoint_demo);
extern struct tracepoint __tracepoint_vendor_tracepoint_demo;

static inline void trace_vendor_tracepoint_demo(
    void *arg)
{
    if (static_key_false(&__tracepoint_vendor_tracepoint_demo.key))
        __DO_TRACE(vendor_tracepoint_demo,
            TP_ARGS(arg),
            TP_CONDITION(cpu_online(raw_smp_processor_id())), 0);
    if (IS_ENABLED(CONFIG_LOCKDEP) && (cpu_online(raw_smp_processor_id()))) {
        rcu_read_lock_sched_notrace();
        rcu_dereference_sched(__tracepoint_vendor_tracepoint_demo.funcs);
        rcu_read_unlock_sched_notrace();
    }
}

__DECLARE_TRACE_RCU( \
    vendor_tracepoint_demo, \
    PARAMS(void *arg), \
    PARAMS(arg), \
    PARAMS(cpu_online(raw_smp_processor_id()))
)

static inline int register_trace_vendor_tracepoint_demo(
    void (*probe)(void *__data, void *arg),
    void *data)
{
    return tracepoint_probe_register(&__tracepoint_vendor_tracepoint_demo, (void *)probe, data);
}

static inline int register_trace_prio_vendor_tracepoint_demo(
    void (*probe)(void *__data, void *arg),
    void *data, int prio)
{
    return tracepoint_probe_register_prio(&__tracepoint_vendor_tracepoint_demo, (void *)probe, data, prio);
}

static inline int unregister_trace_vendor_tracepoint_demo(
    void (*probe)(void *__data, void *arg),
    void *data)
{
    return tracepoint_probe_unregister(&__tracepoint_vendor_tracepoint_demo, (void *)probe, data);
}

static inline void check_trace_callback_type_vendor_tracepoint_demo(
    void (*cb)(void *__data, void *arg))
{
}

static inline bool trace_vendor_tracepoint_demo_enabled(void)
{
    return static_key_false(&__tracepoint_vendor_tracepoint_demo.key);
}
```

### 2.1 宏展开的结果说明

通过 `DECLARE_HOOK(vendor_tracepoint_demo,  TP_PROTO(void *arg),  TP_ARGS(arg));`展开最核心是为了得到以下函数:

- **钩子执行函数**:register_trace_vendor_tracepoint_demo

  调用地方使用
- **钩子注册函数**:register_trace_prio_vendor_tracepoint_demo

  芯片厂家实现功能函数后要调用注册函数
- **钩子注销函数**:unregister_trace_vendor_tracepoint_demo

  同注册函数
- **钩子回调函数**:check_trace_callback_type_vendor_tracepoint_demo

  如果没有调用过注册函数,那么执行钩子函数的时候默认执行该回调函数
- **钩子使能函数**:trace_vendor_tracepoint_demo_enabled

  暂时不清楚,没来的及查看
- **可通过如下命令看出符号表是否加载成功**

  ```shell
  cat /proc/kallsyms | grep vendor_tracepoint_demo
  ```

### 2.2 实现代码

- drivers/hooks里面的实现
- include/trace/hooks里面的代码实现

## 三、芯片厂家实现的功能

```
芯片厂家需要定义一个和钩子回调函数类型相同的函数来实现自己的功能代码,然后通过调用钩子注册函数将实现代码注册进去。
```


```
为了将芯片部分代码成功加载到内核,我们一般使用内核模块实现,即通过module_init和module_exit实现。
```


```
DECLARE_HOOK宏的第二个参数是回调函数除了第一个以外的参数,可以一个,也可以两个三个等多个,第三个参数是要传入的参数值,从宏定义上来看应该很好理解的。
```


```
模块是否加载成功可通过命令
```

`cat /proc/kallsyms | grep vendor_demo_init`查看

### 3.1 参考实现代码

```c
// drivers/test_hook/test_hook.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/slab.h>
#include <trace/hooks/demohook.h>

static void vendor_foo(void *data, void *arg)
{
        printk("******%s************\n", __func__);
}

static int __init vendor_demo_init(void)
{
      return register_trace_vendor_tracepoint_demo(&vendor_foo, NULL);
}

static void __exit vendor_demo_exit(void)
{
        unregister_trace_vendor_tracepoint_demo(&vendor_foo, NULL);
}

module_init(vendor_demo_init);
module_exit(vendor_demo_exit);
MODULE_LICENSE("GPL");
```

## 四、内核调用参考demo

这里是自己实现一个简单符号表中去调用插桩点,目的是为了测试方便,内核加载后可通过如下命令查看该符号表是否加载成功

`cat /proc/kallsyms | grep test_symbol`

```c
// drivers/test_symbol/test_symbol.c

#include <linux/module.h>
#include <linux/init.h>
#include <trace/hooks/demohook.h>
#include <trace/hooks_test/test_symbol.h>

void test_symbol(void)
{
    printk("-----%s-----\n", __FUNCTION__);
    trace_vendor_tracepoint_demo(NULL);
}

EXPORT_SYMBOL(test_symbol);
MODULE_LICENSE("GPL");
```

## 五、测试用例

### 5.1 测试代码

先实现一个设备驱动调用符号表,然后在应用层打开设备从而调用符号表,这里主要考虑到RK3568编译ko文件不方便

```c
// drivers/test_demo/test_demo.c
// 驱动侧代码实现
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/slab.h>
#include <trace/hooks_test/test_symbol.h>

#define CHRNAME     "chrdev_demo"
#define CLASSNAME   "demo_class"
#define DEVICENAME  "demo_device"

struct demo_desc {
    unsigned int dev_major;
    struct class *pcls;
    struct device *pdev;
};


static struct demo_desc *demo_dev = NULL;

int demo_open(struct inode *inode, struct file *filep)
{
    printk("-----%s-----\n", __FUNCTION__);
    /**********************************************************/
    test_symbol();
    /**********************************************************/
    return 0;
}

int demo_release(struct inode *inode, struct file *filep)
{
    printk("-----%s-----\n", __FUNCTION__);
    return 0;
}

static const struct file_operations demo_ops = {
    .open = demo_open,
    .release = demo_release,
};

static int __init test_demo_init(void)
{
    int ret;
    printk("-----%s-----\n", __FUNCTION__);
    demo_dev = kzalloc(sizeof(struct demo_desc), GFP_KERNEL);
    if (IS_ERR(demo_dev)) {
        printk(KERN_ERR "kmalloc error!\n");
        ret = -ENOMEM;
        goto err0;
    }

    demo_dev->dev_major = register_chrdev(0, CHRNAME, &demo_ops);
    if (demo_dev->dev_major < 0) {
        printk(KERN_ERR "register chrdev faidemo!\n");
        ret = -ENODEV;
        goto err1;
    }

    demo_dev->pcls = class_create(THIS_MODULE, CLASSNAME);
    if (IS_ERR(demo_dev->pcls)) {
        printk(KERN_ERR "create demo class faidemo!\n");
        ret = PTR_ERR(demo_dev->pcls);
        goto err2;
    }

    demo_dev->pdev = device_create(demo_dev->pcls, NULL, MKDEV(demo_dev->dev_major, 0), NULL, DEVICENAME);
    if (IS_ERR(demo_dev->pdev)) {
        printk(KERN_ERR "create demo device faidemo!\n");
        ret = PTR_ERR(demo_dev->pdev);
        goto err3;
    }

    return 0;
err3:
    class_destroy(demo_dev->pcls);
err2:
    unregister_chrdev(demo_dev->dev_major, CHRNAME);
err1:
    kfree(demo_dev);
err0:
    return ret;
}

static void __exit test_demo_exit(void)
{
    printk("-----%s-----\n", __FUNCTION__);
    device_destroy(demo_dev->pcls, MKDEV(demo_dev->dev_major, 0));
    class_destroy(demo_dev->pcls);
    unregister_chrdev(demo_dev->dev_major, CHRNAME);
    kfree(demo_dev);
}

module_init(test_demo_init);
module_exit(test_demo_exit);
MODULE_LICENSE("GPL");

// kernel/linux/build/test/unittest/accesstokenid
// 应用层代码实现,这里借助内核的unittest测试框架,主要不用自己去增加配置文件,方便
#include "accesstokenid_test.h"
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <ctime>
#include <climits>
#include <pthread.h>
#include <sys/syscall.h>
#include <grp.h>


namespace {
}

using namespace testing::ext;
using namespace std;

void AccesstokenidTest::SetUp() {}

void AccesstokenidTest::TearDown() {}

void AccesstokenidTest::SetUpTestCase() {}

void AccesstokenidTest::TearDownTestCase() {}

/**
* @tc.name: CheckInitToken
* @tc.desc: Test init value of tokenid and ftokenid
* @tc.desc: tokenid equals to the father(hdcd) and ftokenid equals to 0
* @tc.type: FUNC
*/
HWTEST_F(AccesstokenidTest, CheckInitToken, Function | MediumTest | Level1)
{
    printf("----------------------------trace point test-----------------------------------------\n");
    int fd = open("/dev/demo_device", O_RDWR);
    if (fd < 0) {
        printf("open demo_device failed\n");
        return;
    }
    close(fd);
}
```

### 5.2 用例测试

将版本编译后烧录可以看到在/dev目录下有个demo_device设备,然后将编译好的accesstokenid_test执行后内核中会有如下打印,表示我们插桩点成功

```shell
# shell终端运行结果(不重要)
# ./accesstokenid_test
Running main() from ../../third_party/googletest/googletest/src/gtest_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from AccesstokenidTest
[ RUN      ] AccesstokenidTest.CheckInitToken
----------------------------trace point test-----------------------------------------
[       OK ] AccesstokenidTest.CheckInitToken (0 ms)
[----------] 1 test from AccesstokenidTest (0 ms total)

[----------] Global test environment tear-down
Gtest xml output finished
[==========] 1 test from 1 test case ran. (0 ms total)
[  PASSED  ] 1 test.

# 内核侧的log打印很重要
[52744.227764] -----demo_open-----
[52744.227800] -----test_symbol-----
[52744.227809] ******vendor_foo************
[52744.227827] -----demo_release-----
```
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5