Как программно отследить сбой разветвленного ребенка с помощью C - PullRequest
0 голосов
/ 03 сентября 2018

Есть ли возможность отследить место, где произошел сбой дочернего процесса в Linux с использованием кода C / C ++? Я хочу сделать следующее:

  1. разветвляет новый дочерний процесс и получает его PID
  2. ожидание сбоя разветвленного дочернего процесса ... возможно, с помощью обработчика сигнала для SIGCHLD или с помощью waitpid () / waitid ()
  3. получить трассировку стека дочернего элемента в том месте, где произошел сбой

Это сделает родительский процесс похожим на отладчик при сбое присоединенного процесса. Можно предположить, что дочерний процесс скомпилирован с символами отладки, а родительский процесс имеет права root.

Как проще всего добиться такой функциональности?

1 Ответ

0 голосов
/ 04 сентября 2018

В Linux гораздо проще использовать библиотеку libSegFault, предоставляемую как часть библиотеки GNU C. На моей системе он установлен в /lib/x86_64-linux-gnu/libSegFault.so.

Все, что вам нужно сделать, это установить для переменной среды SEGFAULT_SIGNALS значение all (чтобы можно было отследить все сбои, поддерживаемые библиотекой), дополнительно SEGFAULT_OUTPUT_NAME, чтобы указать файл, в который записывается трассировка стека (по умолчанию) является стандартной ошибкой), а LD_PRELOAD указывает на библиотеку segfault. Пока процесс не изменяет эти переменные среды, они также применяются ко всем дочерним процессам.

Например, если ./yourprog была программа, которая разветвляет дочерний элемент, который падает, и вы хотите, чтобы трассировка стека равнялась ./yourprog.stacktrace, запустите

SEGFAULT_SIGNALS=all \
SEGFAULT_OUTPUT_NAME=./yourprog.stacktrace \
LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so \
  ./yourprog

или все в одной строке без обратной косой черты (\).

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


Если вы настаиваете на том, чтобы сделать это в своей собственной программе на C, я рекомендую вам сначала взглянуть на libSegFault sources .

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

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

Библиотека предварительной загрузки помощника вставляет signal(), sigaction() и, возможно, sigprocmask(), sigwait(), sigwaitinfo(), pthread_sigmask(), чтобы гарантировать выполнение обработчиков сигналов аварийного дампа библиотек помощников, когда такой сигнал доставлено (SIGSEGV, SIGBUS, SIGILL, возможно SIGTRAP). Обработчик сигнала выполняет сброс стека (и печатает / proc / PID / maps), затем устанавливает расположение сигнала по умолчанию и повторно поднимает сигнал (используя raise()).

По сути, это сводится к тому же, что и выше libSegFault, за исключением вашего собственного кода C.


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

Когда трассировка уничтожается сигналом (кроме SIGKILL), поток, принимающий сигнал, останавливается первым ( "signal-delivery-stop" ), поэтому трассировщик может проверить свой стек (и карта памяти трассировки), прежде чем позволить дочернему процессу продолжить / умереть.

На практике ptracing более инвазивен, так как есть много событий, которые приводят к остановке потоков трассировки. Это также намного сложнее для многопоточных процессов, чем подход LD_PRELOAD, потому что ptrace может управлять отдельными потоками в трассировке; Есть намного больше деталей, чтобы получить право.

...