Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

引言

注:本文介紹的調試技巧非常實用,請耐心看下去,相信會有收穫的!

工欲善其事,必先利其器。

熟悉並善用手中的調試工具,很多時候,會帶來事半功倍的效果!

GDB作為Linux環境下最常用的調試工具,它的強大,毋庸置疑。但現實中,發現身邊很多朋友對它的一些高級特性卻知之甚少,導致調試問題的時候,經常把大把的時間浪費在一些原本很簡單的事情上,以至於效率非常低下。

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

本文以C語言中的鏈表節點打印為例,講解如何通過自己定製GDB調試命令,讓一件原本看似非常複雜的事情,變得前所未有的簡單。

示例一

在這個示例中,創建了一個包含5個節點的鏈表,每個鏈表節點的類型定義為:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

完整程序如下圖所示:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

示例程序 一

假設我們調試程序時,想把這個鏈表的所有節點都打印出來,通常大家會怎麼做呢?


最原始的做法:逐個節點手動打印

通常,很多朋友可能首先想到的,是使用GDB的print命令,從鏈表頭開始,逐個節點去手動打印。我們演示一下。

先編譯一下:

<code>gcc -g list.c -o list/<code>

然後用GDB進行調試,先在第34行設置個斷點,保證鏈表已經被創建出來,並且讓程序在return之前停下來:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

然後,使用print命令,逐個節點去打印:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

這種手動打印的方式,在節點個數非常少的情況下,還是非常簡單實用的。

但是,如果鏈表中有幾十個或者更多的節點呢?這個時候用手動的方式去打印,這個工作量可想可知會有多大了。

那麼,除了手動的方式之外,有沒有更好的方式呢?當然有!

GDB自定義命令基礎

GDB中有一個非常強大的功能,它允許用戶根據具體場景需求自定義命令。

具體格式如下:

<code>define cmd_name
command list
end/<code>

自定義命令以define開頭,後面跟命令的名字,並且以end結尾。define和end中間是命令的具體實現邏輯。

與自定義命令相關的幾個GDB內建變量:

<code>$argc  自定義命令的參數個數
$arg0  自定義命令的第一個參數
$arg1  自定義命令的第二個參數
$arg2  自定義命令的第三個參數
$argN  自定義命令的第N+1個參數/<code>

比如,我們要實現一個兩個整數相加的命令,實現如下:

<code>define  add
print $arg0 + $arg1
end/<code>

執行效果如下圖所示:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

簡單瞭解GDB自定義命令的使用方法之後,我們現在來解決我們示例中的鏈表打印問題。

自定義GDB命令:鏈表打印

假如用C語言實現一個鏈表打印函數,我們可能會這樣實現:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

C語言 鏈表打印

接下來,我們仿照C語言,用GDB自定義命令來實現:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

GDB 鏈表打印命令

其實和C語言實現的打印函數是很相似的,簡單解釋一下:

  • 第1行 定義鏈表打印名字為“print-list”
  • 第2行 設置一個$list變量,並且把第一個參數賦值給它,也就是鏈表頭指針
  • 第3~5 行用while命令循環遍歷$list鏈表
  • 第4行 用print命令打印當前$list變量指向的節點

下面,我們來驗證一下,這個自定義命令是否有效。

我在之前的文章中介紹過,GDB支持從腳本文件中加載信息,其實,自定義的命令也可以從腳本中加載。

我們先把上面實現的腳本命令存放在一個print-list.gdb文件中,調試時使用source命令把它加載起來即可。如下圖所示:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

print-list 命令執行

一切工作正常!是不是比逐個節點手動打印,簡單多了呢?

這樣雖然方便了,可是如果鏈表中有幾百個節點,它會把這些節點全部打印出來。但有的時候,我們想查看的可能只是前面的幾個節點,那怎麼辦呢?

下面,我們來解決這個問題。

GDB自定義命令:打印指定個數的鏈表節點

其實,要實現這個功能非常簡單,只需要給print-list命令增加一個指定節點個數的參數就可以了。

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

print-list-2命令

我們重新定義一個print-list-2命令,相比print-list命令,它新引入了一個$count變量,用來接收用戶指定的要打印節點的個數。

關鍵的地方我已經在圖中進行了標註,應該還是比較好理解的。

$count初始值為用戶指定的節點個數,每次打印一個節點後,$count值減1,當$count值小於等於0時,用loop_break退出循環。

我們仍然把print-list-2命令添加到print-list.gdb文件中,然後重新用GDB進行調試,並加載命令,然後打印3個節點:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

print-list-2 命令執行

可以看到,完全符合預期,print-list-2打印了3個節點後,就執行結束了。

print-list-2命令存在的問題

目前print-list-2命令雖然很方便,但是有一個很大的問題,就是它無法通用。

它主要有兩個問題:

  • 第2行中,顯式地指明瞭節點類型是type_t
  • 第9行中,顯示地知名了指向下一個節點的字段名是next

我們再看一下print-list-2的實現,我用紅線把問題在圖中標註出來:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

print-list-2 的問題

下面,我們來解決這個問題,最終實現一個更加通用的GDB自定義鏈表打印命令。

GDB自定義命令:通用的鏈表打印

這個問題也很好解決,我們只需要能夠讓用戶把節點的類型,和節點結構中指向下一個節點的字段的名字傳遞給我們的打印命令就可以了。

實現如下圖所示:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

print-list-4 GDB自定義命令

我們新定義一個print-list-4命令,並且新引入兩個參數,$arg2表示節點的數據類型,$arg3表示節點結構中指向下一個節點的字段名。

同樣,我們把print-list-4的實現存放到print-list.gdb文件中,然後用GDB重新調試並加載自定義的命令。如下圖所示:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

print-list-4 執行

到此,我們已經實現了一個相對來說比較通用的GDB自定義鏈表打印命令了。

下面,為了讓我們的自定義命令顯得更加正式一些,給它添加上使用說明。

GDB自定義命令:添加使用說明

可以使用document - end命令給GDB自定義命令添加使用說明。

如此以來,我們就可以在GDB中使用help命令查看自定義命令的使用方法了。

給print-list-4命令添加使用說明,如下圖所示:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

print-list-4 使用說明

把print-list-4的幫助說明同樣添加到print-list.gdb文件中,然後用GDB重新調試程序,並從腳本文件中加載自定義命令信息。

這樣,就可以在GDB中用help命令查看print-list-4的使用幫助了。

如下圖所示:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

help print-list-4

到此,基本功能已經全部實現完畢了。

但我們自定義的print-list-4命令名字還是稍顯複雜,使用起來稍有不便。

當然,我們可以在定義的時候起一個更加簡單的名字,不過,這裡我們使用另外一種方法。

GDB自定義命令:命令別名

我們知道,在GDB中,很多命令都有對應的縮寫形式。比如break的縮寫是b,info的縮寫是i,continue的縮寫是c等。

在GDB中,可以使用alias命令,給已經存在的命令設置一個命令別名。

下面,給我們自定義的print-list-4命令也設置一個簡寫簡寫形式,使用起來更加方便。

<code>alias pl = print-list-4/<code>

把這個命令,放在print-list-4命令所在的腳本文件中即可,也就是print-list.gdb文件中。

下面我們用GDB重新調試一下,看一下效果:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

print-list-4 命令別名

我們用help命令查看pl的使用幫助時,GDB自動找到了print-list-4的使用說明。

再看一下使用效果:

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

pl 命令執行

好了,現在我們可以用更加容易拼寫的pl命令來打印鏈表信息了!是不是方便好多呢?

結語

本文以C語言的鏈表打印為例,展示了使用GDB的自定義命令,可以把一件原本非常複雜的事情,變得如此簡單。

其實,GDB自定義命令的用途還遠不止如此,它可以完成很多非常實用的功能,比如控制程序執行流,自動化調試等各種強大的功能。但由於篇幅有限,本文不再展開介紹。

Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具

本系列專題是應有些朋友的要求,旨在介紹一些非常簡單實用,又相對比較高階的調試技巧。同時也會對調試器的實現原理進行詳細講解,還會講解一些常見問題的定位方法和思路。感興趣的朋友可以關注一下!

本文是本系列專題的第四篇,感興趣的朋友可以去看下其它幾篇,相信你會有收穫的!

已更新內容:

GDB動態打印:讓你隨時隨地printf,不需修改代碼,不需重新編譯

C語言:當GDB遇到複雜數據結構,兩分鐘帶你掌握四個高效調試技巧

C語言:GDB調試時遇到宏定義怎麼辦?一個小技巧幫你一秒鐘搞定


Linux調試技巧:GDB自定義命令,按需定製適合自己的調試工具


原創不易,如果覺得有用的話,別忘了點贊,謝謝!

對文中的內容有什麼疑問或者不同見解的朋友,歡迎留言討論!

對編譯器、OS內核、虛擬化、性能調優、調試技術等感興趣的童鞋,歡迎右上角關注!

原創聲明:本文原創內容,未經允許,禁止轉載!部分圖片來源網絡,如有侵權,請通知刪除!


分享到:


相關文章: