Linux設備驅動第四篇:以Oops信息定位代碼行為例談驅動調試方法

上一篇我們大概聊了如何寫一個簡單的字符設備驅動,我們不是神,寫代碼肯定會出現問題,我們需要在編寫代碼的過程中不斷調試。在普通的c應用程序中,我們經常使用printf來輸出信息,或者使用gdb來調試程序,那么驅動程序如何調試呢?我們知道在調試程序時經常遇到的問題就是野指針或者數組越界帶來的問題,在應用程序中運行這種程序就會報segmentation fault的錯誤,而由于驅動程序的特殊性,出現此類情況后往往會直接造成系統宕機,并會拋出oops信息。那么我們如何來分析oops信息呢,甚至根據oops信息來定位具體的出錯的代碼行呢?下面就根據一個簡單的實例來說明如何調試驅動程序。
如何根據Oops定位代碼行

我們借用linux設備驅動第二篇:構造和運行模塊里面的hello world程序來演示出錯的情況,含有錯誤代碼的hello world如下

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void)
{
        char *p = NULL;
        memcpy(p, "test", 4);
        printk(KERN_ALERT "Hello, world\n");
        return 0;
}
static void hello_exit(void)
{

        printk(KERN_ALERT "Goodbye, cruel world\n");
}

module_init(hello_init);
module_exit(hello_exit);

Makefile文件如下:

ifneq ($(KERNELRELEASE),)
obj-m := helloworld.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif

clean:
        rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules.order  Module.symvers

很明顯,以上代碼的第8行是一個空指針錯誤。insmod后會出現下面的oops信息:

[  459.516441] BUG: unable to handle kernel NULL pointer dereference at           (null)
[  459.516445] 
[  459.516448] PGD 0 
[  459.516450] Oops: 0002 [#1] SMP 
[  459.516452] Modules linked in: helloworld(OE+) vmw_vsock_vmci_transport vsock coretemp crct10dif_pclmul crc32_pclmul ghash_clmulni_intel aesni_intel vmw_balloon snd_ens1371 aes_x86_64 lrw snd_ac97_codec gf128mul glue_helper ablk_helper cryptd ac97_bus gameport snd_pcm serio_raw snd_seq_midi snd_seq_midi_event snd_rawmidi snd_seq snd_seq_device snd_timer vmwgfx btusb ttm snd drm_kms_helper drm soundcore shpchp vmw_vmci i2c_piix4 rfcomm bnep bluetooth 6lowpan_iphc parport_pc ppdev mac_hid lp parport hid_generic usbhid hid psmouse ahci libahci floppy e1000 vmw_pvscsi vmxnet3 mptspi mptscsih mptbase scsi_transport_spi pata_acpi [last unloaded: helloworld]
[  459.516476] CPU: 0 PID: 4531 Comm: insmod Tainted: G           OE 3.16.0-33-generic #44~14.04.1-Ubuntu
[  459.516478] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 05/20/2014
[  459.516479] task: ffff88003821f010 ti: ffff880038fa0000 task.ti: ffff880038fa0000
[  459.516480] RIP: 0010:[<ffffffffc061400d>]  [<ffffffffc061400d>] hello_init+0xd/0x30 [helloworld]
[  459.516483] RSP: 0018:ffff880038fa3d40  EFLAGS: 00010246
[  459.516484] RAX: ffff88000c31d901 RBX: ffffffff81c1a020 RCX: 000000000004b29f
[  459.516485] RDX: 000000000004b29e RSI: 0000000000000017 RDI: ffffffffc0615024
[  459.516485] RBP: ffff880038fa3db8 R08: 0000000000015e80 R09: ffff88003d615e80
[  459.516486] R10: ffffea000030c740 R11: ffffffff81002138 R12: ffff88000c31d0c0
[  459.516487] R13: 0000000000000000 R14: ffffffffc0614000 R15: ffffffffc0616000
[  459.516488] FS:  00007f8a6fa86740(0000) GS:ffff88003d600000(0000) knlGS:0000000000000000
[  459.516489] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  459.516490] CR2: 0000000000000000 CR3: 0000000038760000 CR4: 00000000003407f0
[  459.516522] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  459.516524] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[  459.516524] Stack:
[  459.516537]  ffff880038fa3db8 ffffffff81002144 0000000000000001 0000000000000001
[  459.516540]  0000000000000001 ffff880028ab5040 0000000000000001 ffff880038fa3da0
[  459.516541]  ffffffff8119d0b2 ffffffffc0616018 00000000bd1141ac ffffffffc0616018
[  459.516543] Call Trace:
[  459.516548]  [<ffffffff81002144>] ? do_one_initcall+0xd4/0x210
[  459.516550]  [<ffffffff8119d0b2>] ? __vunmap+0xb2/0x100
[  459.516554]  [<ffffffff810ed9b1>] load_module+0x13c1/0x1b80
[  459.516557]  [<ffffffff810e9560>] ? store_uevent+0x40/0x40
[  459.516560]  [<ffffffff810ee2e6>] SyS_finit_module+0x86/0xb0
[  459.516563]  [<ffffffff8176be6d>] system_call_fastpath+0x1a/0x1f
[  459.516564] Code: <c7> 04 25 00 00 00 00 74 65 73 74 31 c0 48 89 e5 e8 a2 86 14 c1 31 
[  459.516573] RIP  [<ffffffffc061400d>] hello_init+0xd/0x30 [helloworld]
[  459.516575]  RSP <ffff880038fa3d40>
[  459.516576] CR2: 0000000000000000
[  459.516578] ---[ end trace 7c52cc8624b7ea60 ]---

下面簡單分析下oops信息的內容。
由BUG: unable to handle kernel NULL pointer dereference at (null)知道出錯的原因是使用了空指針。標紅的部分確定了具體出錯的函數。Modules linked in: helloworld表明了引起oops問題的具體模塊。call trace列出了函數的調用信息。這些信息中其中標紅的部分是最有用的,我們可以根據其信息找到具體出錯的代碼行。下面就來說下,如何定位到具體出錯的代碼行。
第一步我們需要使用objdump把編譯生成的bin文件反匯編,我們這里就是helloworld.o,如下命令把反匯編信息保存到err.txt文件中:
objdump helloworld.o -D > err.txt
err.txt內容如下:

helloworld.o:     file format elf64-x86-64


Disassembly of section .text:

<span style="color:#ff0000;">0000000000000000 <init_module>:</span>
   0:    e8 00 00 00 00           callq  5 <init_module+0x5>
   5:    55                       push   %rbp
   6:    48 c7 c7 00 00 00 00     mov    $0x0,%rdi
   d:    c7 04 25 00 00 00 00     movl   $0x74736574,0x0
  14:    74 65 73 74 
  18:    31 c0                    xor    %eax,%eax
  1a:    48 89 e5                 mov    %rsp,%rbp
  1d:    e8 00 00 00 00           callq  22 <init_module+0x22>
  22:    31 c0                    xor    %eax,%eax
  24:    5d                       pop    %rbp
  25:    c3                       retq   
  26:    66 2e 0f 1f 84 00 00     nopw   %cs:0x0(%rax,%rax,1)
  2d:    00 00 00 
<cleanup_module>:
  30:    e8 00 00 00 00           callq  35 <cleanup_module+0x5>
  35:    55                       push   %rbp
  36:    48 c7 c7 00 00 00 00     mov    $0x0,%rdi
  3d:    31 c0                    xor    %eax,%eax
  3f:    48 89 e5                 mov    %rsp,%rbp
  42:    e8 00 00 00 00           callq  47 <cleanup_module+0x17>
  47:    5d                       pop    %rbp
  48:    c3                       retq   

Disassembly of section .rodata.str1.1:
<.rodata.str1.1>:
   0:    01 31                    add    %esi,(%rcx)
   2:    48                       rex.W
   3:    65                       gs
   4:    6c                       insb   (%dx),%es:(%rdi)
   5:    6c                       insb   (%dx),%es:(%rdi)
   6:    6f                       outsl  %ds:(%rsi),(%dx)
   7:    2c 20                    sub    $0x20,%al
   9:    77 6f                    ja     7a <cleanup_module+0x4a>
   b:    72 6c                    jb     79 <cleanup_module+0x49>
   d:    64 0a 00                 or     %fs:(%rax),%al
  10:    01 31                    add    %esi,(%rcx)
  12:    47 6f                    rex.RXB outsl %ds:(%rsi),(%dx)
  14:    6f                       outsl  %ds:(%rsi),(%dx)
  15:    64                       fs
  16:    62                       (bad)  
  17:    79 65                    jns    7e <cleanup_module+0x4e>
  19:    2c 20                    sub    $0x20,%al
  1b:    63 72 75                 movslq 0x75(%rdx),%esi
  1e:    65                       gs
  1f:    6c                       insb   (%dx),%es:(%rdi)
  20:    20 77 6f                 and    %dh,0x6f(%rdi)
  23:    72 6c                    jb     91 <cleanup_module+0x61>
  25:    64 0a 00                 or     %fs:(%rax),%al

Disassembly of section .modinfo:
<__UNIQUE_ID_license0>:
   0:    6c                       insb   (%dx),%es:(%rdi)
   1:    69 63 65 6e 73 65 3d     imul   $0x3d65736e,0x65(%rbx),%esp
   8:    44 75 61                 rex.R jne 6c <cleanup_module+0x3c>
   b:    6c                       insb   (%dx),%es:(%rdi)
   c:    20 42 53                 and    %al,0x53(%rdx)
   f:    44 2f                    rex.R (bad) 
  11:    47 50                    rex.RXB push %r8
  13:    4c                       rex.WR
    ...

Disassembly of section .comment:
<.comment>:
   0:    00 47 43                 add    %al,0x43(%rdi)
   3:    43 3a 20                 rex.XB cmp (%r8),%spl
   6:    28 55 62                 sub    %dl,0x62(%rbp)
   9:    75 6e                    jne    79 <cleanup_module+0x49>
   b:    74 75                    je     82 <cleanup_module+0x52>
   d:    20 34 2e                 and    %dh,(%rsi,%rbp,1)
  10:    38 2e                    cmp    %ch,(%rsi)
  12:    32 2d 31 39 75 62        xor    0x62753931(%rip),%ch        # 62753949 <cleanup_module+0x62753919>
  18:    75 6e                    jne    88 <cleanup_module+0x58>
  1a:    74 75                    je     91 <cleanup_module+0x61>
  1c:    31 29                    xor    %ebp,(%rcx)
  1e:    20 34 2e                 and    %dh,(%rsi,%rbp,1)
  21:    38 2e                    cmp    %ch,(%rsi)
  23:    32 00                    xor    (%rax),%al

Disassembly of section __mcount_loc:
<__mcount_loc>:

由oops信息我們知道出錯的地方是hello_init的地址偏移0xd。而有dump信息知道,hello_init的地址即init_module的地址,因為hello_init即本模塊的初始化入口,如果在其他函數中出錯,dump信息中就會有相應符號的地址。由此我們得到出錯的地址是0xd,下一步我們就可以使用addr2line來定位具體的代碼行:
addr2line -C -f -e helloworld.o d

其他調試手段

以上就是通過oops信息來獲取具體的導致崩潰的代碼行,這種情況都是用在遇到比較嚴重的錯誤導致內核掛掉的情況下使用的,另外比較常用的調試手段就是使用printk來輸出打印信息。printk的使用方法類似printf,只是要注意一下打印級別,詳細介紹在linux設備驅動第二篇:構造和運行模塊中已有描述,另外需要注意的是大量使用printk會嚴重拖慢系統,所以使用過程中也要注意。

以上兩種調試手段是我工作中最常用的,還有一些其他的調試手段,例如使用/proc文件系統,使用trace等用戶空間程序,使用gdb,kgdb等,這些調試手段一般不太容易使用或者不太方便使用,所以這里就不在介紹了。

介紹完驅動的調試方法后,下一篇會介紹下linux驅動的并發與競態,歡迎關注

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,613評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 姓名:吳兆陽 學號:14020199009 轉自韋東山 嵌牛導讀:對嵌入式初學者,沒有足夠的視野選擇一個合適投入方...
    吳兆陽閱讀 2,396評論 0 4
  • 現在天氣變化很大,不少人的鼻炎又開始復發了,雖然流鼻涕、鼻塞、頭痛不是什么大病,但是卻會讓人特別難受,而且久病不治...
    奧西里斯天空龍閱讀 916評論 0 0
  • 古塔同色系
    七夢閱讀 202評論 0 0