Я работаю над Linux с компилятором GCC. Когда моя программа на C ++ падает, я бы хотел, чтобы она автоматически генерировала трассировку стека.
Моя программа запускается многими разными пользователями, а также работает на Linux, Windows и Macintosh (все версии скомпилированы с использованием gcc
).
Мне бы хотелось, чтобы моя программа могла генерировать трассировку стека при сбое, и в следующий раз, когда пользователь запустит ее, она спросит, можно ли отправить мне трассировку стека, чтобы я мог отследить проблему. Я могу обработать отправку информации мне, но я не знаю, как генерировать строку трассировки. Любые идеи?
Ответы:
Для Linux, и я считаю, что Mac OS X, если вы используете gcc или любой другой компилятор, который использует glibc, вы можете использовать функции backtrace ()
execinfo.h
для печати трассировки стека и корректного выхода при возникновении ошибки сегментации. Документация может быть найдена в руководстве по libc .Вот пример программы, которая устанавливает
SIGSEGV
обработчик и печатает трассировку стека,stderr
когда он вызывает ошибки .baz()
Функция здесь вызывает Segfault , который вызывает обработчик:Компилируя с,
-g -rdynamic
вы получаете информацию о символах в вашем выводе, которую glibc может использовать для создания хорошей трассировки стека:Выполнение этого дает вам такой вывод:
Здесь показаны модуль загрузки, смещение и функция, из которой получен каждый кадр в стеке. Здесь вы можете увидеть обработчик сигнала на вершине стека, и LibC функций , прежде чем
main
в дополнение кmain
,foo
,bar
, иbaz
.источник
sigaction()
libc. Хотя ваша обратная трассировка кажется правильной, я иногда обнаруживал, что необходимы дополнительные шаги для обеспечения того, чтобы фактическое местоположение ошибки отображалось в обратной трассировке, поскольку оно может быть перезаписаноsigaction()
ядром.catchsegv
это не то, что нужно OP, но он отлично подходит для выявления ошибок сегментации и получения всей информации.Это даже проще, чем "man backtrace", есть немного документированная библиотека (специфичная для GNU), распространяемая с glibc как libSegFault.so, которая, как я полагаю, была написана Ульрихом Дреппером для поддержки программы catchsegv (см. "Man catchsegv").
Это дает нам 3 возможности. Вместо запуска "программа -o хай":
Запустите внутри catchsegv:
Связь с libSegFault во время выполнения:
Ссылка с libSegFault во время компиляции:
Во всех трех случаях вы получите более четкие трассировки с меньшим количеством символов оптимизации (gcc -O0 или -O1) и отладки (gcc -g). В противном случае вы можете просто получить кучу адресов памяти.
Вы также можете поймать больше сигналов для трассировки стека что-то вроде:
Вывод будет выглядеть примерно так (обратите внимание на обратный след внизу):
Если вы хотите узнать подробности, лучшим источником, к сожалению, является источник: см. Http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c и его родительский каталог. http://sourceware.org/git/?p=glibc.git;a=tree;f=debug
источник
-Wl,--no-as-needed
флаги компилятора. В противном случае,ld
действительно не будет ссылаться наlibSegFault
, потому что он признает, что двоичный файл не использует ни один из своих символов.Linux
Хотя использование функций backtrace () в execinfo.h для печати трассировки стека и корректного выхода при возникновении ошибки сегментации уже было предложено , я не вижу упоминания о тонкостях, необходимых для обеспечения того, чтобы результирующая обратная трассировка указывала на фактическое расположение ошибка (по крайней мере, для некоторых архитектур - x86 и ARM).
Первые две записи в цепочке кадров стека, когда вы попадаете в обработчик сигнала, содержат адрес возврата внутри обработчика сигнала и один внутри sigaction () в libc. Кадр стека последней функции, вызванной до того, как сигнал (который является местоположением ошибки) теряется.
Код
Вывод
Все опасности вызова функций backtrace () в обработчике сигналов все еще существуют, и их не следует упускать из виду, но я считаю, что описанные здесь функции весьма полезны при отладке сбоев.
Важно отметить, что приведенный мной пример разработан / протестирован на Linux для x86. Я также успешно реализовал это на ARM, используя
uc_mcontext.arm_pc
вместоuc_mcontext.eip
.Вот ссылка на статью, где я узнал подробности этой реализации: http://www.linuxjournal.com/article/6391
источник
-rdynamic
чтобы указать компоновщику добавить все символы, а не только используемые, в таблицу динамических символов. Это позволяетbacktrace_symbols()
преобразовывать адреса в имена функцийaddr2line
как-то использовать команду, чтобы получить точную строку, где произошел сбой?glibc
uc_mcontext
отсутствует поле с именемeip
. Теперь есть массив, который нужно проиндексировать,uc_mcontext.gregs[REG_EIP]
это эквивалентно.Несмотря на то, что был предоставлен правильный ответ , который описывает, как использовать
backtrace()
функцию GNU libc 1, и я предоставил свой собственный ответ, который описывает, как гарантировать, что обратная трассировка от обработчика сигнала указывает на фактическое местоположение ошибки 2 , я не вижу любое упоминание о выводе символов C ++ из обратной трассировки.При получении возвратов из программы на C ++ вывод можно выполнить через
c++filt
1 для разборки символов или непосредственно с помощью 1 .abi::__cxa_demangle
c++filt
и__cxa_demangle
являются специфическими для GCCВ следующем примере C ++ Linux используется тот же обработчик сигнала, что и в моем другом ответе, и демонстрируется, как
c++filt
его можно использовать для разбора символов.Код :
Выход (
./test
):Деманглированный вывод (
./test 2>&1 | c++filt
):Нижеследующее основано на обработчике сигналов из моего исходного ответа и может заменить обработчик сигналов в приведенном выше примере, чтобы продемонстрировать, как
abi::__cxa_demangle
можно использовать для разборки символов. Этот обработчик сигнала выдает тот же выходной сигнал, что и вышеприведенный пример.Код :
источник
std::cerr
,free()
иexit()
все нарушают ограничения в отношении вызова не Асинхры-сигнал безопасных вызовов на системах POSIX. Этот код будет тупиковым , если ваш процесс терпит неудачу в любом вызове , такие какfree()
,malloc()
new
илиdetete
.Возможно, стоит взглянуть на Google Breakpad , кроссплатформенный генератор аварийных дампов и инструменты для обработки дампов.
источник
Вы не указали свою операционную систему, поэтому на это сложно ответить. Если вы используете систему, основанную на gnu libc, вы можете использовать функцию libc
backtrace()
.GCC также имеет две встроенные функции, которые могут помочь вам, но которые могут быть или не быть полностью реализованы в вашей архитектуре, и это
__builtin_frame_address
и__builtin_return_address
. Оба из них хотят немедленного целочисленного уровня (под непосредственным я имею в виду, что он не может быть переменной). Если__builtin_frame_address
для заданного уровня ненулевое значение, можно безопасно получить адрес возврата того же уровня.источник
Спасибо энтузиасту за то, что обратили мое внимание на утилиту addr2line.
Я написал быстрый и грязный скрипт для обработки вывода ответа, приведенного здесь : (большое спасибо jschmier!) С помощью утилиты addr2line.
Скрипт принимает один аргумент: имя файла, содержащего вывод утилиты jschmier.
Вывод должен напечатать что-то вроде следующего для каждого уровня трассировки:
Код:
источник
ulimit -c <value>
устанавливает ограничение размера основного файла в unix. По умолчанию ограничение размера основного файла равно 0. Вы можете увидеть вашиulimit
значения с помощьюulimit -a
.также, если вы запустите вашу программу из GDB, она остановит вашу программу из-за «нарушений сегментации» (
SIGSEGV
обычно, когда вы получили доступ к фрагменту памяти, который вы не выделили) или вы можете установить точки останова.DDD и Nemiver являются интерфейсом для GDB, что делает работу с ним намного проще для новичка.
источник
Важно отметить, что после того, как вы сгенерируете файл ядра, вам нужно использовать инструмент gdb, чтобы посмотреть на него. Чтобы gdb имел смысл вашего основного файла, вы должны указать gcc, чтобы он обрабатывал двоичный файл символами отладки: для этого вы компилируете с флагом -g:
Затем вы можете либо установить «ulimit -c unlimited», чтобы он выгружал ядро, либо просто запустить вашу программу внутри gdb. Мне больше нравится второй подход:
Надеюсь, это поможет.
источник
gdb
прямо из вашей программы сбоя. Настройте обработчик для SIGSEGV, SEGILL, SIGBUS, SIGFPE, который будет вызывать GDB. Подробности: stackoverflow.com/questions/3151779/… Преимущество в том, что вы получаете красивую аннотированную обратную трассировку, как вbt full
, а также вы можете получать трассировки стека всех потоков.Я смотрел на эту проблему некоторое время.
И похоронен глубоко в Google Performance Tools README
http://code.google.com/p/google-perftools/source/browse/trunk/README
говорит о либунвинде
http://www.nongnu.org/libunwind/
Хотелось бы услышать мнения этой библиотеки.
Проблема с -rdynamic заключается в том, что в некоторых случаях он может значительно увеличить размер двоичного файла.
источник
Некоторые версии libc содержат функции, которые имеют дело со следами стека; вы можете использовать их:
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
Я помню, как давно использовал libunwind для получения трассировки стека, но он может не поддерживаться на вашей платформе.
источник
Вы можете использовать DeathHandler - небольшой класс C ++, который делает все для вас, надежно.
источник
execlp()
для выполнения вызовов addr2line ... было бы неплохо полностью остаться в собственной программе (что возможно при включении кода addr2line в некоторой форме)Забудьте об изменении ваших источников и сделайте несколько хаков с помощью функции backtrace () или макросов - это просто плохие решения.
Как правильно работающее решение, я бы посоветовал:
Это напечатает правильную читаемую трассировку вашей программы в удобочитаемом виде (с именами исходных файлов и номерами строк). Более того, этот подход даст вам свободу в автоматизации вашей системы: создайте короткий сценарий, который проверяет, создал ли процесс дамп ядра, а затем отправьте обратные следы разработчикам по электронной почте или войдите в какую-либо систему регистрации.
источник
системная переменная, которая позволит создать дамп ядра после сбоя вашего приложения. В этом случае неограниченное количество. Ищите файл с именем core в том же каталоге. Убедитесь, что вы скомпилировали свой код с включенной отладочной информацией!
С уважением
источник
limit coredumpsize unlimited
Смотреть на:
Man 3 Backtrace
А также:
Это расширения GNU.
источник
См. Средство отслеживания стека в ACE (ADAPTIVE Communication Environment). Он уже написан для всех основных платформ (и не только). Библиотека лицензирована в стиле BSD, поэтому вы можете даже скопировать / вставить код, если не хотите использовать ACE.
источник
Я могу помочь с версией для Linux: можно использовать функции backtrace, backtrace_symbols и backtrace_symbols_fd. Смотрите соответствующие страницы руководства.
источник
Похоже, что в одной из последних появившихся библиотек c ++ boost появилась именно та версия, которую Вы хотите, возможно, код будет мультиплатформенным. Это boost :: stacktrace , который вы можете использовать как в примере boost :
В Linux Вы компилируете код выше:
Пример обратного следа, скопированного из документации повышения :
источник
* nix: вы можете перехватить SIGSEGV (обычно этот сигнал перед сбоем) и сохранить информацию в файле. (помимо основного файла, который вы можете использовать для отладки, например, с помощью gdb).
win: проверьте это из msdn.
Вы также можете посмотреть на хромовый код Google, чтобы увидеть, как он обрабатывает сбои. У этого есть хороший механизм обработки исключений.
источник
Я обнаружил, что решение @tgamblin не является полным. Он не может справиться с помощью stackoverflow. Я думаю, потому что по умолчанию обработчик сигнала вызывается с тем же стеком, а SIGSEGV выбрасывается дважды. Для защиты вам необходимо зарегистрировать независимый стек для обработчика сигналов.
Вы можете проверить это с помощью кода ниже. По умолчанию обработчик не выполняется. С определенным макросом STACK_OVERFLOW все в порядке.
источник
Новый король в городе прибыл https://github.com/bombela/backward-cpp
1 заголовок для размещения в вашем коде и 1 библиотека для установки.
Лично я называю это с помощью этой функции
источник
Я бы использовал код, который генерирует трассировку стека для утечки памяти в Visual Leak Detector . Это работает только на Win32, хотя.
источник
Я видел много ответов здесь, выполняющих обработчик сигнала и затем выходящих. Это путь, но помните очень важный факт: если вы хотите получить дамп ядра для сгенерированной ошибки, вы не можете позвонить
exit(status)
. Звонитеabort()
вместо этого!источник
Как решение только для Windows, вы можете получить эквивалент трассировки стека (с гораздо большим количеством информации), используя отчеты об ошибках Windows . С помощью всего лишь нескольких записей реестра его можно настроить для сбора дампов пользовательского режима :
Вы можете установить записи реестра из вашего установщика, который имеет необходимые привилегии.
Создание дампа в режиме пользователя имеет следующие преимущества по сравнению с генерацией трассировки стека на клиенте:
Обратите внимание, что WER может быть вызван только сбоем приложения (т. Е. Системой, завершающей процесс из-за необработанного исключения).
MiniDumpWriteDump
можно позвонить в любое время. Это может быть полезно, если вам нужно вывести текущее состояние для диагностики других проблем, кроме сбоя.Обязательное чтение, если вы хотите оценить применимость мини-дампов:
источник
В дополнение к приведенным выше ответам, здесь вы узнаете, как заставить ОС Debian Linux генерировать дамп ядра
источник
Если вы все еще хотите сделать это в одиночку, как я, вы можете ссылаться
bfd
и избегать использования,addr2line
как я сделал здесь:https://github.com/gnif/LookingGlass/blob/master/common/src/crash.linux.c
Это производит вывод:
источник
В Linux / unix / MacOSX используйте основные файлы (вы можете включить их с помощью ulimit или совместимого системного вызова ). В Windows используйте отчеты об ошибках Microsoft (вы можете стать партнером и получить доступ к данным о сбое вашего приложения).
источник
Я забыл о технологии «apport» в GNOME, но я не очень разбираюсь в ее использовании. Он используется для генерации трассировки стека и другой диагностики для обработки и может автоматически регистрировать ошибки. Это, безусловно, стоит проверить в.
источник