Solaris: Buceando en el Kernel con mdb (II)

Kernel, OpenSolaris, mdb Dejar un comentario

En el artículo anterior vimos las nociones básicas para poder utilizar el debugger, ahora vamos a ver, de una forma práctica, cómo podemos sacar provecho a mdb, para ello vamos a realizar varios ejemplos, en los que podremos obtener información de los procesos, el uso de las CPUs, los ficheros abiertos, etc.

Ejemplo 1: Listado de los ficheros abiertos por un proceso

Todos los procesos disponen de una lista de ficheros abiertos, esta lista la podemos encontrar en el campo uf_info_t p_user.u_finfo, vamos a utilizar el comando ::ps para sacar el listado de todos los procesos que están corriendo y le pasamos la salida al comando grep del SO mediante la utilización del carácter ! a modo de tubería. Utilizamos la dirección de memoria donde se encuentran los datos de la estructura proc_t del proceso.

(root@huelva)# mdb -k
Loading modules: [ unix krtld genunix ip usba ipc random nfs ptm ]
>
> ::ps ! grep syslog
R   5021      1   5021   5021     0 0x00000008 00000301386a5508 syslogd

>
> 00000301386a5508::print -t proc_t p_user.u_finfo
{
    kmutex_t p_user.u_finfo.fi_lock = {
        void * [1] _opaque = [ 0 ]
    }
    kmutex_t p_user.u_finfo.fi_pad = {
        void * [1] _opaque = [ 0 ]
    }
    int p_user.u_finfo.fi_nfiles = 0x1ff
    volatile uf_entry_t *p_user.u_finfo.fi_list = 0x30382f6a000
    uf_rlist_t *p_user.u_finfo.fi_rlist = 0
}
>
> ::sizeof uf_entry_t
sizeof (uf_entry_t) = 0x28
>
>

Hemos utilizado el parámetro -t en el comando ::print para que nos devuelva la estructura con todos los tipos de datos de los distintos miembros. De lo devuelto por ::print, nos interesan dos campos, fi_nfiles que contiene el número de elementos del array y fi_list es un puntero al array de elementos de tipo uf_entry_t. Con el comando ::sizeof tipo podemos obtener el tamaño de los elementos del array.

Dir Pos. Array
0×30382f6a000 1
0×30382f6a028 2
0×30382f6a050 3
0×30382f6a078 4
0×30382f6a0a0 5
0×1ff

Una forma sencilla para obtener todos los elementos del array, consiste en incrementar la dirección de memoria del primero en 0×28 (este es el tamaño de un elemento de tipo uf_entry_t, como hemos comprobado con el comandos ::sizeof) por cada elemento, lo podemos hacer a mano o podemos utilizar la dirección, seguida del número de veces que deseamos repetir la operación, el formato sería dirección,repeticiones, utilizaremos la dirección del primer elemnto de la lista y como valor de repetición el número de elemento de la misma, esto lo podemos hacer porque el comando ::print además de imprimir una dirección de memoria con una determinada estructura, tambien incrementa la posición de memoria actual, realizando un desplazamiento igual al tamaño del tipo de dato que se ha pasado al comando.

>
> 0x30382f6a000,0x1ff::print uf_entry_t uf_file |::print file_t f_vnode
f_vnode = 0x3000039dd40
f_vnode = 0x3000039dd40
f_vnode = 0x3000039dd40
f_vnode = 0x3000373c7b8
f_vnode = 0x300035f3d08
f_vnode = 0x301557b9c88
f_vnode = 0x30012bf3848
f_vnode = 0x3019887d088
f_vnode = 0x3557b326928
f_vnode = 0x3000373d2a8
f_vnode = 0x3012b81c828
mdb: failed to read f_vnode pointer at 10: no mapping for address
mdb: failed to read f_vnode pointer at 10: no mapping for address
mdb: failed to read f_vnode pointer at 10: no mapping for address
mdb: failed to read f_vnode pointer at 10: no mapping for address
mdb: failed to read f_vnode pointer at 10: no mapping for address
mdb: failed to read f_vnode pointer at 10: no mapping for address
...

Cada elemento de array es de tipo uf_entry_t esta estructura de datos tiene un miebro de tipo file_t, el cual a su vez tienen un miembro llamado f_vnode que es un puntero a un vnode, con la linea anterior hemos obtenido la lista del primer vnode de todos los ficheros abiertos por el proceso.

Podríamos utilizar el comando ::vnode2path el cual, como su propio nombre indica, devuelve el pathname de un vnode. Esta vez solo vamos a sacar los 11 primeros elementos del array.

>
> 0x30382f6a000,0xb::print uf_entry_t uf_file |::print -t file_t f_vnode | ::vnode2path
/
/
/
/var/adm/messages
/var/log/syslog
>

Otra forma de conocer el nombre de los ficheros que tiene abierto un proceso es mediante el comando ::dnlc el cual devuelve la lista de todos los nombres de fichero que están registrados en el directory name lookup cache (DNLC), la primera columna de la salida del comando corresponde al vnode del fichero.

> ::dnlc
VP               DVP              NAME
0000030155e040a0 00000300993b51d8 si
00000300994d40a0 00000300ba091c60 statusdock_button.png
0000030155fe00a0 0000030005697118 usba
00000300992a40a0 000003000368ba30 dfl3O9t0f0002778
00000300597900a0 00000300598d8af8 SchoolBoldItalicCE10.pcf.Z
00000300a36ee0a0 000003007d9ba4e8 runtime.html
00000300cf04c0a0 00000300cf064d20 show-path
00000300b9d380a0 00000300b9d38fe0 gmatch.3gen
000003578004a0a0 00000300b9748590 tool_16.gif
...

Ejemplo 2: Estado de las CPUs

Ver qué está ocurriendo con las CPUs de nuestro sistema es algo que puede ayudar a entender donde se puede estar produciendo un cuello de botella. Para chequear el estado de las CPUs de nuestro sistema podemos utilizar el comando ::cpuinfo [-v]. La salida del comando será parecida a la siguiente.

(root@huelva)# mdb -k
Loading modules: [ unix krtld genunix ip usba ipc random nfs ptm ]
> ::cpuinfo
 ID ADDR        FLG NRUN BSPL PRI RNRN KRNRN SWITCH THREAD      PROC
  0 300019d2000  1b    0    0  59   no    no t-0    3578db997a0 mdb
  1 00001400000  1b    0    0  59   no    no t-0    3578dbc6d60 sshd

Existe una línea por cada CPU del sistema, cada una de las cuales dipone de una serie de columnas, de las cuales nos vamos a centrar en:

-ADDR es la dirección de memoria de la estructura de tipo cpu_t que contiene la información con la que trabaja el Kernel.

-FLG son los flag de estado de la CPU.

0×01 RUNNING
0×02 READY
0×04 QUIESCED
0×08 EXISTS
0×10 ENABLE
0×20 OFFLINE
0×40 POWEROFF

-PRI prioridad del thread que se está ejecutando.-THREAD dirección de memoria de la estructura kthread_t que define al thread que se está ejecutando en la CPU.-PROC proceso al que pertenece el thread que se está ejecutando.Vamos a utilizar el comando ::print para ver el contenido de la dirección de memoria de una de las CPUs, la definición del tipo de dato cpu_t la podemos encontrar en el fichero de cabecera /usr/include/sys/cpuvar.h.

> ::cpuinfo
 ID ADDR        FLG NRUN BSPL PRI RNRN KRNRN SWITCH THREAD      PROC
  0 300019d2000  1b    0    0  -1   no    no t-0    2a100013d40 (idle)
  1 00001400000  1b    0    0  59   no    no t-0    3578db997a0 mdb
> 0x300019d2000::print cpu_t
{
    cpu_id = 0
    cpu_seqid = 0x1
    cpu_flags = 0x1b
    cpu_thread = 0x3578db997a0
    cpu_idle_thread = 0x2a100013d40
...

    cpu_runrun = ' 0'
    cpu_kprunrun = ' 0'
    cpu_chosen_level = 0xffff
    cpu_dispthread = 0x3578db997a0
    cpu_thread_lock = 0
    cpu_dontsteal = 0
    cpu_dispatch_pri = 0x3b
    cpu_last_swtch = 0x35998b68
 ...
    cpu_stat = {
        __cpu_stat_lock = [ 0, 0 ]
        cpu_sysinfo = {
            cpu = [ 0x3490af0a, 0x2ea12b, 0xbfc33d, 0x1a756c ]
            wait = [ 0x1a756c, 0, 0 ]
            bread = 0xc47
            bwrite = 0x88b1d7
            lread = 0x3888759
            lwrite = 0x200c105
            phread = 0x502a
            phwrite = 0x52
            pswitch = 0x5c882f7b
            trap = 0x3103dc24
            intr = 0x826f42f
            syscall = 0x7e9bea80
            sysread = 0x65dad0e
            syswrite = 0x12788cd
            sysfork = 0x4bdac6
            sysvfork = 0x239ef
            sysexec = 0x656e0a
...
            nthreads = 0x520ca9
            cpumigrate = 0xa40d9eb
            xcalls = 0x163f39ef
            mutex_adenters = 0x34db367
            rw_rdfails = 0xb55
            rw_wrfails = 0x8e4
...
        }
        cpu_syswait = {
            iowait = 0
            swap = 0
            physio = 0
        }
        cpu_vminfo = {
            pgrec = 0x7182587
            pgfrec = 0x7182587
            pgin = 0x23965
            pgpgin = 0x5a939
            pgout = 0
            pgpgout = 0
            swapin = 0
            pgswapin = 0
            swapout = 0
            pgswapout = 0
            zfod = 0x5b2b425
            dfree = 0
            scan = 0x2fff
            rev = 0
            hat_fault = 0
            as_fault = 0x2a7108c8
            maj_fault = 0x1c360
            cow_fault = 0x86e3a25
            prot_fault = 0x7c9636b
            softlock = 0x1f509
            kernel_asflt = 0
            pgrrun = 0x1
            execpgin = 0x6550
            execpgout = 0
            execfree = 0
            anonpgin = 0
            anonpgout = 0
            anonfree = 0
            fspgin = 0x543e9
            fspgout = 0
            fsfree = 0
        }
    }
    cpu_kstat = 0x30001cc8570
    cpu_info_kstat = 0x30001cc8370
...
    cpu_type_info = {
        pi_state = 0x2
        pi_processor_type = [ "sparcv9" ]
        pi_fputypes = [ "sparcv9" ]
        pi_clock = 0x538
    }
...
}
>

De todos los datos de la estructura son especialmente interesantes cpu_sysinfo que es de tipo cpu_sysinfo_t y contiene
información como el número de llamadas al sistema (syscall), el número de threads creados (nthreads), el número de crosscalls, el número
de llamadas fork, etc. El miembro cpu_syswait que es de tipo cpu_syswait_t, almacena información sobre los estados de espera
del proceso que se está ejecutando. El miembro cpu_vminfo que es de tipo cpu_vminfo_t y presenta información del uso
de la memoria por parte del procesador, páginas de entrada, páginas de salida, número de páginas escaneadas, fallos de página, fallos
de copy-on-write. La definición de estos 3 tipos de datos la podemos encontrar en el fichero de cabecera /usr/include/sys/sysinfo.h.

Buceando en el Kernel con mdb (I) << | >> Buceando en el Kernel con mdb (III)

Technorati Tag(s) :

Los comentarios están cerrados.

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