Presentasjon lastes. Vennligst vent

Presentasjon lastes. Vennligst vent

偵錯技術.

Liknende presentasjoner


Presentasjon om: "偵錯技術."— Utskrift av presentasjonen:

1 偵錯技術

2 Outline 介紹一系列專門用來對付核心程式的監視技術,以及追查錯誤的技巧 訊息除錯法 查詢除錯法 觀測除錯法 排除重大系統失誤 除錯工具

3 訊息除錯法 Printk()讓我們能指定訊息的”登載等級” 定義在<linux/kernel.h>所宣告的八個巨集中
下述代號展開之後,會分別成為<0><1><2>…<7>之類的字串,數字越低,等級越高 如果沒在printk()註明分類代碼,則訊息的預設分類為DEFAULT_MESSAGE_LOGLEVEL(參閱cd /usr /src /kernel /printk.c)

4 P102 KERN_EMERG KERN_ALERT KERN_CRIT KERNERP KERN_WARING KERN_NOTICE
KERN_INFO KERN_DEBUG

5 Console_loglevel變數初值為default_console_loglevel,可用sys_syslog系統呼叫來改變此值(1
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

6 核心訊息輸出流程 printk()將訊息寫入一個環形佇列,然後喚醒正在等待的行程—也就是被syslog()系統呼叫推入修眠狀態或是正在讀取/proc/kmsg 日誌記錄引擎(syslogd和klogd),這兩種是等效的,但是直接讀取/proc/kmsg會消耗掉緩衝區裡的訊息,不會留給其他行程讀取;syslog()能讓我決定是否要將訊息留在緩衝區,也讓其他行程可以讀取。

7 訊息開關 研發驅動程式初期,printk()很好用,可以輔助測試~除錯程式碼,但是正式釋出驅動程式時,該如何亦切拿掉所有除錯用途的printk()? 1..修改巨集名稱,取消或恢復個別列印敘述 2..只要在編譯之前變更cflags變數值,就可以關閉所有除錯訊息 3..同樣的列印敘述,要同時可用在kernel-space與 user-space的程式裡,讓我們可以用一致方式來控制除錯訊息 P106 熟悉C preprocessor的人,甚至可以擴充巨集定義,做出除錯等級的效果,例如以不同數值定義不同等級的程度

8 查詢除錯法 大量使用printk()的結果,將明顯拖慢系統效能,因為syslogd必須隨時將取得的核心訊息寫入日誌檔,美印出一行訊息,就會引發一次磁碟動作 解決辦法1 : 修改/etc/syslog.conf,在日誌檔的檔名之前加一個減號(魔術記號,它要求syslogd不必每次收到新訊息就寫入磁碟) 解決辦法2 : 以其他程式來替代klogd(cat/proc/kmesg) 最佳辦法 : 是必要時才向系統查詢,Unix提供許多ps, netstat, vmstat等取得系統資訊的工具 對driver設計時而言,查詢系統資訊的主要管道有: •將資訊輸出到/proc檔案系統. •適用ioctl作業方式.

9 使用/proc檔案系統 /proc是靠軟體模擬出來的特殊檔案系統,並不實際存在於硬碟上,而是核心提供給user-space的資訊窗口
以/proc/modules為例,當你讀取它時它會顯示目前載入哪些模組,但此檔案系統的長度都為0.

10 Linux許多系統工具,如ps, top,uptime等都是從 /proc取得它們所需要的資訊
任何欲支援/proc的模組,都必須引入<linux/proc_fs.h>,來定義適當的函式 建立/proc檔案 : 驅動程式必須製作一備查函式,讓它在檔案被存取的時,及時供應資料,核心也會配置一個記憶頁給它,核心會自動將我寫在該記憶頁的資料傳回user-space

11 eof:指向一個整數值,驅動程式以此值讓核心知道它沒有資料要傳回
備查函式介面: int (*read_proc)(char *page,char **start,off_t offset,int count,int *eof ,void *data); page指標:指向核心預先配置的記憶頁 start:用來表示有效資料的起點位置 eof:指向一個整數值,驅動程式以此值讓核心知道它沒有資料要傳回 offset:若檔案超過一記憶頁大小,可利用分批傳輸的方式,先將*start指向page,讓呼叫者知道新資料是放在page緩衝區的開頭,將*start指向offset bytes之後的下一個位元組 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 定義好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 第三引數的說明: •在/proc檔案系統下的每一個子目錄,都有各自專屬的proc_dir_entry結構來描述 •描述/proc/driver子目錄的結構為 proc_root_driver,描述/proc/bus子目錄的結構為proc_bus •若此引數設定為NULL,代表入口點設置在/proc目錄下

15 •在/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 建立新的/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 在模組被載卸之前必須先移除相關的/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 Ioctl作業方法 Ioctl()是作用在檔案描述單元(file descriptor)的一種系統呼叫,接受一個數值引數,一個可有可無的指標引數 數值引數--ioctl()所要執行的命令 指標引數--該命令的作業參數 •使用ioctl()來取得資訊,比較困難,因為要另外寫一個測試程式來發出呼叫,但是比讀取/proc的速度快 •沒有單記憶頁的限制,不必將資料拆解成片段 •不同於任何人都可見的/proc檔案,ioctl能將擷取除錯資訊的功能留在驅動程式裡面(因為不會影響效能) •詳情請待第五章

19 觀測除錯法 有時候,對於一些不太嚴重的小問題,用觀察user-space應用程式的行為,就可以察覺出來 將程式交給debugger來逐步執行
在適當地方印出訊息 將程式交給strace來執行

20 strace提供的除錯資訊,直接取自系統核心本身,能顯示出由user-space程式所發出的所有系統呼叫,輸入輸出資料是否一致
#strace ls /dev > /dev/scull0 strace最有用之處,在於能讓我們從系統呼叫中發現執行期的錯誤,一般應用程式中的perror()往往不夠詳細

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

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

23 範例: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不是合理指標值

24 oops訊息包括 : fault當時CPU的狀態,包括各暫存器的值等…
但是~~看不懂那些十六進位的數字訊息,要想辦法把數字轉換成有意義的符號 klogd ( 自動轉換它所經手的核心訊息) ksymoops ( 必須靠使用者自己操作 )

25 klogd 必須先有目前核心的system.map符號表檔案( /boot 下) -p選項( paranoid謹慎 )啟動klogd,讓他每次收到oops訊息,都會重讀一次符號表

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

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

28 >>EIP; df8a90e3 <[faulty]faulty_write+3/14> <=====
Trace; c0145de3 <sys_write+a3/9f0> Trace; c <__up_wakeup+1097/1470> Code; df8a90e3 <[faulty]faulty_write+3/14> <_EIP>: Code; df8a90e3 <[faulty]faulty_write+3/14> <===== 0: c movl $0x0,0x0 <===== Code; df8a90ea <[faulty]faulty_write+a/14> 7: Code; df8a90ed <[faulty]faulty_write+d/14> a: 31 c xor %eax,%eax Code; df8a90ef <[faulty]faulty_write+f/14> c: c leave Code; df8a90f0 <[faulty]faulty_write+10/14> d: c ret Code; df8a90f1 <[faulty]faulty_write+11/14> e: 8d lea 0x0(%esi),%esi Code; df8a90f4 <[faulty]faulty_init+0/0> 11: push %ebp Code; df8a90f5 <[faulty]init_module+1/44> 12: 89 e mov %esp,%ebp

29 除錯工具 GDB gdb /usr/src/linux/vmlinux /proc/kcore
反組譯 : (gdb) x/i <addr> Ex: (gdb) x/20i 0xc

30 0xc : push %ebp 0xc : mov %esp,%ebp 0xc : sub $0x8,%esp 0xc : sub $0x,%esp 0xc : push $0xc 0xc800206e: call 0xc0114e50 <printk> 0xc : add $0x10,%esp

31 KGDB補強程式 為了解決gdb在除錯核心時,不能發揮全部功能的遺憾 準備兩台電腦。一台跑經過kgdb補強過的受測核心,另一台跑gdb,兩台相連

32 CDA分析當機狀態資料 Crash dump analyzers讓系統發生故障時,能記錄下當時的系統狀態,以便我們事後分析故障原因 使用者不會抄寫oops訊息,我們可以事先安裝這套在他們的機器上

33 User-mode Linux移植版 ( 一種有趣的概念,並非在某種新型硬體平台上運行,而是在一個具備linux系統呼叫介面的虛擬機器上執行,適合讓設計師構思程式架構的雛形 ) Linux Trace Toolkit ( 回報時序資訊,可清楚某段時間內所發生的大小事情 ) Dynamic Probes ( IBM,特殊堆疊語法的探針,可回報訊息到user-space,改變暫存器內容 )

34 書中小錯誤 P104 倒數第五行 km”e”sg


Laste ned ppt "偵錯技術."

Liknende presentasjoner


Annonser fra Google