天天看点

使用kretprobes来查找耗时长的中断处理函数

在实时系统中,如果top half时间过长,会导致实时任务得不到调度,产生比较高的延时。使用kretprobes可以查看中断处理的时间,代码如下(参考kretprobe_sample.c做了一些改动):

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/kprobes.h>

#include <linux/ktime.h>

#include <linux/limits.h>

#include <linux/sched.h>

#include <linux/irq.h>

#include <linux/interrupt.h>

#define PT_REGS_PARM1(x) ((x)->regs[0])

static char func_name[NAME_MAX] = "__handle_irq_event_percpu";

module_param_string(func, func_name, NAME_MAX, S_IRUGO);

MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the"

            " function's execution time");

struct my_data {

    ktime_t entry_stamp;

    char func_name[NAME_MAX];

};

static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)

{

    struct my_data *data;

    struct irq_desc *desc;

    char *str;

    if (!current->mm)

        return 1;    

    data = (struct my_data *)ri->data;

    data->entry_stamp = ktime_get();

    str = data->func_name;

    desc = (struct irq_desc *)PT_REGS_PARM1(regs);

    str[0] = 0;

    if (desc->action)

        sprint_symbol_no_offset(str, (unsigned long)desc->action->handler);

    return 0;

}

static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)

{

    unsigned long retval = regs_return_value(regs);

    struct my_data *data = (struct my_data *)ri->data;

    s64 delta;

    ktime_t now;

    now = ktime_get();

    delta = ktime_to_ns(ktime_sub(now, data->entry_stamp));

    pr_info("%s returned %lu and took %lld ns to execute\n",

            data->func_name, retval, (long long)delta);

    return 0;

}

static struct kretprobe my_kretprobe = {

    .handler        = ret_handler,

    .entry_handler        = entry_handler,

    .data_size        = sizeof(struct my_data),

    .maxactive        = 20,

};

static int __init kretprobe_init(void)

{

    int ret;

    my_kretprobe.kp.symbol_name = func_name;

    ret = register_kretprobe(&my_kretprobe);

    if (ret < 0) {

        pr_err("register_kretprobe failed, returned %d\n", ret);

        return -1;

    }

    pr_info("Planted return probe at %s: %p\n",

            my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);

    return 0;

}

static void __exit kretprobe_exit(void)

{

    unregister_kretprobe(&my_kretprobe);

    pr_info("kretprobe at %p unregistered\n", my_kretprobe.kp.addr);

    pr_info("Missed probing %d instances of %s\n",

        my_kretprobe.nmissed, my_kretprobe.kp.symbol_name);

}

module_init(kretprobe_init)

module_exit(kretprobe_exit)

MODULE_LICENSE("GPL");

编译后,运行:

insmod kretprobe.ko; cyclictest xxx;

得到结果如下:

[  272.418346] xhci_msi_irq returned 1 and took 146602 ns to execute

[  272.426034] ahci_single_level_irq_intr returned 1 and took 29161 ns to execute

[  272.426332] xhci_msi_irq returned 1 and took 135103 ns to execute

[  272.434361] xhci_msi_irq returned 1 and took 155142 ns to execute

[  272.442334] xhci_msi_irq returned 1 and took 138082 ns to execute

[  272.448333] xhci_msi_irq returned 1 and took 136542 ns to execute

[  272.456333] xhci_msi_irq returned 1 and took 134342 ns to execute

......

[  274.324755] ahci_single_level_irq_intr returned 1 and took 31560 ns to execute

[  274.330631] e1000_intr_msix_rx [e1000e] returned 1 and took 52401 ns to execute

[  274.347062] ahci_single_level_irq_intr returned 1 and took 29940 ns to execute

[  274.763850] ahci_single_level_irq_intr returned 1 and took 32041 ns to execute

[  274.796840] ahci_single_level_irq_intr returned 1 and took 29040 ns to execute

[  275.031541] ahci_single_level_irq_intr returned 1 and took 30261 ns to execute

[  275.040657] e1000_intr_msix_rx [e1000e] returned 1 and took 47541 ns to execute

[  275.232745] e1000_intr_msix_rx [e1000e] returned 1 and took 53941 ns to execute

[  275.233593] e1000_intr_msix_rx [e1000e] returned 1 and took 48321 ns to execute

[  275.377567] ahci_single_level_irq_intr returned 1 and took 30160 ns to execute

[  275.399842] e1000_intr_msix_rx [e1000e] returned 1 and took 50941 ns to execute

如果是preempt rt的实时系统,该代码只能得到irq_default_primary_handler,因为在preempt rt的方案中,所有的中断处理函数都变成了threaded irq,irq_default_primary_handler被设置为默认的顶半部。

继续阅读