Чи можете ви отримати будь-яку програму в Linux, щоб надрукувати слід стека, якщо він segfault?


20

Якщо я запускаю програму з оболонки, і вона segfaults:

$ buggy_program
Segmentation fault

Однак мені підкажуть, чи існує спосіб отримати програми для друку backtrace, можливо, виконавши щось подібне:

$ print_backtrace_if_segfault buggy_program
Segfault in main.c:35
(rest of the backtrace)

Я також не хотів би використовувати strace або ltrace для такої інформації, оскільки вони друкуються в будь-якому випадку ...

Відповіді:


25

Можливо, буде і кращий спосіб, але цей вид автоматизує це.

Помістіть наступне ~/backtrace:

backtrace
quit

Помістіть це в сценарій, який називається seg_wrapper.shв каталозі на своєму шляху:

#!/bin/bash
ulimit -c unlimited
"$@"
if [[ $? -eq 139 ]]; then
    gdb -q $1 core -x ~/backtrace
fi

ulimitКоманда робить так , ядро скидається. "$@"- це аргументи, надані сценарію, тож це буде ваша програма та її аргументи. $?містить статус виходу, 139, здається, є статусом виходу за замовчуванням для моєї машини для сегмента за замовчуванням.

Бо gdb, -qозначає тихо (без введення повідомлення) і -xговорить gdbвиконувати команди в наданому йому файлі.

Використання

Отже, щоб використовувати його, ви просто:

seg_wrapper.sh ./mycommand and its arguments 

Оновлення

Ви також можете написати обробник сигналу, який це робить, дивіться це посилання .


2
Ваше посилання на рішення обробника сигналів мертве - ось чому відповіді не повинні посилатися на інші ресурси ...
josch

1
ви, мабуть, маєте на увазі "-x каже gdb виконувати" замість "-x повідомляє gdb вийти"
josch

19

Вибачте, що завітав сюди через 2 роки ... натрапив на пошуки чогось іншого. Додамо це для повноти.

1) Хоча я вважаю, що прийнята відповідь чудова, вона вимагає gdb. Знайомий мені метод використовує libSegFault.so.

Якщо ви запускаєте додаток за допомогою

LD_PRELOAD = ... шлях до ... / libSegFault.so myapp

Ви отримаєте звіт із зворотними слідами, завантаженими вкладками тощо

2) catchsegvТакож доступний сценарій обгортки, який намагатиметься використовувати addr2lineдля перекладу адрес у ім’я файлу + номер рядка.

Це набагато легші рішення, ніж основні файли або gdb (наприклад, для вбудованих систем)


Насправді, LD_PRELOAD=libSegFault.soце добре, якщо він знаходиться на шляху dl.
Фернандо Сільвейра

1
@FernandoSilveira добре. Написання відповіді таким чином натякає читачеві, що вони повинні перевірити, що це за шлях.
nhed

6

Вам потрібен друг GDB

gdb <program> [core file]

Після завантаження основного файлу команда 'backtrace' (може бути скорочена до bt) надасть вам поточний стек викликів. Якщо ви запускаєте програму зсередини gdb, ви можете встановити довільні точки перерви та вивчити вміст пам'яті тощо.


Чи є спосіб отримати його просто надрукувати заднім числом і вийти?
Ніл

5

уцегв

Про це було сказано в іншій відповіді (але жодним чином не зосереджено на цьому). Це зручний інструмент в комплекті з проектом glibc. Він надасть зворотній шлях (та іншу корисну інформацію про налагодження), лише якщо програма дійсно є за замовчуванням.

Хороший підправити існує тут .

Ви можете включити його у власні сценарії так, як вважаєте за потрібне.


3

Для цього Ubuntu (як проект) використовує Apport. Ви можете подивитися, як вони це зробили.

https://wiki.ubuntu.com/Apport


2
+1: У повідомленні згадуються деякі корисні механізми, з якими я не був знайомий, як-от/proc/sys/kernel/core_pattern
RobM

2

Ось трохи модифікований варіант сценарію від Кайла Брандта. Він покращується наступними способами:

  • не потребують взаємодії вручну, якщо слід стека довгий
  • деякі послідовності зберігаються разом із основним шаблоном імені. Дотримуйтесь цього параметра
  • не потрібно явного командного файлу, що летить навколо gdb (це створить тимчасовий)
  • чекайте фонових завдань

Сценарій:

#!/bin/bash
gdbcommandfile=$(tempfile)
usepid=$(cat /proc/sys/kernel/core_uses_pid)
printf "set pagination off\nbacktrace\nquit\n" > $gdbcommandfile
ulimit -c unlimited
"$@"&
pid=$!
wait $!
if [[ $? -eq 139 ]]; then
    if [[ $usepid == 1 ]]; then 
        gdb -q $1 core.$pid -x $gdbcommandfile
    else
        gdb -q $1 core -x $gdbcommandfile
    fi
fi
rm $gdbcommandfile

1
Для ланцюжка таких простих команд я б просто використовував -exзамість цього. gdb ... -ex 'set pagination off' -ex backtrace -ex quit
Джош Стоун
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.