Vamos a continuar con algo [poco] mas complejo, implementar un “sustituto” de truss(1)

truss(1) es una utilidad que traza llamadas al sistema y señales para un proceso determinado.

Por ejemplo puedes ejecutar el comando truss ls y truss(1) ejecutaria ls, sacando por la salida de error (stderr) las llamadas al sistema y los resultados de estas.

Probemoslo:

root@enzima2 ~ # truss ls
execve("/usr/bin/ls", 0x08047D3C, 0x08047D44)  argc = 1
resolvepath("/usr/bin/ls", "/usr/bin/ls", 1023) = 11
sysconfig(_CONFIG_PAGESIZE)                     = 4096
resolvepath("/usr/lib/ld.so.1", "/lib/ld.so.1", 1023) = 12
xstat(2, "/usr/bin/ls", 0x08047B28)             = 0
open("/var/ld/ld.config", O_RDONLY)             Err#2 ENOENT
xstat(2, "/lib/libc.so.1", 0x080473E0)          = 0
resolvepath("/lib/libc.so.1", "/lib/libc.so.1", 1023) = 14
open("/lib/libc.so.1", O_RDONLY)                = 3
[...]

He recortado la salida porque las llamadas al sistema del ls ocupaban 3 paginas ;-)

Como puedes ver cada linea representa una llamada al sistema, y suele terminar con el resultado de esa llamada.

Por ejemplo en la novena linea vemos una llamada a open(2) que sirve para abrir un fichero.

open("/lib/libc.so.1", O_RDONLY)                = 3

De la página del manual de open(2) podemos ver que su definicion es:

int open(const char *path, int oflag);

donde path es la ruta al fichero a abrir y oflag es un entero que representa el modo de apertura del fichero (en este caso O_RDONLY o sea, solo lectura) y devuelve un resultado numerico (en este caso 3) que es el handle del fichero (el numero que representa al fichero dentro del proceso, para hacer referencia a el posteriormente, por ejemplo, para recorrer el fichero, leer caracteres, etc).

Esto es una introducción bastante burda tanto al comando truss(1), como a la llamada de sistema open(2), pero espero que hayais pillado la idea.

Además truss(1m) también se puede ejecutar con un proceso que se este ejecutando, pasandole el pid de la siguiente forma:

root@enzima2 ~ # echo $$
1052
root@enzima2 ~ # truss -p 1052
waitid(P_ALL, 0, 0x08047BA0, WEXITED|WTRAPPED|WSTOPPED|WCONTINUED) (sleeping...)
^C
root@enzima2 ~ #

NOTA: la variable $$ contiene el valor del pid del shell desde donde se ejecuta

Vamos editar un programa D que haga algo cuando se llame open64 (la llamada al sistema equivalente a open, pero con el flag de fichero largo activado)

syscall::open64:entry
/pid == 1052/
{
    trace("algo!");
}

Y el resultado lo podeis ver en la siguiente imagen:
DTrace de Open64
En la imagen se aprecia que el terminal que esta en el fondo esta ejecutando el programa open.d y el terminal del primer plano esta ejecutando el bash obligandolo a que habra un fichero (en este caso, ejecutando un pequeño script).

Vamos a explicar las dos lineas conceptos nuevos del programa open.d:

syscall::open64:entry



Esto sigue la sintaxis:

Provider:Module:Function:Name



Aqui hay que explicar que es cada cosa:

Provider (proveedor, en castellano), en este caso es syscall

Este es digamos el programa que implementa las pruebas. Muchas veces corresponde con el nombre del modulo que se carga para que esten disponibles esas pruebas (muchas veces un modulo del kernel)

Module (Modulo)

Se suele referir al modulo dentro del programa donde esta la prueba. Suele ser util cuando hay miles de funciones dentro de un solo programa, para organizarlos por grupos. En este caso no se define Module por tanto se deja vacio

Function (función) en este caso open64

Es el nombre de la funcion donde esta situada la prueba

Name (nombre) en este caso entry

Es un nombre que da idea de cuando se ejecuta el probe. (en este caso se ejecuta al “entrar”)

Resumiendo esta prueba se ejecuta cuando se entra ( entry ) en la función open64 que no esta en ningun modulo (o en el modulo general, segun se mire) del proveedor de llamadas de sistema (syscall). Se pueden listar todos los probes disponibles ejecutando un simple dtrace -l

/pid == 1052/

Esto se llama predicado

En el lenguaje D no existen estructuras de control como if o bucles como while, for , etc.

No obstante , los predicados permiten poner condiciones para que se ejecuten los bloques de codigo.

En este caso ese codigo se ejecutara cuando se entra en open64 y si la variable pid vale 1052.

Otra variable util en este caso es $1 que en los scripts tiene el valor del primer parametro pasado al programa, con lo cual podriamos hacer el script algo mas general, sustituyendo el predicado anterior por:

/pid == $1/

y llamando a dtrace de la siguiente manera:

dtrace -s open.d 1052

Continuará

Como esto se estaba haciendo demasiado largo para un post (sobre todo por la introducción a truss), continuaremos en III con algo de generalización de la captura de pruebas y formateo de la salida, porque nuestro open.d es todavia bastante limitado.