C++: с чего начать, когда мое приложение падает в случайных местах?

я разрабатываю игру, и когда я делаю конкретное действие в игре, он падает. Поэтому я пошел отлаживать, и я увидел, что мое приложение разбилось на простых операторах 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 ответов


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


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

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

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


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

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

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

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


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

  1. запустите игру в отладчике, на момент сбоя проверьте значения всех аргументов. Либо с помощью окна просмотра visual studio, либо с помощью gdb. Используя родительские процедуры проверки "стека вызовов", попробуйте подумать, что может пойти не так.
  2. в подозрительных (потенциально связанных с сбоем) процедурах рассмотрите сброс всех аргументов в stderr (если вы используете libsdl или on * nixlike systems), или написать файл журнала, или отправить dupilcates всех сообщений об ошибках, используя (в Windows) OutputDebugString. Это сделает их видимыми в окне "вывод" в visual studio или отладчике. Вы также можете написать "traces" (log ("функция %s была вызвана", _ _ FUNCTION__))
  3. если вы не можете отладить немедленно, создайте дампы ядра при сбое. В windows это можно сделать с помощью MiniDumpWriteDump, в linux это установлено где-то в переменных конфигурации. дампы ядра могут обрабатываться отладчиком. Я не уверен, что VS express может иметь дело с ними в Windows, но вы все равно можете отлаживать их с помощью WinDBG.
  4. если сбой происходит в классе, проверьте * этот аргумент. Он может быть недействительным или нулевым.
  5. если ошибка действительно злая (неуловимое повреждение стека в многопоточном приложении, которое приводит к задержке сбоя), напишите пользовательский менеджер памяти, который переопределит new / delete, предоставит альтернативу malloc (если ваше приложение по какой-то причине использует его, что может быть возможно), и это блокирует все неиспользуемая память с использованием VirtualProtect (windows) или OS-конкретной альтернативы. В этом случае вся потенциально опасная операция приведет к сбою приложения мгновенно, что позволит вам отладить проблему (если у вас есть отладчик Just-In-Time) и мгновенно найти опасную процедуру. Я предпочитаю такой "пользовательский менеджер памяти" boundschecker и тому подобное - так как по моему опыту это было более полезно. В качестве альтернативы вы можете попробовать использовать valgrind, который доступен только в linux. Обратите внимание, что если ваше приложение очень часто выделяет память, вам понадобится большой объем ОЗУ, чтобы иметь возможность блокировать каждый неиспользуемый блок памяти (потому что для того, чтобы быть заблокированным, блок должен быть большим размером страницы).
  6. в областях, где вам нужно проверить здравомыслие либо использовать ASSERT, либо (IMO лучшее решение) написать процедуру, которая приведет к сбою приложения (бросив std::exception со значимым сообщением), если какое-то условие не выполняется.
  7. если вы определили проблемную процедуру, прогулка через отладчик шаг в шаг. Следите за аргументами.
  8. если вы определили проблемную процедуру, но не можете напрямую отлаживать ее по какой - либо причине, после каждого оператора в этой процедуре, сбросьте все переменные в stderr или logfile (fprintf или iostreams-ваш выбор). Затем проанализируйте результаты и подумайте, как это могло произойти. Не забудьте очистить файл журнала после каждой записи, или вы можете пропустить данные прямо перед сбоем.

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

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

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


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

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


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

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


У меня были такие проблемы раньше. Я пытался обновить GUI из разных потоков.


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

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

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


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

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

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

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

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

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


несколько советов :
- запустите приложение под отладчиком, с файлами символов (PDB) вместе.
- как установить Visual Studio в качестве отладчика по умолчанию после смерти?
- установить отладчик по умолчанию для WinDbg отладка Just-in-time
- проверьте выделение памяти переопределение new и delete и переопределение malloc и free


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


проверить указатели. Думаю, вы разыменования нулевого указателя.


Я нашел "случайные" сбои, когда есть ссылка на удаленный объект. Поскольку память не обязательно перезаписывается, во многих случаях вы ее не замечаете, и программа работает правильно, а затем аварийно завершает работу после обновления памяти и больше не действует.

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


используйте отладчик GNU


рефакторинг.

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

вы, безусловно, обнаружите проблему(ы) таким образом и исправить много других проблем тоже.