Linux設(shè)備驅(qū)動第四篇:以O(shè)ops信息定位代碼行為例談驅(qū)動調(diào)試方法

上一篇我們大概聊了如何寫一個簡單的字符設(shè)備驅(qū)動溃肪,我們不是神算谈,寫代碼肯定會出現(xiàn)問題糠雨,我們需要在編寫代碼的過程中不斷調(diào)試。在普通的c應(yīng)用程序中奖亚,我們經(jīng)常使用printf來輸出信息崎场,或者使用gdb來調(diào)試程序,那么驅(qū)動程序如何調(diào)試呢遂蛀?我們知道在調(diào)試程序時經(jīng)常遇到的問題就是野指針或者數(shù)組越界帶來的問題谭跨,在應(yīng)用程序中運行這種程序就會報segmentation fault的錯誤,而由于驅(qū)動程序的特殊性李滴,出現(xiàn)此類情況后往往會直接造成系統(tǒng)宕機螃宙,并會拋出oops信息。那么我們?nèi)绾蝸矸治鰋ops信息呢所坯,甚至根據(jù)oops信息來定位具體的出錯的代碼行呢谆扎?下面就根據(jù)一個簡單的實例來說明如何調(diào)試驅(qū)動程序。
如何根據(jù)Oops定位代碼行

我們借用linux設(shè)備驅(qū)動第二篇:構(gòu)造和運行模塊里面的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后會出現(xiàn)下面的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信息的內(nèi)容状土。
由BUG: unable to handle kernel NULL pointer dereference at (null)知道出錯的原因是使用了空指針无蜂。標紅的部分確定了具體出錯的函數(shù)。Modules linked in: helloworld表明了引起oops問題的具體模塊蒙谓。call trace列出了函數(shù)的調(diào)用信息斥季。這些信息中其中標紅的部分是最有用的,我們可以根據(jù)其信息找到具體出錯的代碼行累驮。下面就來說下酣倾,如何定位到具體出錯的代碼行。
第一步我們需要使用objdump把編譯生成的bin文件反匯編谤专,我們這里就是helloworld.o躁锡,如下命令把反匯編信息保存到err.txt文件中:
objdump helloworld.o -D > err.txt
err.txt內(nèi)容如下:

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即本模塊的初始化入口,如果在其他函數(shù)中出錯墅垮,dump信息中就會有相應(yīng)符號的地址惕医。由此我們得到出錯的地址是0xd,下一步我們就可以使用addr2line來定位具體的代碼行:
addr2line -C -f -e helloworld.o d

其他調(diào)試手段

以上就是通過oops信息來獲取具體的導(dǎo)致崩潰的代碼行算色,這種情況都是用在遇到比較嚴重的錯誤導(dǎo)致內(nèi)核掛掉的情況下使用的抬伺,另外比較常用的調(diào)試手段就是使用printk來輸出打印信息。printk的使用方法類似printf灾梦,只是要注意一下打印級別峡钓,詳細介紹在linux設(shè)備驅(qū)動第二篇:構(gòu)造和運行模塊中已有描述妓笙,另外需要注意的是大量使用printk會嚴重拖慢系統(tǒng),所以使用過程中也要注意能岩。

以上兩種調(diào)試手段是我工作中最常用的寞宫,還有一些其他的調(diào)試手段,例如使用/proc文件系統(tǒng)拉鹃,使用trace等用戶空間程序辈赋,使用gdb,kgdb等膏燕,這些調(diào)試手段一般不太容易使用或者不太方便使用钥屈,所以這里就不在介紹了。

介紹完驅(qū)動的調(diào)試方法后坝辫,下一篇會介紹下linux驅(qū)動的并發(fā)與競態(tài)篷就,歡迎關(guān)注

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市近忙,隨后出現(xiàn)的幾起案子竭业,更是在濱河造成了極大的恐慌,老刑警劉巖及舍,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件未辆,死亡現(xiàn)場離奇詭異,居然都是意外死亡击纬,警方通過查閱死者的電腦和手機鼎姐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來更振,“玉大人,你說我怎么就攤上這事饭尝】贤螅” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵钥平,是天一觀的道長实撒。 經(jīng)常有香客問我,道長涉瘾,這世上最難降的妖魔是什么知态? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮立叛,結(jié)果婚禮上负敏,老公的妹妹穿的比我還像新娘。我一直安慰自己秘蛇,他們只是感情好其做,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布顶考。 她就那樣靜靜地躺著,像睡著了一般妖泄。 火紅的嫁衣襯著肌膚如雪驹沿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天蹈胡,我揣著相機與錄音渊季,去河邊找鬼。 笑死罚渐,一個胖子當著我的面吹牛梭域,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播搅轿,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼病涨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了璧坟?” 一聲冷哼從身側(cè)響起既穆,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雀鹃,沒想到半個月后幻工,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡黎茎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年囊颅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傅瞻。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡踢代,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嗅骄,到底是詐尸還是另有隱情胳挎,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布溺森,位于F島的核電站慕爬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏屏积。R本人自食惡果不足惜医窿,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望炊林。 院中可真熱鬧姥卢,春花似錦、人聲如沸铛铁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至括眠,卻和暖如春彪标,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背掷豺。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工捞烟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人当船。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓题画,卻偏偏與公主長得像,于是被迫代替她去往敵國和親德频。 傳聞我的和親對象是個殘疾皇子苍息,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,078評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)壹置,斷路器竞思,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 姓名:吳兆陽 學(xué)號:14020199009 轉(zhuǎn)自韋東山 嵌牛導(dǎo)讀:對嵌入式初學(xué)者,沒有足夠的視野選擇一個合適投入方...
    吳兆陽閱讀 2,385評論 0 4
  • 現(xiàn)在天氣變化很大钞护,不少人的鼻炎又開始復(fù)發(fā)了盖喷,雖然流鼻涕、鼻塞难咕、頭痛不是什么大病课梳,但是卻會讓人特別難受,而且久病不治...
    奧西里斯天空龍閱讀 895評論 0 0
  • 古塔同色系
    七夢閱讀 201評論 0 0