GDB是一個由GNU開源組織發(fā)布的、UNIX/LINUX操作系統(tǒng)下的、基于命令行的、功能強(qiáng)大的程序調(diào)試工具。
對于一名Linux下工作的c++程序員,gdb是必不可少的工具;
GDB中的命令固然很多,但我們只需掌握其中十個左右的命令,就大致可以完成日常的基本的程序調(diào)試工作。
以下從一個完整的調(diào)試過程簡單說明最基本的幾個命令;
$gdb programmer # 啟動gdb
>break main # 設(shè)置斷點
>run # 運行調(diào)試程序
>next # 單步調(diào)試
>print var1 # 在調(diào)試過程中,我們需要查看當(dāng)前某個變量值的時候,使用print 命令打印該值
>list # 顯示當(dāng)前調(diào)試處的源代碼
>info b # 顯示當(dāng)前斷點設(shè)置情況
當(dāng)你完成了第一個程序調(diào)試之后,你當(dāng)然會需要更多的命令:關(guān)于gdb常用命令及各種調(diào)試方法詳見 :ref:gdb
;
同時,你需要更高效的調(diào)試:常用的調(diào)試命令都會有單字符的縮寫,使用縮寫更方便;同時,直接敲回車表示重復(fù)執(zhí)行上一步命令;這在單步調(diào)試時非常有用;
pstack是一個腳本工具,可顯示每個進(jìn)程的棧跟蹤。pstack 命令必須由相應(yīng)進(jìn)程的屬主或 root 運行。其核心實現(xiàn)就是使用了gdb以及thread apply all bt命令;
語法
$pstrack <program-pid>
示例
$ pstack 4551
Thread 7 (Thread 1084229984 (LWP 4552)):
#0 0x000000302afc63dc in epoll_wait () from /lib64/tls/libc.so.6
#1 0x00000000006f0730 in ubEPollExpoll ()
#2 0x00000000006f172a in ubNetReactorcallback ()
#3 0x00000000006fbbbb in ubUBTaskCALLBACK ()
#4 0x000000302b80610a in start_thread () from /lib64/tls/libpthread.so.0
#5 0x000000302afc6003 in clone () from /lib64/tls/libc.so.6
#6 0x0000000000000000 in ?? ()
strace常用來跟蹤進(jìn)程執(zhí)行時的系統(tǒng)調(diào)用和所接收的信號。在Linux世界,進(jìn)程不能直接訪問硬件設(shè)備,當(dāng)進(jìn)程需要訪問硬件設(shè)備(比如讀取磁盤文件,接收網(wǎng)絡(luò)數(shù)據(jù)等等)時,必須由用戶態(tài)模式切換至內(nèi)核態(tài)模式,通過系統(tǒng)調(diào)用訪問硬件設(shè)備。strace可以跟蹤到一個進(jìn)程產(chǎn)生的系統(tǒng)調(diào)用,包括參數(shù),返回值,執(zhí)行消耗的時間。
完整程序
strace -o output.txt -T -tt -e trace=all -p 28979
跟蹤28979進(jìn)程的所有系統(tǒng)調(diào)用(-e trace=all),并統(tǒng)計系統(tǒng)調(diào)用的花費時間,以及開始時間(以可視化的時分秒格式顯示),最后將記錄結(jié)果存在output.txt文件里面。
查看進(jìn)程正在做什么(實時輸出進(jìn)程執(zhí)行系統(tǒng)調(diào)用的情況)
$strace -p <process-pid>
關(guān)于strace的詳細(xì)介紹,詳見 :ref:strace
;
nm用來列出目標(biāo)文件的符號清單。
$nm myProgrammer
08049f28 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
080484dc R _IO_stdin_used
w _Jv_RegisterClasses
08049f18 d __CTOR_END__
08049f14 d __CTOR_LIST__
08049f20 D __DTOR_END__
08049f1c d __DTOR_LIST__
080485e0 r __FRAME_END__
08049f24 d __JCR_END__
08049f24 d __JCR_LIST__
0804a014 A __bss_start
0804a00c D __data_start
08048490 t __do_global_ctors_aux
08048360 t __do_global_dtors_aux
0804a010 D __dso_handle
w __gmon_start__
08048482 T __i686.get_pc_thunk.bx
08049f14 d __init_array_end
08049f14 d __init_array_start
08048480 T __libc_csu_fini
08048410 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804a014 A _edata
0804a01c A _end
080484bc T _fini
080484d8 R _fp_hw
080482b4 T _init
08048330 T _start
0804a014 b completed.6086
0804a00c W data_start
0804a018 b dtor_idx.6088
080483c0 t frame_dummy
080483e4 T main
U printf@@GLIBC_2.0
這些包含可執(zhí)行代碼的段稱為正文段。同樣地,數(shù)據(jù)段包含了不可執(zhí)行的信息或數(shù)據(jù)。另一種類型的段,稱為 BSS 段,它包含以符號數(shù)據(jù)開頭的塊。對于 nm 命令列出的每個符號,它們的值使用十六進(jìn)制來表示(缺省行為),并且在該符號前面加上了一個表示符號類型的編碼字符。
常見的各種編碼包括:
可以將目標(biāo)文件中所包含的不同的部分劃分為段。段可以包含可執(zhí)行代碼、符號名稱、初始數(shù)據(jù)值和許多其他類型的數(shù)據(jù)。有關(guān)這些類型的數(shù)據(jù)的詳細(xì)信息,可以閱讀 UNIX 中 nm 的 man 頁面,其中按照該命令輸出中的字符編碼分別對每種類型進(jìn)行了描述。
在目標(biāo)文件階段,即使是一個簡單的 Hello World 程序,其中也包含了大量的細(xì)節(jié)信息。nm 程序可用于列舉符號及其類型和值,但是,要更仔細(xì)地研究目標(biāo)文件中這些命名段的內(nèi)容,需要使用功能更強(qiáng)大的工具。
其中兩種功能強(qiáng)大的工具是 objdump 和 readelf 程序。
關(guān)于nm工具的參數(shù)說明及更多示例詳見 :ref:
nm
;
ogjdump工具用來顯示二進(jìn)制文件的信息,就是以一種可閱讀的格式讓你更多地了解二進(jìn)制文件可能帶有的附加信息。
$objdump -d myprogrammer
a.out: file format elf32-i386
Disassembly of section .init:
080482b4 <_init>:
80482b4: 53 push %ebx
80482b5: 83 ec 08 sub $0x8,%esp
80482b8: e8 00 00 00 00 call 80482bd <_init+0x9>
80482bd: 5b pop %ebx
80482be: 81 c3 37 1d 00 00 add $0x1d37,%ebx
80482c4: 8b 83 fc ff ff ff mov -0x4(%ebx),%eax
80482ca: 85 c0 test %eax,%eax
80482cc: 74 05 je 80482d3 <_init+0x1f>
80482ce: e8 3d 00 00 00 call 8048310 <__gmon_start__@plt>
80482d3: e8 e8 00 00 00 call 80483c0 <frame_dummy>
80482d8: e8 b3 01 00 00 call 8048490 <__do_global_ctors_aux>
80482dd: 83 c4 08 add $0x8,%esp
80482e0: 5b pop %ebx
80482e1: c3 ret
Disassembly of section .plt:
...
每個可執(zhí)行代碼段將在需要特定的事件時執(zhí)行,這些事件包括庫的初始化和該程序本身主入口點。
對于那些著迷于底層編程細(xì)節(jié)的程序員來說,這是一個功能非常強(qiáng)大的工具,可用于研究編譯器和匯編器的輸出。細(xì)節(jié)信息,比如這段代碼中所顯示的這些信息,可以揭示有關(guān)本地處理器本身運行方式的很多內(nèi)容。對該處理器制造商提供的技術(shù)文檔進(jìn)行深入的研究,您可以收集關(guān)于一些有價值的信息,通過這些信息可以深入地了解內(nèi)部的運行機(jī)制,因為功能程序提供了清晰的輸出。
關(guān)于objdump工具的參數(shù)說明及更多示例詳見 :ref:
objdump
;
這個工具和objdump命令提供的功能類似,但是它顯示的信息更為具體,并且它不依賴BFD庫(BFD庫是一個GNU項目,它的目標(biāo)就是希望通過一種統(tǒng)一的接口來處理不同的目標(biāo)文件);
$readelf -all a.out
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048330
Start of program headers: 52 (bytes into file)
Start of section headers: 4412 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 9
Size of section headers: 40 (bytes)
Number of section headers: 30
Section header string table index: 27
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481cc 0001cc 000050 10 A 6 1 4
[ 6] .dynstr STRTAB 0804821c 00021c 00004c 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048268 000268 00000a 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 08048274 000274 000020 00 A 6 1 4
[ 9] .rel.dyn REL 08048294 000294 000008 08 A 5 0 4
[10] .rel.plt REL 0804829c 00029c 000018 08 A 5 12 4
[11] .init PROGBITS 080482b4 0002b4 00002e 00 AX 0 0 4
[12] .plt PROGBITS 080482f0 0002f0 000040 04 AX 0 0 16
[13] .text PROGBITS 08048330 000330 00018c 00 AX 0 0 16
[14] .fini PROGBITS 080484bc 0004bc 00001a 00 AX 0 0 4
[15] .rodata PROGBITS 080484d8 0004d8 000011 00 A 0 0 4
[16] .eh_frame_hdr PROGBITS 080484ec 0004ec 000034 00 A 0 0 4
[17] .eh_frame PROGBITS 08048520 000520 0000c4 00 A 0 0 4
[18] .ctors PROGBITS 08049f14 000f14 000008 00 WA 0 0 4
[19] .dtors PROGBITS 08049f1c 000f1c 000008 00 WA 0 0 4
[20] .jcr PROGBITS 08049f24 000f24 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 08049f28 000f28 0000c8 08 WA 6 0 4
[22] .got PROGBITS 08049ff0 000ff0 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 08049ff4 000ff4 000018 04 WA 0 0 4
[24] .data PROGBITS 0804a00c 00100c 000008 00 WA 0 0 4
[25] .bss NOBITS 0804a014 001014 000008 00 WA 0 0 4
[26] .comment PROGBITS 00000000 001014 00002a 01 MS 0 0 1
[27] .shstrtab STRTAB 00000000 00103e 0000fc 00 0 0 1
[28] .symtab SYMTAB 00000000 0015ec 000410 10 29 45 4
[29] .strtab STRTAB 00000000 0019fc 0001f9 00 0 0 1
...
ELF Header 為該文件中所有段入口顯示了詳細(xì)的摘要。在列舉出這些 Header 中的內(nèi)容之前,您可以看到 Header 的具體數(shù)目。在研究一個較大的目標(biāo)文件時,該信息可能非常有用。
除了所有這些段之外,編譯器可以將調(diào)試信息放入到目標(biāo)文件中,并且還可以顯示這些信息。輸入下面的命令,仔細(xì)分析編譯器的輸出(假設(shè)您扮演了調(diào)試程序的角色)
$readelf --debug-dump a.out | more
調(diào)試工具,如 GDB,可以讀取這些調(diào)試信息,并且當(dāng)程序在調(diào)試器中運行的同時,您可以使用該工具顯示更具描述性的標(biāo)記,而不是對代碼進(jìn)行反匯編時的原始地址值。
關(guān)于readelf工具的參數(shù)說明及更多示例詳見 :ref:`readelf` ;
size這個工具用來查看程序運行時各個段的實際內(nèi)存占用
$size a.out
text data bss dec hex filename
1146 256 8 1410 582 a.out
這個工具用于查看文件的類型;
比如我們在64位機(jī)器上發(fā)現(xiàn)了一個32位的庫,鏈接不上,這就有問題了:
$file a.out
a.out: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped
也可以查看Core文件是由哪個程序生成
$file core.22355
一個文件中包含二進(jìn)制數(shù)據(jù)和文本數(shù)據(jù),如果只需要查看其文本信息,使用這個命令就很方便;過濾掉非字符數(shù)據(jù),將文本信息輸出
$strings <objfile>
顯示所有正在使用著指定的file, file system 或者 sockets的進(jìn)程信息;
$fuser -m -u redis-server
redis-server: 11552rce(weber) 22912rce(weber) 25501rce(weber)
使用了-m和-u選項,用來查找所有正在使用redis-server的所有進(jìn)程的PID以及該進(jìn)程的OWNER;
fuser通常被用在診斷系統(tǒng)的"resource busy"問題。如果你希望kill所有正在使用某一指定的file, file system or sockets的進(jìn)程的時候,你可以使用-k選項
$fuser –k /path/to/your/filename
以十六進(jìn)制方式顯示文件,只顯示文本信息
$xxd a.out
0000000: 7f45 4c46 0101 0100 0000 0000 0000 0000 .ELF............
0000010: 0200 0300 0100 0000 3083 0408 3400 0000 ........0...4...
0000020: 3c11 0000 0000 0000 3400 2000 0900 2800 <.......4. ...(.
0000030: 1e00 1b00 0600 0000 3400 0000 3480 0408 ........4...4...
0000040: 3480 0408 2001 0000 2001 0000 0500 0000 4... ... .......
0000050: 0400 0000 0300 0000 5401 0000 5481 0408 ........T...T...
...
通常使用od命令查看特殊格式的文件內(nèi)容。通過指定該命令的不同選項可以以十進(jìn)制、八進(jìn)制、十六進(jìn)制和ASCII碼來顯示文件。
參數(shù)說明:
-A 指定地址基數(shù),包括:
-t 指定數(shù)據(jù)的顯示格式,主要的參數(shù)有:
說明:od命令系統(tǒng)默認(rèn)的顯示方式是八進(jìn)制,這也是該命令的名稱由來(Octal Dump)。但這不是最有用的顯示方式,用ASCII碼和十六進(jìn)制組合的方式能提供更有價值的信息輸出。
以十六進(jìn)制和字符同時顯示
$od -Ax -tcx4 a.c
000000 # i n c l u d e < s t d i o .
636e6923 6564756c 74733c20 2e6f6964
000010 h > \n \n v o i d m a i n ( ) \n
0a0a3e68 64696f76 69616d20 0a29286e
000020 { \n \t i n t i = 5 ; \n \t p
69090a7b 6920746e 35203d20 70090a3b
000030 r i n t f ( " h e l l o , % d "
746e6972 68222866 6f6c6c65 2264252c
000040 , i ) ; \n } \n
3b29692c 000a7d0a
000047
以字符方式顯示
$od -c a.c
0000000 # i n c l u d e < s t d i o .
0000020 h > \n \n v o i d m a i n ( ) \n
0000040 { \n \t i n t i = 5 ; \n \t p
0000060 r i n t f ( " h e l l o , % d "
0000100 , i ) ; \n } \n
0000107
注:類似命令還有hexdump(十六進(jìn)制輸出)