2010-08-09 1 views
32

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

Я использую GCC на GNU/Linux и MingW на Windows, таким образом, я не могу использовать Visual Studio JIT Debug ...

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

EDIT: Многие люди упомянули, что ... как я делаю сердечник, minidump или что-то еще? Это первый раз, когда мне нужна отладка postmortem.

EDIT2: На самом деле, DrMingw захватил стек вызовов, информации о памяти ... К сожалению, стек вызовов мне не очень помог, потому что в конце концов вдруг он попадает в какую-то библиотеку (или что-то еще) t имеет отладочную информацию, приводящую только к некоторым шестнадцатеричным числам ... Поэтому мне по-прежнему нужен достойный дамп, который дает больше информации (особенно о том, что было в памяти ... конкретно, что было на месте, которое давало «нарушение прав доступа», ошибка)

Кроме того, мое приложение использует Lua и Luabind, возможно, ошибка вызвана сценарием .lua, но я понятия не имею, как отладить это.

+11

Это многопоточное приложение? – Naveen

+2

Я сомневаюсь, что сбои случайны –

+3

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

ответ

8

Запустите программу под отладчиком (я уверен, что есть отладчик вместе с GCC и MingW) и подождите, пока он не сработает под отладчиком. В момент сбоя вы сможете увидеть, какое конкретное действие терпит неудачу, посмотрите на код сборки, регистры, состояние памяти - это часто поможет вам найти причину проблемы.

+2

gdb работает под mingw – sje397

+0

Я не могу этого сделать, производительность под отладчиком слишком медленная, чтобы сделать программу полезной вообще, и это может занять время LOOOOONG перед сбоем. Поэтому мне потребовалось бы использовать GDB все время, и для этого проекта это совершенно необоснованно. – speeder

+0

@speeder: Я лично никогда не видел разницы в скорости при работе под отладчиком. Я не имею в виду шаг за шагом, я имею в виду просто запустить и оставить его работать до тех пор, пока он не сработает. – sharptooth

29

Попробуйте Valgrind (это бесплатно, с открытым исходным кодом):

Распределение Valgrind в настоящее время включает в себя шесть производстве качественных инструментов: детектор ошибок памяти, две нитей детекторов ошибок, кэш и филиала -прогнозирование профилировщика, профилирования кэша генерации диаграммы вызовов, и кучного профилировщика. Он также включает в себя два экспериментальных инструмента: переполнение кучи/стека/глобального массива детектор и базовый блок SimPoint векторный генератор. Он работает на следующих платформах: X86/Linux, AMD64/Linux, PPC32/Linux, PPC64/Linux, и X86/Darwin (Mac OS X).

Valgrind Frequently Asked Questions

Memcheck часть пакета, вероятно, место для начала:

Memcheck является детектор ошибок памяти. Он может обнаруживать следующие проблемы , которые являются общими для программ на C и C++.

  • Доступ к памяти вы не должны, например. overrunning и underrunning heap блокирует, обгоняя верхнюю часть стека и получая доступ к памяти после него был освобожден.

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

  • Неправильного освобождение динамической памяти, такие как блоки двойных освобождения кучи, или несовпадающего использование таНоса/новое/новый [] против свободного/удаления/удаление []

  • Перекрытия SRC и DST указателей в memcpy и связанных с ним функциях.

  • Утечка памяти.

+1

+1.Valgrind часто может передать вам номер строки вашей ошибки для нулевого усилия. Это как волшебство. –

+0

Я тоже это сделаю +1. Только недавно начал использовать это, я нахожу это чертовски незаменимым. – paxdiablo

+1

Valgrind замечательно, но, к счастью, не поймает ошибок в Windows/MINGW, потому что там его не существует. Возможные замены: * http://stackoverflow.com/questions/413477/is-there-a-good-valgrind-substitute-for-windows –

8

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

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

+0

Не могли бы вы дать некоторые подробности? Моя последняя информация о mingw заключается в том, что двоичные файлы mingw-gcc не могут генерировать дампы ядра, а windbg очень мало говорит о двоичных файлах mingw, потому что они используют формат отладки stabs, который windbg не понимает. –

+0

@Luther Blissett, к сожалению, файлы дампов ядра кажутся сгенерированными системой (я работаю для очень большой компании, и я не являюсь частью команды, которая фактически установила это). Однако я уверен, что мои тестовые двоичные файлы (созданные с помощью mingw) «сбрасываются с ядром» при сбоях, и я очень сомневаюсь, что ответственная команда добавила для этого особый случай. – ereOn

+2

Я считаю, что они называются (термин MS) «Minidumps». У windbg есть настройка, чтобы прочитать эти «посмертные» и может раскрыть «материал». – JustBoo

4

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

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

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

Если это не так, убедитесь, что coredumps включены (ulimit -a), а затем, когда он падает, вы сможете узнать, где с gdb.

+0

Действительно ли valgrind запускается в Windows? Я искал это уже много лет. – ereOn

+0

@ereOn: К сожалению, нет, это не так, но OP также использует Linux, поэтому он должен быть для него вариантом. Только сейчас Linux и OS X действительно поддерживаются, хотя для FreeBSD и NetBSD имеются неофициальные порты. см. http://www.valgrind.org/info/platforms.html –

+0

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

3

Это звучит как нечто сложное, как состояние гонки.

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

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

+0

Это ** может быть условием гонки или чем-либо еще, что приводит к неопределенному поведению. У нас недостаточно информации, чтобы делать угадываемые предположения. – ereOn

+0

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

13

Если все остальное не работает (особенно если производительность под отладчиком неприемлема), расширенный журнал. Начните с точек входа - транзакция приложения? Регистрируйте каждую транзакцию по мере ее поступления. Зарегистрируйте все вызовы конструктора для ваших ключевых объектов. Поскольку авария настолько прерывистая, журнал вызывает все функции, которые не могут быть вызваны каждый день.

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

+0

+1 ... Я вижу это в своем текущем проекте. – KedarX

+1

Я делал то же самое, но я заметил, что часто запись ведет к тому, что программа делает I/O, который * иногда * предотвращает некоторые ошибки/условия гонки. Я считаю, что регистрация является более эффективным методом, когда у вас есть ошибка, которая происходит детерминированным способом. – ereOn

+3

Да, я должен любить этих Гейзенбергов. – paxdiablo

1

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

6

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

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

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

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

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

14

Во-первых, вам повезло, что ваш процесс вылетает несколько раз за короткий период времени. Это должно облегчить процесс.

Так вы продолжаете.

  • Получить аварийный дамп
  • Изолировать множество потенциальных подозрительных функций
  • Подтяните состояние проверки
  • Repeat

Получить аварийный дамп

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

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

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

Найти подозрительные функции

Учитывая, что у вас есть аварийный дамп, смотреть на него в БГД или ваш любимый отладчик и помнить, чтобы показать все темы! Возможно, это не тот поток, который вы видите в gdb, который является ошибкой.

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

Глядя на несколько сбоев и разделение разделов кода, которые обычно активны во всех авариях, является реальной экономией времени.

Подтяните состояние проверки

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

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

Если функция содержит цикл, документируйте юридическое состояние, которое оно должно иметь в начале каждой итерации цикла.

Добавить утверждения для всех таких выражений правового государства.

Повторите

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

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

0

Два больше указателей/идеи (помимо основного отвала и Valgrind на Linux):

1) Try от Nokia "Qt Creator". Он поддерживает mingw и может выступать в качестве посмертного отладчика.

2) Если это возможно, возможно, просто запустите приложение в gdb постоянно?

3

Первое, что я хотел бы сделать, это отладить дамп ядра с помощью gdb (как Windows, так и Linux). Второй будет работать с программой, такой как Lint, Prefast (Windows), Clang Analyzer или какой-либо другой программой статического анализа (будьте готовы к множеству ложных срабатываний). Третья вещь - это какая-то проверка времени выполнения, например Valgrind (или ее близкие варианты), Microsoft Application Verifier, или Google Perftools.

И каротаж. Который не должен идти на диск. Например, вы можете войти в глобальный std::list<std::string>, который будет сокращен до последних 100 записей. Когда исключение поймано, отобразите содержимое этого списка.

+0

+1 для Application Verifier. Я бы на самом деле начал там, если valgrind слишком медленный. – leander

0

Если ваше приложение не относится к Windows, вы можете попробовать скомпилировать и запустить свою программу на других платформах, таких как Linux (разные дистрибутивы, 32/64 бит, ... если у вас есть роскошь). Это может помочь вызвать ошибки вашей программы. Конечно, вы должны использовать инструменты, упомянутые в других сообщениях, таких как gdb, valgrind и т. Д.

6

Похоже, ваша программа страдает от повреждения памяти. Как уже говорилось, ваш лучший вариант в Linux - это, наверное, valgrind. Но вот два других варианта:

  • Прежде всего используют debug malloc. Почти все библиотеки C предлагают реализацию debug malloc, которая инициализирует память (обычный malloc хранит «старое» содержимое в памяти), проверяет границы выделенного блока на предмет коррупции и т. Д. И если этого недостаточно, существует широкий выбор сторонних реализаций.

  • Возможно, вам захочется взглянуть на рабочую станцию ​​VMWare. Я не настроил это так, но из своих маркетинговых материалов они поддерживают довольно интересный способ отладки: запустить дебюта на «записывающей» виртуальной машине. Когда происходит повреждение памяти, установите точку прерывания памяти на поврежденном адресе, а затем верните время в виртуальную машину именно в тот момент, когда эта часть памяти была перезаписана. См. this PDF о том, как настроить повторную отладку с помощью Linux/gdb. Я считаю, что для Workstation 7 существует 15 или 30 дней демо, что может быть достаточно, чтобы избавиться от этих ошибок из вашего кода.

4

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

И для Windows: Там является JIT отладчика для MinGW, называется DrMingw:

+0

Ooooh ... Этот материал действительно работал! Есть один конкретный сбой, который я знаю, как вызвать (но я не знаю, как исправить), что я использовал для тестирования DrMingw Слишком плохо, что он не предлагает никакой информации о памяти, только о стеке вызовов ... :( – speeder

3
  1. Начать регистрацию. Поместите отчеты о регистрации в местах, где, по вашему мнению, код взломан. сосредоточьтесь на тестировании кода и повторите, пока вы не сузите проблему до модуля или функции.

  2. Оставить заявку везде!

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

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

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

  6. Не добавляйте больше кода поверх плохого кода, который не работает. Это просто глупая идея.

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

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

  9. Используйте отладочную сборку. Запустите сборку отладки под отладчиком, если это возможно.

  10. Обрезайте приложение, удалив файлы, модули и т. Д., Если это возможно, чтобы у вас было легче попытаться воспроизвести ошибку.

1

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

Также для Windows вы найдете Microsoft Debugging tools for Windows и особенно их инструмент gflags.

+0

Иногда авария происходит только из-за «неправильной сборки». Clean & Rebuild исправит эту проблему. –

2

Здесь есть много хороших ответов, но никто еще не коснулся угла Луа.

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

Простая вещь, которую вы можете сделать, обнаружит многие такие ошибки, чтобы определить макрос lua_assert в luaconf.h. Определение этого (например, стандартного утверждения C) позволит провести множество проверок здравомыслия внутри ядра Lua.