2014-07-22 13 views
4

在Visual Studio中調試C++項目時,某些數據斷點從未命中。爲什麼數據斷點無法在未對齊的地址上工作

所以我寫了一些代碼來進行測試:

#include <iostream> 
#include <stdint.h> 

void test(uint32_t* p) 
{ 
    *p = 0; 

    // set a data breakpoint on p 

    *((char*)p + 2) = 0x1; 

    std::cout << *p << std::endl; 
} 

uint32_t* alloc(size_t offset) 
{ 
    char* p = new char[sizeof(uint32_t) + offset]; 
    p = p + offset; 
    return (uint32_t*)p; 
} 

int main() 
{ 
    test(alloc(0)); // test #1 
    test(alloc(2)); // test #2 
} 

正如你看到的,在性能測試中,* p的值將首先被清零, 那麼它將被隱式改變,我得到了一個litte- endian CPU,所以它 必須是65536.

如果你設置一個數據斷點在p(4字節)檢測變化, 你會得到兩個不同的結果:命中與否。這取決於該p指向的地址 。

在上述我的測試代碼,測試#1將擊中和試驗#2將不會, 之間#1和#2的差別是由 的alloc返回的地址(0)或Alloc(2)。

這篇文章How to: Set a Data Breakpoint在MSDN上不討論這個。

數據斷點不能在未對齊的地址上工作嗎?

回答

3

數據斷點是在CPU的幫助下使用x86上的調試寄存器設置的;關於它們,英特爾手冊說(§17.2.5):

斷點地址寄存器(調試寄存器DR0通過DR3)並且對於每個斷點的LENn字段定義一個 範圍連續字節的地址用於數據或我/ O斷點。 LENn字段允許從相應調試寄存器(DRn)中指定的線性地址開始,指定1,2,4, 或8字節範圍。 兩個字節的 範圍必須在字邊界上對齊; 4字節範圍必須在雙字邊界上對齊。 I/O 地址是零擴展的(從16位到32位,用於與所選調試寄存器中的斷點地址進行比較)。這些要求由處理器執行;它使用LENn字段位來掩碼調試寄存器中的較低地址位 。 未對齊的數據或I/O斷點地址不會產生有效的結果。

(強調)

所以,限制是在硬件中。

0

發生這種情況的詳細說明:

數據斷點使用CPU的調試寄存器。

  • 16位(2個字節)斷點得到的最低地址位清零(addr & -2):在x86這些調試寄存器獲得通過掩蔽地址較低位對準他們的數據大小。
  • 32位(4個字節)的斷點將其最低2位地址位清除爲(addr & -4)
  • 64位(8字節)斷點將清除3個最低地址位(addr & -8)

當CPU 86訪問存儲器它通過以相同的方式到調試寄存器地址和掩蔽它的地址進行比較,如果二者相等它觸發一個斷點。


這是一個簡化調試斷點比較器中電子電路的技巧:只需要比較兩個對齊的地址。

電子將其轉換爲僞代碼:

if(!((address^debug_address) & debug_mask)) 
    breakpoint(); 

相反的:

if((address >= debug_address) & (address < debug_address_plus_length)) 
    breakpoint(); 

這將是更加複雜的硅實現,減緩CPU。即使在軟件中,第一個也會運行得更快。

只要所有存儲器訪問都對齊,地址屏蔽技巧就可以完美工作。


所以我們說p點,以解決0xF02和斷點是32位(4個字節),那麼斷點地址被對準((0xF02 & -4) == 0xF00)

注:-4是0xFFFFFFFC(32位)或0xFFFFFFFFFFFFFFFC(64位)

然後0xF04 ((0xF04 & -4) == 0xF04)比較之前訪問地址(0xF02 + 2 == 0xF04)

然後CPU口罩到調試斷點地址(0xF00)。

它們不匹配,所以CPU不會觸發斷點。

相關問題