当前位置: 首页 > 嵌入式Linux > 正文

一个内核调试函数的实现

最近在研究内核,主要使用printk来跟踪函数的调用过程。但直接使用printk来打印的话,各种信息太多太杂。而且又不想把已经加了的东西删除。于是决定使用打印等级的方式来实现不同各类信息的显示。

思路很简单,使用不同的宏控制打印函数。打印哪些各类的调试信息由用户控制。通过echo方式传递至内核,实际上是32位的数值,每个比特表示一个各类的信息。因此最多有32种,目前看应该是足够了。为了方便查看哪个比特表示哪种打印,同时为了尽量减小添加代码的麻烦,使用了较多宏定义的技巧。

头文件:

// Add by Late Lee 2016.11.15

extern int g_ll_debug;

enum ll_debug_level{
    D_NETCARD = 0, // 网卡层
    D_LINK, // 链路层
    D_IP, // IP层
    D_ICMP, // ICMP
    D_ARP,  // ARP
    D_UDP,  // UDP
    D_TCP,  // TCP
    D_SOCKET, // socket通路调试
    D_PACKET, // AF_PACKET调试
    D_IEEE80211, // 无线调试
    
    // more...
    D_VERBOSE = 31,
    D_MAX_LEVEL,
};

struct ll_debug__info_t{
    const char* name;
    int bit;
};

extern const struct ll_debug__info_t ll_debug_info[D_MAX_LEVEL];

#define __ll_debug(level, format, ...)		\
do {								\
	if (unlikely(g_ll_debug&ll_debug_info[level].bit))		\
		printk(KERN_ERR "[LL %s %d] " format, __func__, __LINE__, ## __VA_ARGS__);	\
} while (0)


// End 

 

实现代码:

//////////////////////////////////////
// <<<<<<<<<<<<<--- my debug Add by Late Lee 2016.11.15

#define PROC_FILE  "lldebug"

#define _BIT(x) (1<<x)
#define __DEFINE_LEVEL(name) [name] = {#name, _BIT(name)}

const struct ll_debug__info_t ll_debug_info[D_MAX_LEVEL] = {
    __DEFINE_LEVEL(D_NETCARD),
    __DEFINE_LEVEL(D_LINK),
    __DEFINE_LEVEL(D_IP),
    __DEFINE_LEVEL(D_ICMP),
    __DEFINE_LEVEL(D_ARP),
    __DEFINE_LEVEL(D_UDP),
    __DEFINE_LEVEL(D_TCP),
    __DEFINE_LEVEL(D_SOCKET),
    __DEFINE_LEVEL(D_PACKET),
    __DEFINE_LEVEL(D_IEEE80211),
    __DEFINE_LEVEL(D_VERBOSE),
};

EXPORT_SYMBOL(ll_debug_info);

static int my_debug_proc_show(struct seq_file *m, void *v)
{
    int i = 0;
    seq_printf(m, "debug info control.\nusage: echo 0xXXX > /proc/%s\n", PROC_FILE);
    seq_printf(m, "current debug level: 0x%08x max debug level type: %d\n", g_ll_debug, D_MAX_LEVEL);
    for (i = 0; i < D_MAX_LEVEL; i++)
    {
        seq_printf(m, "bit 0x%02x %s\n", ll_debug_info[i].bit, ll_debug_info[i].name);
    }
    return 0;


     return 0;
}

static int my_version_proc_open(struct inode *inode, struct file *file)
{
     return single_open(file, my_debug_proc_show, NULL);
}

static ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_ops)
{
    unsigned char buffer[32] = {0};
    int value = 0;

    if (copy_from_user(buffer, buf, (count > 32 ? 32: count)))
        goto out;

    value = simple_strtol(buffer, NULL, 16);
    g_ll_debug = value;

out:
    return count;
}

static const struct file_operations my_debug_proc_fops = {
    .open      = my_version_proc_open,
    .read      = seq_read,
    .write     = my_write,
    .llseek    = seq_lseek,
    .release   = single_release,
};

struct proc_dir_entry* my_proc_entry = NULL; 

void create_debugproc(void)
{
    if (!my_proc_entry)
        my_proc_entry = proc_create(PROC_FILE, 0, NULL, &my_debug_proc_fops); // 在/proc目录下创建
}

void delete_debugproc(void)
{
    if (my_proc_entry)
        proc_remove(my_proc_entry);
}


// >>>>>>>>>>>>>>>>>>>>>>>>>>>.
////////////////////////////////////////////////////

如需要新加调试类型,则在枚举类型ll_debug_level后添加,同时还要在ll_debug_info结构体中定义。
使用

1、在初始化时调用create_debugproc注册到/proc目录。

2、在需要打印调试信息处调用__ll_debug宏定义即可。如__ll_debug(D_IEEE80211, “XXX”);

3、将不同数值写到/proc/lldebug文件。数值可组合,如echo 0x28 > /proc/igbdebug表示同时打印ICMP和UDP的调试信息。

下面是所有调试信息等级的说明:

latelee@latelee:~# cat /proc/lldebug 
debug info control.
usage: echo 0xXXX > /proc/lldebug
current debug level: 0x00000200 max debug level type: 32
bit 0x01 D_NETCARD
bit 0x02 D_LINK
bit 0x04 D_IP
bit 0x08 D_ICMP
bit 0x10 D_ARP
bit 0x20 D_UDP
bit 0x40 D_TCP
bit 0x80 D_SOCKET
bit 0x100 D_PACKET
bit 0x200 D_IEEE80211
bit 0x00 (null)
bit 0x00 (null)
...
bit 0x80000000 D_VERBOSE

 

李迟 2016.11.15 夜

 

本文固定链接: http://www.latelee.org/embedded-linux/linux-kernel-debug-util.html

如无特别说明,迟思堂工作室文章均为原创,转载请注明: 一个内核调试函数的实现 | 迟思堂工作室

目前暂无评论

发表评论

*

快捷键:Ctrl+Enter