操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    DB是我們平時調試c/c++程序的利器, 查起復雜的bug問題,比打印大法要好得多,但是也不得不說, gdb在默認情況下用起來并不是很好用,最近學習到幾個高級點的技巧,分享下:

    一 美化打印

    先上個例子:

    #include <stdio.h>
    
    typedef struct {
     int i ;
     int j;
     char * str;
     int array[10];
    } test_type_t;
    
    int main (int argc, char * argv[])
    {
      test_type_t t={1,2,"test a str" ,{1,2,3,4,5,6,7,8,9,10}};
      printf("i:%d,j:%d\n", t.i,t.j);
      return 0;
    }
    

    gdb調試:

    (gdb) n
    14   printf("i:%d,j:%d\n", t.i,t.j);
    (gdb) p t
    $1={i=1, j=2, str=0x400710 "test a str", array={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}
    (gdb) set print pretty
    (gdb) p t
    $2={
      i=1, 
      j=2, 
      str=0x400710 "test a str", 
      array={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    }
    (gdb) 
    

    set print pretty 通過這個命令可以美化gdb的打印,特別是復雜的數據結構,比較推薦. set print array-indexes on 打印數組的下標

    這些命令如果記不住怎么辦, 類似linux啟動,會執行些初始化腳本,gdb也一樣,腳本名為: .gdbinit將剛才的兩個命令加入進去,就不用每次執行這個命令調試.

    二 動態打印

    一般情況下,我們用gdb調試的時候,先在產生問題的代碼的調用邏輯鏈上設置端點,調試,有時候為了更方便調試,我們會在這些關鍵地方加上打印語句. 有時候加的打印代碼不夠全,來回要反復幾次. 每次加好打印代碼后,需要編譯程序,來回折騰幾次,確實比較麻煩.

    GDB提供動態打印功能,可以在不修改代碼情況下,動態插入打印語句,說的比較抽象,舉例說明:

      2 #include <stdio.h>
      3 
      4 typedef struct {
      5  int i ;
      6  int j;
      7  char * str;
      8  int array[10];
      9 } test_type_t;
     10 
     11 int main (int argc, char * argv[])
     12 {
     13   test_type_t t={1,2,"test a str" ,{1,2,3,4,5,6,7,8,9,10}};
     14   printf("i:%d,j:%d\n", t.i,t.j);
     15 
     16   for (int k=0; k <=10; k++ ) {
     17       t.array[k] +=10;
     18   }
     19   printf("index 0 :%d" ,t.array[0]);
     20   return 0;
     21 }
    
    
    

    上面位置k <=10; 這一行越界,調試下:

    (gdb) dprintf 17,"k:%d,array[k]:%d\n",k,t.array[k]
    Dprintf 2 at 0x40067f: file 1.c, line 17.
    (gdb) r
    Starting program: /test/a.out 
    
    Breakpoint 1, main (argc=1, argv=0x7fffffffe648) at 1.c:13
    13   test_type_t t={1,2,"test a str" ,{1,2,3,4,5,6,7,8,9,10}};
    Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.el7_4.2.x86_64 libgcc-4.8.5-16.el7_4.2.x86_64 libstdc++-4.8.5-16.el7_4.2.x86_64
    (gdb) c
    Continuing.
    i:1,j:2
    k:0,array[k]:1
    k:1,array[k]:2
    k:2,array[k]:3
    k:3,array[k]:4
    k:4,array[k]:5
    k:5,array[k]:6
    k:6,array[k]:7
    k:7,array[k]:8
    k:8,array[k]:9
    k:9,array[k]:10
    k:10,array[k]:0
    index 0 :11[Inferior 1 (process 150109) exited normally]
    (gdb) info b
    Num     Type           Disp Enb Address            What
    1       breakpoint     keep y   0x00000000004005fc in main(int, char**) at 1.c:13
     breakpoint already hit 1 time
    2       dprintf        keep y   0x000000000040067f in main(int, char**) at 1.c:17
     breakpoint already hit 11 times
            printf "k:%d,array[k]:%d\n",k,t.array[k]
    

    我們通過dprintf 17,"k:%d,array[k]:%d\n",k,t.array[k] 在17行動態插入打印語句,然后執行的時候會直接打印出來,17 為行號,后面的語法類似printf.

    動態的語句實際是一個斷點(通過info b 可以看到),只不過這個斷點會自動的恢復,這種動態語句是否可以繼續在上面設置端點的,答案是不行的,通過next執行的時候也執行不到這個語句,只是運行到這里面的時候,可以自動打印變量.

    三 斷點信息的保存和加載

    調試大型程序的時候,往往會設置很多斷點,如果需要多次調試,來回敲這些斷點信息,也是很煩的,可以通過save breakpoints filename的方法,將斷點設置信息保存到filename文件中,GDB啟動后,在通過source filename方式加載:

    (gdb) save breakpoints break.bp
    Saved to file 'break.bp'.
    (gdb) quit
    [root@localhost test]# ls
    1.c  a.out  break.bp
    [root@localhost test]# cat break.bp 
    break main
    dprintf  /test/1.c:17,"k:%d,array[k]:%d\n",k,t.array[k]
    

    加載:

    [root@localhost test]# gdb ./a.out 
    Reading symbols from /test/a.out...done.
    (gdb) source break.bp
    Breakpoint 1 at 0x4005fc: file 1.c, line 13.
    Dprintf 2 at 0x40067f: file 1.c, line 17.
    (gdb) info b
    Num     Type           Disp Enb Address            What
    1       breakpoint     keep y   0x00000000004005fc in main(int, char**) at 1.c:13
    2       dprintf        keep y   0x000000000040067f in main(int, char**) at 1.c:17
            printf "k:%d,array[k]:%d\n",k,t.array[k]
    

    是不是很方便那.

    四 宏打印

    如下示例代碼:

     1 #include <stdio.h>
      2 
      3 #define MAX(x,y) (x)>=(y)?(x):(y)
      4 
      5 int main(int argc , char * argv[])
      6 {
      7     char * p=(char*)0x12345;
      8     printf("max(3,5):%d\n",MAX(3,5));
      9     printf("print a test str\n");
     10     *p=1;
     11     printf("end\n");
     12 }
    
    

    如果通過-g選項編譯是無法查看宏定義的,可以通過-g3 編譯,就可以輕松查看宏定義和宏展開了,如下:

    [root@localhost test]# gdb ./2
    Reading symbols from /test/2...done.
    (gdb) b main
    Breakpoint 1 at 0x40064c: file 2.c, line 7.
    (gdb) r
    Starting program: /test/./2 
    
    Breakpoint 1, main (argc=1, argv=0x7fffffffe648) at 2.c:7
    7     char * p=(char*)0x12345;
    Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.el7_4.2.x86_64 libgcc-4.8.5-16.el7_4.2.x86_64 libstdc++-4.8.5-16.el7_4.2.x86_64
    (gdb) n
    8     printf("max(3,5):%d\n",MAX(3,5));
    (gdb) p MAX(3,5)
    $1=5
    (gdb) macro expand MAX(3,5)
    expands to: (3)>=(5)?(3):(5)
    
    

    可以直接通過p命令打印宏,也可以進行macro expand MAX(3,5)宏展開,調試宏的時候比較有用.

    五 gdb調試界面

    gdb的調試一般是通過n執行下一條命令,然后通過l查看當前的代碼,不夠直觀,gdb其實也可以類似IDE那樣,顯示一行行代碼執行,簡單的直接輸入layout 切換布局:

    再輸入n的時候,箭頭自動下移一行.

    更棒的是可以同時顯示源碼和匯編,輸入layout split

    六 Debug匯編

    有時候,比較麻煩的bug,可以需要反編譯為匯編(disassemble /m main),然后調試, 還是如上的例子程序,調試如下,匯編模式常需要看寄存器的值,輸入layout regs可以同時展示匯編和寄存器的值:

    這個模式下好處,可以通過n命令執行一條條看匯編指令.

    我還是更喜歡用以下的方式調試:

    gdb) disassemble /m main
    Dump of assembler code for function main(int, char**):
    6 {
       0x000000000040063d <+0>: push   %rbp
       0x000000000040063e <+1>: mov    %rsp,%rbp
       0x0000000000400641 <+4>: sub    $0x20,%rsp
       0x0000000000400645 <+8>: mov    %edi,-0x14(%rbp)
       0x0000000000400648 <+11>: mov    %rsi,-0x20(%rbp)
    
    7     char * p=(char*)0x12345;=> 0x000000000040064c <+15>: movq   $0x12345,-0x8(%rbp)
    
    8     printf("max(3,5):%d\n",MAX(3,5));
       0x0000000000400654 <+23>: mov    $0x5,%esi
       0x0000000000400659 <+28>: mov    $0x400720,%edi
       0x000000000040065e <+33>: mov    $0x0,%eax
       0x0000000000400663 <+38>: callq  0x400510 <printf@plt>
    
    9     printf("print a test str\n");
       0x0000000000400668 <+43>: mov    $0x40072d,%edi
       0x000000000040066d <+48>: callq  0x400530 <puts@plt>
    
    10     *p=1;
       0x0000000000400672 <+53>: mov    -0x8(%rbp),%rax
       0x0000000000400676 <+57>: movb   $0x1,(%rax)
    
    11     printf("end\n");
       0x0000000000400679 <+60>: mov    $0x40073e,%edi
       0x000000000040067e <+65>: callq  0x400530 <puts@plt>
    
    12 }
       0x0000000000400683 <+70>: mov    $0x0,%eax
       0x0000000000400688 <+75>: leaveq 
       0x0000000000400689 <+76>: retq   
    
    End of assembler dump.
    (gdb) ni
    8     printf("max(3,5):%d\n",MAX(3,5));
    (gdb) ni
    0x0000000000400659 8     printf("max(3,5):%d\n",MAX(3,5));
    (gdb) ni
    0x000000000040065e 8     printf("max(3,5):%d\n",MAX(3,5));
    (gdb) ni
    0x0000000000400663 8     printf("max(3,5):%d\n",MAX(3,5));
    (gdb) ni
    max(3,5):5
    9     printf("print a test str\n");
    (gdb) ni
    0x000000000040066d 9     printf("print a test str\n");
    (gdb) ni
    print a test str
    10     *p=1;
    (gdb) info register
    
    

    通過disassemble /m main 將源碼和匯編一起顯示,ni 執行下一條匯編指令. info register來顯示匯編信息,core的位置的指令為: 0x0000000000400676 <+57>: movb >0x0000000000400676 <+57>: movb $0x1,(%rax)<> 將1賦值給rax寄存器保存的地址, 我們可以看下rax寄存器,p $rax 直接打印,然后通過x /x 0x12345 來測試下這個地址是否合法,這個地址不是棧的地址,也不是我們申請的堆地址,直接寫,就引起的段錯誤.

    七 詩詞欣賞

    一、GDB 實用調試技巧( 上)

    1、print 打印結果顯示完整

    當使用 print 命令打印一個字符串或者字符數組時,如果該字符串太長,print 命令默認顯示不全的,我們可以通過在 GDB 中輸入 set print element 0 命令設置一下,這樣再次使用 print 命令就能完整地顯示該變量的所有字符串了。

    void ChatSession::OnGetFriendListResponse(const std::shared_ptr<TcpConnection>& conn)

    {

    std::string friendlist;

    MakeUpFriendListInfo(friendlist, conn);

    std::ostringstream os;

    os << "{\"code\": 0, \"msg\": \"ok\", \"userinfo\":" << friendlist << "}";

    Send(msg_type_getofriendlist, m_seq, os.str());

    LOG_INFO << "Response to client: userid=" << m_userinfo.userid << ", cmd=msg_type_getofriendlist, data=" << os.str();

    }

    以上代碼中,當第一次打印 friendlist 這個變量值時,只能顯示部分字符串。使用 set print element 0 設置以后就能完整地顯示出來了。

    2、讓被 GDB 調試的程序接收信號

    請看下面的代碼:

    void prog_exit(int signo)

    {

    std::cout << "program recv signal [" << signo << "] to exit." << std::endl;

    }

    int main(int argc, char* argv[])

    {

    //設置信號處理

    signal(SIGCHLD, SIG_DFL);

    signal(SIGPIPE, SIG_IGN);

    signal(SIGINT, prog_exit);

    signal(SIGTERM, prog_exit);

    int ch;

    bool bdaemon=false;

    while ((ch=getopt(argc, argv, "d")) !=-1)

    {

    switch (ch)

    {

    case 'd':

    bdaemon=true;

    break;

    }

    }

    if (bdaemon)

    daemon_run();

    //省略無關代碼...

    }

    在這個程序中,我們接收到 Ctrl + C 信號(對應信號 SIGINT)時會簡單打印一行信息,而當用 GDB 調試這個程序時,由于 Ctrl + C 默認會被 GDB 接收到(讓調試器中斷下來),導致無法模擬程序接收這一信號。解決這個問題有兩種方式:

    • 在 GDB 中使用 signal 函數手動給程序發送信號,這里就是 signal SIGINT;
    • 改變 GDB 信號處理的設置,通過 handle SIGINT nostop print 告訴 GDB 在接收到 SIGINT 時不要停止,并把該信號傳遞給調試目標程序 。

    (gdb) handle SIGINT nostop print pass

    SIGINT is used by the debugger.

    Are you sure you want to change it? (y or n) y

    Signal Stop Print Pass to program Description

    SIGINT No Yes Yes Interrupt

    (gdb)

    3、函數明明存在,添加斷點時卻無效

    有時候一個函數明明存在,并且我們的程序也存在調試符號,使用 break functionName 添加斷點時 GDB 卻提示:

    Make breakpoint pending on future shared library load? y/n

    即使輸入 y 命令,添加的斷點可能也不會被正確地觸發,此時需要改變添加斷點的方式,使用該函數所在的代碼文件和行號添加斷點就能達到效果

    二、GDB 實用調試技巧( 下)

    1、多線程下禁止線程切換

    假設現在有 5 個線程,除了主線程,工作線程都是下面這樣的一個函數:

    void thread_proc(void* arg)

    {

    //代碼行1

    //代碼行2

    //代碼行3

    //代碼行4

    //代碼行5

    //代碼行6

    //代碼行7

    //代碼行8

    //代碼行9

    //代碼行10

    //代碼行11

    //代碼行12

    //代碼行13

    //代碼行14

    //代碼行15

    }

    為了能說清楚這個問題,我們把四個工作線程分別叫做 A、B、C、D。

    假設 GDB 當前正在處于線程 A 的代碼行 3 處,此時輸入 next 命令,我們期望的是調試器跳到代碼行 4 處;或者使用“u 代碼行10”,那么我們期望輸入 u 命令后調試器可以跳轉到代碼行 10 處。

    但是在實際情況下,GDB 可能會跳轉到代碼行 1 或者代碼行 2 處,甚至代碼行 13、代碼行 14 這樣的地方也是有可能的,這不是調試器 bug,這是多線程程序的特點,當我們從代碼行 4 處讓程序 continue 時,線程 A 雖然會繼續往下執行,但是如果此時系統的線程調度將 CPU 時間片切換到線程 B、C 或者 D 呢?那么程序最終停下來的時候,處于代碼行 1 或者代碼行 2 或者其他地方就不奇怪了,而此時打印相關的變量值,可能就不是我們需要的線程 A 的相關值。

    為了解決調試多線程程序時出現的這種問題,GDB 提供了一個在調試時將程序執行流鎖定在當前調試線程的命令:set scheduler-locking on。當然也可以關閉這一選項,使用 set scheduler-locking off。除了 on/off 這兩個值選項,還有一個不太常用的值叫 step,這里就不介紹了。

    2、條件斷點

    在實際調試中,我們一般會用到三種斷點:普通斷點、條件斷點和硬件斷點。

    硬件斷點又叫數據斷點,這樣的斷點其實就是前面課程中介紹的用 watch 命令添加的部分斷點(為什么是部分而不是全部,前面介紹原因了,watch 添加的斷點有部分是通過軟中斷實現的,不屬于硬件斷點)。硬件斷點的觸發時機是監視的內存地址或者變量值發生變化。

    普通斷點就是除去條件斷點和硬件斷點以外的斷點。

    下面重點來介紹一下條件斷點,所謂條件斷點,就是滿足某個條件才會觸發的斷點,這里先舉一個直觀的例子:

    void do_something_func(int i)

    {

    i ++;

    i=100 * i;

    }

    int main()

    {

    for(int i=0; i < 10000; ++i)

    {

    do_something_func(i);

    }

    return 0;

    }

    在上述代碼中,假如我們希望當變量 i=5000 時,進入 do_something_func() 函數追蹤一下這個函數的執行細節。此時可以修改代碼增加一個 i=5000 的 if 條件,然后重新編譯鏈接調試,這樣顯然比較麻煩,尤其是對于一些大型項目,每次重新編譯鏈接都需要花一定的時間,而且調試完了還得把程序修改回來。

    有了條件斷點就不需要這么麻煩了,添加條件斷點的命令是 break [lineNo] if [condition],其中 lineNo 是程序觸發斷點后需要停下的位置,condition 是斷點觸發的條件。這里可以寫成 break 11 if i==5000,其中,11 就是調用 do_something_fun() 函數所在的行號。當然這里的行號必須是合理行號,如果行號非法或者行號位置不合理也不會觸發這個斷點。

    (gdb) break 11 if i==5000

    Breakpoint 2 at 0x400514: file test1.c, line 10.

    (gdb) r

    The program being debugged has been started already.

    Start it from the beginning? (y or n) y

    Starting program: /root/testgdb/test1

    Breakpoint 1, main () at test1.c:9

    9 for(int i=0; i < 10000; ++i)

    (gdb) c

    Continuing.

    Breakpoint 2, main () at test1.c:11

    11 do_something_func(i);

    (gdb) p i

    =5000

    把 i 打印出來,GDB 確實是在 i=5000 時停下來了。

    添加條件斷點還有一個方法就是先添加一個普通斷點,然后使用“condition 斷點編號斷點觸發條件”這樣的方式來添加。添加一下上述斷點:

    (gdb) b 11

    Breakpoint 1 at 0x400514: file test1.c, line 11.

    (gdb) info b

    Num Type Disp Enb Address What

    1 breakpoint keep y 0x0000000000400514 in main at test1.c:11

    (gdb) condition 1 i==5000

    (gdb) r

    Starting program: /root/testgdb/test1

    y

    Breakpoint 1, main () at test1.c:11

    11 do_something_func(i);

    Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.el7_4.2.x86_64

    (gdb) p i

    =5000

    (gdb)

    同樣的規則,如果斷點編號不存在,也無法添加成功,GDB 會提示斷點不存在:

    (gdb) condition 2 i==5000

    No breakpoint number 2.

    3、使用 GDB 調試多進程程序

    這里說的多進程程序指的是一個進程使用 Linux 系統調用 fork() 函數產生的子進程,沒有相互關聯的進程就是普通的 GDB 調試,不必刻意討論。

    在實際的應用中,如有這樣一類程序,如 Nginx,對于客戶端的連接是采用多進程模型,當 Nginx 接受客戶端連接后,創建一個新的進程來處理這一路連接上的信息來往,新產生的進程與原進程互為父子關系,那么如何用 GDB 調試這樣的父子進程呢?一般有兩種方法:

    • 用 GDB 先調試父進程,等子進程 fork 出來后,使用 gdb attach 到進程上去,當然這需要重新開啟一個 session 窗口用于調試,gdb attach 的用法在前面已經介紹過了;
    • GDB 調試器提供了一個選項叫 follow-fork,可以使用 show follow-fork mode 查看當前值,也可以通過 set follow-fork mode 來設置是當一個進程 fork 出新的子進程時,GDB 是繼續調試父進程還是子進程(取值是 child),默認是父進程( 取值是 parent)。(gdb) show follow-fork modeDebugger response to a program call of fork or vfork is “parent”.(gdb) set follow-fork child(gdb) show follow-fork modeDebugger response to a program call of fork or vfork is “child”.(gdb)

    建議讀者自己寫個程序,然后調用 fork() 函數去實踐一下,若要想閱讀和調試 Apache HTTP Server 或者 Nginx 這樣的程序,這個技能是必須要掌握的。

    三、自定義 GDB 調試命令

    在某些場景下,我們需要根據自己的程序情況,制定一些可以在調試時輸出程序特定信息的命令,這在 GDB 中很容易做到,只要在 Linux 當前用戶家(home)目錄下,如 root 用戶是 “/root” 目錄,非 root 用戶則對應 “/home/ 用戶名”目錄。

    在上述目錄中自定義一個名叫 .gdbinit 文件,在 Linux 系統中以點號開頭的文件名一般都是隱藏文件,因此 .gdbinit 也是一個隱藏文件,可以使用 ls -a 命令查看(a 的含義是 all 的意思,即顯示所有文件,當然也就包括顯示隱藏文件);如果不存在,使用 vim 或者 touch 命令創建一個就可以,然后在這個文件中寫上你自定義命令的 shell 腳本即可。

    以 Apache Web 服務器的源碼為例(Apache Server 的源碼下載地址請點擊這里),在源碼根目錄下有個文件叫 .gdbinit,這個就是 Apache Server 自定義的 GDB 命令:

    # gdb macros which may be useful for folks using gdb to debug

    # apache. Delete it if it bothers you.

    define dump_table

    set $t=(apr_table_entry_t *)((apr_array_header_t *)$arg0)->elts

    set $n=((apr_array_header_t *)$arg0)->nelts

    set $i=0

    while $i < $n

    if $t[$i].val==(void *)0L

    printf "[%u] '%s'=>NULL\n", $i, $t[$i].key

    else

    printf "[%u] '%s'='%s' [%p]\n", $i, $t[$i].key, $t[$i].val, $t[$i].val

    end

    set $i=$i + 1

    end

    end

    # 省略部分代碼

    # Set sane defaults for common signals:

    handle SIGPIPE noprint pass nostop

    handle SIGUSR1 print pass nostop

    當然在這個文件的最底部,Apache 設置了讓 GDB 調試器不要處理 SIGPIPE 和 SIGUSR1 這兩個信號,而是將這兩個信號直接傳遞給被調試的程序本身(即 Apache Server)。

    四、GDB TUI——在 GDB 中顯示程序源碼

    很多 Linux 用戶或者其他平臺用戶習慣了有強大的源碼顯示窗口的調試器,可能對 GDB 用 list 顯示源碼的方式非常不習慣,主要是因為 GDB 在調試的時候不能很好地展示源碼。

    GDB 中可以用 list 命令顯示源碼,但是 list 命令顯示沒有代碼高亮,也不能一眼定位到正在執行的那行代碼在整個代碼中的位置??梢院敛豢鋸埖卣f,這個問題是阻止很多人長期使用 GDB 的最大障礙,如此不便,以至于 GNU 都想辦法解決了——使用 GDB 自帶的 GDB TUI。

    先來看一張效果圖,是我在使用 GDB TUI 調試 redis-server 時的截圖,這樣看代碼比使用 list 命令更方便。

    1、開啟 GDB TUI 模式

    開啟 GDB TUI 模式有兩個方法。

    • 方法一:使用 gdbtui 命令或者 gdb-tui 命令開啟一個調試。gdbtui -q 需要調試的程序名
    • 方法二:直接使用 GDB 調試代碼,在需要的時候使用切換鍵 Ctrl + X + A 調出 GDB TUI 。
    2、GDB TUI 模式常用窗口

    默認情況下,GDB TUI 模式會顯示 command 窗口和 source 窗口,如上圖所示,還有其他窗口,如下列舉的四個常用的窗口:

    • (cmd)command 命令窗口,可以輸入調試命令
    • (src)source 源代碼窗口, 顯示當前行、斷點等信息
    • (asm)assembly 匯編代碼窗口
    • (reg)register 寄存器窗口

    可以通過“layout + 窗口類型”命令來選擇自己需要的窗口,例如,在 cmd 窗口輸入 layout asm 則可以切換到匯編代碼窗口。

    layout 命令還可以用來修改窗口布局,在 cmd 窗口中輸入 help layout,常見的有:

    Usage: layout prev | next |

    Layout names are:

    src : Displays source and command windows.

    asm : Displays disassembly and command windows.

    split : Displays source, disassembly and command windows.

    regs : Displays register window. If existing layout

    is source/command or assembly/command, the

    register window is displayed. If the

    source/assembly/command (split) is displayed,

    the register window is displayed with

    the window that has current logical focus.

    另外,可以通過 winheight 命令修改各個窗口的大小,如下所示:

    (gdb) help winheight

    Set the height of a specified window.

    Usage: winheight <win_name> [+ | -] <#lines>

    Window names are:

    src : the source window

    cmd : the command window

    asm : the disassembly window

    regs : the register display

    ##將代碼窗口的高度擴大 5 行代碼

    winheight src + 5

    ##將代碼窗口的高度減小 4 代碼

    winheight src - 4

    當前 GDB TUI 窗口放大或者縮小以后,窗口中的內容不會自己刷新以適應新的窗口尺寸,我們可以通過 space 鍵強行刷新 GDB TUI 窗口。

    3、窗口焦點切換

    在默認設置下,方向鍵和 PageUp/PageDown 都是用來控制 GDB TUI 的 src 窗口的,因此,我們常用上下鍵顯示前一條命令和后一條命令的功能就沒有了,不過可以通過 Ctrl + N/Ctrl + P 來獲取這個功能。

    注意:通過方向鍵調整了GDB TUI 的 src 窗口以后,可以用 update 命令重新把焦點定位到當前執行的代碼上。

    我們可以通過 focus 命令來調整焦點位置,默認情況下焦點是在 src 窗口,通過 focus next 命令可以把焦點移到 cmd 窗口,這時候就可以像以前一樣,通過方向鍵來切換上一條命令和下一條命令。同理,也可以使用 focus prev 切回到源碼窗口,如果焦點不在 src 窗口,我們就不必使用方向鍵來瀏覽源碼了。

    (gdb) help focus

    help focus

    Set focus to named window or next/prev window.

    Usage: focus { | next | prev}

    Valid Window names are:

    src : the source window

    asm : the disassembly window

    regs : the register display

    cmd : the command window

    五、GDB 高級擴展工具——CGDB

    在使用 GDB 單步調試時,代碼每執行一行才顯示下一行,很多用慣了圖形界面 IDE 調試的讀者可能會覺得非常不方便,而 GDB TUI 可能看起來不錯,但是存在經?;ㄆ恋膯栴},也讓很多讀者不勝其煩。那 Linux 下有沒有既能在調試時動態顯示當前調試處的文件代碼,又能不花屏的工具呢?有的,這就是 CGDB。

    CGDB 本質上是對 GDB 做了一層“包裹”,所有在 GDB 中可以使用的命令,在 CGDB 中也可以使用。

    1、CGDB 的安裝

    (略)

    2、CGDB 的使用

    安裝成功以后,就可以使用 CGDB 了,在命令行輸入 cgdb 命令啟動 CGDB ,啟動后界面如下:

    界面分為上下兩部分:上部為代碼窗口,顯示調試過程中的代碼;下部就是 GDB 原來的命令窗口。默認窗口焦點在下部的命令窗口,如果想將焦點切換到上部的代碼窗口,按鍵盤上的 Esc 鍵,之后再次按字母 i 鍵將使焦點回到命令窗口。

    注意:這個“焦點窗口”的概念很重要,它決定著你當前可以操作的是上部代碼窗口還是命令窗口( 和GDB TUI 一樣)。

    我們用 Redis 自帶的客戶端程序 redis-cli 為例,輸入以下命令啟動調試:

    cgdb redis-cli

    啟動后的界面如下:

    然后加兩個斷點,如下圖所示:

    如上圖所示,我們在程序的 main ( 上圖中第 2824 行 )和第 2832 行分別加了一個斷點,添加斷點以后,代碼窗口的行號將會以紅色顯示,另外有一個綠色箭頭指向當前執行的行( 這里由于在 main 函數上加了個斷點,綠色箭頭指向第一個斷點位置 )。單步調試并步入第 2827 行的 sdsnew() 函數調用中,可以看到代碼視圖中相應的代碼也發生了變化,并且綠色箭頭始終指向當前執行的行數:

    更多 CGDB 的用法可以查閱官網,也可以參考 CGDB 中文手冊,點擊這里可查看詳情。

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有