Решение случайных сбоев - PullRequest
       57

Решение случайных сбоев

34 голосов
/ 09 августа 2010

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

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

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

РЕДАКТИРОВАТЬ: Многие люди упоминали, что ... как я делаю дамп ядра, мини-дамп или что-то еще?Это первый раз, когда мне нужна посмертная отладка.

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

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

Ответы [ 17 ]

29 голосов
/ 09 августа 2010

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

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

Часто задаваемые вопросы Valgrind

Возможно, стоит начать с Memcheck пакета:

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

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

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

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

  • Перекрывающиеся указатели src и dst в memcpy и связанных с ними функциях.

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

14 голосов
/ 09 августа 2010

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

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

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

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

Сначала вам действительно нужно получить аварийный дамп.

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

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

Поиск подозрительных функций

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

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

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

Проверка состояния с повышением

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

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

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

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

Повторите

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

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

13 голосов
/ 09 августа 2010

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

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

8 голосов
/ 09 августа 2010

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

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

8 голосов
/ 09 августа 2010

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

6 голосов
/ 09 августа 2010

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

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

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

6 голосов
/ 09 августа 2010

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

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

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

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

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

4 голосов
/ 09 августа 2010

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

А для Windows: Для Mingw существует JIT-отладчик, называемый DrMingw:

4 голосов
/ 09 августа 2010

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

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

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

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

3 голосов
/ 09 августа 2010
  1. Начало регистрации. Размещайте операторы журналирования в тех местах, где вы считаете код ненадежным. сосредоточиться на тестировании кода и повторять до тех пор, пока вы не сузите проблему до модуля или функции.

  2. Положите утверждения везде!

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

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

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

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

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

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

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

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

...