簡體   English   中英

Visual Studio無法在發布模式下顯示“this”的值(帶有調試信息)

[英]Visual Studio not able to show the value of 'this' in release mode (with debug information)

原始問題:

為什么VS c ++版本中的this指針為0?

在使用/ Zi(編譯器:調試信息格式 - 程序數據庫)和/ DEBUG(鏈接器:生成調試信息,是)選項打破Visual Studio 2008 SP1發行版時,為什么'this'指針總是0x00000000?

編輯:改述問題:

我原來的問題很不清楚,對不起。 當使用Visual Studio 2008調試器逐步執行程序時,我可以看到除本地對象的成員變量之外的所有變量。 這可能是因為調試器從this指針派生這些,但VS總是說它是0x00000000,所以它不能派生當前對象的成員變量(它不知道對象的內存位置)

當加載一個megadump(就像一個Windows minidump,但包含進程的整個內存空間)時,我可以查看所有我的局部變量(在函數中定義)和堆上的整個樹結構,即使我有指針。

例如:在Release模式下打破A :: foo()時

'this'的值為0x00000000
'f_'會顯示垃圾

不知何故,這些信息需要提供給流程。 這是VS2008中缺少的功能嗎? 是否有其他正確處理此問題的調試器?

class A
{
  void foo() { /*break here*/ }
  int f_;
};

正如其他人所提到的,在Release模式下進行編譯會進行某些優化(特別是不使用ebp / rbp作為幀指針),這會破壞調試器依賴於計算局部變量的假設。 但是,知道它發生的原因對調試程序沒有多大幫助!

這是一種可以解決它的方法:在方法調用的最開始(打破函數的第一行,而不是左大括號), this指針總是會在特定的寄存器中找到(ecx on 32-bit)系統或64位系統上的rcx)。 調試器知道這一點,因此您應該能夠在方法調用開始時看到this權限的值。 然后,您可以將地址從值列復制和觀看特異性(如(MyObject *)0x003f00f0或其他),這將讓你看到到this以后的方法。

如果這還不夠好(例如,因為你只想在一個bug出現時停止,這只是調用給定方法的一小部分時間),你可以試試這個稍高級(並且不太可靠)的技巧。 通常, this指針在函數調用的早期就從ecx / rcx中取出,因為這是一個“調用者保存”寄存器,這意味着它的值可能被破壞而不能通過你的方法調用的函數調用來恢復(它也是需要的)對於某些只能將該寄存器用作其操作數的指令,如REP *和一些移位指令)。 但是,如果你的方法使用this指針很多(包括隱式使用指成員變量或調用虛成員函數),編譯器可能會保存this另一個寄存器,一個“被調用方保存”登記(意思是任何在返回之前它必須恢復它的功能)。

這樣做的實際結果是,在你的觀察窗口中,你可以嘗試用其他寄存器查看(MyObject *) ebp(MyObject *) esi等,直到你發現你正在查看一個指針。可能是正確的(因為成員變量與您在斷點時this內容的期望一致)。 在x86上,calle保存的寄存器是ebp,esi,edi和ebx。 在x86-64上,它們是rbp,rsi,rdi,rbx,r12,r13,r14和r15。 如果您不想搜索所有這些,您可以嘗試查看函數序言的反匯編,以查看正在復制的ecx(或rcx)。

在Locals窗口中查看的局部變量(包括this )不能在Release構建中依賴於它們在Debug構建中的方式。 在任何給定指令處顯示的變量值是否正確取決於在該點如何使用基礎寄存器。 如果代碼在Debug中運行正常,那么該值實際上不太可能為0。

Release版本中的優化使得Locals窗口中的值成為肉眼可見的垃圾。 如果沒有“反匯編”窗口的並發顯示和關聯,則無法確定“局部”窗口是否告訴您變量的實際值。 如果您通過代碼(也許在不拆卸來源)一步實際使用的線this ,它更可能你會看到一個有效的價值存在。

因為您編寫了一個錯誤的程序並在NULL指針上調用了一個成員函數。

編輯:重讀您的問題。 最有可能的原因是,優化器在您的代碼上執行了一個數字,調試器無法再讀取它。 如果你有一個特定於Release版本的問題,那么它暗示你的代碼中有一個狡猾的#ifdef,或者你調用的UB恰好在Debug模式下工作。 否則,使用Debug構建進行調試。 但是,如果您在發布模式中遇到問題,則無法找到,這並不是非常有用。

你的函數fooinline (它在類定義中聲明,因此是隱式inline ),並且不訪問任何成員。 因此,優化器在編譯代碼時可能根本不會實際傳遞this指針,因此調試器無法使用它。

在發布版本中,優化器將大大重新排列代碼以提高性能, 尤其是 inline函數(盡管它確實優化了其他功能,尤其是在啟用了整個程序優化的情況下)。 而不是傳遞this ,它可以替代地直接將指針傳遞給一個用構件,或甚至只是通過在寄存器中,它加載一個以上函數調用該成員的值。

有時調試信息足以使調試器實際上可以將this指針和局部變量的值拼湊在一起。 通常,它不是,並且監視窗口中顯示的this指針(以及因此成員變量)是無意義的。

因為它是一個發布版本。 優化的重點是更改程序的實現細節,同時保留整體功能。

該計划是否仍然有效? 那么this指針看似無效並不重要

通常,當您使用發布版本時,您應該期望調試器會變得混亂。 代碼將被重新排序,變量被完全刪除,或包含奇怪的意外值。

啟用優化后,不會對這些事情提供任何保證。 但是編譯器不會破壞你的程序。 如果它在沒有優化的情況下工作,它仍然可以用於優化。 如果它突然不起作用,那是因為你有一個只暴露的錯誤,因為編譯器優化並修改了代碼。

它們是“常量”功能嗎?

const函數是使用關鍵字const聲明的函數,這表示它不會更改任何成員,只讀取它們(如訪問器函數)

如果甚至不從非靜態成員變量讀取,優化編譯器可能不會打擾將'this'指針傳遞給某些const函數

優化編譯器可以搜索可能是const的函數,使它們保持不變,然后不將this指針傳遞給它們,從而導致調試器無法找到鈎子。

this指針不是NULL,而是用於調用成員函數的指針:

class A
{
public:
    void f() {}
};

int main()
{
    A* a = NULL;
    a->f(); // DO'H!  NULL pointer access ...

    // FIX
    A* a = new A;
    a->f(); // Aha!
}

正如其他人已經說過的那樣,你應該確保編譯器不會做任何可能混淆調試器的事情,但優化很可能會發生。 你有事實NULL指針可能是因為您靜態調用的函數,如:

A* b=NULL;
b->foo();

該函數在這里不是靜態的,而是稱為靜態方式。

找到真正的this指針的最佳位置是看看堆棧。 對於非靜態類函數, this指針必須是函數的第一個(隱藏)參數。

class A
{
  void foo() { } // this is "void foo(A *this)" really
  int f_;
};

如果你的this prointer在這里是null,那么在調用函數之前你就有問題了。 如果指針在這里是正確的,那么你的調試器有點搞砸了。

我多年來一直在使用帶有Mingw的Code :: Blocks,內置調試器(gdb)當我打開優化時我只有指針問題,否則它總是知道這個指針並且可以隨時引用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM