ptrace
- это средство, позволяющее родительскому процессу наблюдать и контролировать
протекание другого процесса, просматривать и изменять его данные и регистры.
Обычно эта функция используется для создания точек прерывания в программе отладки
и отслеживания системных вызовов.
Родительский процесс может начать трассировку, сначала вызвав функцию
fork(2),
а затем получившийся дочерний процесс может выполнить PTRACE_TRACEME, за которым
(обычно) следует выполнение
exec(3).
С другой стороны, родительский процесс может начать отладку существующего процесса
при помощи PTRACE_ATTACH.
При трассировке дочерний процесс останавливается каждый раз при
получении сигнала, даже если этот сигнал игнорируется. (Исключением является
SIGKILL, работающий обычным образом.) Родительский процесс будет уведомлен об этом
при вызове
wait(2),
после которого он может просматривать и изменять содержимое дочернего процесса
до его запуска. После этого родительский процесс разрешает дочернему
продолжать работу, в некоторых случаях игнорируя посылаемый ему сигнал или
отправляя вместо этого другой сигнал).
По окончании трассировки родительский процесс может прекратить работу
дочернего при помощи функции PTRACE_KILL или дать ему возможность работать
в обычном, нетрассируемом, режиме, используя PTRACE_DETACH.
Значение аргумента request определяет действие, выполняемое
функцией:
PTRACE_TRACEME
Означает, что этот процесс должен быть трассирован его родительским
процессом. Любой сигнал (кроме SIGKILL), получаемый этим процессом, приведет
к его приостановке, и родительский процесс будет уведомлен об этом с помощью функции
wait.
Кроме того, если этот процесс вызовет
exec,
то к нему придет сигнал SIGTRAP, в результате чего родительский процесс
получит управление до того, как запустится новая программа.
Скорее всего, процессу не следует посылать этот вызов, если
родительский процесс не готов к трассировке дочернего.
(Параметры pid, addr и data игнорируются).
Вышеописанный запрос посылается только дочерним процессом; все остальные
отправляются родительским. В запросах, описанных ниже, параметр pid
задает номер процесса, над которым будут производиться действия. При вызове
всех запросов (кроме PTRACE_KILL) дочерний процесс должен быть остановлен.
PTRACE_PEEKTEXT, PTRACE_PEEKDATA
Читает слово с адресом
addr,
находящееся в памяти дочернего процесса, возвращая это слово как
результат функции
ptrace .
Linux не разделяет понятий адресных пространств текста и данных, поэтому
оба вызова абсолютно идентичны. (Аргумент data игнорируется.)
PTRACE_PEEKUSR
Читает слово с адресом
addr
в области
USER
дочернего процесса, которая содержит информацию о регистрах и процессе
(см. файлы <linux/user.h> и <sys/user.h>). Слово возвращается в качестве
результата функции
ptrace .
Обычно смещение должно быть выровнено по границе слова, хотя это может
зависеть от архитектуры системы (параметр data игнорируется).
PTRACE_POKETEXT, PTRACE_POKEDATA
Копирует слово
data
в память дочернего процесса с адресом
addr.
Как описано выше, эти два запроса в данное время аналогичны.
PTRACE_POKEUSR
Копирует слово
data
по смещению
addr
в области
USER
дочернего процесса.
Обычно смещение должно быть выровнено по границе слова.
Для того, чтобы сохранить упорядоченность данных в системе, некоторые изменения
в процесс вносить запрещено.
PTRACE_GETREGS, PTRACE_GETFPREGS
Копирует общие регистры или, соответственно, регистры сопроцессора, дочернего
процесса в переменную data родительского. Формат передаваемой структуры
описан в файле <linux/user.h>. (Параметр addr игнорируется).
PTRACE_SETREGS, PTRACE_SETFPREGS
Изменяет содержимое общих (или, соответственно, математических) регистров
дочернего процесса на основании структуры, находящейся в переменной
data родительского процесса. Как и в случае c PTRACE_POKEUSER,
изменение некоторых общих регистров запрещено (параметр addr
игнорируется).
PTRACE_CONT
Возобновляет работу остановленного дочернего процесса. Если параметр
data не равен нулю или SIGSTOP, то он считается сигналом, который
надо передать дочернему процессу; в противном случае сигнал не передается.
Таким образом родительский процесс сможет контролировать
передачу сигнала дочернему процессу (параметр addr игнорируется).
PTRACE_SYSCALL, PTRACE_SINGLESTEP
Аналогично PTRACE_CONT они возобновляют работу остановленного дочернего
процесса, но указывают, что процесс должен быть остановлен при следующем входе
в систему или выходе из нее при вызове или после исполнения одной из команд.
(Дочерний процесс будет также остановлен при получении сигнала). С точки
зрения родительского процесса, дочерний будет остановлен при получении
сигнала SIGTRAP. Таким образом, PTRACE_SYSCALL позволяет
изучить содержимое аргументов при первой остановке процесса, возникшей при
вызове системной функции; а затем просмотреть результат
исполнения системного вызова при второй остановке. (Параметр addr
игнорируется.)
PTRACE_KILL
Посылает дочернему процессу сигнал SIGKILL, для того чтобы процесс прекратил
свою работу (параметры addr и data игнорируются).
PTRACE_ATTACH
Подключается к процессу, заданному в
pid,
делая его трассируемым дочерним процессом; после этого дочерний процесс
работает так, как будто он вызвал PTRACE_TRACEME. Текущий процесс, на самом деле,
становится родительским для дочернего по многим критериям (например,
он будет получать сообщения об операциях дочернего процесса и будет
становиться родительским при работе команды
ps(1)),
но функция
getppid(2),
вызванная дочерним процессом, будет возвращать pid предыдущего родителя.
Дочерний процесс получит сигнал SIGSTOP, но это необязательно вызовет
его остановку; используйте для этого функцию
wait.
(Параметры addr и data игнорируются.)
PTRACE_DETACH
Возобновляет работу остановленного дочернего процесса аналогично
PTRACE_CONT, но сначала отключается от него, аннулируя эффект
PTRACE_ATTACH и эффект PTRACE_TRACEME. Хотя этого не было предусмотрено,
в Linux при помощи этого вызова трассируемый дочерний процесс может
возобновить свою нормальную работу (с отключением трассировки) независимо от
того, как эта трассировка была запущена.
(Поле addr игнорируется).
ЗАМЕЧАНИЯ
Несмотря на то, что параметры
ptrace
обрабатываются в соответствии с описанным выше прототипом, в GNU libc
в данное время функция
ptrace
определена как вариативная функция с одним обязательным параметром request.
Это означает, что ненужные параметры могут быть проигнорированы, что приведет
в действие неописанный здесь механизм
gcc(1).
init(8),
процесс с pid 1, не может быть трассирован.
Структура памяти и области USER дочернего процесса зависят
от ОС и архитектуры системы.
Размер "слова" определяется вариантом ОС (например, для 32-битного варианта Linux
слово будет 32-битным и т.п.).
Трассировка вызывает небольшие изменения в поведении трассируемых процессов.
Например, если трассировка процесса запущена при помощи PTRACE_ATTACH, то его
предыдущий родительский процесс не сможет при помощи
wait
получить сообщение о событиях в дочернем процессе, а для нового
родительского процесса эффективного способа передать это сообщение старому
процессу не существует.
Эта страница описывает работу системного вызова
ptrace
в Linux. Его работа может быть иной в других системах Unix.
Man-страница
ptrace
в SunOS описывает этот вызов как "уникальный и заумный", каким он
на самом деле и является. Интерфейс отладки процессов, основанный на proc
и представленный в Solaris 2, содержит дополнительные вызовы
ptrace,
обеспечивающие большую гибкость и функциональность отладки.
ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ
При удачном завершении работы функции запросы PTRACE_PEEK*
возвращают данные памяти, а остальные запросы возвращают нулевое значение.
При ошибке все запросы возвращают -1, а переменной
errno
присваивается номер ошибки.
Значение, возвращаемое при удачном завершении PTRACE_PEEK*,
может равняться -1, поэтому после вызова данной функции
необходимо проверить содержимое
errno,
чтобы узнать, вернула ли функция ошибку или запрос был удовлетворен.
НАЙДЕННЫЕ ОШИБКИ
EPERM
Заданный процесс не может быть трассирован. Это может произойти потому, что
родительский процесс не имеет достаточного количества прав на трассирование:
не-root процессы не могут по очевидным причинам трассировать процессы,
которым невозможно послать сигналы или которые работают в режиме setuid/setgid.
С другой стороны, процесс может уже быть трассируемым или называться
init
(pid 1).
ESRCH
Заданный процесс не существует, не трассируется вызывающим процессом
или не остановлен (согласно запросам).
EIO
Задано неверное значение request, или была попытка записи информации
в неподходящую область памяти дочернего или родительского процесса;
было нарушено выравнивание слов по границе, или при возобновлении
работы дочернего процесса номер сигнала был задан неверно.
EFAULT
Была сделана попытка записи информации в область памяти дочернего или родительского
процесса, но, скорее всего, эта память не существует или недоступна. К
сожалению, в Linux в разных ситуациях в результате этой ошибки возвращаются
значения EIO или EFAULT, что не всегда поддается объяснению.