C ++: с чего начать, когда мое приложение падает в случайных местах? - PullRequest
6 голосов
/ 07 июля 2010

Я занимаюсь разработкой игры, и когда я выполняю в игре определенное действие, она вылетает. Итак, я пошел на отладку и увидел, что мое приложение зависало при простых инструкциях C ++, таких как if, return, ... Каждый раз, когда я перезапускался, он зависал случайным образом на одной из 3 строк и никогда не выполнялся.

строка 1:

if (dynamic) { ... } // dynamic is a bool member of my class

строка 2:

return m_Fixture; // a line of the Box2D physical engine. m_Fixture is a pointer.

строка 3:

return m_Density; // The body of a simple getter for an integer.

Я не получаю ошибок ни из приложения, ни из ОС ...

Существуют ли подсказки, советы или рекомендации для более эффективной отладки и получения информации о происходящем?

Вот почему я люблю Java ...

Спасибо

Ответы [ 15 ]

10 голосов
/ 07 июля 2010

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

5 голосов
/ 07 июля 2010

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

повреждение стека происходит в большинстве случаев из-за ошибки "off by one".
Повреждение кучи происходит из-за того, что new / delete не обрабатывается аккуратно, например, двойное удаление.

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

4 голосов
/ 07 июля 2010

Есть ли подсказки, советы или хитрости для более эффективной отладки и получения информации о происходящем?

  1. Запустите игру в отладчике, в точке сбоя проверьте значенияиз всех аргументов.Либо с помощью окна просмотра Visual Studio или с помощью GDB.Используя «стек вызовов», проверьте родительские процедуры, попробуйте подумать, что может пойти не так
  2. В подозрительных (потенциально связанных с сбоем) подпрограммах рассмотрите возможность выгрузки всех аргументов в stderr (если вы используете libsdl или в * nixlike системах), либо создайте файл журнала или отправьте дубликаты всех сообщений об ошибках, используя (в Windows)OutputDebugString.Это сделает их видимыми в окне «Вывод» в Visual Studio или отладчике.Вы также можете написать «трассировки» (журнал («была вызвана функция% s», __FUNCTION__))
  3. Если вы не можете отладить сразу, создайте дамп ядра при сбое.В Windows это можно сделать с помощью MiniDumpWriteDump, в Linux это задается где-то в переменных конфигурации.дамп ядра может быть обработан отладчиком.Я не уверен, что VS Express может справиться с ними в Windows, но вы все равно можете отлаживать их с помощью WinDBG.
  4. если происходит сбой в классе, проверьте * этот аргумент.Это может быть недействительным или ноль.
  5. Если ошибка действительно злая (неуловимое повреждение стека в многопоточном приложении, которое приводит к задержке сбоя), напишите собственный менеджер памяти, который будет переопределять new / delete, предоставьте альтернативу malloc (если ваше приложение по какой-то причине использует его, чтоэто возможно), И это блокирует всю неиспользуемую память, используя VirtualProtect (windows) или альтернативу, специфичную для ОС.В этом случае все потенциально опасные операции мгновенно завершат работу приложения, что позволит вам отладить проблему (если у вас есть отладчик Just-In-Time) и мгновенно найдет опасную подпрограмму.Я предпочитаю такой «пользовательский диспетчер памяти», чем boundschecker, и тому подобное - по моему опыту это было более полезно.В качестве альтернативы вы можете попробовать использовать valgrind, который доступен только в Linux.Обратите внимание, что если ваше приложение очень часто выделяет память, вам потребуется большой объем оперативной памяти, чтобы иметь возможность блокировать каждый неиспользуемый блок памяти (потому что для блокировки блок должен иметь размер PAGE_SIZE в байтах).
  6. В областях, где вам нужна проверка работоспособности, либо используйте ASSERT, либо (лучшее решение IMO) напишите подпрограмму, которая приведет к аварийному завершению приложения (с помощью выдачи std :: exception со значимым сообщением), если какое-либо условие не будет выполнено.
  7. Если вы обнаружили проблемную подпрограмму, пройдитесь по ней, используя шаг отладчика в / шаг за шагом.Смотрите аргументы.
  8. Если вы определили проблемную подпрограмму, но не можете напрямую отладить ее по какой-либо причине, после каждого оператора в этой подпрограмме выведите все переменные в stderr или logfile (fprintf или iostreams - ваш выбор).Затем проанализируйте результаты и подумайте, как это могло произойти.Обязательно очищайте файл журнала после каждой записи, иначе вы можете пропустить данные прямо перед сбоем.

В общем, вы должны быть рады, что приложение где-то падает.Сбой означает ошибку, которую вы можете быстро найти с помощью отладчика и уничтожить.Ошибки, которые не приводят к сбою программы, гораздо сложнее (пример действительно сложной ошибки: при 100000 значений ввода, после нескольких сотен манипуляций со значениями среди тысяч выходов, приложение выдает 1 абсолютно неверный результат, который не должен иметьслучилось вообще)

Вот почему я люблю Java ...

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

4 голосов
/ 07 июля 2010

Я обычно предпочитаю сделать шаг назад и обдумать код, пытаясь отследить любые логические ошибки.

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

Помимо этих двух вещей, вы можете попробовать использовать отладчик, такой как Visual Studio или Eclipse и т. Д. *

Наконец, вы можете попытаться опубликовать свой код и сообщение об ошибке на веб-сайте сообщества, которое знает программирование и может помочь вам справиться с ошибкой (читай: stackoverflow)

3 голосов
/ 07 июля 2010

Сбои / ошибки Seg обычно происходят, когда вы обращаетесь к области памяти, к которой у нее нет доступа, или вы пытаетесь получить доступ к области памяти недопустимым способом (например, пытаетесь записать данные только для чтения). место).

Существует много инструментов для анализа памяти, например, я использую Valgrind, который действительно хорошо показывает, в чем проблема (не только номер строки, но и причина сбоя).

2 голосов
/ 07 июля 2010

Нет простых операторов C ++.Условие if так же просто, как и условие, которое вы оцениваете.Возврат только так же прост, как и возвращаемое вами выражение.

Вы должны использовать отладчик и / или опубликовать часть кода сбоя.Не может быть очень полезным с "мое приложение упало" в качестве информации.

1 голос
/ 08 июля 2010

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

1 голос
/ 07 июля 2010

несколько советов:
- запустите ваше приложение под отладчиком с файлами символов (PDB) вместе.
- Как установить Visual Studio в качестве посмертного отладчика по умолчанию?
- установить отладчик по умолчанию для WinDbg Оперативная отладка
- проверить выделение памяти Переопределение новых и удаление и Переопределение malloc и free

1 голос
/ 07 июля 2010

Посмотрите на разборку.Практически любой отладчик C / C ++ будет рад показать вам машинный код и регистры, в которых произошел сбой программы.Регистры включают в себя указатель инструкций (EIP или RIP на x86 / x64), где находилась программа, когда она остановилась.Другие регистры обычно имеют адреса памяти или данные.Если адрес памяти равен 0 или неверный указатель, это ваша проблема.

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

На Linux / BSD / Mac использование скриптовых функций GDB может очень помочь здесь.Вы можете написать сценарий так, чтобы после того, как точка останова была достигнута 20 раз, она включила аппаратное наблюдение за адресом элемента массива 17. И т. Д.

Вы также можете записать отладку в свою программу.Используйте функцию assert ().Везде!

Используйте assert для проверки аргументов каждой функции.Используйте assert для проверки состояния каждого объекта перед выходом из функции.В игре утверждают, что игрок находится на карте, что у игрока есть здоровье от 0 до 100, утверждают все, что вы можете придумать.Для сложных объектов пишите функции verify () или validate () в сам объект, который проверяет все о нем, а затем вызывает их из assert ().

Другой способ записи в отладке - использовать сигнал использования программы.() в Linux или asm int 3 в Windows, чтобы взломать отладчик из программы.Затем вы можете написать временный код в программу, чтобы проверить, находится ли он на итерации 1117321 основного цикла.Это может быть полезно, если ошибка всегда происходит в 1117322. Программа будет выполняться намного быстрее, чем при использовании точки останова отладчика.

1 голос
/ 07 июля 2010

Если операторы if включают разыменование указателей, вы почти наверняка повредите стек (это объясняет, почему невинный return 0 вылетит ...)

Это можетпроизойдет, например, выходя за пределы массива (вы должны использовать std::vector!), пытаясь strcpy строка на основе char [] пропустить конец '\0' (вы должны использовать std::string!), передав неправильный размер memcpy (вы должны использовать конструкторы копирования!) и т. д.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...