Подтвердить что ты не робот

Когда программа работает правильно во время отладки, но в противном случае?

Возможный дубликат:
Общие причины ошибок в версии выпуска, не присутствующих в режиме отладки

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

Вы когда-нибудь встречали такую ​​ситуацию и почему?

Обновление

Чтобы доказать, есть логические причины, которые приведут к такой неприятной ситуации:

Я думаю, что одна из больших возможностей - это куча доступа. Мне когда-то была написана функция, которая выделяет небольшой буфер, но позже я выхожу из boudary.It будет работать правильно в gdb, cdb и т.д. (я не знаю почему, но он работает корректно), но прерывается ненормально при нормальной работе.

Я использую С++.

Я не думаю, что моя проблема дублирует предыдущую.

Это сравнение между режимом освобождения и режимом отладки, но моя - отладка, а не отладка, в которой есть слово heisenbug, как отмечалось многими другими.

спасибо.

4b9b3361

Ответ 1

У вас есть heisenbug.

Отладчик может инициализировать значения

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

Релиз может быть создан с оптимизацией

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

Могут быть другие причины для heisenbugs.

Ответ 2

Вот общий вопрос, который может привести к Гейзенбугу (любите это имя!):

    // Sanity check - this should never fail
    ASSERT( ReleaseResources() == SUCCESS);

В сборке отладки это будет работать так, как ожидалось, но аргумент макроса ASSERT игнорируется в сборке релиза. Я игнорирую, я имею в виду, что не только не будет сообщен результат, но выражение вообще не будет оцениваться (т.е. ReleaseResources() не будет вызываться).

Это распространенная ошибка, и именно поэтому SDK Windows определяет макрос VERIFY() в дополнение к макросу ASSERT(). Они генерируют диалог утверждения во время выполнения в сборке отладки, если аргумент оценивается как false. Однако их поведение отличается от выпуска. Здесь разница:

    ASSERT( foo() == true );  // Confirm that call to foo() was successful
    VERIFY( bar() == true );  // Confirm that call to bar() was successful

В сборке отладки два вышеуказанных макроса ведут себя одинаково. Однако в сборке релизов они по существу эквивалентны:

    ;       // Confirm that call to foo() was successful
    bar();  // Confirm that call to bar() was successful



Кстати, если ваша среда определяет макрос ASSERT(), но не макрос VERIFY(), вы можете легко определить свой собственный:

    #ifdef _DEBUG
       // DEBUG build: Define VERIFY simply as ASSERT
    #  define VERIFY(expr)  ASSERT(expr)
    #else
       // RELEASE build: Define VERIFY as the expression, without any checking
    #  define VERIFY(expr)  ((void)(expr))
    #endif

Надеюсь, что это поможет.

Ответ 3

По-видимому, stackoverflow не позволяет мне отправлять ответ, содержащий только одно слово:)

VALGRIND

Ответ 4

При использовании отладчика иногда инициализируется память (например, zero'ed), тогда как без сеанса отладки память может быть случайной. Это может объяснить поведение, которое вы видите.

Ответ 5

У вас есть диалоги, поэтому в приложении могут быть потоки. Если есть потоки, есть возможность условий гонки.

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

Ответ 6

В дополнение к тому, что сказал JeffH, вам нужно подумать, есть ли у развертывающего компьютера (или сервера) одна и та же среда/библиотеки/whatever_related_to_the_program.

Иногда очень сложно правильно отлаживать, если вы отлаживаете другие условия.

Джованни

Ответ 7

Один реальный пример heisenbug из Raymand Zhang.

/*-------------------------------------------------------------- 
GdPage.cpp : a real example to illustrate Heisenberg Effect
   related with guard page by Raymond Zhang, Oct. 2008
--------------------------------------------------------------*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
  LPVOID lpvAddr;               // address of the test memory
  lpvAddr = VirtualAlloc(NULL, 0x4096,
                         MEM_RESERVE | MEM_COMMIT,
                         PAGE_READONLY | PAGE_GUARD);
  if(lpvAddr == NULL) 
  {
    printf("VirtualAlloc failed with %ld\n", GetLastError());
    return -1;
  }

  return *(long *)lpvAddr;  
}

Программа закончила бы ненормально компиляцию с помощью Debug или Release, потому что указав флаг PAGE_GUARD, вызовет:

Страницы в регионе становятся охранниками страницы. Любая попытка чтения или запись на страницу защиты вызывает система для создания STATUS_GUARD_PAGE исключение и отключить страницу защиты положение дел. Таким образом, страницы стражей действуют как одноразовый сигнал доступа.

Таким образом, вы получили бы STATUS_GUARD_PAGE при попытке получить доступ к *lpvAddr. Но если вы используете отладчик, загрузите программу и посмотрите *lpvAddv или выполните сборку с последним утверждением return *(long *)lpvAddr по сборке, отладчик должен был увидеть охрану чтобы определить значение *lpvAddr. Так что отладчик очистил бы охранную сигнализацию для нас, прежде чем мы получим доступ к *lpvAddr.

Ответ 8

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

Для MSVC это можно отключить с помощью env-var _NO_DEBUG_HEAP=1. (Отладочная куча медленная, поэтому это помогает, если ваши отладочные запуски также ужасно медленны..).

Другим способом получения этого же является запуск процесса вне отладчика, поэтому вы получаете обычный запуск, затем ждите первую строку в главном и присоедините отладчик для обработки. Это должно работать для "любой" системы. при условии, что вы не потерпите крах перед основным. (Вы можете подождать на ctor на статически премани, сконструированном объекте, тогда...)

Но у меня нет опыта работы с gcc/gdb в этом вопросе, но все может быть похоже там... (Комментарии приветствуются.)

Ответ 9

Какой язык программирования вы используете. Некоторые языки, такие как С++, ведут себя немного по-разному между версиями выпуска и отладки. В случае с С++ это означает, что при объявлении var, например int i;, в отладочных сборках он будет инициализирован до 0, тогда как в релиз-сборках он может принимать любое значение (независимо от того, что было ранее сохранено в его ячейке памяти).

Ответ 10

Одна большая причина заключается в том, что код отладки может определять макрос _DEBUG, который можно использовать в коде, чтобы добавить лишний материал в отладочные сборки.

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

Я не знаю, добавляет ли код отладки в стек стек, чтобы отмечать фреймы стека. Любой дополнительный материал в стеке может скрыть эффекты переполнения буфера.

Попробуйте использовать те же параметры команд, что и ваша сборка, и просто добавьте флаг -g (или эквивалентный флаг отладки). gcc позволяет параметр отладки вместе с параметрами оптимизации.

Ответ 11

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

Пример того, где это может произойти, - это физическое моделирование в реальном времени с переменным шагом времени, основанное на истекшем системном времени. Вот почему есть такие статьи: http://gafferongames.com/game-physics/fix-your-timestep/