320 likes | 416 Views
偵錯技術. Reporter:Ko-Wei,Chang 601430074. Outline. 介紹一系列專門用來對付核心程式的監視技術,以及追查錯誤的技巧 訊息除錯法 查詢除錯法 觀測除錯法 排除重大系統失誤 除錯工具. 訊息除錯法. Printk() 讓我們能指定訊息的”登載等級” 定義在 <linux/kernel.h> 所宣告的八個巨集中 下述代號展開之後,會分別成為 <0><1><2>…<7> 之類的字串,數字越低,表示嚴重程度越高
E N D
偵錯技術 Reporter:Ko-Wei,Chang 601430074
Outline • 介紹一系列專門用來對付核心程式的監視技術,以及追查錯誤的技巧 • 訊息除錯法 • 查詢除錯法 • 觀測除錯法 • 排除重大系統失誤 • 除錯工具
訊息除錯法 • Printk()讓我們能指定訊息的”登載等級” • 定義在<linux/kernel.h>所宣告的八個巨集中 • 下述代號展開之後,會分別成為<0><1><2>…<7>之類的字串,數字越低,表示嚴重程度越高 • 如果沒在printk()註明分類代碼,會以DEFAULT_MESSAGE_LOGLEVEL為預設的等級,此變數值是4(相當於KERN_WARING) (參閱cd /usr /src /kernel /printk.c)
P82 • KERN_EMERG • KERN_ALERT • KERN_CRIT • KERN_ERR • KERN_WARING • KERN_NOTICE • KERN_INFO • KERN_DEBUG
Console_loglevel變數初值為default_console_loglevel,可用sys_syslog系統呼叫來改變此值Console_loglevel變數初值為default_console_loglevel,可用sys_syslog系統呼叫來改變此值 • 1.先殺掉執行中的klogd再以-c選項啟動之 • 2.自己寫一個程式來觸發 書中範例:misc-progs/setlevel.c • 當在控制台上工作,遭遇到kernel fault,想修改console_level的現值可用下列命令迫使所有核心訊息都出現在操控台上#echo 8 > /proc/sys/kernel/printk
核心訊息輸出流程 • printk()將訊息寫入一個環形佇列,然後喚醒正在等待的行程—也就是被syslog()系統呼叫推入修眠狀態或是正在讀取/proc/kmsg • 日誌記錄引擎(syslogd和klogd),這兩種是等效的,但是直接讀取/proc/kmsg會消耗掉緩衝區裡的訊息,不會留給其他行程讀取;syslog()能讓我決定是否要將訊息留在緩衝區,也讓其他行程可以讀取。
訊息開關 • 研發驅動程式初期,printk()很好用,可以輔助測試~除錯程式碼,但是正式釋出驅動程式時,該如何亦切拿掉所有除錯用途的printk()? • 1..修改巨集名稱,取消或恢復個別列印敘述2..只要在編譯之前變更CFLAGS變數值,就可以關閉所有除錯訊息3..同樣的列印敘述,要同時可用在kernel-space與 user-space的程式裡,讓我們可以用一致方式來控制除錯訊息 • P86 • 熟悉C preprocessor的人,甚至可以擴充巨集定義,做出除錯等級的效果,例如:以不同數值定義不同等級的程度
查詢除錯法 • 大量使用printk()的結果,將明顯拖慢系統效能,因為syslogd必須隨時將取得的核心訊息寫入日誌檔,美印出一行訊息,就會引發一次磁碟動作 • 解決辦法1 : 修改/etc/syslog.conf,在日誌檔的檔名之前加一個減號(魔術記號,它要求syslogd不必每次收到新訊息就寫入磁碟) • 解決辦法2 : 以其他程式來替代klogd(cat/proc/kmesg) • 最佳辦法 : 必要時才向系統查詢,Unix提供許多像 ps, netstat, vmstat等取得系統資訊的工具 • 對driver設計時而言,查詢系統資訊的主要管道有: •將資訊輸出到/proc檔案系統. •適用ioctl作業方式.
使用/proc檔案系統 • /proc是靠軟體模擬出來的特殊檔案系統,並不實際存在於硬碟上,而是核心提供給user-space的資訊窗口 • 在proc/下的每一個檔案,接聯繫到核心內的專屬函式,這些函式在使用者讀取檔案時,及時(real-time)產生檔案的內容 以/proc/modules為例,當你讀取它時它會顯示目前載入哪些模組,但此檔案系統的長度都為0.
Linux許多系統工具,如ps, top,uptime等都是從 /proc取得它們所需要的資訊 • 全功能的 /proc檔案可以很複雜,不僅可被讀出資訊,甚至容許使用者寫入一些值,藉此改變特定核心功能的行為 • 任何欲支援/proc的模組,都必須引入<linux/proc_fs.h>,來定義適當的函式 • 建立/proc檔案 : 驅動程式必須製作一備查函式,讓它在檔案被存取的時,及時供應資料,核心也會配置一個記憶頁給它,核心會自動將我寫在該記憶頁的資料傳回user-space
備查函式介面: int (*read_proc)(char*page,char**start,off_toffset,intcount,int*eof ,void*data); • page指標:指向核心預先配置的記憶頁 • start:用來表示有效資料的起點位置 • eof:指向一個整數值,驅動程式以此值讓核心知道它沒有資料要傳回 • offset:若檔案超過一記憶頁大小,可利用分批傳輸的方式,先將*start指向page,讓呼叫者知道新資料是放在page緩衝區的開頭,將*start指向offset bytes之後的下一個位元組 • skcull程式中有對read_proc的實作:
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; }
定義好read_proc作業方式後,必須為它在/proc下設置一個入口點,使用create_proc_read_entry()函式定義好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的資料的指標
第三引數base的說明: •在/proc檔案系統下的每一個子目錄,都有各自專屬的proc_dir_entry結構來描述 •描述/proc/driver子目錄的結構為 proc_root_driver,描述/proc/bus子目錄的結構為proc_bus •若此引數設定為NULL,代表入口點設置在/proc目錄下
static void scull_create_proc() { create_proc_read_entry(“scullmem”, 0 /* 預設模式(0x444) */, proc_root_driver/* 代表/proc/driver子目錄*/, scull_read_procmem, NULL /* 提供給read_proc使用的資料 */); } •在/proc子目錄下( /proc/driver)建立新入口點
建立新的/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); }
在模組被載卸之前必須先移除相關的/proc入口點,使用remove_proc_entry()在模組被載卸之前必須先移除相關的/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);
Ioctl作業方法 • Ioctl()是作用在檔案描述單元(file descriptor)的一種系統呼叫,接受一個數值引數,一個可有可無的指標引數 • 數值引數--ioctl()所要執行的命令 • 指標引數--該命令的作業參數 •使用ioctl()來取得資訊,比較困難,因為要另外寫一個測試程式來發出呼叫,但是比讀取/proc的速度快 •沒有單記憶頁的限制,不必將資料拆解成片段 •不同於任何人都可見的/proc檔案,ioctl能將擷取除錯資訊的功能留在驅動程式裡面(因為不會影響效能) •詳情請待第六章
觀測除錯法 • 有時候,對於一些不太嚴重的小問題,用觀察user-space應用程式的行為,就可以察覺出來 • 將程式交給debugger來逐步執行 • 在適當地方印出訊息 • 將程式交給strace來執行
strace提供的除錯資訊,直接取自系統核心本身,能顯示出由user-space程式所發出的所有系統呼叫,輸入輸出資料是否一致strace提供的除錯資訊,直接取自系統核心本身,能顯示出由user-space程式所發出的所有系統呼叫,輸入輸出資料是否一致 • #strace ls /dev > /dev/scull0 • strace最有用之處,在於能讓我們從系統呼叫中發現執行期的錯誤,一般應用程式中的perror()往往不夠詳細,或者不足以用於偵錯
排除重大系統錯誤 • 除了監視,除錯技術,驅動程式可能還是有很多意料之外的bug存在,嚴重可能造成system fault • fault(失誤)不等於panic(死當),失誤通常會摧毀目前的行程,但是系統本身功能正常 • 若fault發生在process context之外,或是破壞系統的關鍵部分,就有可能造成panic • oops訊息:往往發生在不當的操作指標所引起,如dereference(提領)或者是誤用指標的值
page fault:在protect mode(保護模式)下,所使用的是virtual address(虛擬記憶體),藉由page table換算出physical address(實體位置),若程式提領一個無效指標,分頁機制則沒有辦法算出實體位置,因而發生page fault • 若在user-space發生提領無效,後果頂多是無法”page in“該位址 • 但若發生在kernel則迫使核心發出oops訊息
範例: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不是合理指標值
oops訊息包括 : fault當時CPU的狀態,包括各暫存器的值及一些匪夷所思的資訊 • 但是~~看不懂那些十六進位的數字訊息,要想辦法把數字轉換成有意義的符號
除錯工具 • gdb • #gdb /usr/src/linux/vmlinux /proc/kcore • 第一個引數是核心映像檔 • 第二個引數是core檔的位置 • 可用還檢驗模組裡的變數。 • 反組譯: (gdb) x/i <addr> • Ex: (gdb) x/20i 0xc8002060
KGDB補強程式 • 為了解決gdb在除錯核心時,不能發揮全部功能的遺憾 • 準備兩台電腦。一台跑經過kgdb補強過的受測核心,另一台跑gdb,兩台相連
User-mode Linux移植版 ( 一種有趣的概念,並非在某種新型硬體平台上運行,而是在一個具備linux系統呼叫介面的虛擬機器上執行,適合讓設計師構思程式架構的雛形 ) • Linux Trace Toolkit ( 回報時序資訊,可清楚某段時間內所發生的大小事情 ) • Dynamic Probes ( IBM,特殊堆疊語法的探針,可回報訊息到user-space,改變暫存器內容 )