Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Сборки / MT и / MD завершаются сбоем, но только когда отладчик не подключен: как отлаживать?

Автор вопроса: leanderОтветов: 3

У меня есть небольшое однопоточное приложение C ++, скомпилированное и связанное с использованием Visual Studio 2005, которое использует ускорение (crc, program_options и tokenizer), немного STL и различные другие системные заголовки.

(Его основная цель - прочитать файл .csv и создать собственный двоичный файл .dat и парный .h, объявляющий структуры, которые "объясняют" формат .dat.)

Инструмент дает сбой (нарушение прав доступа на NULL) при запуске вне отладчика, только в выпуске. Например. нажатие F5 не приводит к сбою инструмента, в отличие от Ctrl-F5. Когда я снова подключаю отладчик, я получаю такой стек:

ntdll.dll!_RtlAllocateHeap@12()  + 0x26916 bytes
csv2bin.exe!malloc(unsigned int size=0x00000014)  Line 163 + 0x63 bytes C
csv2bin.exe!operator new(unsigned int size=0x00000014)  Line 59 + 0x8 bytes C++
>csv2bin.exe!Record::addField(const char * string=0x0034aac8)  Line 62 + 0x7 bytes  C++
csv2bin.exe!main(int argc=0x00000007, char * * argv=0x00343998)  Line 253   C++
csv2bin.exe!__tmainCRTStartup()  Line 327 + 0x12 bytes  C

Строка, в которой происходит сбой, выглядит довольно безобидно:

pField = new NumberField(this, static_cast<NumberFieldInfo*>(pFieldInfo));

... Я не верю, что он дошел до конструктора, он просто выделяет память перед переходом к конструктору. Он также выполнил этот код десятки раз к моменту сбоя, обычно в согласованном (но в остальном не подозрительном) месте.

Проблема исчезает при компиляции с / MTd или / MDd (время выполнения отладки) и возвращается при использовании / MT или / MD.

NULL загружается из стека, и я могу видеть его в памяти. _RtlAllocateHeap @ 12 + 0x26916 байт кажется огромным смещением, как будто был сделан неправильный переход.

Я пробовал _HAS_ITERATOR_DEBUGGING в отладочной сборке, и это не вызвало ничего подозрительного.

Удаление HeapValidate в начале и в конце Record :: addField показывает "кучу ОК" вплоть до момента ее сбоя.

Раньше это работало - я не совсем уверен, что изменилось с этого момента до последней компиляции инструмента (возможно, много лет назад, возможно, в более старой версии VS). Мы пробовали более старую версию boost (1.36 против 1.38).

Прежде чем вернуться к исследованию кода вручную или передать его в PC-Lint и просмотреть его вывод, есть ли предложения о том, как это эффективно отлаживать?

[Я буду рад дополнить вопрос дополнительной информацией, если вы запросите информацию в комментариях.]

  • Однажды я имел удовольствие полу-реверс-инжиниринг материала RtlHeap для подобной ошибки. Пусть вас не смущают огромные смещения - это нормально. В отладочных символах (которые вы, кажется, используете), по-видимому, отсутствуют некоторые частные функции (вероятно, объявленные статическими в исходном файле), а получение огромных смещений для RtlAllocateHeap или RtlAllocateHeapSlowly просто означает, что это был ближайший найденный символ. arke 14.05.2010
  • @arke: да, я понял это вскоре после того, как разместил вопрос. (Надо было вернуться, чтобы отредактировать его.) Задолго до этого я написал инструмент поиска, который анализировал файлы xMAP, сгенерированные Codewarrior на работе, и время от времени встречал то же самое - просто не приходило мне в голову, что я бы не стал ' По какой-то причине здесь обязательно присутствуют все символы. leander 14.05.2010

c++visual-studioboostvisual-studio-2005

Ответы

Ответ 1 · рейтинг 12 · Suma

Одно небольшое различие между запуском с подключенным отладчиком или без него - это куча отладки ОС (см. Также Почему мой код работает медленно, когда ко мне подключен отладчик?). Вы можете отключить отладочную кучу с помощью переменной среды _NO_DEBUG_HEAP. Вы можете указать это либо в свойствах вашего компьютера, либо в настройках проекта в Visual Studio.

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

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

Ответ 2 · рейтинг 3 · lothar

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

Проверьте, можете ли вы перезаписать ранее выделенное пространство.

Если ваше приложение переносимо, вы можете попробовать собрать его в Linux и запустить под Valgrind.

Ответ 3 · рейтинг 3 · leander

Application Verifier был превосходным -полезно для решения этой проблемы, когда у меня был _NO_DEBUG_HEAP = 1 в среде, см. принятый ответ здесь: Ищете, где последний раз была освобождена память?

Вероятно, также стоит упомянуть pageheap, которую я обнаружил, просматривая Application Verifier. Похоже, это касается аналогичных вопросов.

(К вашему сведению, это было переполнение односимвольного буфера:

m_pEnumName = (char*)malloc(strlen(data) /* missing +1 here */);
strcpy(m_pEnumName, data);

... еще один смехотворно хороший аргумент не использовать strcpy напрямую.)

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы