歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

《Linux4.0設備驅動開發詳解》筆記

21.1 GDB調式的方法

GDB的四個功能:啟動程序,可以按照工程師自定義的要求運行程序

讓被調使得程序可以在指定的地方停住,斷點可以是條件表達式

當程序停住時,可以檢查此程序發生的事,並追蹤上文

動態的改變程序的執行環境

調式內核和應用程序時調試的命令是相同的

基本命令

list命令(縮寫l):列出代碼

list ,顯示程序第linenum行周圍的源代碼

list ,顯示函數名為function的函數源程序

list,顯示當前行前後的源程序

list -,顯示當前行前面的源程序

run命令:運行程序

程序運行參數

set args:指定運行時的參數,如set args 10 20 30

show args:查看設置好的運行參數

運行環境

path:設定程序的運行路徑

how paths:查看程序的運行路徑

set environment varname[=value]:設置環境變量,如set env USER=baohua;

show environment[varname]:查看環境變量

工作目錄

cd:相當於shell的cd命令

pwd:顯示當前所在的目錄

程序的輸入輸出

info terminal:顯示程序用到的終端的模式

run>outfile:重定向控制程序輸出

tty:指定輸入的終端設備,如tty/dev/ttyS1

break命令

break:在進入指定函數時停住

break:指定行號停住

break+offset/break-offset:當前 行號的前面或者後面offset行停住

break filename:linenum:在源文件filename的linenum行處停住

break filename:function:在源文件filename的function函數處停住

break*address:在程序運行的內存地址處停住

break:break命令沒有參數時,表示在下一條指令處停住

beak…if:…可以是上述的break、break+offset/break-offset中的參數,condition表示條件,在條件成立時停住

例如:在循環體中,可以設置break if i=100,表示當i為100時停住程序

info:查看斷點,如info breakpoints

、info break

(n表示斷點號)

單步命令

step:單步跟蹤,如果有函數調用,則進入該函數(進入該函數的前提是,此函數被編譯有debug信息),默認一條條執行,加上count,執行後面count指令然後停止

next:單步跟蹤,有函數則跳過,不加count,一條條執行,加上count則執行後面count條之後停住

set step-mode:set step-mode on用於打開step-mode模式

在進行step時,若跨過某沒有調試信息的函數,程序的執行會在該函數的第一條指令處停住,而不會跳過整個函數,這樣可以查看該函數的機器指令

finish:運行程序,直到當前函數完成返回,並打印函數返回時的堆棧地址、返回值及參數值等信息

until(縮寫為u):一直在循環體內執行單步而退步出來是一件令人煩惱的事情,用until命令可以運行程序直到退出循環體

stepi(縮寫為si)和nexti(縮寫為ni):這兩個命裡用於單步跟蹤一條機器指令,step和next時C語言級別的命令

運行display/i $pc命令之後,單步跟蹤會在大廚程序代買的同時打出機器指令,即匯編代碼

continue命令:當程序被停住後,可以用continue命令(縮寫為c,fg命令同continue命令)恢復程序的執行直到程序結束,或到達下一個斷點

命令格式為:continue/c/fg [ignore-count],ignore-count表示忽略其後多少次斷點

例如:假設設置了函數斷點add(),並觀察i,則在continue過程中,每次遇到add()函數或者i發生變化,程序就會停住

print命令:再掉是程序時,當程序被停住時,可以使用print命令(縮寫為p),或是同義命令inspect來查看當前程序的運行數據

命令格式:print print / 其中是表達式,也是被調試的程序總的表達式,時輸出的格式,比如,如果表達式按十六進制輸出,則時/x

表達式中,有幾種GDB所支持的操作符,他們可以用在任何一種語言中

@:是一個和數組有關的操作符

:: :指定一個在文件或是函數中的變量

{}:表示一個指向內存地址的類型為type的對象

例1:演示了查看sum[]數組的值的過程

當需要查看一段連續內存空間的值時,可以使用GDB的@操作符,@的左邊是第一個內存地址,@的右邊是想查看內存的長度

例2:動態申請內存

輸出格式:

x:十六進制

d:十進制

u:按十六進制,顯示無符號整型

o:八進制

t:二進制

a:十六進制

c:字符格式

f:浮點數格式

display命令:設置一些自動顯示的變量,當程序停住時,或是單步跟蹤時,這些變量會自動顯示

修改變量:print 變量=值

當GDB的print查看程序運行時的數據時,每個print都會被GDB記錄下來。GDB會以1,1,2,$3。。。這樣的方式為每一個print命令編號,可以用這個編號訪問前面的表達式

watch命令:觀察某個表達式(變量也是一種表達式)的值是否有了變化,有則馬上停止運行

watch:為表達式expr設置一個觀察點,一旦這個表達式發生了變化則停止運行

rwatch:當表達式(變量)被讀時,停止程序執行

awatch:當表達式(變量)的值被讀或者被寫時,停止運行

info watchpoints:列出當前所設置的所有觀察點

examine命令:查看內存地址中的值

語法:x/

[code]**//例1:**
(gdb) print sum
$2 = {133, 155, 0, 0, 0 ,0 ,0 ,0 ,0 ,0}
(gdb) next

Breakpoint 1, main () at gdb-example.c:25
25         sum[i] = add(array1[i], array2[i]):
(gdb) next
23         for(i = 0; i< 10; i++)
(gdb) print sum
$3 = ({133, 155, 143, 0, 0 ,0 ,0 ,0 ,0 ,0}

//例2:
int *array = (int *) malloc (len * sizeof(int));
在GDB調試過程中個,這樣實現這個動態數組的值:
p *array@len

//例3:
main()
{
    void *p = malloc(16);
    while(1);
}
//用如下命令來修改p指向的內存
(gdb) set *(unsigned char *)p='h'
(gdb) set *(unsigned char *)p='e'
(gdb) set *(unsigned char *)p='l'
(gdb) set *(unsigned char *)p='l'
(gdb) set *(unsigned char *)p='e'
//查看結果
(gdb) x/s p
0x804b008 "hello"
//查看函數func反匯編代碼
(gdb) disassemble func
Dump of assembler code for function func:
0x8048450 <func>:    push  %ebp
0x8048451 <func+1> mov   %esp,%ebp
...

21.2 內核調試

21.2.1 內核打印信息—-printk()

內核打印語句printk()會將內核信息輸出到內核信息緩沖區中,內核緩沖區時候在kernel/printk.c中通過以下語句靜態定義:

內核緩沖區是一個環形緩沖區(Ring Buffer),如果消息過多,則會將之前的消息沖刷掉

[code]static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
printk()的8個消息級別,分別是0~7,級別越低(數值越大),消息越不重要,0級時緊急事件級,7級時調試級

#define KERN_EMERG “<0>”:緊急事件,一般是系統崩潰前的提示信息

#define KERN_ALERT “<1>”:必須立即采取行動

#define KERN_CRIT “<2>”:臨界狀態,通常涉及嚴重的硬件或軟件操作失敗

#define KERN_ERR “<3>”:用於報告錯誤狀態,設備驅動程序會經常調用KERN_ERR來報告來自硬件的問題

#define KERN_WARNING “<4>”:對可能出現問題的情況進行警告,這類情況通常不會對系統造成嚴重的問題

#define KERN_NOTICE “<5>”:有必要進行提示的正常情形,許多與安全相關的狀況用這個級別進行匯報

#define KERN_INFO “<6>”:內核提示信息,很多驅動程序在啟動的時候,用這個級別打印他們找到的硬件信息

#define KERN_DEBUG “<7>”:用於調試

通過/proc/sys/kernel/printk文件可以調節printk()的輸出等級,該文件有4個等級

控制台日志級別:當前的打印級別,優先級高於該值的信息將被打印紙控制台

默認的信息日志級別:將用該優先級來打印沒有優先級前綴的消息,也就是直接寫printk(“xxx”)而不帶打印級別的情況下,會用該級別打印

最低的控制台日志級別:控制台日志級別可被設置的最小值(一般都是1)

默認的控制台日志級別:控制台日志級別的默認值

例5:Ubuntu上的輸出級別

[code]//例5:Ubuntu上的輸出級別
$ cat /proc/sys/kernel/printk
4   4   1   7
顯示內核打印信息方法

通過dmesg命令,如果使用dmesg -c命令,則不僅會顯示__log_buf,還會清除該緩沖區的內容

使用cat /proc/kmsg 命令,/proc/kmsg是一個“永無休止的文件”,因此,cat /porc/kmsg的進程只能通過“Ctrl+C”或kill終止

設備驅動中的調試函數

pr_debug(),pr_info()

使用pr_xxx()族API的好處是,可以在文件開頭通過pr_fmt()定義一個打印格式

例6:在kernel/watchdog.c的最開頭通過如下定義可以保證之後watchdog.c調用的所有pr_xxx()打印的消息都自動帶有“NMI watchdog: ”的前綴

dev_debug():如dev_dbg()、dev_err()、dev_info()等

使用dev_xxx()族API打印的時候,設備名稱會被自動加到打印消息的前頭

打印的附加信息,例7

func:輸出printk()調用所在的函數名LINE:輸出其所在的代碼行

FILE:輸出源代碼命令名

[code]//pr_debug()與pr_info()定義
#ifdef DEBUG
#define pr_debug(fmt,arg...) \
    printk(KERN_DEBUG fmt,##arg)
#else
static inline int __attribute__ ((format (printf,1,2))) pr_debug(cost char * fmt, ...)
{
    return 0;
}
#endif

#define pr_infor(fmt,arg ...) \
    printk(KERN_INFO fmt, ##arg)

[code]//例6:
#define pr_fmt(fmt) "NMI watchdog: " fmt

#include <linux/mm.h>
#include <linux/cpu.h>
#include <linux/nmi.h>
...
//例7:
printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d", #expr, __FILE__, __func__, LINE);

21.2.2 DEBUG_LL和EARLY_PRINTK

DEBUG_LL對應內核的Kernel low-level debugging功能,EARLY_PRINTK對應內核中一個早起的控制台

為了在內核的drivers/serial下的控制台驅動初始化之前支持打印,可以選擇上述兩個配置項,另外需要在bootargs中設置earlyprintk的選項

21.2.3 使用“/proc”

“/proc”是一個虛擬文件系統,通過它可以在Linux內核空間和用戶控件之間進行通信 在“/proc”文件系統中,可以將對虛擬文件的讀寫作為與內核中實體進行通信的一種手段,這些虛擬文件的內容都是動態的

“/proc”下的絕大多數文件是只讀的,以顯示內核信息為主,也不都是只讀,如修改/proc/sys/kernel/printk以改變printk()的打印級別

Linux系統的許多命令本身都是通過分析”/proc”下的文件來完成的,如ps、top等,例如,free命令通過分析/proc/meminfo文件的到可用內存信息

21.2.4 Oops

內核出現類似用戶空間的Segmentation Fault時,Oops會被打印到控制台和寫入內核log緩沖區 反匯編一個目標文件:arm-linux-gnueabihf-objdump -d -s xxx.o

21.2.5 BUG_ON()和WARN_ON()

內核中許多地方調用類似BUG()的語句,它非常像一個內核運行時的斷言,以為這本來不該執行到BUG()這條語句,一旦執行到即跑出Oops BUG()語句定義如下:

panic()定義在kernel/panic.c中,會導致內核崩潰,並打開Oops

[code]#define BUG()  do { \
    printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__"); \
    panic("BUG!");
}while(0)
BUG_ON():時BUG()的變體,只有當括號內的條件成立的時候,才拋出Oops

WARN_ON():在括號裡的條件成立的時候,內核會拋出棧回溯,但是不會panic(),這通常用於內核拋出一個警告,暗示某種不太合理的事情發生了

21.2.8 strace

strace是個有效的跟蹤工具,它的主要特點是可以被用來監視系統調用 既可以調試一個新開始的程序,也可以調試一個已經在運行的程序(這意味著把strace綁定到一個已有的PID上)

Copyright © Linux教程網 All Rights Reserved