Solaris: Buceando en el Kernel con mdb (I)

Kernel, OpenSolaris, mdb Dejar un comentario

MDB (Modular Debugger) es una herramienta igual de potente que desconocida para la gran mayoría de administradores de sistemas, mucha gente desconoce la potencia de este debugger, ya que la asocian con una herramienta de desarrollo y nada más lejos de la realidad, una de las funcionalidades de este debugger es que trabaja con los ficheros core que generan las aplicaciones, tal como pudimos ver en el artículo Solaris: Analizando ficheros Core Dump, el debugger nos puede ayudar a conocer las causan que provocaron el fallo, recordemos que MDB trabaja con los ficheros core, que son una imagen de memoria del proceso que ha sufrido el fallo, esto nos permitía averiguar cuales fueron la causa de dicho fallo.

MDB no solo puede trabajar con los volcados de memoria de los procesos, sino que también lo puede hacer con las imágenes de memoria del kernel mientras este está ejecutándose, esto nos permite poder acceder a toda la información, si sabemos como leerla, con la que trabaja el kernel, lista de procesos, páginas de memoria, ficheros abiertos, listas de espera, conexiones de red, bloqueos, etc.

Por dónde empezar

Para que MDB acceda a la imagen del memoria del Kernel debemos saber como fichero objeto debemos utilizar /dev/ksyms el cual contiene la tabla de símbolos y como fichero core utilizaremos /dev/kmem, por lo tanto podemos ejecutar la siguiente línea.


(root@huelva)# mdb /dev/ksyms /dev/kmem
Loading modules: [ unix krtld genunix ip usba ipc random nfs ptm ]
>

Otra forma que podemos utilizar en la llamada a mdb es mediante el parámetro -k, el cual es similar a lo anterior.


(root@huelva)# mdb -k
Loading modules: [ unix krtld genunix ip usba ipc random nfs ptm ]
>
>

Lo que debemos conocer de MDB para comenzar a trabajar con él es que se basa en los siguientes tres elemento:

-dmods son los módulos que se cargan en el debugger, dependiendo de los módulos que se hayan cargado podremos realizar más o menos acciones.

-dcmds son los comandos que podemos utilizar en el debugger.

-walkers son sencillos programas que nos permiten iterar sobre una serie de datos.

Guía ultrarápida de mdb

$q Para salir de mdb
::help Ayuda
::dcmds Devuelve la lista de dcmds disponibles.
::walkers Devuelve la lista de walkers disponibles.
::dmods Devuelve la lista de módulos cargados.
::ps Listado de los procesos que están corriendo.
::cpuinfo Información sobre los procesadores.
::memstat Información sobre la memoria del sistema.
::dis Desensambla una dirección de memoria.
::dnlc Devuelve el contenido de DNLC
::findstack Busca un segmento de stack
::kmastat Listado de los allocators del Kernel.
::pid2proc Dado un PID, devuelve la dirección de memoria del proceso
::ptree Devuelve el árbol de un proceso.
::print Dada una dirección de memoria, formatea su contenido según una estructura de datos concreta.
::stack Devuelve la pila de un proceso.
::swapinfo Información sobre la swap que esté configurada.
::vnode2path Dado un VNODE, devuelve el path del fichero.
::wchaninfo Información sobre una WCHAN determinada.
::whatis Información sobre una dirección de memoria.
::walk Ejecuta uno de los walkers
::walkers Listado de todos los walkers disponibles.

Cosas que debemos conocer para trabajar con mdb

Para poder sacarle provecho a mdb necesitamos tener unas nociones básicas de programación en C, ya que toda la información con la que trabaja el Kernel, se organiza según una serie de tipos de datos definidos en los ficheros de cabecera que podemos encontrar en el directorio /usr/include/. De una forma rápida, podemos decir que los ficheros de cabecera *.h son en los que se definen los tipos de datos, cuando realizamos un programa en C, los ficheros de cabeceras, definen los tipos y las estructuras de datos, el conocer esto es necesario, ya que la mayoría de las salidas de los comandos de mdb son direcciones de memoria, dichas direcciones de memoria hacen referencia a datos o grupos de datos que están organizados según una estructura de datos, la cual está definida en alguno de los ficheros de cabecera de /usr/include/.

Lo anterior es un tanto farragoso, por lo que vamos a ver un sencillo ejemplo, en el cual vamos a mostrar la información que tiene el kernel de un proceso determinado. Para hacer esto utilizaremos el comando ::ps el cual muestra los procesos que se están ejecutando.

(root@huelva)# mdb -k
Loading modules: [ unix krtld genunix ip usba ipc random nfs ptm ]
> ::ps
S    PID   PPID   PGID    SID   UID      FLAGS             ADDR NAME
R      0      0      0      0     0 0x00000019 0000000001438788 sched
R      3      0      0      0     0 0x00020019 0000030001d9e008 fsflush
R      2      0      0      0     0 0x00020019 0000030001d9ea20 pageout
R      1      0      0      0     0 0x00004008 0000030001d9f438 init
R   4164      1   4164    263   110 0x10000008 000003578db015c8 nagios
R   4165   4164   4164    263   110 0x00004008 0000036ccb9b74c0 sh
R   4166   4165   4164    263   110 0x00004008 0000035785faab38 check_disk_snmp.
R    263      1    263    263   110 0x10000008 00000357860f54d0 nagios
R    178      1    173  13676   110 0x10014008 000003578da6c150 collect2.pl
R   4608      1   4608   4608     0 0x00000008 000003578dadf5e0 httpd
R    667   4608   4608   4608   110 0x10000008 000003578da86140 httpd
R    666   4608   4608   4608   110 0x10000008 000003578da14b50 httpd
R    663   4608   4608   4608   110 0x10000008 00000357860d4ad0 httpd
R    658   4608   4608   4608   110 0x10000008 00000300881b6ad0 httpd
R    657   4608   4608   4608   110 0x10000008 000003578db24a30 httpd
R    656   4608   4608   4608   110 0x10000008 000003578dad61b8 httpd
R    655   4608   4608   4608   110 0x10000008 00000362b7050aa0 httpd
R    654   4608   4608   4608   110 0x10000008 00000300881b60b8 httpd
R  29124      1  29121  29121     0 0x00004008 000003578db56a60 sh
R  28252      1  28249  28249     0 0x00004008 000003578db15450 sh
R  28685      1  28682  28682     0 0x00004008 000003578db6ea50 sh
R  27382      1  27379  27379     0 0x00004008 000003578db2c010 sh
R   5021      1   5021   5021     0 0x00000008 00000301386a5508 syslogd
>

Podemos ver que entre toda la información que devuelve el comando aparece una columna llamada ADDR la cual corresponde a la dirección de memoria donde está definida toda la información del proceso 00000301386a5508, podemos utilizar esta dirección para buscar todos los threads que tiene asociado este proceso, para ello utilizaremos uno de los walkers disponibles ::walk thread

> 00000301386a5508::walk thread
3013823efc0
3578da852a0
3578606cfc0
35786048800
3578da85000
3578daf22e0
35785feefc0
35786181540
3007d8a2aa0
3007d8a3a60
3000360ad60
3000360bd20
30003814fe0
300db323540
>

La salida consiste en una serie de direcciones de memoria, las cuales contienen la información de los distintos threads que están asociado al proceso. Vamos a ver el contenido de la pila de alguno de los threads.

> 3578da85000::findstack
stack pointer for thread 3578da85000: 2a101306ff1
[ 000002a101306ff1 cv_wait_sig_swap+0x184() ]
  000002a1013070a1 cv_waituntil_sig+0x14()
  000002a101307161 lwp_park+0x148()
  000002a101307241 syslwp_park+0x34()
  000002a1013072f1 syscall_trap32+0xa8()
>

Como podemos ver este thread en concreto están en un condition wait. La información de un thread con la que trabaja el Kernel está definida por la estructura de datos kthread_t, se le puede echar un vistazo en el fichero de cabecera /usr/include/sys/thread.h

Con el comando ::print podemos ver la representación en el formato de la estructura de datos kthread_t de la información de un threads.

> 3578da85000::print kthread_t
{
    t_link = 0
    t_stk = 0x2a101307af0
    t_startpc = 0
    t_bound_cpu = 0
    t_affinitycnt = 0
    t_bind_cpu = 0xffff
    t_flag = 0x1002
    t_proc_flag = 0x4
    t_schedflag = 0x1
...
    t_lwpchan = {
        lc_wchan0 = 0
        lc_wchan = 0x3578da8517e
    }
    t_sobj_ops = cv_sobj_ops
    t_cid = 0x1
    t_clfuncs = ts_classfuncs+0x48
...
    t_tid = 0x23c2
...
    t_procp = 0x301386a5508
    t_audit_data = 0x300597e7348
    t_next = 0x3578daf22e0
    t_prev = 0x35786048800
...
    t_taskq = 0
    t_anttime = 0
}

>

De la salida del comando ::print podemos destacar los siguientes campos:

lc_wchan contiene la dirección del WAIT CHANNEL que utiliza este thread.

t_tid es el ID del thread.

t_procp contiene la dirección del proceso al que pertenece este thread, como podemos comprobar la dirección 0x301386a5508 es la misma que obtuvimos mediante el ::ps.

t_next y t_prev son punteros a la lista doblemente enlazada de los threads de un proceso.

Vamos a ver el contenido de la dirección de memoria del proceso, para ello utilizaremos el contenido del campo t_procp, con ::print podemos ver el contenido, no solo de una estructura entera, sino de uno de los campos de dicha estructura y podemos utilizar | una tubería para pasar dicha dirección al nuevo comando ::print


> 3578da85000::print kthread_t t_procp
t_procp = 0x301386a5508
>
>
> 3578da85000::print kthread_t t_procp | ::print proc_t
{
    p_exec = 0x3000363c4f0
    p_as = 0x300085ce730
    p_lockp = 0x30000e90e00
...
    p_next = 0x300037eea98
    p_prev = 0x30008644ab8
...
    p_tlist = 0x3013823efc0
...
    p_user = {
        u_execsw = execsw
...
        u_comm = [ "syslogd" ]
        u_psargs = [ "/usr/sbin/syslogd" ]
        u_argc = 0x1

	}

}

De la estructura proc_t podemos destacar:

-p_exec es un puntero al vnode del fichero binario que se ha utilizado como ejecutable del proceso.

-p_as es un puntero al espacio de direcciones del proceso.

-p_next puntero al proximo proceso en la lista de procesos.

-p_prev puntero al proceso anterior en la lista de procesos.

-p_user es una estructura de tipo user_t.

-p_user.u_comm es una cadena con el nombre del ejecutable del proceso.

-p_user.u_psargs es una cadena con la lista de argumentos que se han pasado al proceso.

-p_user.u_argc es un entero con el número de argumentos que se han pasado al proceso.

-p_user.u_finfo es una estructura de tipo uf_info_t con la información de los ficheros abiertos.

<< | >> Buceando en el Kernel con mdb (II)

Technorati Tag(s) :

Los comentarios están cerrados.

My name is null, /dev/null
Algunos derechos reservados. Licencia Creative Commons