Сбой программы только в режиме Release вне отладчика - PullRequest
9 голосов
/ 27 февраля 2010

У меня довольно массивная программа (> 10 тысяч строк кода на C ++). Он отлично работает в режиме отладки или в режиме выпуска при запуске из Visual Studio, но двоичный файл режима выпуска обычно происходит сбой при запуске вручную из командной строки (не всегда !!!).

Строка с удалением вызывает сбой:

bool Save(const short* data, unsigned int width, unsigned int height, 
          const wstring* implicit_path, const wstring* name = NULL, 
          bool enable_overlay = false)
{
    char* buf = new char[17];
    delete [] buf;
}

РЕДАКТИРОВАТЬ: По запросу расширен пример.

"len" имеет длину 16 в моем тестовом случае. Неважно, если я что-то делаю с buf или нет, при удалении происходит сбой.

РЕДАКТИРОВАТЬ: приложение работает нормально без строки delete [], но я полагаю, что это приводит к утечке памяти (так как блок никогда не выделяется). Буф никогда не используется после строки удаления. Также кажется, что он не падает с любым другим типом, кроме char. Теперь я в замешательстве.

Сообщение о сбое является очень неопределенным (типичная Windows "xyz.exe перестал работать"). Когда я нажимаю опцию «Отладить программу», она входит в VS, где указывается ошибка «Место записи нарушения доступа xxxxxxxx». Невозможно определить место ошибки, хотя «Ни один символ не был загружен ни для одного стекового кадра».

Полагаю, это довольно серьезный случай повреждения кучи, но как это отладить? Что я должен искать?

Спасибо за помощь.

Ответы [ 9 ]

11 голосов
/ 27 февраля 2010

Вы проверяли утечки памяти в других местах?

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

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

5 голосов
/ 27 февраля 2010

Самое большое различие между запуском в отладчике и его запуском в том, что когда приложение запускается из отладчика, Windows предоставляет «кучу отладки», которая заполнена шаблоном 0xBAADF00D; обратите внимание, что это не куча отладки, предоставляемая CRT, которая вместо этого заполняется шаблоном 0xCD (IIRC).

Здесь является одним из немногих упоминаний, которые Microsoft делает об этой функции, и здесь вы можете найти некоторые ссылки об этом.

Также упоминается в этой ссылке: «запуск программы и присоединение к ней с помощью отладчика НЕ заставляет ее использовать« специальную кучу отладки », которая будет использоваться».

2 голосов
/ 27 февраля 2010

Возможно, вы где-то перезаписали память, и delete [] просто в первый раз вызывает проблему. Но сама перезапись может быть расположена в совершенно другой части вашей программы. Сложность в поиске перезаписи.

Добавьте следующую функцию

#include <malloc.h>

#define CHKHEAP()  (check_heap(__FILE__, __LINE__))

void check_heap(char *file, int line)
{
    static char *lastOkFile = "here";
    static int lastOkLine = 0;
    static int heapOK = 1;

    if (!heapOK) return;

    if (_heapchk() == _HEAPOK)
    {
        lastOkFile = file;
        lastOkLine = line;
       return;
    }

    heapOK = 0;
    printf("Heap corruption detected at %s (%d)\n", file, line);
    printf("Last OK at %s (%d)\n", lastOkFile, lastOkLine);
}

Теперь часто вызывайте CHKHEAP () во всей вашей программе и запускайте снова. Он должен показать вам исходный файл и строку, где куча становится поврежденной и где все в порядке в последний раз.

1 голос
/ 27 февраля 2010

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

С другой стороны, поскольку вы используете C++, вы можете обойтись без использования выделенного вручную буфера std::string >> существует причина, по которой существует RAII;)

0 голосов
/ 21 марта 2013

Одна из проблем, с которыми я столкнулся, когда наблюдал этот симптом, состояла в том, что у меня произошел сбой многопроцессорной программы при запуске в оболочке, но он работал без ошибок при вызове из valgrind или gdb. Я обнаружил (к моему большому смущению), что у меня в системе несколько запущенных процессов той же программы, все еще работающих в системе, в результате чего вызов mq_send() вернулся с ошибкой. Проблема заключалась в том, что этим ошибочным процессам ядро ​​/ система также назначал дескриптор очереди сообщений, и поэтому mq_send() в моем недавно порожденном процессе (ах) не удалось, но недетерминировано (согласно обстоятельствам планирования ядра).

Как я уже сказал, тривиально, но пока вы не узнаете об этом, вы вырвете свои волосы!

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

[Makefile]
all:
      ...
...

obj:
      ...
clean:
      ...
prep:
  @echo "\n!! ATTENTION !!!\n\n"
  @echo "First: Create and mount mqueues onto /dev/mqueue (Change for non ubuntu)"
  rm -rf /run/shm/*Pool /run/shm/sem.*;
  rm -rf /dev/mqueue/Test;
  rm -rf /dev/mqueue/*Task;
  killall multiProcessProject || true;
0 голосов
/ 18 сентября 2011

У меня возникла та же проблема, и я обнаружил, что моя программа зависала только тогда, когда я удалил [] char-указатели с длиной строки 1.

void DeleteCharArray(char* array){
 if(strlen(array)>1){delete [] array;}
 else{delete array;}
}

Это решило проблему, но все еще подвержено ошибкам, но могло быть изменено, чтобы быть иначе. Во всяком случае, причина, по которой это происходит, я подозреваю, что C ++ char * str = new char [1] и char * str = new char; - это одно и то же, вы пытаетесь удалить указатель с помощью delete [] , который предназначен только для массивов, тогда результаты будут неожиданными и часто фатальными.

0 голосов
/ 27 февраля 2010

Похоже, что у вас есть где-то в коде единичная переменная.

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

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

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

0 голосов
/ 27 февраля 2010

Вы пытались просто изолировать это с помощью того же файла сборки, но кода, основанного только на том, что вы положили выше? Что-то вроде:

int main(int argc, char* argv[] )
{
    const int len( 16 );
    char* buf = new char[len + 1]; 

    delete [] buf;
}

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

Вы пытались создать новый проект и поместить в него строки C + 10K +? Может не потребоваться слишком много времени, чтобы доказать свою точку зрения. Особенно, если существующий проект был импортирован или сильно изменен.

0 голосов
/ 27 февраля 2010

Эти две первые две строки в своей функции.

Если вы действительно имеете в виду, что, как я это понимаю, то в первой строке объявляется локальная переменная buf в одной функции, но удаление удаляет некоторый другой buf, объявленный вне второй функции.

Возможно, вам следует показать две функции.

...