"I think the major good idea in Unix was its clean
and simple interface: open, close, read, and write"
Ken Thompson
- Solaris: Buceando en el Kernel con mdb (I)
- Solaris: Navegando en el /proc (III)
- Solaris: Navegando en el /proc (II)
- Solaris: Crea tu propio comando fuser
- Solaris: Navegando en el /proc (I)
- Solaris: Analizando ficheros Core Dump
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.
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.
| $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. |
| 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. |
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) : Solaris OpenSolaris MDB
 
 
 
Algunos derechos reservados. Licencia Creative Commons