一个内核调试函数的实现

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

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

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 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

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// <<<<<<<<<<<<<--- 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/%sn", PROC_FILE);
seq_printf(m, "current debug level: 0x%08x max debug level type: %dn", g_ll_debug, D_MAX_LEVEL);
for (i = 0; i < D_MAX_LEVEL; i++)
{
seq_printf(m, "bit 0x%02x %sn", 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的调试信息。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 夜