自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

比Printf高效1000倍!如何精準(zhǔn)捕捉C/C++野指針

開(kāi)發(fā) 前端
在多線程程序中software watchpoint作用有限,因?yàn)槿绻粰z測(cè)的一段內(nèi)存被其它線程修改(就像本文中的示例)那么gdb可能捕捉不到該事件。

大家好,我是島主小風(fēng)哥。

內(nèi)存是C/C++程序員的好幫手,我們通常說(shuō)C/C++程序性能更高其原因之一就在于可以自己來(lái)管理內(nèi)存,然而計(jì)算機(jī)科學(xué)中沒(méi)有任何一項(xiàng)技術(shù)可以包治百病,內(nèi)存問(wèn)題也給C/C++程序員帶來(lái)無(wú)盡的煩惱。

野指針、數(shù)組越界、錯(cuò)誤的內(nèi)存分配或者釋放、多線程讀寫(xiě)導(dǎo)致內(nèi)存被破壞等等,這些都會(huì)導(dǎo)致某段內(nèi)存中的數(shù)據(jù)被”無(wú)意“的破壞掉,這類(lèi)bug通常很難定位,因?yàn)楫?dāng)程序開(kāi)始表現(xiàn)異常時(shí)通常已經(jīng)距離真正出問(wèn)題的地方很遠(yuǎn)了,常用的程序調(diào)試方法往往很難排查此類(lèi)問(wèn)題。

既然這類(lèi)問(wèn)題通常是由于內(nèi)存的讀寫(xiě)造成,那么如果要是某一段內(nèi)存被修改或者讀取時(shí)我們能觀察到此事件就好了,幸運(yùn)的是這類(lèi)技術(shù)已經(jīng)實(shí)現(xiàn)了。

圖片圖片


一段示例

在GDB中你可以通過(guò)添加watchpoint來(lái)觀察一段內(nèi)存,這段內(nèi)存被修改時(shí)程序?qū)?huì)停止,此時(shí)我們就能知道到底是哪行代碼對(duì)該內(nèi)存進(jìn)行了修改,這功能是不是很強(qiáng)大。

接下來(lái)我們用示例來(lái)講解一下,有這樣一段代碼:

#include <iostream>
#include <thread>
using namespace std;

// 線程修改變量值
void memory_write(int* value) {
  *value = 1;
}

int main()
{
    int a = 10;
    // 獲取局部變量a的地址
    int* c = &a;

    for (int i = 0; i < 100; i++) {
      a += i;
    }

    cout << a << endl;

    // 將變量a的地址傳遞到線程
    thread t(memory_write, c);
    t.join();

    return 0;
}

這段代碼非常簡(jiǎn)單,創(chuàng)建局部變量a,然后獲取變量a的地址并賦值給指針c,此后對(duì)變量a進(jìn)行累加和,然后輸出a的值,此時(shí)a的值為4960。

假設(shè)此后你發(fā)現(xiàn)變量a的值竟然變?yōu)榱?,然而由于代碼非常復(fù)雜你并不知道到底是哪段代碼對(duì)變量a進(jìn)行修改,在上述代碼中我們利用線程a來(lái)模擬這個(gè)場(chǎng)景,線程獲取變量a的地址后對(duì)其進(jìn)行了修改,將其變?yōu)榱?,接下來(lái)我們利用調(diào)試工具gdb來(lái)定位到底是誰(shuí)修改了變量a。

開(kāi)始捕捉“肇事者”

對(duì)上述代碼進(jìn)行編譯,接下來(lái)利用gdb進(jìn)行調(diào)試,假設(shè)源文件的名稱是a.cc,編譯后的可執(zhí)行程序名字為a:

$ gdb a.out
(gdb) b a.cc:20
Breakpoint 1 at 0x400f23: file a.cc, line 20.
(gdb) r
Starting program: /bin/a
Breakpoint 1, main () at a.cc:20
20          cout << a << endl;

上述調(diào)試命令(b a.cc:20)表示我們?cè)诖a的第20行加斷點(diǎn),當(dāng)程序運(yùn)行到這里后暫停,調(diào)試命令r表示開(kāi)始運(yùn)行程序,可以看到運(yùn)行到第20行后暫停,此時(shí)我們查看一下變量a的地址:

(gdb) p &a
$1 = (int *) 0x7fffffffe508

可以看到,變量a位于內(nèi)存地址0x7fffffffe508,接下來(lái)重點(diǎn)來(lái)了,我們?cè)撛鯓痈嬖Vgdb讓它幫我們時(shí)刻監(jiān)測(cè)0x7fffffffe508這個(gè)內(nèi)存地址中的值有沒(méi)有被修改呢?很簡(jiǎn)單:

(gdb) watch *(int*)0x7fffffffe508
Hardware watchpoint 2: *(int*)0x7fffffffe508

我們利用watch命令,讓gdb幫我們時(shí)刻監(jiān)測(cè)一段從0x7fffffffe508開(kāi)始大小為4字節(jié)的內(nèi)存區(qū)域(假設(shè)int占據(jù)4字節(jié)),這就是watch *(int*)0x7fffffffe508這行指令的含義:

圖片圖片

除此之外上面gdb的輸出中還有一段值得注意:

Hardware watchpoint 2: *(int*)0x7fffffffe508

注意看,什么是Hardware watchpoint呢?先賣(mài)個(gè)關(guān)子,我們稍后聊,接下來(lái)我們運(yùn)行g(shù)db中的c命令,意思是continue,讓程序繼續(xù)運(yùn)行:

(gdb) c
Continuing.
4960

此時(shí)第20行執(zhí)行完畢并打印出了變量a的值4960,我們接著往下看:

[New Thread 0x7ffff6f5c700 (LWP 531823)]
[Switching to Thread 0x7ffff6f5c700 (LWP 531823)]
Hardware watchpoint 2: *(int*)0x7fffffffe508

Old value = 4960
New value = 1
memory_write (value=0x7fffffffe508) at a.cc:8
8       }
(gdb)

哈哈,gdb成功的捕捉到了是哪一行代碼修改了0x7fffffffe508這塊內(nèi)存,而且詳細(xì)的告訴我們所有信息,可以看到gdb打印出了這塊內(nèi)存之前保存的數(shù)據(jù)是數(shù)字4960,修改后的值為1,并且是在a.cc:8這里被修改的,而這里正是我們創(chuàng)建的線程對(duì)變量a進(jìn)行修改的地方,gdb成功的捕捉到了”肇事者“,原來(lái)是這個(gè)線程”無(wú)意“修改了變量a的值。

圖片圖片

是不是很神奇,那么這一切都是怎樣實(shí)現(xiàn)的呢?

watchpoint是怎樣實(shí)現(xiàn)的?

原來(lái)這一切都是CPU的功勞。

現(xiàn)代處理器中具有特殊的debug寄存器,x86處理器中是DR0到DR7寄存器,利用這些寄存器硬件可以持續(xù)檢測(cè)處理器發(fā)出的用于讀寫(xiě)內(nèi)存的地址,更強(qiáng)大的是,不但硬件watchpoint可以檢查內(nèi)存地址,而且還是可以監(jiān)測(cè)到底是在讀內(nèi)存還是在寫(xiě)內(nèi)存。

利用gdb中的rwatch命令你可以來(lái)監(jiān)測(cè)是否有代碼讀取了某段內(nèi)存;利用gdb中的awatch命令你可以來(lái)檢查是否有代碼修改了某段內(nèi)存;利用gdb中的watch命令你可以檢查對(duì)某段內(nèi)存是否有讀或者寫(xiě)這兩種情況。

一旦硬件監(jiān)測(cè)到相應(yīng)事件,就會(huì)暫停程序的運(yùn)行并把控制權(quán)交給debugger,也就是這里的gdb,此時(shí)我們就可以對(duì)程序的狀態(tài)進(jìn)行詳細(xì)的查看了,這種硬件本身支持的調(diào)試能力就是剛才提到的Hardware watchpoint。

有hardware watchpoint就會(huì)有software watchpoint,當(dāng)硬件不支持hardware watchpoint時(shí)gdb會(huì)自動(dòng)切換到software watchpoint,此時(shí)你的程序每被執(zhí)行一條機(jī)器指令gdb就會(huì)查看相應(yīng)的事件是否發(fā)生,因此software watchpoint要遠(yuǎn)比hardware watchpoint慢,你可以利用gdb中的”set can-use-hw-watchpoints“命令來(lái)控制gdb該使用哪類(lèi)watchpoint。

值得注意的是,在多線程程序中software watchpoint作用有限,因?yàn)槿绻粰z測(cè)的一段內(nèi)存被其它線程修改(就像本文中的示例)那么gdb可能捕捉不到該事件。

責(zé)任編輯:武曉燕 來(lái)源: 碼農(nóng)的荒島求生
相關(guān)推薦

2023-12-26 12:13:31

野指針C++編程

2010-01-28 13:57:19

C++指針基礎(chǔ)

2021-12-21 15:31:10

C++語(yǔ)言指針

2021-08-06 13:48:53

C語(yǔ)言野指針內(nèi)存

2010-01-26 13:42:28

C++指針

2011-04-11 11:09:50

this指針

2024-12-26 10:45:08

2024-05-15 16:01:04

C++編程開(kāi)發(fā)

2014-01-24 09:49:01

C++指針

2021-10-27 16:27:20

C++指針操控

2011-07-12 13:01:00

CC++

2024-07-26 00:22:34

2021-06-10 08:51:57

C++指針聲明指針相關(guān)概念

2021-08-30 19:03:09

C++指針數(shù)據(jù)

2011-04-19 16:38:00

對(duì)象指針指針C++

2024-03-26 10:13:54

日志引擎SigLens

2024-04-10 12:14:36

C++指針算術(shù)運(yùn)算

2024-01-09 09:23:12

指針C++

2012-05-08 15:51:05

愛(ài)普生投影機(jī)

2010-02-04 09:33:08

C++指針重載
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)