Linux gdb多進程、多線程調試

目錄

常用命令

堆棧相關命令

更爲詳細的斷點調試

gdb多進程調試

gdb多線程調試


前言

gdb 是 linux 平臺下進行程序調試的最常用的工具。簡單的程序調試就是加斷點,然後一步一步讓程序運行,直到找到 bug 。一般的程序調試起來比較簡單,但是在多進程或多線程情況下調試起來就比較麻煩。

若 test.c 是你想要調試的程序,那麼在編譯時需要加 -g,即 gcc test.c -g -o test。完成編譯後使用命令:gdb test。

 

 

常用命令

命令

命令縮寫及例子

說明

list + n

l + n

顯示源碼第n行前後的代碼,顯示範圍有限。

break + n

b + n

在第n行設置斷點

info

i

描述程序狀態

run

r

開始運行程序

display

disp

跟蹤查看某個變量的值

info display

 

用於顯示當前所有要顯示值的表達式的情況

undisplay

undisplay + 編號

用於結束某個表達式值的顯示

step

s

執行下一條語句,如果該語句爲函數調用,則進入函數執行其中的第一條語句

next

n

執行下一條語句,如果該語句爲函數調用,不會進入函數內部執行

print

p

打印內部變量的值

continue

c

繼續運行,直到遇到下一個斷點

start

st

開始執行程序,在main函數的第一條語句前面停下來

kill

k

終止正在調試的程序

quit

q

退出gdb

set args

set args arg1 arg2

設置運行參數

show args

show args

查看運行參數

finish

finish

一直運行到函數返回並打印函數返回時的堆棧地址和返回值及參數值等信息

 


堆棧相關命令

命令

例子

說明

backtrace

bt

查看堆棧信息

frame

f 1

查看棧幀

info reg

info reg/ i r

查看寄存器使用情況

info stack

info stack

查看堆棧使用情況

up/down

up/down

跳到上一層/下一層函數

這裏以一個簡單的程序爲例,進行調試。

#include <bits/stdc++.h>
using namespace std;
#define M 5

int fact(int n)             //線性遞歸
{
    if (n < 0)
        return 0;
    else if(n == 0 || n == 1)
        return 1;
    else
        return n * fact(n - 1);
}
 
int facttail(int n, int a)   //尾遞歸
{
    if (n < 0)
        return 0;
    else if (n == 0)
        return 1;
    else if (n == 1)
        return a;
    else
        return facttail(n - 1, n * a);
}

int facttail1(int n, int a)  
{
    while(n > 0)
    {
        a = n * a;
        n--;
	}
	return a;
}
 
int main()
{
    //printf("%p", facttail);
    int a = fact(M);
    int b = facttail(M, 1);
    cout << "A:" << a <<endl;
    cout << "B:" << b <<endl;
}

 

(1)開始 gdb 調試

 

(2)設置斷點

 

(3)查看棧的使用情況

 

 

更爲詳細的斷點調試

命令

例子

說明

break + 設置斷點的行號

break n

在n行處設置斷點

tbreak + 行號或函數名

tbreak n/func

設置臨時斷點,到達後被自動刪除

break + filename + 行號

break main.c:10

用於在指定文件對應行設置斷點

break + <0x...>

break 0x3400a

用於在內存某一位置處暫停

break + 行號 + if + 條件

break 10 if i==3

用於設置條件斷點,在循環中使用非常方便

info breakpoints/watchpoints [n]

info break

n表示斷點編號,查看斷點/觀察點的情況

clear + 要清除的斷點行號

clear 10

用於清除對應行的斷點,要給出斷點的行號,清除時GDB會給出提示

delete + 要清除的斷點編號

delete 3

用於清除斷點和自動顯示的表達式的命令,要給出斷點的編號,清除時GDB不會給出任何提示

disable/enable + 斷點編號

disable 3

讓所設斷點暫時失效/使能,如果要讓多個編號處的斷點失效/使能,可將編號之間用空格隔開

awatch/watch + 變量

awatch/watch i

設置一個觀察點,當變量被讀出或寫入時程序被暫停

rwatch + 變量

rwatch i

設置一個觀察點,當變量被讀出時,程序被暫停

catch

 

設置捕捉點來補捉程序運行時的一些事件。如:載入共享庫(動態鏈接庫)或是C++的異常

tcatch

 

只設置一次捕捉點,當程序停住以後,應點被自動刪除


 

gdb多進程調試

命令

例子

說明

set follow-fork-mode [parent|child]

 

 

set follow-fork-mode parent or child

設置調試器的模式mode參數可以是

parent fork之後調試原進程,子進程不受影響,這是缺省的方式

child fork之後調試新的進程,父進程不受影響。

 

show follow-fork-mode

show follow-fork-mode

顯示當前調試器的模式

set detach-on-fork [on|off]

 

set detach-on-fork on or off

設置gdb在fork之後是否detach進程中的其中一個,或者繼續保留控制這兩個進程 

on子進程(或者父進程,依賴於follow-fork-mode的值)會detach然後獨立運行,這是缺省的mode

off兩個進程都受gdb控制,一個進程(子進程或父進程,依賴於follow-fork-mode)被調試,另外一個進程被掛起

info inferiors

info inferiors

顯示所有進程

inferiors processid

inferiors 2

切換進程

detach inferiors processid

detach inferiors processid

detach 一個由指定的進程,然後從fork 列表裏刪除。這個進程會被允許繼續獨立運行。

kill inferiors  processid

kill inferiors  processid

殺死一個由指定的進程,然後從fork 列表裏刪除。

catch fork

catch fork

讓程序在fork,vfork或者exec調用的時候中斷

調試多進程時,需要設置 detach-on-fork 的值,默認值爲 on設置爲 off 的含義:一個進程被調試,另外一個進程被掛起,這樣就可以交替的調試進程。follow-fork-mode 默認值爲 parent,即默認調試父進程。調試代碼:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	int num = 0;
	pid_t pid = fork();
	if(pid == 0) //子進程
	{
		while(1)
		{
			num++;
			printf("child:pid:[%d] num:[%d]\n", getpid(), num);
			sleep(2);
		}
	}
	else
	{
		while(1){
			num = num + 2;
			printf("parent:pid:[%d] num:[%d]\n", getpid(), num);
			sleep(2);
		}
	}
	return 0;
}

 

(1) 查看系統默認的 follow-fork-mode 和 detach-on-fork 設置follow-fork-mode 和 detach-on-fork

show follow-fork-mode
show detach-on-fork
set follow-fork-mode [parent|child]   
set detach-on-fork [on|off]

 

(2) 設置斷點並查看斷點信息

 

(3) 運行程序並使用 info inferiors 命令 (顯示GDB調試的所有進程,其中帶有*的進程是正在調試的進程)

 

(4) 使用 inferior + [編號] 切換進程,對子進程進行調試

 

(5) 繼續運行程序,觀察子進程的輸出

 

(6) 中斷子進程運行後,開始逐步調試

 

(7) 再次切換回父進程完成調試

 

 

gdb多線程調試

命令

例子

說明

info threads

info threads

查詢線程信息

thread + 線程號

thread 2

切換線程

thread apply [threadno] [all] + 命令

thread apply [threadno] [all] bt

線程根據相應的命令完成操作

set print thread-events

set print thread-events

控制線程開始和結束時的打印信息

show print thread-events

show print thread-events

顯示線程打印信息的開關狀態

調試代碼:

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
 
/*全局變量*/
int sum = 0;
/*互斥量 */
pthread_mutex_t mutex;
/*聲明線程運行服務程序*/
void* pthread_function1 (void*);
void* pthread_function2 (void*);
 
int main (void)
{
    /*線程的標識符*/
    pthread_t pt_1 = 0;
    pthread_t pt_2 = 0;
    int ret = 0;
    /*互斥初始化*/
    pthread_mutex_init (&mutex, NULL);
    /*分別創建線程1、2*/
    ret = pthread_create( &pt_1,                  //線程標識符指針
                           NULL,                  //默認屬性
                           pthread_function1,     //運行函數
                           NULL);                 //無參數
    if (ret != 0)
    {
        perror ("pthread_1_create");
    }
	
    ret = pthread_create( &pt_2,                  //線程標識符指針
                          NULL,                   //默認屬性
                          pthread_function2,      //運行函數
                          NULL);                  //無參數
    if (ret != 0)
    {
        perror ("pthread_2_create");
    }
    
    /*等待線程1、2的結束*/
    pthread_join (pt_1, NULL);
    pthread_join (pt_2, NULL);
 
    printf ("main programme exit!\n");
    return 0;
}
 
/*線程1的服務程序*/
void* pthread_function1 (void*a)
{
    int i = 0;
    printf ("This is pthread_1!\n");
    for( i=0; i<3; i++ )
    {
        pthread_mutex_lock(&mutex); /*獲取互斥鎖*/
        /*臨界資源*/
        sum++;
        printf ("Thread_1 add one to num:%d\n",sum);
        pthread_mutex_unlock(&mutex); /*釋放互斥鎖*/
        /*注意,這裏以防線程的搶佔,以造成一個線程在另一個線程sleep時多次訪問互斥資源,所以sleep要在得到互斥鎖後調用*/
        sleep (1);
    }
    pthread_exit ( NULL );
}
 
/*線程2的服務程序*/
void* pthread_function2 (void*a)
{
    int i = 0;
    printf ("This is pthread_2!\n");
    for( i=0; i<5; i++ )
    {
        pthread_mutex_lock(&mutex); /*獲取互斥鎖*/
        /*臨界資源*/
        sum++;
        printf ("Thread_2 add one to num:%d\n",sum);
        pthread_mutex_unlock(&mutex); /*釋放互斥鎖*/
        /*注意,這裏以防線程的搶佔,以造成一個線程在另一個線程sleep時多次訪問互斥資源,所以sleep要在得到互斥鎖後調用*/
        sleep (1);
    }
    pthread_exit ( NULL );
}

 

(1) 設置斷點並查看信息

 

(2) 運行程序,這裏可以不設第一個斷點

 

(3) 兩個線程交替運行,觀察不同線程的輸出結果

 

(4) 使用 info threads 查看線程信息,使用 thread + [編號] 切換線程

 

(5) 使用 thread apply + [編號] + 命令 

 

鎖定其他線程,只讓當前線程運行

(1) 設置 set scheduler-locking on

 

(2)  觀察線程 1 運行的情況並完成調試


其他命令

(1) thread apply ID1 ID2 IDN command: 讓線程編號是ID1,ID2…等等的線程都執行command命令。

(2) thread apply all command:所有線程都執行command命令。

(3) set scheduler-locking off|on|step: 在調試某一個線程時,其他線程是否執行。在使用step或continue命令調試當前被調試線程的時候,其他線程也是同時執行的,如果我們只想要被調試的線程執行,而其他線程停止等待,那就要鎖定要調試的線程,只讓他運行。

        off:不鎖定任何線程,默認值。

        on:鎖定其他線程,只有當前線程執行。

        step:在step(單步)時,只有被調試線程運行。

(4) set non-stop on/off當調試一個線程時,其他線程是否運行。

(5) set pagination on/off: 在使用backtrace時,在分頁時是否停止。

(6) set target-async on/ff: 同步和異步。同步,gdb在輸出提示符之前等待程序報告一些線程已經終止的信息。而異步的則是直接返回。

(7) show scheduler-locking: 查看當前鎖定線程的模式

 

參考:

https://blog.csdn.net/snow_5288/article/details/72982594

https://www.cnblogs.com/euphie/p/9781482.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章