OpenHarmony开发者论坛

标题: OpenHarmony tracepoint的机制总结 [打印本页]

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

## 一、tracepoint的机制

### 1.1 DECLARE_HOOK解析

```c
DECLARE_HOOK(vendor_bond_check_dev_link,
    TP_PROTO(const struct bonding *bond, const struct slave *slave, int *state),
    TP_ARGS(bond, slave, state));
```

```c
// 在已经实现如下宏定义的前提下
// CONFIG_TRACEPOINTS 和 CONFIG_VENDOR_HOOKS
#include <trace/hooks/vendor_hooks.h> // 没有的化自行添加
#define DECLARE_HOOK DECLARE_TRACE

#include <linux/tracepoint.h>
#define DECLARE_TRACE(name, proto, args) \
        __DECLARE_TRACE(name, PARAMS(proto), PARAMS(args), \
                cpu_online(raw_smp_processor_id()), \
                PARAMS(void *__data, proto))

#define PARAMS(args...) args
#define TP_PROTO(args...)   args
#define TP_ARGS(args...)    args
```

经过转换后

```c
__DECLARE_TRACE(vendor_bond_check_dev_link, PARAMS(const struct bonding *bond, const struct slave *slave, int *state), PARAMS(bond, slave, state), \
                cpu_online(raw_smp_processor_id()), \
                PARAMS(void *__data, const struct bonding *bond, const struct slave *slave, int *state))
    |||||
    |||||
    |||||
    \|||/
     \|/
      |
__DECLARE_TRACE(vendor_bond_check_dev_link, \
    PARAMS(const struct bonding *bond, const struct slave *slave, int *state), \
    PARAMS(bond, slave, state), \
        cpu_online(raw_smp_processor_id()), \
        PARAMS(void *__data, const struct bonding *bond, const struct slave *slave, int *state))
```

进一步解析

```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_bond_check_dev_link, \
    PARAMS(const struct bonding *bond, const struct slave *slave, int *state), \
    PARAMS(bond, slave, state), \
        cpu_online(raw_smp_processor_id()), \
        PARAMS(void *__data, const struct bonding *bond, const struct slave *slave, int *state) \
)
    |||||
    |||||
    |||||
    \|||/
     \|/
      |
extern int __traceiter_vendor_bond_check_dev_link(
    void *__data, const struct bonding *bond, const struct slave *slave, int *state);
DECLARE_STATIC_CALL(tp_func_vendor_bond_check_dev_link, __traceiter_vendor_bond_check_dev_link);
extern struct tracepoint __tracepoint_vendor_bond_check_dev_link;


static inline void trace_vendor_bond_check_dev_link(
    const struct bonding *bond, const struct slave *slave, int *state)
{
    if (static_key_false(&__tracepoint_vendor_bond_check_dev_link.key))
        __DO_TRACE(vendor_bond_check_dev_link,
            TP_ARGS(bond, slave, state),
            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_bond_check_dev_link.funcs);
        rcu_read_unlock_sched_notrace();
    }
}

__DECLARE_TRACE_RCU( \
    vendor_bond_check_dev_link, \
    PARAMS(const struct bonding *bond, const struct slave *slave, int *state), \
    PARAMS(bond, slave, state), \
    PARAMS(cpu_online(raw_smp_processor_id()))
)

static inline int register_trace_vendor_bond_check_dev_link(
    void (*probe)(void *__data, const struct bonding *bond, const struct slave *slave, int *state),
    void *data)
{
    return tracepoint_probe_register(&__tracepoint_vendor_bond_check_dev_link, (void *)probe, data);
}

static inline int register_trace_prio_vendor_bond_check_dev_link(
    void (*probe)(void *__data, const struct bonding *bond, const struct slave *slave, int *state),
    void *data, int prio)
{
    return tracepoint_probe_register_prio(&__tracepoint_vendor_bond_check_dev_link, (void *)probe, data, prio);
}

static inline int unregister_trace_vendor_bond_check_dev_link(
    void (*probe)(void *__data, const struct bonding *bond, const struct slave *slave, int *state),
    void *data)
{
    return tracepoint_probe_unregister(&__tracepoint_vendor_bond_check_dev_link, (void *)probe, data);
}

static inline void check_trace_callback_type_vendor_bond_check_dev_link(
    void (*cb)(void *__data, const struct bonding *bond, const struct slave *slave, int *state))
{
}

static inline bool trace_vendor_bond_check_dev_link_enabled(void)
{
    return static_key_false(&__tracepoint_vendor_bond_check_dev_link.key);
}
```

### 1.2 DECLARE_STATIC_CALL解析

参考知识点[内核的static-key机制](https://blog.csdn.net/phenix_lord/article/details/49232297)

```c
#define DECLARE_STATIC_CALL(name, func) \
        extern struct static_call_key STATIC_CALL_KEY(name); \
        extern typeof(func) STATIC_CALL_TRAMP(name);

#define STATIC_CALL_KEY(name)       __PASTE(STATIC_CALL_KEY_PREFIX, name)
#define __PASTE(a,b) ___PASTE(a,b)
#define ___PASTE(a,b) a##b
#define STATIC_CALL_KEY_PREFIX      __SCK__

#define STATIC_CALL_TRAMP(name)     __PASTE(STATIC_CALL_TRAMP_PREFIX, name)
#define STATIC_CALL_TRAMP_PREFIX    __SCT__
    |||||
    |||||
    |||||
    \|||/
     \|/
      |
#define DECLARE_STATIC_CALL(name, func) \
        extern struct static_call_key __SCK__##name; \
        extern typeof(func) __SCT__##name;
```

转换后

```c
extern int __traceiter_vendor_bond_check_dev_link(
    void *__data, const struct bonding *bond, const struct slave *slave, int *state);
DECLARE_STATIC_CALL(tp_func_vendor_bond_check_dev_link, __traceiter_vendor_bond_check_dev_link);
    |||||
    |||||
    |||||
    \|||/
     \|/
      |
extern int __traceiter_vendor_bond_check_dev_link(
    void *__data, const struct bonding *bond, const struct slave *slave, int *state);
extern struct static_call_key __SCK__tp_func_vendor_bond_check_dev_link;
extern typeof(__traceiter_vendor_bond_check_dev_link) __SCT__tp_func_vendor_bond_check_dev_link; // 定义一个和__traceiter_vendor_bond_check_dev_link相同类型的变量
```

### 1.3 trace_vendor_bond_check_dev_link函数解析---执行钩子函数

参考链接:[Linux 内核静态追踪技术的实现](https://os.51cto.com/article/690117.html)

[ trace系列3 - trace event学习笔记_HZero.chen的博客](https://blog.csdn.net/jasonactions/article/details/121098704)

```c
struct tracepoint {
    const char *name;
    struct static_key key;
    struct static_call_key *static_call_key;
    void *static_call_tramp;
    void *iterator;
    int (*regfunc)(void);
    void (*unregfunc)(void);
    struct tracepoint_func __rcu *funcs;
};

extern struct tracepoint __tracepoint_vendor_bond_check_dev_link;

static inline void trace_vendor_bond_check_dev_link(
    const struct bonding *bond, const struct slave *slave, int *state)
{
    // 使用了全局变量__tracepoint_vendor_bond_check_dev_link,必定有地方会初始化了该变量
    if (static_key_false(&__tracepoint_vendor_bond_check_dev_link.key))
        __DO_TRACE(vendor_bond_check_dev_link,
            TP_ARGS(bond, slave, state),
            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_bond_check_dev_link.funcs);
        rcu_read_unlock_sched_notrace();
    }
}
```

```c

```

### 1.4 __DECLARE_TRACE_RCU解析

```c
#define __DECLARE_TRACE_RCU(name, proto, args, cond) \
    static inline void trace_##name##_rcuidle(proto) \
    { \
        if (static_key_false(&__tracepoint_##name.key)) \
            __DO_TRACE(name, \
                TP_ARGS(args), \
                TP_CONDITION(cond), 1); \
    }
```

### 1.5 register_trace_vendor_bond_check_dev_link函数介绍---注册钩子函数

```c
static inline int register_trace_vendor_bond_check_dev_link(
    void (*probe)(void *__data, const struct bonding *bond, const struct slave *slave, int *state),
    void *data)
{
    return tracepoint_probe_register(&__tracepoint_vendor_bond_check_dev_link, (void *)probe, data);
}
```

```c
int tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data)
{
    return tracepoint_probe_register_prio(tp, probe, data, TRACEPOINT_DEFAULT_PRIO);
}
```

```c
int tracepoint_probe_register_prio(
    struct tracepoint *tp, void *probe, void *data, int prio)
{
    struct tracepoint_func tp_func;
    int ret;

    mutex_lock(&tracepoints_mutex);
    tp_func.func = probe;
    tp_func.data = data;
    tp_func.prio = prio;
    ret = tracepoint_add_func(tp, &tp_func, prio, true); // 整个过程就是显示tp = tp_func
    mutex_unlock(&tracepoints_mutex);
    return ret;
}
```

参考链接:[什么是RCU](https://www.dubaojiang.com/category/linux/linux_kernel/)

[rcu_assign_pointer、rcu_dereference、ACCESS_ONCE_DenzilXu的博客](https://blog.csdn.net/DenzilXu/article/details/8148257)

```c
static int tracepoint_add_func(struct tracepoint *tp,
    struct tracepoint_func *func, int prio, bool warn)
{
    struct tracepoint_func *old, *tp_funcs;
    int ret;

    if (tp->regfunc && !static_key_enabled(&tp->key)) {
        ret = tp->regfunc();
        if (ret < 0)
            return ret;
    }

    // 核心代码1,拿到钩子列表
    tp_funcs = rcu_dereference_protected(tp->funcs,
            lockdep_is_held(&tracepoints_mutex));
    // 核心代码2,注册新的钩子到列表
    old = func_add(&tp_funcs, func, prio);
    if (IS_ERR(old)) {
        WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM);
        return PTR_ERR(old);
    }

    /*
     * rcu_assign_pointer has as smp_store_release() 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/tracepoint.h using rcu_dereference_sched().
     */
    switch (nr_func_state(tp_funcs)) {
    case TP_FUNC_1:        /* 0->1 */
        /*
         * Make sure new static func never uses old data after a
         * 1->0->1 transition sequence.
         */
        tp_rcu_cond_sync(TP_TRANSITION_SYNC_1_0_1);
        /* Set static call to first function */
        tracepoint_update_call(tp, tp_funcs);
        /* Both iterator and static call handle NULL tp->funcs */
        rcu_assign_pointer(tp->funcs, tp_funcs);
        static_key_enable(&tp->key);
        break;
    case TP_FUNC_2:        /* 1->2 */
        /* Set iterator static call */
        tracepoint_update_call(tp, tp_funcs);
        /*
         * Iterator callback installed before updating tp->funcs.
         * Requires ordering between RCU assign/dereference and
         * static call update/call.
         */
        fallthrough;
    case TP_FUNC_N:        /* N->N+1 (N>1) */
        rcu_assign_pointer(tp->funcs, tp_funcs);
        /*
         * Make sure static func never uses incorrect data after a
         * N->...->2->1 (N>1) transition sequence.
         */
        if (tp_funcs[0].data != old[0].data)
            tp_rcu_get_state(TP_TRANSITION_SYNC_N_2_1);
        break;
    default:
        WARN_ON_ONCE(1);
        break;
    }

    release_probes(old);
    return 0;
}
```

### 1.6 unregister_trace_vendor_bond_check_dev_link函数解析---注销钩子函数

不做介绍了,和register类似

## 二、tracepiont其他的功能介绍

【待补充】

## 三、Demo实例讲解

后面实现用例的时候补充,当前仅介绍官方的demo

### 3.1 驱动侧代码实现

参考链接:[ 添加bond驱动vendor hook ](https://gitee.com/openeuler/kernel/issues/I4UV43)

```c
#include <trace/hooks/bonding.h>

static void vendor_foo(void *data, const struct bonding *bond,
                       const struct slave *slave, int *state)
{
        pr_info("%s\n", __func__);                                                                                                                                                                                                                                                                           
}

static int __init vendor_bond_init(void)
{
      return register_trace_vendor_bond_check_dev_link(&vendor_foo, NULL);
}

static void __exit vendor_bond_exit(void)
{
        unregister_trace_vendor_bond_check_dev_link(&vendor_foo, NULL);
}

module_init(vendor_bond_init);
module_exit(vendor_bond_exit);
```

### 3.2 内核侧代码实现

[代码合入链接](https://gitee.com/openeuler/kern ... e5ee45192cda2130890)

### 3.3 测试代码

```c
#include <linux/kernel.h>                                                                                                                                                                                                                                                                                       
#include <linux/module.h>
#include <net/bonding.h>
#include <trace/hooks/bonding.h>

static void vendor_bond_check_dev_link(void *data, const struct bonding *bond,
                                       const struct slave *slave, int *state)
{
        pr_info("%s\n", __func__);
}

static int __init bondingTestInit(void)
{
        return register_trace_vendor_bond_check_dev_link(
                        &vendor_bond_check_dev_link, NULL);
}

static void __exit bondingTestExit(void)
{
        printk("bonding test exit\n");
        unregister_trace_vendor_bond_check_dev_link(&vendor_bond_check_dev_link, NULL);
}

/* 模块入口和出口函数 */
module_init(bondingTestInit)
module_exit(bondingTestExit)

/* 模块信息 */
MODULE_AUTHOR("Wei Yongjun <weiyongjun1@huawei.com>");
MODULE_LICENSE("GPL");
MODULE_ESCRIPTION("bondtest driver");
```
[/md]




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