Visual Studio не может показать значение «this» в режиме выпуска (с отладочной информацией) - PullRequest
12 голосов
/ 11 января 2011

Исходный вопрос:

Почему этот указатель 0 в сборке выпуска VS c ++?

При разрыве в сборке выпуска Visual Studio 2008 SP1 с /Опции Zi (компилятор: формат отладочной информации - база данных программы) и / DEBUG (компоновщик: генерировать отладочную информацию, да), почему указатели 'this' всегда 0x00000000?

EDIT: перефразированный вопрос:

Мой оригинальный вопрос был довольно неясен, извините за это.При использовании отладчика Visual Studio 2008 для пошагового выполнения программы я вижу все переменные, кроме переменных-членов локального объекта.Вероятно, это связано с тем, что отладчик выводит их из указателя this, но VS всегда сообщает, что это 0x00000000, поэтому он не может получить переменные-члены текущего объекта (он не знает позицию объекта в памяти)

При загрузкеmegadump (как и мини-дамп Windows, но содержащий все пространство памяти процесса), я могу просматривать все свои локальные переменные (определенные в функции) и целые древовидные структуры в куче, даже если у меня есть указатели.

Например: при взломе A :: foo () в режиме Release

'this' будет иметь значение 0x00000000
'f_' будет отображать мусор

Каким-то образом эта информация должнабыть доступным для процесса.Это отсутствующая функция в VS2008?Любой другой отладчик, который обрабатывает это правильно?

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

Ответы [ 8 ]

33 голосов
/ 10 марта 2011

Как уже упоминали некоторые другие, компиляция в режиме Release делает определенные оптимизации (особенно исключающие использование ebp / rbp в качестве указателя кадра), которые нарушают предположения, на которые опирается отладчик для определения ваших локальных переменных.Однако, знание того, почему это происходит, не очень полезно для отладки вашей программы!

Вот способ, которым вы можете обойти это: в самом начале вызова метода (прерывание на первой строке функции, а неоткрывающая скобка), указатель this всегда будет найден в определенном регистре (ecx в 32-битных системах или rcx в 64-битных системах).Отладчик знает это, и поэтому вы должны видеть значение this прямо в начале вызова метода.Затем вы можете скопировать адрес из столбца «Значение» и посмотреть его конкретно (как (MyObject *)0x003f00f0 или как угодно), что позволит вам заглянуть в this позже в методе.

Если это недостаточно хорошо (Например, поскольку вы хотите останавливаться только при появлении ошибки, что составляет очень небольшой процент времени вызова данного метода), вы можете попробовать этот более продвинутый (и менее надежный) прием.Обычно указатель this извлекается из ecx / rcx очень рано при вызове функции, потому что это регистр "сохранения вызывающего абонента", что означает, что его значение может быть забито и не восстановлено вызовами функций, которые создает ваш метод (этотакже необходимо для некоторых команд, которые могут только использовать этот регистр для своего операнда, например REP * и некоторые из команд сдвига).Однако, если ваш метод часто использует указатель this (включая неявное использование ссылки на переменные-члены или вызов виртуальных функций-членов), компилятор, вероятно, сохранит this в другом регистре, регистре «callee-saves»(имеется в виду, что любая функция, которая поглощает ее, должна восстановить ее перед возвратом).

Практический результат этого заключается в том, что в окне просмотра вы можете попробовать посмотреть (MyObject *) ebp, (MyObject *) esi и т. д.с другими регистрами, пока вы не обнаружите, что вы смотрите на указатель, который, вероятно, является правильным (потому что переменные-члены совпадают с вашим ожиданием содержимого this во время вашей точки останова).На x86 регистры, сохраняемые при вызове, - это ebp, esi, edi и ebx.На x86-64 это rbp, rsi, rdi, rbx, r12, r13, r14 и r15.Если вы не хотите искать все это, вы всегда можете попробовать разобрать пролог функции, чтобы увидеть, во что копируется ecx (или rcx).

7 голосов
/ 11 января 2011

На локальные переменные (включая this) при просмотре в окне Locals нельзя полагаться в сборке выпуска так же, как в сборках отладки. Правильно ли показано значение переменной в любой данной инструкции, зависит от того, как используется базовый регистр в этой точке. Если в Debug код работает нормально, маловероятно, что это значение равно 0.

Оптимизация в сборках Release делает значения в окне Locals просто дерьмом, невооруженным глазом. Без одновременного отображения и сопоставления окна «Разборка» вы не можете быть уверены, что окно «Местные жители» сообщает вам фактическое значение переменной. Если вы шагаете по коду (возможно, в разделе «Разборка, а не исходный код») к строке, которая фактически использует this, более вероятно, что вы увидите там допустимое значение.

6 голосов
/ 11 января 2011

Поскольку вы написали программу с ошибками и вызвали функцию-член по нулевому указателю.

Редактировать: перечитайте ваш вопрос. Скорее всего, это связано с тем, что оптимизатор сделал число в вашем коде, а отладчик больше не может его читать. Если у вас есть проблема, специфичная для сборки выпуска, то это подсказка, что в вашем коде есть хитрый #ifdef, или вы вызвали UB, который просто работает в режиме отладки. Иначе, отладка с помощью Debug build. Однако это не ужасно полезно, если у вас действительно есть проблема в режиме выпуска, которую вы не можете найти.

3 голосов
/ 10 марта 2011

Ваша функция foo равна inline (она объявлена ​​в определении класса, поэтому неявно inline) и не имеет доступа ни к каким членам. Следовательно, оптимизатор, скорее всего, вообще не пропустит указатель this при компиляции кода, поэтому он недоступен для отладчика.

В выпусках сборки оптимизатор будет существенно перестраивать код для повышения производительности, , в частности с inline функциями (хотя он также оптимизирует и другие функции, особенно если включена оптимизация всей программы). Вместо передачи this он может вместо этого передать указатель на используемый член напрямую или даже просто передать значение члена в регистр, который он загрузил для предыдущего вызова функции.

Иногда информации отладки достаточно, чтобы отладчик мог фактически собрать вместе указатель this и значения локальных переменных. Часто это не так, и указатель this, показанный в окне наблюдения (и, следовательно, переменные-члены), является бессмысленным.

1 голос
/ 14 марта 2011

Это "постоянные" функции?

Функция const - это функция, которая объявлена ​​с ключевым словом const, и это означает, что она не изменит ни одного члена, а только прочитает их (например, функции доступа)

Оптимизирующий компилятор может не беспокоиться о передаче указателя 'this' некоторым константным функциям, если он даже не читает нестатические переменные-члены

Оптимизирующий компилятор может искать функции, которые могут быть константными, делать их постоянными, а затем не передавать в них указатель this, в результате чего отладчик не сможет найти ловушку.

1 голос
/ 11 января 2011

Потому что это релизная сборка. Весь смысл оптимизации состоит в том, чтобы изменить детали реализации программы, сохранив при этом общую функциональность.

Программа все еще работает? Тогда не имеет значения , что указатель this, по-видимому, нулевой.

В целом, когда вы работаете со сборкой релиза, вы должны ожидать, что отладчик будет сбит с толку. Код будет переупорядочен, переменные удалены полностью или содержат странные неожиданные значения.

Когда оптимизация включена, никаких гарантий по всем этим вопросам не предоставляется. Но компилятор не будет ломать вашу программу. Если он работал без оптимизации, он все равно будет работать с оптимизацией. Если он вдруг не работает, это потому, что у вас есть ошибка, которая была обнаружена только потому, что компилятор оптимизировал и изменил код.

0 голосов
/ 15 марта 2011

Как уже говорили другие, вы должны убедиться, что компилятор не делает ничего, что может сбить с толку отладчик, оптимизация, скорее всего, сделает.Тот факт, что у вас есть указатель NULL, может произойти, если вы вызываете функцию статически, как:

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

Функция здесь не статична, а называется статическим способом.

Лучшее место длянайти настоящий this указатель - это посмотреть на стек.Для нестатических функций класса указатель this ДОЛЖЕН быть первым (скрытым) аргументом вашей функции.

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

Если ваш this prointer здесь равен нулю, то у вас есть проблема перед вызовом функции,Если указатель здесь верен, то ваш отладчик немного испорчен.

Я использую Code :: Blocks с Mingw уже много лет, со встроенным отладчиком (gdb) у меня только проблемы с указателемкогда у меня были включены оптимизации, в противном случае он всегда знает указатель this и может обращаться к нему в любое время.

0 голосов
/ 11 января 2011

Это не указатель 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!
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...