Отображение отладочной информации исключения пользователям

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

В C# и Python, когда необработанное исключение происходит, пользователь получает хорошее отслеживание стека и в целом большую очень полезную бесценную отладочную информацию. Если Вы работаете над приложением OSS, имея пользовательскую вставку, та информация в отчеты о проблеме... хорошо, позвольте нам просто сказать, что я нахожу трудным жить без этого. Для этого проекта C++ я получаю "Приложение, разрушенное", или от более информированных пользователей, "Я сделал X, Y и Z, и затем это отказало". Но я хочу ту отладочную информацию также!

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

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

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

Таким образом, то, что я хочу сделать, является переносом моя основная точка входа внутри a try блок с a catch это создает специальное диалоговое окно, которое сообщает пользователю, что ошибка произошла в приложении с более подробной информацией, представленной, когда пользователь нажимает "More" или "Debug info" или что бы то ни было. Это содержало бы строку от diagnostic_information. Я мог затем дать пользователям команду вставлять эту информацию в отчеты о проблеме.

Но ворчащее инстинктивное чувство говорит мне, что обертывание всего в блоке попытки является действительно плохой идеей. То, что я собираюсь сделать глупый? Если это (и даже если это не), что лучший путь состоит в том, чтобы достигнуть того, что я хочу?

6
задан Lucas 26 December 2009 в 21:55
поделиться

4 ответа

Обертывание всего кода в один блок try/catch является a-ok. Это не замедлит выполнение, например, чего-либо внутри него. На самом деле, все мои программы имеют (код подобен) этот фреймворк:

int execute(int pArgc, char *pArgv[])
{
    // do stuff
}

int main(int pArgc, char *pArgv[])
{
    // maybe setup some debug stuff,
    // like splitting cerr to log.txt

    try
    {
        return execute(pArgc, pArgv);
    }
    catch (const std::exception& e)
    {
        std::cerr << "Unhandled exception:\n" << e.what() << std::endl;
        // or other methods of displaying an error

        return EXIT_FAILURE;
    }
    catch (...)
    {
        std::cerr << "Unknown exception!" << std::endl;

        return EXIT_FAILURE;
    }
}
3
ответ дан 8 December 2019 в 02:26
поделиться

Помещение блока try/catch в main() нормально, не вызывает никаких проблем. Программа все равно мертва на необработанном исключении. Но это совсем не поможет в вашем поиске, чтобы получить все-таки важную трассу стека. Эта информация будет gonzo, когда блок-ловушка поймает исключение.

Поймать исключение на C++ тоже будет не очень полезно. Вероятность того, что программа умрет на исключении, производном от std::exception, довольно мала. Хотя это может произойти. Гораздо более вероятно, что в приложении на Си/Си++ смерть наступит из-за аппаратных исключений, AccessViolation - нумеро uno. Отслеживание этих исключений требует использования ключевых слов __try и __except в методе main(). Опять же, очень мало контекста доступно, в основном вы получили только код исключения. AV также сообщает, какое именно место в памяти вызвало исключение.

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

Что вам нужно сделать, так это отладить такие проблемы способом на Си/Си++. Вам нужно создать мини-сумму. Он примерно аналогичен "дампу ядра" старого, снимку образа процесса в момент, когда происходит исключение. Тогда вы действительно получили полный дамп ядра. Прогресс был, сейчас это "мини", в какой-то степени необходимо, потому что полный сброс ядра занял бы около 2 гигабайт. В Windows, которая начинается с вызова SetUnhandledExceptionFilter(), вы предоставляете указатель на функцию обратного вызова, которая будет запущена, когда ваша программа умрет на необработанном исключении. Любое исключение, как C++, так и SEH. Ваш следующий ресурс - dbghelp.dll, доступный в разделе Инструменты отладки для Windows. Он имеет точку входа под названием MiniDumpWriteDump(), он создает мини-дамп.

Как только вы получаете файл, созданный MiniDumpWriteDump(), вы становитесь довольно золотым. Загрузить файл .dmp можно в Visual Studio, как будто это проект. Нажмите F5 и VS отшлифует на некоторое время, пытаясь загрузить .pdb файлы для DLL, загруженных в процессе работы. Вы захотите настроить сервер символов, это очень важно для получения хороших стековых трасс. Если все сработает, вы получите "отладочный перерыв" в том месте, где было выброшено исключение". С трассировкой стека.

Вещи, которые вам нужно сделать, чтобы это работало гладко:

  • Используйте сборочный сервер для создания двоичных файлов. Он должен подтолкнуть отладочные символы (.pdb файлы) к символьному серверу, чтобы они были легко доступны при отладке мини-суммы.
  • Настройте отладчик так, чтобы он мог найти отладочные символы для всех модулей. Вы можете получить отладочные символы для Windows от Microsoft, символы для вашего кода должны прийти с символьного сервера, упомянутого выше.
  • Напишите код, чтобы поймать необработанное исключение и создать мини-сумму. Я упоминал SetUnhandledExceptionFilter(), но код, который создает мини-сумпу, не должен быть в программе, которая разбилась. Шансы на то, что он сможет успешно написать минидамп, довольно малы, состояние программы неопределено. Лучше всего запустить "сторожевой" процесс, который следит за именем Мьютекса. Ваш фильтр исключений может установить мьютекс, охранник может создать мини-сампа.
  • Создайте способ перевода мини-сампа с машины клиента на вашу. Для этого мы используем сервис Amazon S3, терабайты по разумной цене.
  • Проведите обработчик минидампов в вашу отладочную базу данных. Мы используем Jira, у него есть веб-сервис, который позволяет нам сверить кэш-память с базой данных более ранних кэш-памятей с той же "подписью". Когда он уникален или не имеет достаточного количества хитов, мы просим код обработчика крэш-менеджера загрузить минидамп в Amazon и создать запись в базе данных ошибок.

Ну, это то, что я делал для компании, в которой работаю. Получилось очень хорошо, это сократило частоту падений с тысяч до десятков. Личное сообщение создателям компонента ffdshow с открытым исходным кодом: Я ненавижу тебя со страстью. Но вы больше не разбиваете наше приложение! Ублюдки.

33
ответ дан 8 December 2019 в 02:26
поделиться

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

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

Я включаю регистры, версию приложения и номер сборки, тип исключения и дамп стека в hex, аннотированный с именами модулей и смещениями для hex-значений, которые могут быть адресами к коду. Обязательно включите номер версии и номер сборки/дату вашего exe.

Вы также можете использовать VirtualQuery, чтобы довольно легко превратить значения стека в "ModuleName+Offset". И это, в сочетании с .MAP-файлом, часто скажет Вам, где именно Вы разбились.

Я обнаружил, что могу довольно легко обучить бета-тестеров посылать мой текст, но в ранние годы я получал не текст, а картинку с диалогом ошибок. Я думаю, что это потому, что многие пользователи не знают, что вы можете щелкнуть правой кнопкой мыши по любому элементу управления Edit, чтобы получить меню с "Select All" и "Copy". Если бы я собирался сделать это снова, я бы добавил кнопку. который скопировал этот текст в буфер обмена, чтобы его можно было легко вставить в электронную почту.

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

.
1
ответ дан 8 December 2019 в 02:26
поделиться

На самом деле, Boost :: Diagnostic_information был разработан специально для использования в блоке «Global» (...), для отображения информации о исключениях, которые не должны были достичь его. Однако обратите внимание, что строка, возвращаемая повышением :: Diagnostic_Information, не является удобной.

0
ответ дан 8 December 2019 в 02:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: