Capítulo 9. Depurando el Kernel

Tabla de contenidos
9.1. Depuración de un Kernel Crash Dump con kgdb
9.2. Depurando un crash dump con DDD
9.3. Analisis Post-mortem de un Dump
9.4. Depuración En-línea del Kernel Usando DDB
9.5. Depuración En-Línea Usando El GDB remoto
9.6. Depurando Un Driver de Consola

Contribución de Paul Richards y Jörg Wunsch

9.1. Depuración de un Kernel Crash Dump con kgdb

Aquí se dan instrucciones para hacer funcionar la depuración del kernel sobre un crash dump. En ellos se asume que usted tiene suficiente espacio de swap para un crash dump. Si usted tiene varias particiones de swap y la primera es demasiado pequeña para albergar un dump, puede configurar su kernel para que use un dispositivo alternativo (en la línea config kernel=, o puede especificar una alternativa usando el comando dumpon(8). La mejor manera de usar dumpon(8) es asignar la variable dumpdev en /etc/rc.conf. Generalmente usted va a querer especificar uno de los dispositivos de swap especificados en /etc/fstab. Actualmente no se soportan dumps a dispositivos que no sean de swap, como por ejemplo una unidad de cinta. Configure su kernel usando config -g. Vea Configuración del Kernel para más detalles sobre la configuración del kernel FreeBSD.

Use el comando dumpon(8) para decirle al kernel donde hacer el dump (Tenga en cuenta que esto debera hacerse luego de configurar la partición como dispositivo de swap usando swapon(8)). Esto se hace normalmente usando los archivos /etc/rc.conf y /etc/rc. Como alternativa se puede fijar el dispositivo usando la cláusula dump en la línea config de su archivo de configuración del kernel. Esta práctica esta en desuso y sólo debería ser usada para obtener un dump de un kernel que falla durante el inicio.

Nota: En adelante, el término kgdb se referirá al gdb corriendo en ``kernel debug mode''. Esto puede lograrse ya sea iniciando al gdb con la opción -k, o enlazandolo y arrancandolo con el nombre kgdb. Esto no se hace por default, y la idea está basicamente en desuso ya que a la gente de GNU no le gusta que sus herramientas se comporten distinto cuando se las llama por otro nombre. Esta posibilidad podrá perfectamente ser descontinuada en futuras versiones.

Una vez que el kernel ha sido compilado haga una copia, digamos kernel.debug, y luego corra strip -g sobre el original. Instale el original normalmente. Usted tambien podría instalar el kernel sin hacer el strip, pero los tiempos de busqueda de algunos programas en la tabla de símbolos aumentarán sensiblemente, y dado que el kernel se carga completo en memoria durante el inicio y no puede intercambiarse a disco luego, se desperdiciarán varios megabytes de memoria.

Si usted esta probando un nuevo kernel, por ejemplo tipeando el nombre del nuevo kernel en el prompt de inicio, pero necesita arrancar otro para tener su sistema funcionando de nuevo, arranquelo solo en modo monousuario usando la opción -s en el prompt de inicio , y luego siga los siguientes pasos:

# fsck -p
# mount -a -t ufs       # asi su file system para /var/crash se puede escribir
# savecore -N /kernel.panicked /var/crash
# exit                  # ...a modo multiusuario

Esto instruye a savecore(8) que use otro kernel para la extracción de nombres de símbolo. De otro modo usaria el kernel en uso y probablemente no haga nada ya que los símbolos del kernel y los del dump son diferentes.

Ahora, luego de un crash dump, vaya a /sys/compile/WHATEVER y corra kgdb. desde kgdb haga:

symbol-file kernel.debug
exec-file /var/crash/kernel.0
core-file /var/crash/vmcore.0
y voila, ya puede depurar el crash dump usando las fuentes del kernel como lo hace con cualquier otro programa.

A continuación se muestra el log de una sesión de kgdb que ilustra el procedimiento. Las líneas fueron numeradas para referencia, y las largas fueron cortadas para mejorar la legibilidad. Mas alla de esto es el registro de un error real tomado durante el desarrollo del driver pcvt de consola.

 1:Script started on Fri Dec 30 23:15:22 1994
 2:# cd /sys/compile/URIAH
 3:# kgdb kernel /var/crash/vmcore.1 
 4:Reading symbol data from /usr/src/sys/compile/URIAH/kernel
...done.
 5:IdlePTD 1f3000
 6:panic: because you said to!
 7:current pcb at 1e3f70
 8:Reading in symbols for ../../i386/i386/machdep.c...done.
 9:(kgdb) where
10:#0  boot (arghowto=256) (../../i386/i386/machdep.c line 767)
11:#1  0xf0115159 in panic ()
12:#2  0xf01955bd in diediedie () (../../i386/i386/machdep.c line 698)
13:#3  0xf010185e in db_fncall ()
14:#4  0xf0101586 in db_command (-266509132, -266509516, -267381073)
15:#5  0xf0101711 in db_command_loop ()
16:#6  0xf01040a0 in db_trap ()
17:#7  0xf0192976 in kdb_trap (12, 0, -272630436, -266743723)
18:#8  0xf019d2eb in trap_fatal (...)
19:#9  0xf019ce60 in trap_pfault (...)
20:#10 0xf019cb2f in trap (...)
21:#11 0xf01932a1 in exception:calltrap ()
22:#12 0xf0191503 in cnopen (...)
23:#13 0xf0132c34 in spec_open ()
24:#14 0xf012d014 in vn_open ()
25:#15 0xf012a183 in open ()
26:#16 0xf019d4eb in syscall (...)
27:(kgdb) up 10
28:Reading in symbols for ../../i386/i386/trap.c...done.
29:#10 0xf019cb2f in trap (frame={tf_es = -260440048, tf_ds = 16, tf_\
30:edi = 3072, tf_esi = -266445372, tf_ebp = -272630356, tf_isp = -27\
31:2630396, tf_ebx = -266427884, tf_edx = 12, tf_ecx = -266427884, tf\
32:_eax = 64772224, tf_trapno = 12, tf_err = -272695296, tf_eip = -26\
33:6672343, tf_cs = -266469368, tf_eflags = 66066, tf_esp = 3072, tf_\
34:ss = -266427884}) (../../i386/i386/trap.c line 283)
35:283                             (void) trap_pfault(&frame, FALSE);
36:(kgdb) frame frame->tf_ebp frame->tf_eip
37:Reading in symbols for ../../i386/isa/pcvt/pcvt_drv.c...done.
38:#0  0xf01ae729 in pcopen (dev=3072, flag=3, mode=8192, p=(struct p\
39:roc *) 0xf07c0c00) (../../i386/isa/pcvt/pcvt_drv.c line 403)
40:403             return ((*linesw[tp->t_line].l_open)(dev, tp));
41:(kgdb) list
42:398        
43:399             tp->t_state |= TS_CARR_ON;
44:400             tp->t_cflag |= CLOCAL;  /* cannot be a modem (:-) */
45:401     
46:402     #if PCVT_NETBSD || (PCVT_FREEBSD >= 200)
47:403             return ((*linesw[tp->t_line].l_open)(dev, tp));
48:404     #else
49:405             return ((*linesw[tp->t_line].l_open)(dev, tp, flag));
50:406     #endif /* PCVT_NETBSD || (PCVT_FREEBSD >= 200) */
51:407     }
52:(kgdb) print tp
53:Reading in symbols for ../../i386/i386/cons.c...done.
54:$1 = (struct tty *) 0x1bae
55:(kgdb) print tp->t_line
56:$2 = 1767990816
57:(kgdb) up
58:#1  0xf0191503 in cnopen (dev=0x00000000, flag=3, mode=8192, p=(st\
59:ruct proc *) 0xf07c0c00) (../../i386/i386/cons.c line 126)
60:       return ((*cdevsw[major(dev)].d_open)(dev, flag, mode, p));
61:(kgdb) up
62:#2  0xf0132c34 in spec_open ()
63:(kgdb) up
64:#3  0xf012d014 in vn_open ()
65:(kgdb) up
66:#4  0xf012a183 in open ()
67:(kgdb) up
68:#5  0xf019d4eb in syscall (frame={tf_es = 39, tf_ds = 39, tf_edi =\
69: 2158592, tf_esi = 0, tf_ebp = -272638436, tf_isp = -272629788, tf\
70:_ebx = 7086, tf_edx = 1, tf_ecx = 0, tf_eax = 5, tf_trapno = 582, \
71:tf_err = 582, tf_eip = 75749, tf_cs = 31, tf_eflags = 582, tf_esp \
72:= -272638456, tf_ss = 39}) (../../i386/i386/trap.c line 673)
73:673             error = (*callp->sy_call)(p, args, rval);
74:(kgdb) up
75:Initial frame selected; you cannot go up.
76:(kgdb) quit
77:# exit
78:exit
79:
80:Script done on Fri Dec 30 23:18:04 1994

Comentarios al listado anterior:

línea 6:

Este es un dump tomado desde el DDB (vea abajo), a partir del comentario del panic ``because you said to!'', y un listado de la pila bastante largo; la razón incial para entrar al DDB fue un trap de falta de pagina.

línea 20:

Esta es la ubicación de la función trap() en el listado de la pila.

línea 36:

Se fuerza el uso de un nuevo marco de pila; esto ya no es necesario ahora. Actualmente se supone que los marcos de pila apuntan a las ubicaciones correctas, aun en caso de un trap. (No tengo un dump más nuevo a mano <g>, mi kernel no ha hecho un panic desde hace bastante tiempo.) Mirando al código en la línea 403 del código fuente, hay una alta probabilidad de que la referencia al puntero ``tp'' sea erronea, o el acceso al arreglo estuviera fuera de sus límites.

línea 52:

El puntero se ve sospechoso, pero contiene una dirección válida.

línea 56:

De todos modos, obviamente apunta a basura, asi que encontramos nuestro error! (Para los que no esten familiarizados con esa pieza de código en particular: tp->t_line hace referencia a la disciplina de línea de este dispositivo de consola, el cual debe ser un entero por demás pequeño)

Éste y otros documentos pueden obtenerse en ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

Para preguntas acerca de FreeBSD, leer la documentación antes de contactar con la lista <questions@FreeBSD.org>.
Para preguntas acerca de esta documentación, e-mail a <doc@FreeBSD.org>.