由于以下原因,驱动程序代码比用户程序更难调试:
驱动程序直接与硬件进行交互
驱动程序在运行时不能受到操作系统的保护,而用户进程可以
请确保您的驱动程序可以支持调试。此支持便于进行维护工作和未来的开发工作。
每个函数、数据元素和驱动程序预处理程序定义的名称必须对每个驱动程序都唯一。
驱动程序模块将链接到内核。对特定驱动程序唯一的每个符号名称不得与其他内核符号冲突。为避免这种冲突,特定驱动程序的每个函数和数据元素的名称必须带有该驱动程序共有的前缀。该前缀必须足以让每个驱动程序符号的名称保持唯一。通常,该前缀是驱动程序的名称,或者是驱动程序名称的缩写。例如,xx_open() 是驱动程序 xx 的 open(9E) 例程的名称。
在构建驱动程序时,驱动程序一定包含许多系统头文件。这些头文件中的全局可见名称无法预测。为避免与这些名称产生冲突,必须使用一个标识前缀为每个驱动程序预处理程序定义指定唯一的名称。
在进行错误诊断时,还可以借助唯一的驱动程序符号前缀来解读系统日志和故障消息。您看到的将是与 xx_attach() 有关的错误消息,而不是与二义性 attach() 函数有关的错误。
使用 cmn_err(9F) 函数可以将来自设备驱动程序的消息列显到系统日志中。用于内核模块的 cmn_err (9F) 函数与用于应用程序的 printf(3C) 函数类似。cmn_err(9F) 函数可提供其他格式字符,如用于列显设备寄存器位的 %b 格式。cmn_err(9F) 函数可以将消息写入系统日志中。使用 tail(1) 命令可以监视 /var/adm/messages 中的这些消息。
% tail -f /var/adm/messages |
断言是活动文档一种极有价值的形式。ASSERT(9F) 的语法如下:
void ASSERT(EXPRESSION)
如果预期为 true 的条件实际为 false,则 ASSERT() 宏会停止执行内核。ASSERT() 为程序员提供了对某段代码所做的假设进行验证的方法。
仅当定义了 DEBUG 编译符号时,才会定义 ASSERT() 宏。如果未定义 DEBUG,ASSERT() 宏将无效。
以下示例断言将测试特定指针值不是 NULL 的假设:
ASSERT(ptr != NULL);
如果驱动程序已使用 DEBUG 进行编译,并且执行至此时 ptr 的值为 NULL,则在控制台上会列显以下故障消息:
panic: assertion failed: ptr != NULL, file: driver.c, line: 56
由于 ASSERT(9F) 使用 DEBUG 编译符号,因此任何条件调试代码也应使用 DEBUG。
mutex_owned(9F) 的语法如下:
int mutex_owned(kmutex_t *mp);
驱动程序开发过程中的一项重要工作涉及正确处理多个线程。获得了互斥锁时,应始终使用注释。在未获得明确需要的互斥锁时,注释将更有用。要确定线程是否持有互斥锁,请在 ASSERT(9F) 中使用 mutex_owned():
void helper(void) { /* this routine should always be called with xsp's mutex held */ ASSERT(mutex_owned(&xsp->mu)); /* ... */ }
mutex_owned() 只在 ASSERT() 宏内有效。应该使用 mutex_owned() 控制驱动程序的行为。
可以使用预处理程序符号(例如 DEBUG)或全局变量,借助条件编译将用于调试的代码插入驱动程序中。使用条件编译,可在生产驱动程序中删除不必要的代码。使用变量可以在运行时设置调试输出量。在运行时使用 ioctl 或调试程序设置调试级别可以指定输出。通常,可以组合使用这两种方法。
以下示例依赖编译器来删除无法访问的代码(在本例中是跟在始终为 false 的零测试之后的代码)。此示例还提供了可在 /etc/system 中设置或由调试程序修补的局部变量。
#ifdef DEBUG /* comments on values of xxdebug and what they do */ static int xxdebug; #define dcmn_err if (xxdebug) cmn_err #else #define dcmn_err if (0) cmn_err #endif /* ... */ dcmn_err(CE_NOTE, "Error!\n");
此方法可处理 cmn_err(9F) 具有可变数量参数的情况。另一种方法依赖于宏只有一个参数的情况,即 cmn_err(9F) 的用括号括起的参数列表。宏可以删除此参数。此宏还可在未定义 DEBUG 的情况下,通过将宏扩展为不包含任何内容来消除对优化程序的依赖性。
#ifdef DEBUG /* comments on values of xxdebug and what they do */ static int xxdebug; #define dcmn_err(X) if (xxdebug) cmn_err X #else #define dcmn_err(X) /* nothing */ #endif /* ... */ /* Note:double parentheses are required when using dcmn_err. */ dcmn_err((CE_NOTE, "Error!"));
可以采用多种方法扩展此技术。一种方法是根据 xxdebug 的值指定来自 cmn_err(9F) 的不同消息。但在此类情况下,必须注意不要用大量的调试信息使代码变得晦涩难懂。
另一种常见方案是编写 xxlog() 函数,以便使用 vsprintf(9F) 或 vcmn_err(9F) 来处理变量参数列表。