GDB調試(gdb調試命令大全)

      網友投稿 2704 2025-04-04

      約定:對gdb的命令,如果有縮寫形式,會在第一次出現的時候小括號內給出縮寫,比如運行命令寫成run(r);本文中尖括號< >用來表達一類實體,比如表示這個地方可以放置程序;中括號[]表示括號中的內容是可寫可不寫,比如[=],表示“=”可以有也可以沒有(本身又是一類實體);“|”表示或的關系。


      GDB簡介

      編譯的時候加上-g參數,編譯器就會在目標文件中添加調試信息(關于編譯鏈接可參閱《從四個問題透析Linux下C++編譯&鏈接》),對應的strip命令可以去除調試信息。通過objdump/readelf等工具可以看到目標文件中有很多包含“debug”字符的section。這些section里保存了調試信息,目前ELF文件采用DWARF 3(Debug With Arbitrary Record Format)標準的調試信息格式。

      使用GDB你可以:

      1. 自定義程序運行方式

      2. 讓程序停止在你指定的位置:設置斷點

      3. 在停止點查看當前程序的狀態:變量、寄存器的值

      4. 動態改變程序的狀態

      通常GDB命令都會有一個簡短的表達,比如設置斷點的break命令可以簡寫為b,方便減少輸入,本文中對第一次出現的命令都會在括號內給出對應的簡短表達。回車在GDB相當于重復上一個命令。

      啟動GDB運行程序

      運行GDB調試a.out程序有以下幾種方式:

      方式一:直接運行gdb,然后在gdb中執行“file a.out”加載程序。

      方式二:gdb ,命令行給出可執行程序運行,即運行“gdb a.out”

      方式三:gdb ,帶core文件運行,“gdb a.out /tmp/core-19475”(假設a.out運行產生了/tmp/core-1975的core dump文件)

      方式四:gdb ,對運行中的a.out假設pid為19475,則可以通過“gdb a.out 19475”來調試運行中的a.out。

      方式五:對運行中的a.out,可以先按方式二啟動,然后在gdb中中心“attach 19475”調試運行中的a.out

      進入到GDB后,可以通過help命令來獲取幫助,GDB對命令做了分類,要獲取詳細說明可以查看help的相關輸出。

      啟動GDB后, 就可以運行a.out了,本例比較簡單直接執行run(r)命令即可,但對于稍微復雜點的程序可能需要做一些額外的設置工作:

      1. 設置運行參數:通過“set args ”設置命令行需要的參數,比如程序需要一個輸入文件/tmp/input.txt,則可以“set args /tmp/input.txt”。設置完成后可以通過“show args”命令查看當前設置的參數。

      2. 設置運行環境:

      通過“path

      ”設置可執行文件搜索路徑,“show paths”顯示當前配置。

      通過“set environment [=value]”設置環境變量,比如要設置用戶名可以“set environment USER=wanggaofei”,show environment可以查看所有的環境變量。

      通過cd命令可以更改目錄,pwd顯示當前所在目錄

      準備就緒后就可以真正開始用GDB來調試程序了。

      暫停、恢復程序運行

      調試程序,首先是要讓程序是某些感興趣的點上停下來,GDB有以下幾種方式通知GDB暫停程序的運行:斷點、觀察點、捕捉點(GDB中這三種都統稱為斷點breakpoints)、信號、線程停止。

      斷點

      設置斷點:break(b)命令

      break:在下一條指令上設置斷點,GDB是基于機器指令工作。

      break :在指定函數設置斷點,function可以是class::function/function(type, type)形式

      break :在當前文件的指定行號設置斷點

      break ::在指定文件的指定行號設置斷點

      break ::在指定個文件的指定函數設置斷點,主要針對重載函數

      break +:在往后數offset行設置斷點

      break -:在往前數offset行設置斷點

      break *

      :在指定的虛擬地址上設置斷點

      break if :條件斷點,break_args可以是上面break后面的參數,condition是具有布爾值的條件表達式,如break 20 if i == 5,在i等于5的時候20行觸發斷點

      tbreak :臨時斷點,功能同break,區別是斷點在第一次停住以后,自動被刪除

      查看斷點:

      info breakpoints [n]:其中n是斷點序號,是可選參數,不提供則顯示所有斷點

      刪除斷點:delete(d)

      delete [break_num_list] [range]:break_num_list是可選參數,可以是一個斷點序號的列表,用空格分開,range可以是一個范圍例如1-5,刪除編號區間[1,5]的斷點,如果不提供任何參數則刪除所有的斷點。

      clear :和break命令對應的反操作,根據位置清除斷點,不指定參數則清除所有斷點。

      禁用斷點:disable(dis)

      有的時候你想臨時讓斷點不起作用,又不想刪除斷點,否則過一會還要再設置這個斷點,這時候可以暫時禁用斷點。

      disable [break_num_list] [range]:參數和delete的參數意義相同

      啟用斷點:enable

      當你想再次啟用斷點時可以enable它。

      enable [break_num_list] [range]:參數意義同disable,enable有不少子命令,具體參考help enable

      條件維護:condition

      condition :修改斷點序號為break_num的條件為expression

      condition :清除斷點break_num的條件

      隨著調試的進行,你可能需要修改停止條件,比如在for循環中,剛開始你會在循環變量等于N的時候停住程序,查看相關變量,發現沒問題后,你會選取一個更大的M,讓循環變量等于M的時候停住,看看有沒有問題,這時候就需要更改條件,這就是condition大顯身手的時候。

      斷點命令:commands

      commands [break_num]

      command_list

      end

      通常在斷點處都是為了查看某些變量的值,如果能在斷點處自動打印這些值,豈不爽歪歪?commands就是用來干這個的,省的你動手。如下示例

      commands 1 slient printf “i is %d\n”, i end

      在觸發斷點1時打印變量i的值,slient是讓GDB安靜的觸發斷點,不要打印一些沒用的信息。

      恢復執行:

      continue [ignore_count]:continue(c)命令恢復程序運行直到下一個斷點或者結束,參數ignore_count是個數字,代表忽略之后的斷點次數。

      step [count]:單步跟蹤,碰到函數會進入,count參數相當于執行count次step的效果,對單步跟蹤,有各選項step-mode可以通過set命令設置其為on或者off,設置為on后,對沒有debug信息的函數會停止在函數的第一條指令上。否則step會跳過該函數。

      next [count]:單步跟蹤,跟step的區別是碰到函數時不會進入函數,count效果同step中參數。

      finish:運行程序直到函數完成,打印返回的堆棧地址和返回值及參數信息。

      util [break_args]:until(u)不帶參數跳出循環,break_args同clear中參數。

      stepi(si)、nexti(ni),這里的i代表指令級別,其他和step,next相同

      觀察點

      觀察點用來觀察某個表達式的值是否發生了變化,如果有變化,則馬上暫停程序。觀察點和斷點的一個顯著區別是觀察點由于是觀察表達式的值,而表達式中變量是有作用域的,當離開作用域時觀察點自動刪除,但斷點是和代碼綁定,只要代碼不變斷點就一直存在。

      設置觀察點:

      watch :為表達式expression設置一個觀察點,一旦表達式值發生變化,馬上停住程序。

      rwatch :當表達式被讀時,停住程序

      awatch :當表達式被讀或寫時,停住程序

      查看觀察點:

      info watchpoints:info breakpoints也可以把觀察點列出,但這個命令會把所有breakpoints都列出來。

      刪除觀察點:

      通過delete命令

      捕捉點

      捕捉點用來捕捉程序運行中的一些事件,比如加載共享庫或者異常

      catch :當event發生時,停住程序,具體event可以通過help catch查看各種事件類型。

      tcatch :只捕捉一次,程序停住后,捕捉點自動刪除

      信號

      信號是unix/linux下的一項重要技術,GDB可以讓你在收到指定信號時采取行動

      處理信號:

      handle [actions]:收到signals時采取行動actions,signals可以是一個信號范圍,actions可以是:

      stop:收到該信號時,GDB會停住程序

      nostop:收到信號時,GDB不會停住程序,但是會打印消息告訴你收到該信號

      print:收到信號時,打印一條消息

      noprint:收到信號時,GDB不會高告訴你收到信號

      pass/noignore:收到信號時,GDB不做處理,讓程序的信號處理程序接手

      nopass/ignore:收到信號時,GDB不會讓程序看到整個信號

      查詢信號處理情況:

      info signals

      info handle

      線程

      info threads:顯示所有線程

      thread :切換到編號為thread_num的線程

      break thread [if ]:線程斷點和普通斷點的區別就是多了個指定線程號的操作。

      thead apply |all :thread_num_list是線程列表,如果要對所有線程操作可以用all代替,command可以是之前的任何調試命令

      set scheduler-locking off|on|step:默認是off,也就是調試的時候所有線程都會執行;on表示只有當前線程執行;step表示在step單步執行的話只有當前線程執行,只有在next跨過函數的時候其他線程可能運行

      查看棧信息

      程序停住后,你可以查看程序的當前狀態,比如目前程序現在執行到哪了?是執行了哪條路徑的代碼?GDB通過幾個命令幫助你分析棧信息,以及在棧間切換。

      backtrace [n]:backtrace(bt)命令打印當前調用棧的信息,n為可選參數,既可以是整數也可以是負數,表示只打印棧頂上n層的棧信息或棧底n層信息。

      frame [n]:frame(f)切換幀,n為一個從0開始的數,表示棧中的層次編號,0代表棧頂。

      up [n]:向棧的上面移動n層

      down [n]:向棧的下面移動n層

      info frame:打印詳細的棧信息,主要以程序的虛擬地址信息為主

      info args:打印當前函數參數和對應值

      info locals:打印當前函數局部變量和對應值

      查看源代碼

      在查看棧信息的同時,你可能會對源代碼感興趣,以幫助你更好的理解程序的來龍去脈(如果你用的是Emacs編輯器,這種需求就會大大減少,因為Emacs和GDB配合的非常好),GDB提供了相應的命令來顯示和查找源代碼。

      顯示源碼:

      list [list_args]:list(l)顯示源代碼,list_args類似break中的break_args參數,可以是行號,函數等,詳細參考help list。有一個參數listsize控制一次顯示源代碼的行數,可以通過show listsize顯示該值,通過set listsize 來重新設置該值。

      查找源碼:

      forward-search :regexp是正則表達式,下同,關于正則表達式請參與相關資料。

      search :兩個命令都是向前搜索

      reverse-search :向后搜索

      GDB調試(gdb調試命令大全)

      指定源代碼搜索路徑:

      directory :對多個路徑,可以用冒號“:”連接,directory不帶參數時表示清除自定義的源碼搜索路徑。

      show directories:顯示當前源碼搜索路徑。

      顯示源代碼虛擬地址:

      info line [line_args]:顯示源碼虛擬地址,line_args和前面的list_args類似,詳細參考help info line。

      disassemble:反匯編代碼,細節查看help disassemble

      檢查和設置變量

      調試最終要查看程序運行的狀態,通過觀察當前各個變量或者表達式的值來判斷程序當前是否符合預期,如果不符合預期,及時分析原因,從而排查bug。GDB提供了相關命令查看和設置變量。

      查看變量類型:

      ptype type_name|expression:type_name可以是一個類型名,比如結構體或者類名,expression可以是某個變量

      whatis expression:可以理解為精簡版ptype,ptype會展開所有類型定義,whatis則不會

      打印表達式:

      print [/] :print(p)打印命令有兩部分,可選的/表示輸出格式,expression是要打印的表達式。在GDB中當前可見的變量(全局變量、全局靜態變量、當前作用域的局部變量)可以隨時打印。format詳細說明如下。

      x

      按十六進制格式顯示變量

      d

      按十進制格式顯示變量

      u

      按無符號十進制顯示變量

      o

      按八進制格式顯示變量

      t

      按二進制格式顯示變量

      a

      address和x效果差不多

      c

      按字符格式顯示變量

      f

      按浮點格式顯示變量

      打印數組:

      print *pArr@10:pArr是指向數組的指針,10表示要打印的元素的個數

      通過“::”打印文件、函數或者C++類的變量:

      print main::value

      打印內存:

      x [/] :x命令第二部分是可選的,可以分成三塊,n是要打印內存的數目,f是打印格式,詳見print部分的format說明,u表示每個對象占用的字節數,默認是4字節,其他值包括b表示單字節,h表示雙字節,w表示四字節,g表示八字節。

      x /10dw pArr:表示從內存地址pArr開始打印10個元素,每個元素占用4字節(w控制),以十進制顯示(d控制)

      自動打印:

      要是在每次程序停住的時候,能自動幫你打印變量的值,可以大大減少手工輸入,display就可以做到。

      display [/format] :參數意義同print

      undisplay :刪除自動顯示,display_num類似斷點的編號。

      delete display :display_num_list是空格分開的display_num列表

      disable display :和斷點類似

      enable display :和斷點類似

      歷史記錄:

      用GDB的print命令查看狀態時,GDB會以$1,$2這樣的編號標記之前的表達式,這些編號稱為值歷史。對于那種很長的表達式,通過值歷史查看可以省去很多輸入

      設置變量:

      調試的過程中,可能需要人為的設置變量的值,從而可以快速的了解,當變量是這個值的時候,程序是什么表現,通過set命令可以很簡單的實現。

      set value=11:設置變量value的值為11

      方便變量:

      有時候想挨個打印數組的值,如果GDB能提供一個變量作為數組的下標,隨著循環的進行變量值也隨著變化,這樣查看數組元素的值就非常方便了。

      (gdb) set $i = 0 (gdb) p arr[$i++]

      $i就是方便變量,后面通過回車就可以不斷打印arr中的值。

      查看寄存器:

      有時候可能會關心寄存器中的值,比如在core dump后,想查看下當時現場。

      info registers [register_name]:查看寄存器,如果給出具體的register_name則只顯示指定寄存器的值

      info all-registers:比上面顯示更多的寄存器值,比如浮點寄存器

      改變程序的運行

      在用GDB不斷調試的過程中,你慢慢已經掌握了程序的執行脈絡,這時候你肯定希望按照自己的調試策略來改變程序的路徑,有了這個能力,在調試中對程序就可以為所欲為,一次走完程序的所有路徑。

      修改變量:

      上節在設置變量中提到可以通過set命令來設置變量的值,但當你代碼中的變量和GDB中的參數名字一樣時,需要如下設置。

      (gdb) set var width=80

      另外通過print命令也可以方便的設置變量

      (gdb) print value=11

      跳轉執行:

      jump :location可以是行號或者地址(*address,同break中參數)

      產生信號:

      在前面信號一節中只提到了處理信號,我們也可以在GDB中隨時產生一個信號。

      signal :給程序產生一個信號signal和handle命令中參數意義相同

      強制函數返回:

      return []:強制函數返回,如果提供了expression則會當做返回值。

      強制調用函數:

      call :調用函數,expression為函數名及其參數

      開發常見問題

      調試是一種事后補救措施,最好是盡可能避免調試,或者盡可能將調試的工作壓縮在開發階段,在線上出問題和調試,那種酸爽只有經歷過的人才知道。因此開發階段務必加強對測試的重視,代碼都需要單元測試來覆蓋,各個模塊集成測試,線上的性能和穩定性壓測等都必不可少。

      下面我們針對開發過程中常見的問題做一個梳理:

      問題一:編譯問題

      在寫一個稍微大一點的cpp時,由于括號沒有匹配導致很奇怪的報錯,這個時候可以采用二分法來注釋代碼,從而快速定位問題發生的區域。這給我們一個啟示,在寫代碼的時候注意保持良好的輸入習慣:在輸入括號的時候先把左右括號都輸完整,再在中間填代碼;在寫一個新函數的時候首先把return語句寫上;在寫if語句的時候最好else語句也先填上,后面如果用衛語句的話,直接刪掉else就好了,這樣不會漏掉邏輯;調用函數的時候也立刻寫代碼假設函數返回出錯的處理。

      問題二:段錯誤

      寫C、C++代碼最常見的問題是對內存的不當處理,最常見的莫過于段錯誤,典型的如訪問不存在的內存地址、訪問了不允許訪問的地址(試圖往只讀的位置寫數據)。常見產生的原因:1. 訪問空指針;2. 內存越界訪問;3. 棧溢出;4. 地址保護。

      空指針:我們先來看一下64位Linux下運行時虛擬地址的分布情況如圖,可以看到有效的虛擬地址是從0X400000開始的,對任何低于該地址的虛擬地址都是非法的,因此訪問空指針(地址為0X0)會引發段錯誤,另外在調試過程中有一些地址雖然不是0地址,比如查看某個對象的成員,但實際上this指針已經是0地址,但由于訪問成員的時候加上了地址偏移,這種地址和0地址沒什么區別。

      內存越界:并非所有的越界訪問都會導致段錯誤,因為Linux系統分配內存都以頁(一個頁通常是4K大小)的方式進行,當你有內存越界時,雖然超出了你代碼預期的內存空間,但如果還在當前頁面內,你訪問的內存空間還是一個有效的空間,并不會引發段錯誤。對這類問題最好在單元測試中用4.8.5以上的gcc打開地址消毒,或者用valgrind進行檢測。

      棧溢出:當在棧上分配很大的數組時很容易導致棧溢出,對于較大內存的使用最好是通過動態內存分配來獲取。

      地址保護:在mmap做內存映射時,如果嘗試往只讀的映射區寫入數據會導致段錯誤。

      問題三:總線錯誤

      在開發中出發總線錯誤的兩個常見場景:1. 內存地址不滿足對齊要求,比如Intel的intrinsic接口中很多對地址有對齊要求,如果不滿足對齊要求就會報總線錯誤;2. 在mmap時,映射了一個文件,但其他進程將底層的文件截短,當訪問到這部分截掉的內容時,會發生總線錯誤。

      問題四:全局符號介入

      在《從四個問題透析Linux下C++編譯&鏈接》中提到全局符號介入,這種問題通常會引起core dump,要定位相關問題需要對代碼執行路徑有一定了解,通過GDB反饋的當前幀符號來源來定位符號是否來自非預期的庫中。對于某些飄忽不定的core dump,還要看是不是由于當前這次發布引入了錯誤版本的動態庫?由于接口的變化導致類似全局符號介入的效果。

      問題五:無源碼調試

      在沒有源代碼的時候strace就可以發揮神威了,strace會記錄程序所產生的每次系統調用,系統調用的名字,參數,返回值會在同一行顯示,通過觀察返回值的異常對于快速定位問題非常有幫助。

      Linux 任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:關于協同平臺考試項目推薦表的信息
      下一篇:Python 前端開發之Javascript介紹
      相關文章
      亚洲av日韩av天堂影片精品| 亚洲高清无码综合性爱视频| 久久久久久A亚洲欧洲AV冫| 亚洲中文无码卡通动漫野外| 亚洲综合小说久久另类区| 国产成人亚洲综合色影视| 精品国产人成亚洲区| 久久水蜜桃亚洲AV无码精品| 亚洲欧洲专线一区| 国产成人亚洲综合一区| 亚洲av片不卡无码久久| 亚洲国产精品久久人人爱| 亚洲精品亚洲人成在线观看麻豆| 亚洲视频在线一区| 久久久久亚洲av无码专区| 亚洲色图国产精品| 久久亚洲AV无码精品色午夜| 激情内射亚洲一区二区三区| 久久精品国产亚洲AV高清热| 亚洲综合无码一区二区| 亚洲日本中文字幕| 亚洲综合激情另类小说区| 亚洲精品乱码久久久久久下载 | 久久精品国产亚洲AV大全| 久久亚洲国产伦理| 亚洲性天天干天天摸| 亚洲自偷精品视频自拍| 亚洲国产一区在线观看| 亚洲香蕉在线观看| 亚洲另类无码一区二区三区| 亚洲AV无码成人精品区狼人影院| 久久水蜜桃亚洲AV无码精品| 无码欧精品亚洲日韩一区夜夜嗨 | 67194在线午夜亚洲| 中文字幕在线观看亚洲视频| 中文字幕在线观看亚洲视频| 在线亚洲高清揄拍自拍一品区| 亚洲熟妇无码八V在线播放| 亚洲AV无码国产一区二区三区| 国产成人va亚洲电影| 亚洲精品视频在线观看你懂的|