GDB調試實戰二啟動調試

2021-02-14 太平洋工作室

一、線程增加斷點

基本的功能有了,可以開始了,首先在發送和接收線程增加斷點:

(gdb) b 36
Breakpoint 14 at 0x417e3e: file /root/projects/gdbTest/inet/Server.cpp, line 36.
(gdb) c
Continuing.

Breakpoint 14, inet::Server::__lambda0::operator() (__closure=0xc913c0) at /root/projects/gdbTest/inet/Server.cpp:36
36                                      int len = strlen(barr[num % 10]);
(gdb) 

(gdb) b 55
Breakpoint 15 at 0x417fd6: file /root/projects/gdbTest/inet/Server.cpp, line 55.
(gdb) c
Continuing.
[Switching to Thread 0x7f18878d7700 (LWP 4146)]

Breakpoint 15, inet::Server::__lambda1::operator() (__closure=0xc91660) at /root/projects/gdbTest/inet/Server.cpp:55
55                                      int len = pData_->GetMemCount(0);
(gdb) 

通過c命令來啟動進程的運行。

(gdb) l
50              {
51                      this->idwork = std::thread([&]() {
52                              //ת53                           while (!quit_)
54                              {
55                                      int len = pData_->GetMemCount(0);
56                                      if (len > 0)
57                                      {
58                                              for (int num = 0; num < len; num++)
59                                              {
(gdb) 
60                                                      std::shared_ptr<global::MemData> tmp = pData_->GetMemData(num, 0);
61                                                      std::cout << "dataparse tmp comData:" << tmp->len << std::endl;
62                                              }
63
64                                      }
65
66                                      len = pData_->GetMemCount(1);
67                                      if (len > 0)
68                                      {
69                                              for (int num = 0; num < len; num++)
(gdb) p tmp
No symbol "tmp" in current context.
(gdb) n
[Switching to Thread 0x7f18880d8700 (LWP 4145)]

Breakpoint 14, inet::Server::__lambda0::operator() (__closure=0xc913c0) at /root/projects/gdbTest/inet/Server.cpp:36
36                                      int len = strlen(barr[num % 10]);
(gdb) n
37                                      memmove(dbuf_, barr[num%10], len);
(gdb) p num
$1 = (int &) @0x7ffc47677f9c: 33010
(gdb) p barr
$2 = {0x41cde3 "abce459v,$#@!`pit", 0x41cdf5 "NBG^*(_rrtthth", 0x41ce04 "YT%$#dghhfgdh88980", 0x41ce17 "##$gryryejhetrjrj", 0x41ce29 "whethtwrh8967777", 0x41ce3a "346346546*&****jjkkIO", 
  0x41ce50 "JL)(567568jgjhjgk", 0x41ce62 "ghjgktytuk", 0x41ce6d "45747476466&&***HJJKKTryret", 0x41ce89 "truytritrkmb<>NNHGG$##@"}
(gdb) p /x barr
$3 = {0x41cde3, 0x41cdf5, 0x41ce04, 0x41ce17, 0x41ce29, 0x41ce3a, 0x41ce50, 0x41ce62, 0x41ce6d, 0x41ce89}
(gdb) p /s barr
$4 = {0x41cde3 "abce459v,$#@!`pit", 0x41cdf5 "NBG^*(_rrtthth", 0x41ce04 "YT%$#dghhfgdh88980", 0x41ce17 "##$gryryejhetrjrj", 0x41ce29 "whethtwrh8967777", 0x41ce3a "346346546*&****jjkkIO", 
  0x41ce50 "JL)(567568jgjhjgk", 0x41ce62 "ghjgktytuk", 0x41ce6d "45747476466&&***HJJKKTryret", 0x41ce89 "truytritrkmb<>NNHGG$##@"}
(gdb) p /c barr
$5 = {227 '\343', 245 '\365', 4 '\004', 23 '\027', 41 ')', 58 ':', 80 'P', 98 'b', 109 'm', 137 '\211'}
(gdb) p *barr@3
$7 = {0x41cde3 "abce459v,$#@!`pit", 0x41cdf5 "NBG^*(_rrtthth", 0x41ce04 "YT%$#dghhfgdh88980"}
(gdb) 

上面通過查看數據的命令來查看關心的數據。

二、線程切換控制

在前面看到了線程的切換,下面使用命令鎖定一個線程來監控:

(gdb) show scheduler-locking
Mode for locking scheduler during execution is "step".
(gdb) set schedule
schedule-multiple  scheduler-locking  
(gdb) set scheduler-locking on
(gdb) n
37                                      memmove(dbuf_, barr[num%10], len);
(gdb) n
38                                      pData_->ParseComData(dbuf_, len);
(gdb) n
39                                      pData_->ParseNetData(dbuf_, len);
(gdb) n
41                                      num++;
(gdb) n
42                                      sleep(1);
(gdb) n
34                              while (!quit_)
(gdb) n

Breakpoint 1, inet::Server::__lambda0::operator() (__closure=0x6a93c0) at /root/projects/gdbTest/inet/Server.cpp:36
36                                      int len = strlen(barr[num % 10]);
(gdb) n
37                                      memmove(dbuf_, barr[num%10], len);
(gdb) n
38                                      pData_->ParseComData(dbuf_, len);
(gdb) n
39                                      pData_->ParseNetData(dbuf_, len);
(gdb) n
41                                      num++;
(gdb) 

可以發現,即使在線程Sleep退出時間片佔用後,仍然再次回到本線程,而沒有跳到另外一個線程中去。這樣就很好的解決了多線程,只想對某個線程進行控制的方法。
如果線程很多,可以用命令:

(gdb) info thread
  Id   Target Id         Frame 
* 3    Thread 0x7f5c9d2e3700 (LWP 97767) "gdbTest" inet::Server::__lambda0::operator() (__closure=0x6a93c0) at /root/projects/gdbTest/inet/Server.cpp:41
  2    Thread 0x7f5c9cae2700 (LWP 97768) "gdbTest" 0x00007f5c9d3d3a1d in write () from /lib64/libc.so.6
  1    Thread 0x7f5c9e305740 (LWP 97766) "gdbTest" 0x00007f5c9d6bb017 in pthread_join () from /lib64/libpthread.so.0
(gdb) thread 2

這種方法先進入指定線程,再進行鎖定即可。需要說明的是,調試完成後,要記得將線程調度重新設置為off。
如果想達到在IDE軟體中對某個變量的不間斷的監視,可以使用命令:

(gdb) display num
2: num = (int &) @0x7ffc447985bc: 32883
(gdb) c
Continuing.

Breakpoint 1, inet::Server::__lambda0::operator() (__closure=0x6a93c0) at /root/projects/gdbTest/inet/Server.cpp:36
36                                      int len = strlen(barr[num % 10]);
2: num = (int &) @0x7ffc447985bc: 32884
(gdb) c
Continuing.
[Switching to Thread 0x7f5c9cae2700 (LWP 97768)]

Breakpoint 2, inet::Server::__lambda1::operator() (__closure=0x6a9660) at /root/projects/gdbTest/inet/Server.cpp:55
55                                      int len = pData_->GetMemCount(0);
1: len = 21
(gdb) c
Continuing.
[Switching to Thread 0x7f5c9d2e3700 (LWP 97767)]

Breakpoint 1, inet::Server::__lambda0::operator() (__closure=0x6a93c0) at /root/projects/gdbTest/inet/Server.cpp:36
36                                      int len = strlen(barr[num % 10]);
2: num = (int &) @0x7ffc447985bc: 32885
(gdb) c
Continuing.

Breakpoint 1, inet::Server::__lambda0::operator() (__closure=0x6a93c0) at /root/projects/gdbTest/inet/Server.cpp:36
36                                      int len = strlen(barr[num % 10]);
2: num = (int &) @0x7ffc447985bc: 32886
(gdb) c
Continuing.
[Switching to Thread 0x7f5c9cae2700 (LWP 97768)]

Breakpoint 2, inet::Server::__lambda1::operator() (__closure=0x6a9660) at /root/projects/gdbTest/inet/Server.cpp:55
55                                      int len = pData_->GetMemCount(0);
1: len = 21
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
2:   y  num (cannot be evaluated in the current context)
1:   y  len
(gdb) 

假如對線程中的數據操作達到某個條件感興趣,可以使用:

(gdb) b 61 if num == 3
Breakpoint 3 at 0x41802a: file /root/projects/gdbTest/inet/Server.cpp, line 61.
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000417e3e in inet::Server::__lambda0::operator()() const at /root/projects/gdbTest/inet/Server.cpp:36
        breakpoint already hit 5 times
2       breakpoint     keep y   0x0000000000417fd6 in inet::Server::__lambda1::operator()() const at /root/projects/gdbTest/inet/Server.cpp:55
        breakpoint already hit 3 times
3       breakpoint     keep y   0x000000000041802a in inet::Server::__lambda1::operator()() const at /root/projects/gdbTest/inet/Server.cpp:61
        stop only if num == 3
(gdb) c
Continuing.
[Switching to Thread 0x7f5c9d2e3700 (LWP 97767)]

Breakpoint 1, inet::Server::__lambda0::operator() (__closure=0x6a93c0) at /root/projects/gdbTest/inet/Server.cpp:36
36                                      int len = strlen(barr[num % 10]);
2: num = (int &) @0x7ffc447985bc: 32887
(gdb) c
Continuing.
[Switching to Thread 0x7f5c9cae2700 (LWP 97768)]

Breakpoint 3, inet::Server::__lambda1::operator() (__closure=0x6a9660) at /root/projects/gdbTest/inet/Server.cpp:61
61                                                      std::cout << "dataparse tmp comData:" << tmp->len << std::endl;
1: len = 36
(gdb) display num
3: num = 3
(gdb) 

三、臨時斷點的使用

如果只關心一次的情況可以使用臨時斷點:
先刪除原有斷點:

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000417e3e in inet::Server::__lambda0::operator()() const at /root/projects/gdbTest/inet/Server.cpp:36
        breakpoint already hit 9 times
2       breakpoint     keep y   0x0000000000417fd6 in inet::Server::__lambda1::operator()() const at /root/projects/gdbTest/inet/Server.cpp:55
        breakpoint already hit 4 times
3       breakpoint     keep y   0x000000000041802a in inet::Server::__lambda1::operator()() const at /root/projects/gdbTest/inet/Server.cpp:61
        stop only if num == 3
        breakpoint already hit 2 times
(gdb) delete 3
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000417e3e in inet::Server::__lambda0::operator()() const at /root/projects/gdbTest/inet/Server.cpp:36
        breakpoint already hit 9 times
2       breakpoint     keep y   0x0000000000417fd6 in inet::Server::__lambda1::operator()() const at /root/projects/gdbTest/inet/Server.cpp:55
        breakpoint already hit 4 times

再重新使用臨時斷點:

(gdb) tb 61 if num == 3
Temporary breakpoint 4 at 0x41802a: file /root/projects/gdbTest/inet/Server.cpp, line 61.
(gdb) c
Continuing.
[Switching to Thread 0x7f5c9d2e3700 (LWP 97767)]

Breakpoint 1, inet::Server::__lambda0::operator() (__closure=0x6a93c0) at /root/projects/gdbTest/inet/Server.cpp:36
36                                      int len = strlen(barr[num % 10]);
2: num = (int &) @0x7ffc447985bc: 32891
(gdb) c
Continuing.

Breakpoint 1, inet::Server::__lambda0::operator() (__closure=0x6a93c0) at /root/projects/gdbTest/inet/Server.cpp:36
36                                      int len = strlen(barr[num % 10]);
2: num = (int &) @0x7ffc447985bc: 32892
(gdb) c
2: num = (int &) @0x7ffc447985bc: 32893
(gdb) c
Continuing.
[Switching to Thread 0x7f5c9cae2700 (LWP 97768)]

Temporary breakpoint 4, inet::Server::__lambda1::operator() (__closure=0x6a9660) at /root/projects/gdbTest/inet/Server.cpp:61
61                                                      std::cout << "dataparse tmp comData:" << tmp->len << std::endl;
3: num = 3
1: len = 38
(gdb) 

四、智能指針查看

可以查看一下智能指針的數據內容:

(gdb) n
56                                      if (len > 0)
1: len = 42
(gdb) n
58                                              for (int num = 0; num < len; num++)
3: num = 41
1: len = 42
(gdb) n
60                                                      std::shared_ptr<global::MemData> tmp = pData_->GetMemData(num, 0);
3: num = 0
1: len = 42
(gdb) p tmp
$3 = std::shared_ptr (count 1, weak 0) 0x7f5c98003518
(gdb) p tmp->len
$4 = 60
(gdb) p tmp->buf
$5 = "awhet45747476466&&***HJJKKTryrettruytritrkmb<>NNHGG$##@abce4", '\000' <repeats 39 times>

五、總結

一個初步的GDB實戰項目就啟動了,靈活的應用各種命令和監控方式,來驗證和程序設計本身的目標是否吻合,下一步,就針對具體的數據流程,走一個完整的過程。

相關焦點

  • GDB入門教程之如何使用GDB啟動調試
    GDB (The GNU Project Debugger)是 Linux 系統下調試 C 和 C++ 程序的主要神兵。本文介紹多種方式下使用 GDB 啟動進程調試的方法和命令。要使得 C 和 C++ 程序能在 GDB 下正常進行調試,必須在程序編譯期間把基本的調試信息(如變量名、函數名、函數調用堆棧等)添加到可執行文件中。gcc、cc、g++等編譯器可通過編譯參數 -g 指定添加調試信息。當使用GDB加載不包含調試信息的二進位文件或進程時,GDB終端會提示錯誤信息:"no debugging symbols found"。
  • 原來gdb的底層調試原理這麼簡單
    我們都知道,在使用 GCC 編譯時,可以增加 -g 選項在可執行文件中嵌入更多的調試信息,那麼具體嵌入了哪些調試信息呢?這些調試信息是如何與二進位的指令之間進行相互交互的呢?作業系統首先會啟動 gdb 進程,這個進程會調用系統函數 fork(),創建一個子進程,這個子進程做兩件事情:(1) 調用系統函數 ptrace(PTRACE_TRACEME,[其他參數]);(2) 通過 execc 來加載、執行可執行程序 test,那麼 test 程序就在這個子進程中開始執行了。
  • GDB調試實戰三完整的數據流程
    一、基本流程介紹經過了前面的各種準備,再結合學習過的GDB的各種調試方法和技巧,下面進行一次完整的數據調試流程。整個程序的流程基本是模擬網絡和網口通信,區別在於完整包的大小不同,網口是100個字節而串口是60個字節。程序在啟動時,啟動了兩個線程,生產者直接從一個固定的字符串數組取大小不同的數組來送到數據處理的兩個std::vector 中。
  • GDB調試還不會?看這篇就夠了!
    調試方式運行程序程序還未啟動時,可有多種方式啟動調試。調試啟動無參程序例如:$ gdb helloWorld(gdb)輸入run命令,即可運行程序調試啟動帶參程序假設有以下程序,啟動時需要帶參數:#include<stdio.h>int main(int argc,char *argv[]){    if(1 >=
  • 用圖文帶你徹底弄懂GDB調試原理
    相信每位嵌入式開發工程師都使用過gdb來調試程序,如果你說沒有用過,那只能說明你的開發經歷還不夠坎坷,還需要繼續被 BUG吊打。我們都知道,在使用gcc編譯時,可以使用-g選項在可執行文件中嵌入更多的調試信息,那麼具體嵌入了哪些調試信息?這些調試信息是如何與二進位的指令之間進行相互交互?
  • GDB 調試 .NET 程序實錄 - .NET 調用 .so 出現問題怎麼解決
    沒有測試的原因有兩個:一是,眾所周知 .NET Core 是跨平臺的,既然在 ARM64 下已經測試過,那麼應該沒問題;二是,項目是華為 edge IoT 項目,必須走華為雲註冊邊緣設備,然後通過雲服務下發應用(Docker)到機器才能成功運行(有許多系統自動創建的環境變量和設備連接華為 IoT 的憑證)。在機器上直接啟動,是無法正常完成整個流程的。
  • 你還在用GDB調試程序嗎?
    你還在用GDB調試程序嗎?如果是,那麼我們是同道中人。但是你知道GDB有一個很強大的功能,Python scripting嘛?如果是的,那麼恭喜你,你是一個大牛。本文主要講述如何使用Python來提高你的GDB調試技能, 讓你從繁重的重複的工作裡面掙脫出來呼吸新鮮空氣。首先,第一件事,使用gdb7.x以上的版本,最好9.x的。因為Python的支持是從gdb7.0(2009年?)開始的。
  • Linux C/C++ 開發人員要熟練掌握 GDB 調試代碼塊
    一、啟動GDB調試使用 GDB 調試程序一般有三種方式: gdb filename gdb attach pid gdb filename corename1、直接調試目標程序2、附加進程3、調試 core
  • Linux 系統內核的調試
    gdb和調試stub之間通過gdb串行協議進行通訊。gdb串行協議是一種基於消息的ASCII碼協議,包含了各種調試命令。當設置斷點時,kgdb負責在設置斷點的指令前增加一條trap指令,當執行到斷點時控制權就轉移到調試stub中去。此時,調試stub的任務就是使用遠程串行通信協議將當前環境傳送給gdb,然後從gdb處接受命令。
  • android平臺arm指令學習和調試
    三、調試可執行程序1)Push編譯好的elf到樣機中2)使用gdbserver啟動該文件「/data/local/gdbserver:12345/data/local/helloa」3)adbforwardtcp:12345tcp:123454)啟動gdb5)Targetremote127.0.0.1
  • Android動態調試-不用IDA Pro
    為什麼不用gdb,因為lldb的出現,取代gdb只是遲早的事情,可以說gdb是Depracated。在 Android逆向之ARM64靜態分析對app的中的so進行了靜態分析,這篇文章介紹兩種動態調試的方式,一種是radare2,另一種是lldb。
  • 在VIM中實現對嵌入式軟體的調試
    1 gdb對嵌入式軟體的調試模式 許多非Linux的嵌入式系統已經在使用gdb與gdbstub對目標板進行遠程「交叉調試」;然而,因為Linux內核實現了ptrace()系統調用,所以在對嵌入式應用程式進行調試的時候並不需要gdb stub,而採用gdb套件提供的gdb伺服器來對目標板上的嵌入式應用程式進行調試。
  • 如何調試多線程程序
    在上一篇文章《使用 gdb 調試多進程程序 —— 以調試 nginx 為例》我們介紹了如何使用 gdb 調試多進程程序,這篇文章我們來介紹下如何使用 gdb 調試多線程程序,同時這個方法也是我閱讀和分析一個新的 C/C++ 項目常用的方法。當然,多線程調試的前提是你需要熟悉多線程的基礎知識,包括線程的創建和退出、線程之間的各種同步原語等。
  • 用Visual Studio調試Linux程序
    使用Visual Studio+VisualGDB調試遠程linux程序需要工具:Visual Studio 2013或以上版本(以下簡稱VS)VisualGDB(一款VS插件,官網為:http://visualgdb.com/)含有調試符號的linux程序文件(該程序文件為調試目標)Visual Assistant(番茄助手
  • C/C++程序調試和內存檢測
    使用條件編譯,可以清楚的辨別哪些是調試代碼,有利於調試後的代碼整理。例如:#ifdef DEBUG std::cout << x :  #endif程序編譯時可以選擇性的加上-DDEBUG。如果加上這個標誌,就定義了DEBUG這個符號,從而在程序中包含調試用的額外代碼,沒有加上該標誌,這些調試代碼將刪除。
  • GDB簡明教程:快速入門
    接下來我們編譯這個程序:# gcc -g -O0 main.c -o a.out生成的可執行文件a.out裡都是二進位的機器指令,如果你想要源碼級調試,就需要在編譯程序時選中:debug選項(-g),生成的debug
  • GDB與Valgrind ,調試C++代碼內存的工具
    筆者 入"坑"C++之後,在調試 C++代碼的過程之中,學習了不少調試代碼內存的工具。希望借這個機會來介紹一下筆者常用的工具,GDB,Valgrind等等,相信大家通過好好運用這些工具,能更好的馴服內存這匹"野馬"。
  • [原]排錯實戰——拯救加載調試符號失敗的IDA
    在windows下,調試器(比如vs, windbg)可以通過調試符號(PDB)把地址與符號名對應起來,為我們提供更可讀的信息。IDA應該也支持加載PDB,通過查看IDA安裝目錄下的idahelp.chm(打開後搜索PDB即可找到相關說明)發現還真支持。但是當我加載符號的時候,卻失敗了。本文記錄了整個調查過程。效果對比 先放兩張對比圖,大家直觀感受下區別。
  • Go 快速入門篇(三):單元測試、問題定位及代碼調試
    二、問題定位與代碼調試列印變量當然,對於一些簡單的測試,還可以通過列印變量的方式來定位問題,通常我們在 PHP 中就是這麼做的,比如通過 var_dump、printf、echo 之類的語句或函數列印返回的結果,在 Laravel 框架中還可以通過 dd 或 dump 方法進行簡單高效的變量列印調試,在 Go 語言中,對應的列印函數是前面介紹過的 Printf 或 Println 方法
  • 常用 GDB 命令中文速覽
    run — 啟動被調試的程序,縮寫為 rbacktrace — 查看程序調用棧的信息,縮寫為 btptype — 列印類型 TYPE 的定義break使用 break 命令(縮寫 b)來設置斷點。警告:如果當控制在沒有調試信息的情況下編譯的函數中使用 step 命令,則執行將繼續進行,直到控制到達具有調試信息的函數。 同樣,它不會進入沒有調試信息編譯的函數。要執行沒有調試信息的函數,請使用 stepi 命令,詳見後文。reverse-step反向單步執行程序,直到到達另一個源碼行的開頭。