天天看點

linux 核心調試

核心中有多項用于調試的功能,但這些功能會造成額外輸出,并導緻性能下降,是以發行版本廠商通過都禁止發行版核心中的這些功能。但作為一名核心開發者,調試需求具有更高的優先級,是以應該構造并安裝自己的核心,并打開這些調試選項。

一、核心中的調試選項

CONFIG_DEBUG_KERNEL

This option just makes other debugging options available; it should be turned on but does not, by itself, enable any features.

CONFIG_DEBUG_SLAB

This crucial option turns on several types of checks in the kernel memory allocation functions; with these checks enabled, it is possible to detect a number of memory overrun and missing initialization errors. Each byte of allocated memoryis set to 0xa5 before being handed to the caller and then set to 0x6b when it is freed. If you ever see either of those “poison” patterns repeating in output from your driver (or often in an oops listing), you’ll know exactly what sort of error to look for. When debugging is enabled, the kernel also places special guard values before and after every allocated memory object; if those values ever get changed, the kernel knows that somebody has overrun a memory allocation, and it complains loudly. Various checks for more obscure errors are enabled as well.

CONFIG_DEBUG_PAGEALLOC

Full pages are removed from the kernel address space when freed. This option can slow things down significantly, but it can also quickly point out certain kinds of memory corruption errors.

CONFIG_DEBUG_SPINLOCK

With this option enabled, the kernel catches operations on uninitialized spinlocks and various other errors (such as unlocking a lock twice).

CONFIG_DEBUG_SPINLOCK_SLEEP

This option enables a check for attempts to sleep while holding a spinlock. In fact, it complains if you call a function that could potentially sleep, even if the call in question would not sleep.

CONFIG_INIT_DEBUG

Items marked with __init (or __initdata) are discarded after system initialization or module load time. This option enables checks for code that attempts to access initialization-time memory after initialization is complete.

CONFIG_DEBUG_INFO

This option causes the kernel to be built with full debugging information included. You’ll need that information if you want to debug the kernel with gdb. You may also want to enable CONFIG_FRAME_POINTER if you plan to use gdb.

CONFIG_MAGIC_SYSRQ

Enables the “magic SysRq” key. We look at this key in the section “System Hangs,” later in this chapter.

CONFIG_DEBUG_STACKOVERFLOW

CONFIG_DEBUG_STACK_USAGE

These options can help track down kernel stack overflows. A sure sign of a stack overflow is an oops listing without any sort of reasonable back trace. The first option adds explicit overflow checks to the kernel; the second causes the kernel to monitor stack usage and make some statistics available via the magic SysRq key.

CONFIG_KALLSYMS

This option (under “General setup/Standard features”) causes kernel symbol information to be built into the kernel; it is enabled by default. The symbol information is used in debugging contexts; without it, an oops listing can give you a kernel traceback only in hexadecimal, which is not very useful.

CONFIG_IKCONFIG

CONFIG_IKCONFIG_PROC

These options (found in the “General setup” menu) cause the full kernel configuration state to be built into the kernel and to be made available via /proc. Most kernel developers know which configuration they used and do not need these options (which make the kernel bigger). They can be useful, though, if you are trying to debug a problem in a kernel built by somebody else.

CONFIG_ACPI_DEBUG

Under “Power management/ACPI.” This option turns on verbose ACPI

(Advanced Configuration and Power Interface) debugging information, which can be useful if you suspect a problem related to ACPI.

CONFIG_DEBUG_DRIVER

Under “Device drivers.” Turns on debugging information in the driver core, which can be useful for tracking down problems in the low-level support code. We’ll look at the driver core in Chapter 14.

CONFIG_SCSI_CONSTANTS

This option, found under “Device drivers/SCSI device support,” builds in information for verbose SCSI error messages. If you are working on a SCSI driver, you probably want this option.

CONFIG_INPUT_EVBUG

This option (under “Device drivers/Input device support”) turns on verbose logging of input events. If you are working on a driver for an input device, this option may be helpful. Be aware of the security implications of this option, however: it logs everything you type, including your passwords.

CONFIG_PROFILING

This option is found under “Profiling support.” Profiling is normally used for system performance tuning, but it can also be useful for tracking down some kernel hangs and related problems.

二、通過列印調試

1、printk

與printf類似,差别之一是通過附加不同日志級别,讓printk根據這些級别所表示的嚴重程度對消息分類。

printk(KERN_DEBUG "Here I am: %s:%i\n", __FILE__, __LINE__);
printk(KERN_CRIT "I'm trashed; giving up on %p\n", ptr);
           

<linux/kernel.h>中定義了八種可用的日志級别

#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately*/
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
           

未指定優先級的printk采用預設級别DEFAULT_MESSAGE_LOGLEVEL,這個宏在kernel/printk.c中定義為一個整數。

#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
           

根據日志級别,核心可能會将消息列印到目前控制台上。當優先級小于console_loglevel時,消息才能顯示出來。如果同時運作了klogd和syslogd,則無論console_loglevel為何值,核心消息都将追加到/var/log/messages中。如果klogd沒有運作,這些消息就不會傳遞到使用者空間,此時隻能檢視/proc/kmsg檔案(使用dmesg指令輕松做到)。如果使用klogd,則應該了解它不會儲存連續相同的資訊行,它隻會儲存連續相同的第一行,并在最後列印這一行的重複次數。

變量console_loglevel的初始值為DEFAULT_CONSOLE_LOGLEVEL(kernel/printk.c),而且還可以通過sys_syslog系統調用修改。調用klogd時可以指定-c開關項來修改這個變量。注意,要修改目前值,必須先殺掉klogd,然後再用新的-c選項重新開機它。

#define DEFAULT_CONSOLE_LOGLEVEL 7 /*anything MORE serious than KERN_DEBUG*/
           

也可以通過對文本檔案/proc/sys/kernel/printk的通路來讀取和修改控制台的日志級别。

#echo 7 > /proc/sys/kernel/printk
#cat /proc/sys/kernel/printk
7 4 1 7
           

4個整數值分别是:目前的loglevel、預設loglevel、最小允許的loglevel、引導時的預設loglevel。

2、重定向控制台消息

可以在任何一個控制台上調用ioctl(TIOCLINUX)來指定接收消息的其他虛拟機終端。

setconsole程式,可以選擇專門用來接收核心消息的控制台。

* You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>

int main(int argc, char **argv)
{
    char bytes[2] = {11,0}; /* 11 is the TIOCLINUX cmd number */

    if (argc==2) bytes[1] = atoi(argv[1]); /* the chosen console */
    else {
        fprintf(stderr, "%s: need a single arg\n",argv[0]); exit(1);
    }
    if (ioctl(STDIN_FILENO, TIOCLINUX, bytes)<0) { /* use stdin */
        fprintf(stderr,"%s: ioctl(stdin, TIOCLINUX): %s\n",
                argv[0], strerror(errno));
        exit(1);
    }
    exit(0);
}
           

3、消息如何被記錄

printk函數将消息寫到一個長度為__LOG_BUF_LEN位元組的環形緩沖區,可在配置核心時為__LOG_BUF_LEN指定為4KB~1MB之間。

如果循環緩沖區填滿了,printk就繞回緩沖區的開始出填寫新的資料,将覆寫最早的資料。

對/proc/kmsg進行讀操作,日志緩沖區被讀取的資料就不再保留。

syslog系統調用能通過選項傳回日志資料并保留資料。

dmesg指令可在不重新整理緩沖區資料的情況下獲得緩沖區内容。

4、開啟和關閉消息

通過預處理指令開啟和關閉調試資訊

#undef PDEBUG /* undef it, just in case */
#ifdef SCULL_DEBUG
# ifdef __KERNEL__
     /* This one if debugging is on, and kernel space */
# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args)
# else
     /* This one for user space */
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
# endif
#else
# define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif

#undef PDEBUGG
#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */
           

相應的Makefile檔案中添加以下代碼

# Comment/uncomment the following line to disable/enable debugging
DEBUG = y

# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
  DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
else
  DEBFLAGS = -O2
endif

CFLAGS += $(DEBFLAGS)
           

5、速度限制

為了避免printk重複輸出過快而阻塞系統,核心使用以下函數跳過部分輸出:

int printk_ratelimit(void);
           

應用的例子

if (printk_ratelimit( ))
    printk(KERN_NOTICE "The printer is still on fire\n");
           

可以通過修改/proc/sys/kernel/printk_ratelimit(重開資訊前應等待的秒數)和/proc/sys/kernel/printk_ratelimit_burst(在速度限制前可接受的資訊數)來定制printk_ratelimit的行為。

6、列印裝置編号

#include <linux/kdev_t.h>

int print_dev_t(char *buffer, dev_t dev);
char *format_dev_t(char *buffer, dev_t dev);
           

這2個宏都是将裝置編号列印到給定的緩沖區,唯一差別是前者傳回列印字元數,後者傳回緩沖區。

繼續閱讀