歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> 更多Linux >> gdb (GNU 調試器):基礎

gdb (GNU 調試器):基礎

日期:2017/2/27 14:14:04   编辑:更多Linux

摘要 關於調試 Linux 代碼的有用技巧(2004-03-02 10:31:01) By 泛舟, 出處:http://www-900.ibm.com/developerWorks/cn/linux/tips/l-gdb/index2.sHtml    就調試本機可執行文件(即不是 Java* 或 perl 等)而言,使用 gdb 就對了。gdb 可用於源代碼級調試,以及跟蹤沒有源代碼的程序或檢查某個終止的程序留下的核心文件。遺憾的是,當您從來沒有使用過它,或者已經有一段時間沒有使用它時,使用它來做這些工作可能很困難。圖 1 展示了使用 gdb 來進行調試所需的每個命令。 Command Description file load program b set breakpoint r run c continue s step (line) si step (machine instrUCtion) n next (step over function call) finish run until function returns i r show all registers i r show specific register l list source p display value set args set command line arguments 圖 1    要將 gdb 用作源代碼級調試器,請確保在包括調試符號的情況下編譯程序;這就是 gcc 的 -g 選項。對於啟動 gdb,您可以通過輸入 gdb programname(此例中是 gdb simple),或者通過單獨運行 gdb 本身並使用 file 命令加載可執行文件來達到目的。    要設置基本的斷點,您可以在某個函數名稱或行號上中斷。例如,b 27 將在當前文件的第 27 行上設置了一個斷點。有兩種使用函數名稱的方式:b main 在函數 main 中的第一行可執行代碼上中斷,b *main 在 main 的入口地址上設置一個斷點(如果打算單步調試函數的每條指令,這樣是很有用的)。    一旦設置了第一個斷點,可使用 run 或 r 來啟動程序並運行到第一個斷點。還可以不帶任何斷點運行程序,如果您不知道程序是在何處崩潰的,這樣將很有幫助。當您命中一個斷點 c 或 continue 時,程序將恢復執行,直至命中下一個斷點。    step“單步”調試源代碼行。Step instruction (si) 單步調試機器代碼行(當您單步調試優化過的代碼時,si 指令可能特別有用,這將在後面介紹)。 next 工作起來就像 step,但是它不跟蹤進入函數調用(如果的確錯誤地跟蹤進入了函數調用,可使用 finish 來完成該函數,然後在它返回的地方中斷)。    單獨的 info register(i r)本身顯示所有寄存器的值(浮點值除外),不過您可以指定一個寄存器名稱。在 31 位系統上,通用寄存器被命名為 gpr0、gpr1、gpr2,等等;在 64 位系統上,它們被命名為 r0、r1、r2,等等。浮點寄存器遵循相同的命名約定:在 31 位系統上是 fpr0、fpr1、fpr2,等等;在 64 位系統上是 f0、f1、f2,等等。    “l”列出程序當前停止位置周圍的源代碼。您還可以指定開始列出代碼的行號或要列出的函數名稱。print 允許您打印程序中任何變量的值。 print 的一個最好的優點在於,它會為您取出一個結構中的所有值,或允許您直接引用該結構的一部分: Breakpoint 1, main () at simple.c:30 30 boink.boik = &r1; (gdb) print boink $3 = {boik = 0x0} (gdb) print boink.boik $4 = (int *) 0x0    最後,set args 為程序設置命令行參數。您也可以在執行 run 時指定命令行參數,但是 set args 將使參數在 run 的多次執行中都有效。 gdb Post Mortem    當程序意外地終止時,內核會嘗試產生一個核心文件,以圖判斷發生了什麼錯誤。然而,核心文件通常不是在默認設置值下產生的。這可以使用 ulimit 命令來改變。ulimit -c unlimited 幫助確保您獲得應用程序的完整核心文件。    雖然核心文件當前僅提供多線程應用程序中的有限的值,不過 2.5 版的開發內核已開始處理這個問題。預計 2.6 版的內核中會提供一些理想的線程改進。 Command Description


file load program core load core file BT back trace where same as back trace i f frame information up move up stack down move down stack frame jump to frame disassem display function's machine code i locals display local variable values 圖 2    圖 2 突出顯示了一系列便利的 post mortem 命令。 (gdb) file simple Reading symbols from simple...done. (gdb) core core Core was generated by `./simple'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld.so.1...done. Loaded symbols for /lib/ld.so.1 #0 0x400ab738 in memcpy () from /lib/libc.so.6 (gdb) where #0 0x400ab738 in memcpy () from /lib/libc.so.6 #1 0x40066e in main () at simple.c:34 #2 0x40041eb8 in __libc_start_main () from /lib/libc.so.6 #3 0x4004ac in _start () (gdb) i f Stack level 0, frame at 0x7ffff7a0: pswa = 0x400ab738 in memcpy; saved pswa 0x0 (FRAMELESS), called by frame at 0x7ffff7a0 Arglist at 0x7ffff7a0, args: Locals at 0x7ffff7a0, Previous frame's sp is 0x0 (gdb) up #1 0x40066e in main () at simple.c:34 34 memcpy (doink.boik, boink.boik, sizeof(boink.boik)); (gdb) i locals doink = {boik = 0x4019a0} boink = {boik = 0x0} (gdb) ptype boink.boik type = int * (gdb) print *boink.boik Cannot Access memory at address 0x0 (gdb) print *doink.boik $1 = 4 圖 3    圖 3 簡要顯示了一個核心程序的完整運行過程。同樣,我們使用了 simple 程序。 但不是手動加載程序和核心文件,而是從命令行調入: gdb simple core    在加載符號之後,gdb 將指出程序在何處終止。注意當前幀 #0 包含前一節中計算的地址。gdb 將在 31 位系統上截去高位,僅顯示指令地址。 還要注意幀 #1 包含 gpr14 中的返回地址。    接著往下看,i f 提供了關於當前堆棧幀的信息。在堆棧幀中往上移到 main,這就是我們離開該幀的地方(即調用 memcpy 的地方)。簡單的 i locals 提供了傳遞給 memcpy 的變量的值,其中一個變量 boink.boik 的值為 0x0。使用 ptype 來檢查變量類型,這樣將確認它是一個整型指針,並且如果目的是為了拷貝內容到其中,它就不應該是 0x0。最後一個選項是使用 print,通過一個星號(*)來解除指針引用,以便接收值。 處理優化過的代碼    先前,我曾提到當您在源代碼級調試優化過的代碼時,gdb 可能變得有點棘手。編譯器優化一些代碼的執行順序以最大化性能。圖 4 顯示了這樣一個例子。您可以看到行號如何從 32 切換到 30 然後又切換回 32。 (gdb) break main Breakpoint 1 at 0x800007a8: file simple.c, line 32. (gdb) r Starting program: /home/grundym/foo/simple Breakpoint 1, main () at simple.c:32 32 do_one_thing(&doink); (gdb) s

30 doink.boik = &r1; (gdb) 32 do_one_thing(&doink); (gdb) do_one_thing (pnum_times=0x1fffffff690) at simple.c:47 47 for (i = 0; i < 4; i++) { 圖 4    如何處理這種情況呢?使用 si 和 ni(next instruction;它類似 si,但是會跳過子例程調用)將非常有幫助。 在這個層次上,很好理解 zArchitecture 是有所幫助的。 (gdb) break *main Breakpoint 1 at 0x80000794: file simple.c, line 27. (gdb) display /i $pswa (gdb) r Starting program: /home/grundym/foo/simple Breakpoint 1, main () at simple.c:27 27 { 1: x/i $pswa 0x80000794 : EB AF F0 50 00 24 stmg %r10,%r15,80(%r15) (gdb) si 0x8000079a 27 { 1: x/i $pswa 0x8000079a : B9 04 00 1F lgr %r1,%r15 (gdb) 0x8000079e 27 { 1: x/i $pswa 0x8000079e : A7 FB FF 58 aghi %r15,-168 (gdb) 0x800007a2 in main () at simple.c:27 27 { 1: x/i $pswa 0x800007a2 : E3 10 F0 00 00 24 stg %r1,0(%r15) (gdb) 32 do_one_thing(&doink); 1: x/i $pswa 0x800007a8 : 41 C0 F0 A0 la %r12,160(%r15) (gdb) 30 doink.boik = &r1; 1: x/i $pswa 0x800007ac : C0 10 00 00 08 C2 larl %r1,0x80001930 (gdb) 0x800007b2 30 doink.boik = &r1; 1: x/i $pswa 0x800007b2 : E3 10 F0 A0 00 24 stg %r1,160(%r15) (gdb)



圖 4    如何處理這種情況呢?使用 si 和 ni(next instruction;它類似 si,但是會跳過子例程調用)將非常有幫助。 在這個層次上,很好理解 zArchitecture 是有所幫助的。 (gdb) break *main Breakpoint 1 at 0x80000794: file simple.c, line 27. (gdb) display /i $pswa (gdb) r Starting program: /home/grundym/foo/simple Breakpoint 1, main () at simple.c:27 27 { 1: x/i $pswa 0x80000794 : EB AF F0 50 00 24 stmg %r10,%r15,80(%r15) (gdb) si 0x8000079a 27 { 1: x/i $pswa 0x8000079a : B9 04 00 1F lgr %r1,%r15 (gdb) 0x8000079e 27 { 1: x/i $pswa 0x8000079e : A7 FB FF 58 aghi %r15,-168 (gdb) 0x800007a2 in main () at simple.c:27 27 { 1: x/i $pswa 0x800007a2 : E3 10 F0 00 00 24 stg %r1,0(%r15) (gdb) 32 do_one_thing(&doink); 1: x/i $pswa 0x800007a8 : 41 C0 F0 A0 la %r12,160(%r15) (gdb) 30 doink.boik = &r1; 1: x/i $pswa 0x800007ac : C0 10 00 00 08 C2 larl %r1,0x80001930 (gdb) 0x800007b2 30 doink.boik = &r1; 1: x/i $pswa 0x800007b2 : E3 10 F0 A0 00 24 stg %r1,160(%r15) (gdb)



Copyright © Linux教程網 All Rights Reserved