ptrace - rastreo de un proceso
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request
petición, pid_t pid, void
*direc, void *datos);
La llamada al sistema ptrace proporciona un medio por el
que un proceso padre puede observar y controlar la ejecución de otro
proceso y examinar y cambiar su imagen de memoria y registros. Se usa
principalmente en la implementación de depuración con puntos
de ruptura y en el rastreo de llamadas al sistema.
El padre puede inicar un rastreo llamando a fork(2)
haciendo que el hijo restultante realice un PTRACE_TRACEME, seguido
(normalmente) por un exec(3). Alternativamente, el padre puede
comenzar a rastrear un proceso existente usando PTRACE_ATTACH.
Mientras está siendo rastreado, el hijo se detendrá
cada vez que reciba una señal, aun cuando la señal se haya
ignorado. (La excepción es SIGKILL que tiene su efecto habitual.) El
padre será informado en su siguiente wait(2) y puede
inspeccionar y modificar el proceso hijo mientras está parado. A
continuación, el padre puede hacer que el hijo continúe,
ignorando opcionalmente la señal recibida (o incluso entregando una
señal distinta en su lugar). Cuando el padre termina de rastrear,
puede terminar el hijo con PTRACE_KILL o hace que se continúe
ejecutando en un modo normal sin rastreo mediante PTRACE_DETACH.
El valor del argumento petición determina la
acción a realizar:
- PTRACE_TRACEME
- Indica que este proceso va a ser rastreado por su padre. Cualquier
señal (excepto SIGKILL) entregada a este proceso hará que se
pare y su padre será informado mediante wait.
También, cualquier llamada posterior a exec realizada por
este proceso hará que se le envíe un SIGTRAP, dando al padre
la posibilidad de obtener el control antes de que el nuevo programa
comience su ejecución. Probablemente, un proceso no debería
hacer esta petición si su padre no está esperando para
rastrearlo. (pid, direc y datos se ignoran.)
La petición anterior la usa sólo el proceso hijo. El
resto las usa sólo el padre. En las siguientes peticiones, pid
indica el proceso hijo sobre el que se actuará. Para peticiones
distintas de PTRACE_KILL, el proceso hijo debe estar parado.
- PTRACE_PEEKTEXT,
PTRACE_PEEKDATA
- Lee una palabra de la posición direc.
- PTRACE_PEEKUSER
- Lee una palabra en el desplazamiento direc del área
USER del hijo, que contiene los registros y otra información
sobre el proceso (vea <linux/user.h> y <sys/user.h>). La
palabra se devuelve como resultado de la llamada ptrace.
Típicamente, el desplazamiento debe está alineado en una
frontera de palabra, aunque esto podría variar en cada
arquitectura. (datos se ignora).
- PTRACE_POKETEXT,
PTRACE_POKEDATA
- Copia una palabra de la posición datos a la posición
direc de la memoria del hijo. Como antes, las dos peticiones son
actualmente equivalentes.
- PTRACE_POKEUSER
- Copia una palabra de la posición datos al desplazamiento
direc en el área USER del hijo. Al igual que antes,
el desplazamiento debe estar típicamente alineado en una frontera
de palabra. Para conservar la integridad del núcleo, algunas
modificaciones al área USER se encuentran
deshabilitadas.
- PTRACE_GETREGS,
PTRACE_GETFPREGS
- Copia los registros de propósito general o de punto flotante del
hijo, respectivamente, a la posición datos del padre. Vea
<linux/user.h> para obtener información sobre el formato de
estos datos. (direc se ignora.)
- PTRACE_SETREGS,
PTRACE_SETFPREGS
- Copia los registros de propósito general o de punto flotante del
hijo, respectivamente, desde la posición datos del padre. Al
igual que para PTRACE_POKEUSER, alguna modificaciones de los registros de
propósito general pueden estar deshabilitadas. (direc se
ignora.)
- PTRACE_CONT
- Reinicia el proceso hijo parado. Si datos no es cero y tampoco
SIGSTOP se interpreta como una señal que se entregará al
hijo. En otro caso, no se entrega ninguna señal. Así, por
ejemplo, el padre puede controlar si una señal enviada al hijo es
entregada o no. (direc se ignora.)
- PTRACE_SYSCALL,
PTRACE_SINGLESTEP
- Reinicia el proceso hijo parado al igual que PTRACE_CONT pero prepara al
hijo para que se pare en la siguiente entrada a o salida de una llamda al
sistema o tras la ejecución de una única intrucción,
respectivamente. (Como es usual, el hijo también se detendrá
al recibir una señal). Desde la perspectiva del padre, el hijo
aparecerá como si se hubiera detenido al recibir una señal
SIGTRAP. Por lo que, por ejemplo, para PTRACE_SYSCALL, la idea es
inspeccionar los argumentos de la llamada al sistema en la primera parada,
realizar a continuación otra PTRACE_SYSCALL e inspeccionar los
valores devueltos por la llamada al sistema cuando se detenga la segunda
vez. (direc se ignora.)
- PTRACE_KILL
- Envía al hijo una señal SIGKILL para que termine.
(direc y datos se ignoran.)
- PTRACE_ATTACH
- Ata al proceso especificado en pid, convirtiéndolo en un
"hijo" rastreado. El hijo se comporta como si hubiera realizado
un PTRACE_TRACEME. El proceso actual realmente se convierte en el padre
del proceso hijo para la mayoría de propósitos (por ejemplo,
recibirá notificación de los eventos del hijo y
aparecerá en la salida de ps(1) como padre del hijo), pero
un getppid(2) por parte del hijo todavía devolverá el
pid del padre original. Al hijo se le envía un SIGSTOP pero,
necesariamente, no tiene por qué haberse parado cuando esta llamada
haya terminado. Use wait para esperar a que el hijo se pare.
(direc y datos se ignoran.)
- PTRACE_DETACH
- Reinicia el hijo parado al igual que PTRACE_CONT pero primero lo desata
del proceso, deshaciendo el efecto de reparentesco de PTRACE_ATTACH y los
efectos de PTRACE_TRACEME. Aunque quizás no sea intencionado, bajo
Linux un proceso rastreado puede ser desatado de esta manera sin tener en
cuenta qué método se usó para iniciar el rastreo.
(direc is ignored.)
Aunque los argumentos de ptrace se interpretan según
el prototipo dado, GNU libc declara actualmente ptrace como una
función en la que sólo el argumento petición
tiene sentido. Esto significa que se pueden omitir los argumentos del final
innecesarios, aunque al hacerlo así se hace uso de comportamiento de
gcc(1) sin documentar.
init(8), el proceso con PID 1, no puede ser rastreado.
La disposición de los contenidos de memoria y del
área USER son bastante específicos del sistema operativo (y la
arquitectura).
El tamaño de una "palabra" viene determinado por
la variante del sistema operativo (por ejemplo, para un Linux de 32 bits es
de 32 bits, etc.)
El rastreo provoca unas pocas diferencias sutiles en la
semántica de los procesos rastreados. Por ejemplo, si se ata un
proceso con PTRACE_ATTACH, su padre original ya no puede recibir
notificaciones mediante wait cuando se detiene y no hay forma de que
el nuevo padre pueda simular de forma efectiva esta notificación.
Esta página documenta la forma en que funciona actualmente
la llamada ptrace en Linux. Su comportamiento difiere notablemente en
otros Unix. En cualquier caso, el uso ptrace es altamente
específico del sistema operativo (y la arquitectura).
La página de manual de SunOS describe ptrace como
"única y arcaica", que lo es. La interfaz de
depuración basada en el sistema de ficheros virtual "proc"
presente en Solaris 2 implementa un superconjunto de la funcionalidad de
ptrace de forma más potente y uniforme.
En caso de éxito, las peticiones PTRACE_PEEK* devuelven los
datos solicitados, mientras que las otras peticiones devuelven cero. En caso
de error, todas las peticiones devuelven -1 y a errno(3) se le asigna
un valor apropiado. Ya que el valor devuelto por una petición
PTRACE_PEEK* con éxito puede ser -1, el invocador debe comprobar
errno después de tales peticiones para determinar si hubo
error o no.
- EPERM
- El proceso indicado no puede ser rastreado. Esto podría deberse a
que el padre no tiene suficientes privilegios. Los procesos que no son del
root no pueden rastrear procesos a los que no pueden enviar señales
o programas en ejecución setuid/setgid por razones obvias.
Alternativamente, puede que el proceso ya se esté rastreando o ser
el proceso init (pid 1).
- ESRCH
- El proceso especificado no existe o el invocador no lo está
rastreando actualmente o no está parado (para peticiones que
necesiten que lo esté).
- EIO
- Petición no es válida o se ha intentado leer de o
escribir en una área inválida de la memoria del padre o del
hijo, o se ha producido una violación en la alineación de
palabra o se ha especificado una señal inválida durante una
petición de reinicio.
- EFAULT
- Se ha intentado leer de o escribir en una área inválida de
la memoria del padre o del hijo, probablemente porque el área no
estaba asignada o no era accesible. Desafortunadamente, en Linux,
diferentes versiones de este fallo devolverán EIO o EFAULT de forma
más o menos arbitraria.
SVr4, SVID EXT, AT&T, X/OPEN, BSD 4.3