Linux學習——Gdb基本調試方法&&多線程調試

1.Gdb的基本調試

示例代碼

//e.c
 #include <stdio.h>
 void debug(char *str)
{
    printf("debug info :%s\n",str );
}

int main(int argc,char *argv[]){
    int i,j;
    j=0;
    for(i=0;i<10;i++){
        j+=5;
        printf("now a=%d\n", j);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

gcc -g -o e e.c
調試gdb e
或者輸入gdb
然後 file e

1. list 命令用法

list命令顯示多行源代碼,從上次的位置開始顯示,默認情況下,一次顯示10行,第一次使用時,從代碼其實位置顯示。

list n顯示已第n行未中心的10行代碼
list functionname顯示以functionname的函數爲中心的10行代碼
  • 1
  • 2

2. 斷點命令break
break location:在location位置設置斷點,改位置可以爲某一行,某函數名或者其它結構的地址。gdb會在執行該位置的代碼之前停下來.

使用delete breakpoints 斷點號 刪除斷點
這裏的斷點號表示的是第幾個斷點,剛纔執行break 10返回 
reakpoint 1 at 0x40050a: file e.c, line 10.
中的1表示該斷點的標號,因此使用 delete breakpoints 1表示刪除第10行所定義的斷點

clear n表示清除第n行的斷點,因此clear 10等同於delete breakpoints 1
disable/enable n表示使得編號爲n的斷點暫時失效或有效
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

可使用info查看斷點相關的信息
info breakpoints

c(continue),繼續程序運行直到下一個斷點

3.display命令
查看參數的值

4.step及next命令
step可使得程序逐條執行,即執行完一條語句然後在下一跳語句前停下來,等待用戶的命令。一般使用step命令是,可使用display或者watch命令查看變量的變化,從而判斷程序行爲是否符合要求。當下一條指令爲函數時,s進入函數內部,在其第一條語句前停下來。next單步執行,但不進入函數內部。
step n,next n 表示連續但不執行n條指令,如果期間遇到斷點,則停下來
5.print打印內部變量值
6.bt 查看堆棧信息
7.watch 監視變量值的變化

watch通常需要和break,run,continue聯合使用。

下面舉例說明:

代碼如下:
#include <stdio.h>

int main()
{
int a=0;
for(int i=0; i<10; i++)
a+=i;
}
調試的時候過程如下:

(gdb) l
1 #include <stdio.h>
2
3 int main()
4 {
5 int a=0;
6 for(int i=0; i<10; i++)
7 a+=i;
8 }
(gdb) b 5 -------在第5行設置斷電
Breakpoint 1 at 0x80483ba: file a.cpp, line 5.
(gdb) r -------執行到斷點處停止
Starting program: /a.o

Breakpoint 1, main () at a.cpp:5
5 int a=0;
(gdb) watch a -------觀察a的值,當有變化時,停止
Hardware watchpoint 2: a
(gdb) c -------繼續執行,當a的值變化時停止
Continuing.
Hardware watchpoint 2: a

Old value = 0
New value = 1
main () at a.cpp:6
6 for(int i=0; i<10; i++)
(gdb)
Continuing.
Hardware watchpoint 2: a

Old value = 1
New value = 3
main () at a.cpp:6
6 for(int i=0; i<10; i++)
(gdb)
Continuing.
Hardware watchpoint 2: a

即,在使用watch時步驟如下:

  1. 使用break在要觀察的變量所在處設置斷點;

  2. 使用run執行,直到斷點;

  3. 使用watch設置觀察點;

  4. 使用continue觀察設置的觀察點是否有變化。

8.set variable value=x 動態改變變量值
1,調試中需要修改臨時變量的值時,可以使用set命令
語法:

set variable key = value
set var key = value
示例:
(gdb) set variable array[1] = 12

2,另一種更簡單的方式,使用print命令修改
語法:
print key=value
在這裏插入圖片描述

2.多線程調試

1. 線程的查看
首先創建兩個線程:

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

void* pthread_run1(void* arg)
{
    (void)arg;

    while(1)
    {
        printf("I am thread1,ID: %d\n",pthread_self());
        sleep(1);
    }
}

void* pthread_run2(void* arg)
{
    (void)arg;

    while(1)
    {
        printf("I am thread2,ID: %d\n",pthread_self());
        sleep(1);
    }
}


int main()
{

    pthread_t tid1;
    pthread_t tid2;

    pthread_create(&tid1,NULL,pthread_run1,NULL);
    pthread_create(&tid2,NULL,pthread_run2,NULL);

    printf("I am main thread\n");

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

分析:上面程序中創建了兩個線程,程序執行起來,main函數所在程序爲主線程,在這個主線程中有兩個新線程運行。
命令行查看:

//查看當前運行的進程
ps aux|grep a.out
//查看當前運行的輕量級進程
ps -aL|grep a.out
//查看主線程和新線程的關係
pstree -p 主線程id
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在這裏插入圖片描述
2. 線程棧結構的查看

  1. 獲取線程ID
  2. 通過命令查看棧結構 ps stack 線程ID
    在這裏插入圖片描述
    3. 利用gdb查看線程信息
    將進程附加到gdb調試器當中,查看是否創建了新線程:gdb attach 主線程ID
    在這裏插入圖片描述

查看線程的一些信息

//1.查看進程:info inferiors
//2.查看線程:info threads
//3.查看線程棧結構:bt
//4.切換線程:thread n(n代表第幾個線程)
在這裏插入圖片描述

4. 利用gdb調試多線程
  當程序沒有啓動,線程還沒有執行,此時利用gdb調試多線程和調試普通程序一樣,通過設置斷點,運行,查看信息等等,在這裏不在演示,最後會加上調試線程的命令

設置斷點

//1. 設置斷點:break 行號/函數名
//2. 查看斷點:info b
  • 1
  • 2

在這裏插入圖片描述
執行線程2的函數,指行完畢繼續運行到斷點處

1. 繼續使某一線程運行:thread apply 1-n(第幾個線程) n
2. 重新啓動程序運行到斷點處:r
  • 1
  • 2

在這裏插入圖片描述
3.只運行當前線程

. 設置:set scheduler-locking on 2
. 運行:n
  • 1
  • 2

在這裏插入圖片描述
4.所有線程併發執行

    1. 設置:set scheduler-locking off
    2. 運行:n
  • 1
  • 2

在這裏插入圖片描述

注意點:
ctrl+c ctrl+d ctrl+z 的區別和使用場景

Ctrl+C :強制中斷程序,程序無論運行哪裏都停止。

Ctrl+D :發送一個 exit 的信號,退出當前的用戶或者是客戶端。

Ctrl+Z :暫停程序,在進程中維持掛起狀態。

引用別人的說法:

1、Ctrl+C比較暴力,就是發送Terminal到當前的程序,比如你正在運行一個查找功能,文件正在查找中,Ctrl+C就會強制結束當前的這個進程。
2、Ctrl+Z 是把當前的程序掛起,暫停執行這個程序,比如你正在mysql終端中,需要出來搞點其他的文件操作,又不想退出mysql終端(因爲下次還得輸入用戶名密碼進入,挺麻煩),於是可以ctrl+z將mysql掛起,然後進行其他操作,然後輸入 fg 回車後就可以回來,當然可以掛起好多進程到後臺,然後 fg 加編號就能把掛起的進程返回到前臺。當然,配合bg(後臺)和fg命令進行前後臺切換會非常方便。
3、Ctrl+D 是發送一個exit信號,沒有那麼強烈,類似ctrl+C的操作,比如你從管理員root退回到你的普通用戶就可以這麼用。

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