1 / 29

CHAP 4

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

nevina
Download Presentation

CHAP 4

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CHAP 4

  2. 訊息除錯法(1) • printk()函式 • 通用的除錯技巧,對應用程式而言使用printf();對於核心程式而言則使用printk(). • printk()能讓你指定訊息的loglevel(等級),共分為八纇.定義在<linux/kernel.h>裡. •KERN_EMERG:緊急訊息,出現在系統崩潰前. •KERN_ALERT:危險通知,發生在需要立即採 取行動的事件. •KERN_CRIT:嚴重狀況,涉及硬體或軟體故障

  3. 訊息除錯法(2) •KERN_ERR:錯誤狀況回報,通常回報硬體上的困難. •KERN_WARNING:緊急訊息,程度上不影響系統. •NERN_NOTICE:通知,意料中會發生且值得注意的 狀況. •KERN_INFO:資訊性訊息,driver在啟動階段所印出 的硬體資訊. •KERN_DEBUG:供除錯用途的訊息.

  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.

  5. 訊息除錯法(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

  6. 核心訊息的輸出流程(1) • printk()會將訊息寫入一個環型queue,長度為LOG_BUF_LEN(定義在kernel/printk.c),且喚醒 •正在等待訊息的行程(使用syslog()). •正在讀取/proc/kmesg的行程. • 在環型queue被填滿時,printk()將會繞回原點,將新訊息覆蓋在最就訊息上,其優點: •固定記憶體,既使沒跑日誌紀錄引擎 (klogd,syslogd)也不至於消耗所有的記憶體. •核心內部到處都可以直接呼叫printk().

  7. 核心訊息的輸出流程(2) • klogd所取得的核心訊息會被轉交給syslogd,由他依據/etc/syslog.conf來決定如何處裡收到的訊息. • 若系統沒跑klogd,核心訊息將會一值留在環型佇列,直到有人讀取他或者是被新訊息蓋掉. • 若不希望driver所發出得訊息擾亂的系統的日誌檔,可以用-f選項來從新啟動klogd並將其訊息寫入特定檔案 . kernel /proc/kmsg [klogd] /dev/log [syslogd] syslog.conf

  8. 查詢除錯法(1) • 大量使用printk()的結果將導致系統效能變慢,因為syslogd每次必須隨時將取得的核心訊息寫入日誌檔. • 一般而言,取得相關資訊的最佳辦法,是再必要時才向系統查詢,Unix提供許多ps,netstat,vmstat等取得系統資訊的工具. • 對driver設計時而言,查詢系統資訊的只要管道有: •將資訊輸出到/proc檔案系統. •適用ioctl作業方式.

  9. 查詢除錯法(2)(使用/proc檔案系統) • /proc是靠軟體模擬出來的特殊檔案系統,並不實際存在於硬碟上,而是核心提供給user-space的資訊窗口. • 在proc/下的每一個檔案,接聯繫到核心內的專屬函式,這些函式在使用者讀取檔案時,及時產生檔案的內容. •ex: 以/proc/modules為例,當你讀取它時它會顯示目前載入哪些模組,但此檔案系統的長度都為0.

  10. 查詢除錯法(3)(使用/proc檔案系統) • Linux許多系統工具,如ps,top,uptime等都是從 /proc取的它們所需要的資訊. • 建立/proc檔案:driver必須製作一備查程式,讓它在檔案被存取的時,及時供應資料,核心也會配置一記憶頁給它 • 備查程式介面: int (*read_proc)(char*page,char**start,off_toffset,intcount,int*eof ,void*data); read() /proc 備查函式 記憶頁 User-space

  11. 查詢除錯法(4)(使用/proc檔案系統) • page指標:指向核心預先配置的記憶頁 • *start與offset:若檔案超過一記憶頁大小,可利用分批傳輸的方式,先將*start指向page再利用offset指向下一各位元組. • skcull程式中有對read_proc的實作:

  12. 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; }

  13. 查詢除錯法(5)(使用/proc檔案系統) • 定義好read_proc作業方式後,必須為它在/proc下設置一個入口點,使用create_proc_read_entry(). • 若希望使用者能透過/proc/scullmem取得該函式提供的資料,則driver需宣告如下: /proc入口點名稱 static void scull_create_proc() { create_proc_read_entry(“scullmem”, 0 /* 預設模式(0x444) */, NULL /* 上層目錄(*proc_dir_entry)*/, scull_read_procmem, NULL /* 提供給read_proc使用的資料 */); } 檔案權限 入口點上層目錄 read_proc作業方法的指標 傳給read_proc的資料的指標

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

  15. 查詢除錯法(7)(使用/proc檔案系統) •ex:在/proc子目錄下( /proc/driver)建立新入口點 static void scull_create_proc() { create_proc_read_entry(“scullmem”, 0 /* 預設模式(0x444) */, proc_root_driver/* 代表/proc/driver子目錄*/, scull_read_procmem, NULL /* 提供給read_proc使用的資料 */); }

  16. 查詢除錯法(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); }

  17. 查詢除錯法(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);

  18. 查詢除錯法(8)(使用ioctl) • Ioctl()是作用在”檔案描述單元(file descriptor)”的一種system call. •比讀取/proc的速度快. •沒有記憶頁的限制. •不同於任何人都可見的/proc檔案,ioctl能將擷取除錯資訊的功能留在驅動程式裡面. •詳情請期待第五章.

  19. 觀測除錯法 • strace,檢驗位於kernel-space的程式碼. • 能顯示出由user-space程式所發出的所有system call. • #strace ls /dev > /dev/scull0

  20. 排除重大系統錯誤(1) • 除了監視,除錯技術,驅動程式可能還是意料之外的bug存在,嚴重可能造成system fault. • fault(失誤)不等於panic(死當),失誤通常會摧毀目前的行程,系統本身能正常. • 若fault發生在process context之外,或是破壞系統的關鍵部分,即有可能早成panic. • oops訊息:往往發生在不當的操作指標所引起,如dereference(提領)或者是誤用指標的值.

  21. 排除重大系統錯誤(2) • page fault: 在protect mode(保護模式)下,所使用的是virtual address(虛擬記憶體),藉由page table換算出physical address(實體位置),若程式提領一個無效指標,分頁機制則沒有辦法算出實體位置,因而發生page fault. • 若在user-space發生提領無效,後果頂多是無法”page in “該位址. • 若發生在kernel則迫使核心發出oops訊息.

  22. 排除重大系統錯誤(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不是合理指標值

  23. 排除重大系統錯誤(3) • oops訊息包括:fault當時CPU的狀態,包括各站存器的值等…

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

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

  26. >>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 00 Code; df8a90ed <[faulty]faulty_write+d/14> a: 31 c0 xor %eax,%eax Code; df8a90ef <[faulty]faulty_write+f/14> c: c9 leave Code; df8a90f0 <[faulty]faulty_write+10/14> d: c3 ret Code; df8a90f1 <[faulty]faulty_write+11/14> e: 8d 76 00 lea 0x0(%esi),%esi Code; df8a90f4 <[faulty]faulty_init+0/0> 11: 55 push %ebp Code; df8a90f5 <[faulty]init_module+1/44> 12: 89 e5 mov %esp,%ebp

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

  28. 0xc8002060: push %ebp 0xc8002061: mov %esp,%ebp 0xc8002063: sub $0x8,%esp 0xc8002066: sub $0xc,%esp 0xc8002069: push $0xc8002098 0xc800206e: call 0xc0114e50 <printk> 0xc8002073: add $0x10,%esp

  29. 其他核心除錯器: • kdb • kgdb

More Related