1. 程式人生 > >gdb除錯c++的STL容器

gdb除錯c++的STL容器



我一直都是在Linux下做開發的,但是我對GDB的使用並不多。因為平都是用QtCreator除錯程式的。因為工作的原因,以後可能不能再依賴QtCreator了。於是我好好研究一下~

之前為什麼沒有深入使用GDB,QtCreator帶來一定的便利是一方面,另一方面是覺得GDB遇到了vector, map, set, list就沒辦法看資料了。

今天我研究了一下,其實也是Easy的。

示例程式碼:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> #include <string>
#include <vector> #include <map> using namespace std; int main() { int i1 = 32; int i2 = 45;  double d = i1 + i2 / 3; vector<string> vstr; vstr.push_back("Hello"); vstr.push_back("World"); vstr.push_back("!"); map<string, int> m_si; m_si["A"] = 12; m_si["D"] = 93; m_si["B"
] = 77;
return 0; }

編譯的時候:

?
1 $ g++ -o test-gdb test-gdb.cpp -g

在GDB除錯中,可以用print指令檢視變數或常量的值。

可能是我本機所安裝的GDB版本較高的緣故,本機的GDB本謝就很支援STL中的容器。如下是我GDB的版本資訊:

?
1 2 3 4 5 6 7 8 9 10 gdb --version GNU gdb (GDB) Red Hat Enterprise Linux (7.2-75.el6) Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.  Type "show copying" and "show warranty" for details. This GDB was configured as "i686-redhat-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>.

對STL容器的支援是極好的:

?
1 2 3 4 5 6 7 8 9 (gdb) p vstr $1 = std::vector of length 3, capacity 4 = {"Hello""World""!"} (gdb) p m_si $2 = std::map with 3 elements = { ["A"] = 12, ["B"] = 77, ["D"] = 93 }

能看到這樣的資訊其實已經很不錯了。

但是我公司系統的GDB可沒這麼智慧。列印一下vector,就會蹦出好大堆資訊。如果是map的話,那就更嚇人了。

<此處展示可怕的提示資訊>

其實,在這大堆資訊裡面只有小部分是我們關注的。GDB很靈活,我們可以在裡面自定義函式。我可以在GDB裡定義一個函式從vector裡提取重要的資訊進行顯示。

這裡 下載一個stl-views.gdb檔案。在這個文裡定義了很多緒如:pvector, pmap, pstring之類的GDB函式供我們使用。

將這個檔案下載到HOME目錄,然後:

?
1 cat stl-views-1.0.3.gdb >> ~/.gdbinit

這樣,每次啟動 gdb的時候,都會自動載入 ~/.gdbinit 檔案中的內容。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 13        vector<string> vstr; (gdb) n 14        vstr.push_back("Hello"); (gdb) n 15        vstr.push_back("World"); (gdb) n 16        vstr.push_back("!"); (gdb) p<TAB><TAB> passcount     plist_member  print         pstring       python path          pmap          print-object  ptype          pbitset       pmap_member   printf        pvector        pdequeue      ppqueue       pset          pwd            plist         pqueue        pstack        pwstring

輸入了p之後,每次連續按兩次TAB鍵都會列出以p開頭的命令。這裡我們看到了:pstring, pvector, pwstring, pstack, pmap, pmap_member等等。

我們用一下pvector來檢視vstr中的內容:

?
1 2 3 4 5 6 7 (gdb) pvector vstr elem[0]: $3 = "Hello" elem[1]: $4 = "World" elem[2]: $5 = "!" Vector size = 3 Vector capacity = 4 Element type = std::basic_string<char, std::char_traits<char>, std::allocator<char> > *

這個命令果然打印出了很多有價值的資訊。

博主有個習慣,我不僅要知其然,我還要知其所以然。於是我研究了一下 .gdbinit檔案裡的內容。這個函式都是在 ~/.gdbinit 裡定義的。我們開啟這個檔案看一下。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 define pvector if $argc == 0 help pvector else set $size = $arg0._M_impl._M_finish - $arg0._M_impl._M_start set $capacity = $arg0._M_impl._M_end_of_storage - $arg0._M_impl._M_start set $size_max = $size - 1 end if $argc == 1 set $i = 0 while $i < $size printf "elem[%u]: ", $i p *($arg0._M_impl._M_start + $i) set $i++ end end if $argc == 2 set $idx = $arg1 if $idx < 0 || $idx > $size_max printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max else printf "elem[%u]: ", $idx p *($arg0._M_impl._M_start + $idx) end end if $argc == 3 set $start_idx = $arg1 set $stop_idx = $arg2 if $start_idx > $stop_idx set $tmp_idx = $start_idx set $start_idx = $stop_idx set $stop_idx = $tmp_idx end if $start_idx < 0 || $stop_idx < 0 || $start_idx > $size_max || $stop_idx > $size_max printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max else set $i = $start_idx while $i <= $stop_idx printf "elem[%u]: ", $i p *($arg0._M_impl._M_start + $i) set $i++ end end end if $argc > 0 printf "Vector size = %u\n", $size printf "Vector capacity = %u\n", $capacity printf "Element " whatis $arg0._M_impl._M_start end end document pvector Prints std::vector<T> information. Syntax: pvector <vector> <idx1> <idx2> Note: idx, idx1 and idx2 must be in acceptable range [0..<vector>.size()-1]. Examples: pvector v - Prints vector content, size, capacity and T typedef pvector v 0 - Prints element[idx] from vector pvector v 1 2 - Prints elements in range [idx1..idx2] from vector end

看全部有點多,我們暫且不看多個引數的那部分,我們分析一下只有一個引數的一部分:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 define pvector if $argc == 0           # 如果沒有帶引數,那麼就列印幫助提示資訊 help pvector else                    # 如果有引數,那麼接下來準備一下size, capacity, size_max 這三個重要的引數。 set $size = $arg0._M_impl._M_finish - $arg0._M_impl._M_start        # arg0 就是第一個引數,也就是vstr陣列物件。注重 size 是怎麼計算的。 set $capacity = $arg0._M_impl._M_end_of_storage - $arg0._M_impl._M_start set $size_max = $size - 1 end if $argc == 1           # 如果只有一個引數,說明要求打印出vector中所有的元素 set $i = 0           while $i < $size    # 用一個 while 迴圈,用printf與p,打印出列表中的所有元素 printf "elem[%u]: ", $i p *($arg0._M_impl._M_start + $i)     # 注意看哦!!!! set $i++ end end

所有的說明都寫在上面的註釋中了,自己去悟吧!