29
CHAP 4

CHAP 4

  • Upload
    nevina

  • View
    18

  • Download
    2

Embed Size (px)

DESCRIPTION

CHAP 4. 訊息除錯法 (1). printk() 函式 通用的除錯技巧 , 對應用程式而言使用 printf(); 對於核心程式而言則使用 printk(). printk() 能讓你指定訊息的 loglevel( 等級 ), 共分為八纇 . 定義在 裡 . • KERN_EMERG : 緊急訊息 , 出現在系統崩潰前 . • KERN_ALERT : 危險通知 , 發生在需要立即採 取行動的事件 . - PowerPoint PPT Presentation

Citation preview

Page 1: CHAP 4

CHAP 4

Page 2: CHAP 4

訊息除錯法 (1)

printk() 函式 通用的除錯技巧 ,對應用程式而言使用 printf();對於核心程式而言則使用 printk().

printk() 能讓你指定訊息的 loglevel( 等級 ),共分為八纇 .定義在 <linux/kernel.h> 裡 .

•KERN_EMERG: 緊急訊息 ,出現在系統崩潰前 . •KERN_ALERT: 危險通知 ,發生在需要立即採 取行動的事件 . •KERN_CRIT: 嚴重狀況 ,涉及硬體或軟體故障

Page 3: CHAP 4

訊息除錯法 (2)

•KERN_ERR: 錯誤狀況回報 ,通常回報硬體上的困難 .

•KERN_WARNING: 緊急訊息 ,程度上不影響系統 .•NERN_NOTICE: 通知 ,意料中會發生且值得注意的 狀況 .•KERN_INFO: 資訊性訊息 ,driver 在啟動階段所印出

的硬體資訊 .•KERN_DEBUG: 供除錯用途的訊息 .

Page 4: CHAP 4

訊息除錯法 (3) 上述代號展開後分別成為 :<0>,<1>,<2>…<7> 之類的字串 ,括弧內數字越低表示等級越高 .

•ex:printk(KERN_DEBUG “HI”); 不同等級的訊息會被輸出到不同的地點 ,有可能是目前的操控台 (console) 或者是某種文字終端機 (Xterm 視窗 ,Telnet 或 SSH 連線 ).

任何等值低於 console_loglevel 的變數訊息都會被顯示在操控台上 (console).

不過若系統上同時執行 klogd 與 syslogd 不管 console_loglevel 的值為何 ,所有核心訊息都會被加入/var/log/messages.

Page 5: CHAP 4

訊息除錯法 (4)

如果沒跑 klogd, 則訊息不會流入 user-space, 除非主動讀取 /proc/kmsg.

修改 console_loglevel 的值 •使用 sys_syslog() 利用 klogd 的 – c 選項 . •本書範例 :misc-progs/setlevel.c, 2.1.31 版本開始 ,可以直接透 /proc/sys/kernel/

printk 檔案來修改或檢視 console_levle 的值 . • # echo 8 > /proc/sys/kernel/printk

Page 6: CHAP 4

核心訊息的輸出流程 (1)

printk() 會將訊息寫入一個環型 queue,長度為 LOG_BUF_LEN( 定義在 kernel/printk.c), 且喚醒

•正在等待訊息的行程 (使用 syslog()). •正在讀取 /proc/kmesg 的行程 . 在環型 queue 被填滿時 ,printk() 將會繞回原點 ,將新訊息覆蓋在最就訊息上 ,其優點 :

•固定記憶體 ,既使沒跑日誌紀錄引擎 (klogd,syslogd)也不至於消耗所有的記憶體 . •核心內部到處都可以直接呼叫 printk().

Page 7: CHAP 4

核心訊息的輸出流程 (2)

klogd 所取得的核心訊息會被轉交給 syslogd,由他依據 /etc/syslog.conf 來決定如何處裡收到的訊息 .

若系統沒跑 klogd, 核心訊息將會一值留在環型佇列 , 直到有人讀取他或者是被新訊息蓋掉 .

若不希望 driver 所發出得訊息擾亂的系統的日誌檔 ,可以用 -f選項來從新啟動 klogd並將其訊息寫入特定檔案 .

kernel /proc/kmsg [klogd] /dev/log [syslogd] syslog.conf

Page 8: CHAP 4

查詢除錯法 (1)

大量使用 printk() 的結果將導致系統效能變慢 ,因為 syslogd每次必須隨時將取得的核心訊息寫入日誌檔 .

一般而言 ,取得相關資訊的最佳辦法 , 是再必要時才向系統查詢 ,Unix提供許多 ps,netstat,vmstat 等取得系統資訊的工具 .

對 driver設計時而言 ,查詢系統資訊的只要管道有 :

•將資訊輸出到 /proc 檔案系統 . •適用 ioctl作業方式 .

Page 9: CHAP 4

查詢除錯法 (2)( 使用 /proc 檔案系統 )

/proc 是靠軟體模擬出來的特殊檔案系統 ,並不實際存在於硬碟上 , 而是核心提供給 user-space 的資訊窗口 .

在 proc/下的每一個檔案 , 接聯繫到核心內的專屬函式 ,這些函式在使用者讀取檔案時 , 及時產生檔案的內容 .

•ex: 以 /proc/modules 為例 ,當你讀取它時它會顯示目前載入哪些模組 ,但此檔案系統的長度都為 0.

Page 10: CHAP 4

查詢除錯法 (3)( 使用 /proc 檔案系統 )

Linux許多系統工具 , 如 ps,top,uptime 等都是從 /proc 取的它們所需要的資訊 . 建立 /proc 檔案 :driver必須製作一備查程式 , 讓它在檔案被存取的時 , 及時供應資料 , 核心也會配置一記憶頁給它

備查程式介面 :int (*read_proc)(char *page,char **start,off_t offset,int count,int *eof ,void *data);

read() /proc 備查函式 記憶頁 User-space

Page 11: CHAP 4

查詢除錯法 (4)( 使用 /proc 檔案系統 )

page 指標 : 指向核心預先配置的記憶頁 *start 與 offset: 若檔案超過一記憶頁大小 , 可利用分批傳輸的方式 ,先將 *start 指向 page再利用 offset 指向下一各位元組 .

skcull 程式中有對 read_proc 的實作 :

Page 12: CHAP 4

int scull_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data){ int i, j, len = 0; int limit = count - 80; /* Don't print more than this */

for (i = 0; i < scull_nr_devs && len <= limit; i++) { Scull_Dev *d = &scull_devices[i]; if (down_interruptible(&d->sem)) return -ERESTARTSYS; len += sprintf(buf+len,"\nDevice %i: qset %i, q %i, sz %li\n", i, d->qset, d->quantum, d->size); for (; d && len <= limit; d = d->next) { /* scan the list */ len += sprintf(buf+len, " item at %p, qset at %p\n", d, d->data); if (d->data && !d->next) /* dump only the last item - save space */ for (j = 0; j < d->qset; j++) { if (d->data[j]) len += sprintf(buf+len," % 4i: %8p\n",j,d->data[j]); } } up(&scull_devices[i].sem); } *eof = 1; return len;}

Page 13: CHAP 4

查詢除錯法 (5)( 使用 /proc 檔案系統 )

定義好 read_proc作業方式後 ,必須為它在 /proc下設置一個入口點 ,使用 create_proc_read_entry().

若希望使用者能透過 /proc/scullmem 取得該函式提供的資料 ,則 driver 需宣告如下 :static void scull_create_proc()

{ create_proc_read_entry(“scullmem”, 0 /* 預設模式 (0x444) */, NULL /* 上層目錄 (*proc_dir_entry)*/, scull_read_procmem, NULL /* 提供給 read_proc 使用的資料 */);}

/proc 入口點名稱檔案權限

入口點上層目錄

read_proc 作業方法的指標

傳給 read_proc 的資料的指標

Page 14: CHAP 4

查詢除錯法 (6)( 使用 /proc 檔案系統 )

第三參數的說明 : •在 /proc檔案系統下的每一個子目錄 ,都有各自 專屬的proc_dir_entry結構來描述. •ex:描述/proc/driver子目錄的結構為 proc_root_driver,描述/proc/bus子目錄的結 構為proc_bus. •若此引數設定為NULL代表入口點設置在/proc 目錄下 .

Page 15: CHAP 4

查詢除錯法 (7)( 使用 /proc 檔案系統 )

static void scull_create_proc()

{

create_proc_read_entry(“scullmem”,

0 /* 預設模式 (0x444) */,

proc_root_driver/* 代表 /proc/driver 子目錄*/,

scull_read_procmem,

NULL /* 提供給 read_proc 使用的資料 */);

}

•ex: 在 /proc 子目錄下 ( /proc/driver) 建立新入口點

Page 16: CHAP 4

查詢除錯法 (6)( 使用 /proc 檔案系統 )

建立新的 /proc 子目錄 , 利用 proc_mkdir()

struct proc_dir_entry *scull_procdir=NULL;static void scull_create_proc(){ // 建立 /proc/scull 子目錄

scull_procdir=proc_mkdir(“scull”,NULL);create_proc_read_entry(“scullmem”,0,scull_procdir,scull_read_proc,NULL);

}

Page 17: CHAP 4

查詢除錯法 (7)( 使用 /proc 檔案系統 )

在模組被載卸之前必須先移除相關的 /proc 入口點 ,使用 remove_proc_entry()

// 移除 /proc/scullmem; remove_pcor_entry(“scullmem”,NULL);

// 移除 /proc/driver/scullmem; remove_proc_entry(“scullmem”,proc_root_driver);

// 移除 /proc/scull/scullmem; remove_proc_entry(“scullmem”,scull_procdir);

Page 18: CHAP 4

查詢除錯法 (8)( 使用 ioctl)

Ioctl() 是作用在”檔案描述單元 (file descriptor)”的一種 system call.

•比讀取 /proc 的速度快 .

•沒有記憶頁的限制 .

•不同於任何人都可見的 /proc 檔案 ,ioctl 能將擷取除錯資訊的功能留在驅動程式裡面 .

•詳情請期待第五章 .

Page 19: CHAP 4

觀測除錯法

strace, 檢驗位於 kernel-space 的程式碼 . 能顯示出由 user-space 程式所發出的所有 syste

m call. #strace ls /dev > /dev/scull0

Page 20: CHAP 4

排除重大系統錯誤 (1)

除了監視 , 除錯技術 ,驅動程式可能還是意料之外的 bug存在 , 嚴重可能造成 system fault.

fault(失誤 ) 不等於 panic(死當 ),失誤通常會摧毀目前的行程 , 系統本身能正常 .

若 fault 發生在 process context 之外 , 或是破壞系統的關鍵部分 , 即有可能早成 panic.

oops 訊息 :往往發生在不當的操作指標所引起 ,如 dereference(提領 ) 或者是誤用指標的值 .

Page 21: CHAP 4

排除重大系統錯誤 (2)

page fault: 在 protect mode(保護模式 )下 ,所使用的是 virtual address(虛擬記憶體 ),藉由 page table換算出 physical address(實體位置 ), 若程式提領一個無效指標 , 分頁機制則沒有辦法算出實體位置 ,因而發生 page fault.

若在 user-space 發生提領無效 , 後果頂多是無法” page in “該位址 .

若發生在 kernel 則迫使核心發出 oops 訊息 .

Page 22: CHAP 4

排除重大系統錯誤 (2)

範例 :misc-modules/faulty.c

ssize_t faulty_write (struct file *filp, const char *buf, size_t count, loff_t *pos){ /* 提領一個 NULL 指標 , 刻意製造簡單的 fault 狀況 */ *(int *)0 = 0; return 0;} 0 不是合理指標值

Page 23: CHAP 4

排除重大系統錯誤 (3)

oops 訊息包括 :fault 當時 CPU 的狀態 , 包括各站存器的值等…

Page 24: CHAP 4

ksymoops 用法 : systme.map 檔 (-v) /usr/src/linux/system.map 模組清單 (-l): /proc/modules 核心符號表 (-k): /proc/ksyms 映像檔 (-v): 模組 object 檔存放位置 (-o):

Page 25: CHAP 4

指令 :#ksymoops -v /usr/src/linux-2.4.20-8/vmlinux -m /usr/src/linux-2.4.20-8/Systme.map oops.txt

Page 26: CHAP 4

>>EIP; df8a90e3 <[faulty]faulty_write+3/14> <=====Trace; c0145de3 <sys_write+a3/9f0>Trace; c0109537 <__up_wakeup+1097/1470>Code; df8a90e3 <[faulty]faulty_write+3/14>00000000 <_EIP>:Code; df8a90e3 <[faulty]faulty_write+3/14> <===== 0: c7 05 00 00 00 00 00 movl $0x0,0x0 <=====Code; df8a90ea <[faulty]faulty_write+a/14> 7: 00 00 00Code; df8a90ed <[faulty]faulty_write+d/14> a: 31 c0 xor %eax,%eaxCode; df8a90ef <[faulty]faulty_write+f/14> c: c9 leaveCode; df8a90f0 <[faulty]faulty_write+10/14> d: c3 retCode; df8a90f1 <[faulty]faulty_write+11/14> e: 8d 76 00 lea 0x0(%esi),%esiCode; df8a90f4 <[faulty]faulty_init+0/0> 11: 55 push %ebpCode; df8a90f5 <[faulty]init_module+1/44> 12: 89 e5 mov %esp,%ebp

Page 27: CHAP 4

gdb 使用 : 指令 :gdb /usr/src/linux/vmlinux /proc/kcore (gdb) x/i <addr> Ex: (gdb) x/20i 0xc8002060 參考網址 :http://es-sun2.fernuni-hagen.de/cg

i-bin/info2html?(gdb)Top

Page 28: CHAP 4

0xc8002060: push %ebp0xc8002061: mov %esp,%ebp0xc8002063: sub $0x8,%esp0xc8002066: sub $0xc,%esp0xc8002069: push $0xc80020980xc800206e: call 0xc0114e50 <printk>0xc8002073: add $0x10,%esp

Page 29: CHAP 4

其他核心除錯器 : kdb kgdb