En este post vamos a ver como relacionar las direcciones de memoria con nuestro código fuente para ver en qué línea se está produciendo el memory leak. En el post anterior se mostraron algunos métodos para detectar memory leaks, de los cuales vamos a tomar como base el método 3 (el que usa un programa que cumpliría la función del testnap y además nos deja un archivo core para analizar con mdb).
Read this post in english.
Read this post in english.
Para desarrollar este ejemplo se utilizará un archivo (fm_inv_pol_demo_leaks.c) con funciones que produzcan memory leaks al utilizar las macros y funciones del api de BRM. Este archivo fomará parte de la librería fm_inv_pol.so para poder realizar las pruebas.
fm_inv_pol_demo_leaks.c (no hay chequeo de errores para que el código fuente no sea tan largo y no ocupe mucho lugar en el post)
Luego de haber aplicado el método 3 para obtener el archivo core empezamos el análisis con mdb.
Aquí vemos que hay 18 buffers con leaks, empezaremos el análisis del primero (dirección bufctl: 00454780) con ::bufctl_audit
Mirando el stack trace, la entrada libumem.so.1`malloc en cada stack es la función que esta asignando la memoria al buffer leakeado. Lo que estamos analizando es nuestro código que se utiliza dentro de la librería fm_inv_pol.so, entonces el leak está en la primer línea en la que aparece el string fm_inv_pol.so (en este caso es fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xc0) y la línea anterior es la función de la PIN library que se está invocando dentro de nuestro código (libcmpin.so`pbo_decimal_copy+0x14).
Entonces por lo dicho en el párrafo de arriba podemos ver que dentro de la función fm_inv_pol_demo_leaks_pin_decimal_t se esta llamando a la función pbo_decimal_copy y esa es la memoria que estamos leakeando (conjungando la palabra inventada).
Lo que queda por hacer ahora es buscar donde está la definición de la función fm_inv_pol_demo_leaks_pin_decimal_t entre los archivos que conforman la librería fm_inv_pol.so. En este caso el archivo es el fm_inv_pol_demo_leaks.c y la función fm_inv_pol_demo_leaks_pin_decimal_t es:
En éste caso solamente hay una invocación a la función pbo_decimal_copy (línea 116) por lo que resulta fácil saber que hay que liberar la memoria de la variable demo_copy_decimalp. Pero.... que sucedería en caso de que la función fm_inv_pol_demo_leaks_pin_decimal_t no fuese tan pequeña y que hubiese más de una llamada a la función pbo_decimal_copy y que hubiese muchos for, while con if anidados y dentro de ellos varias veces la llamada a pbo_decimal_copy, ahí la cosa no estaría tan facil de detectar. Por suerte dentro de mdb tenemos más comandos dcmd para buscar nuestro memory leak en el código fuente. Utilizaremos ::dis para desensamblar instrucciones cercanas al puntero del código actual (nos dará una línea cercana a donde se está produciendo el leak, a veces nos da la línea exacta, más adelante en este post mostraré cuando sí podremos obtener la línea exacta y cuando no).
Vamos a aplicar ::dis en la línea donde aparece por primer vez la librería fm_inv_pol.so. Lo cual nos da: fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xc0::dis (también puede ser fm_inv_pol_demo_leaks_pin_decimal_t+0xc0::dis es lo mismo).
Listo, ya hemos encontrado donde esta el leak en nuestro código fuente. Ahora analicemos el segundo buffer que nos dio el ::findleaks.(dirección bufctl: 0045a830) con ::bufctl_audit
Siguiendo los pasos del buffer analizado anteriormente vemos que el leak está en la función fm_inv_pol_demo_leaks_pin_decimal_t al ejecutar la función pbo_decimal_add. Le aplicamos el ::dis a fm_inv_pol_demo_leaks_pin_decimal_t+0x1c8
En este caso tampoco obtendremos la dirección exacta(las instrucciones no están centradas), pero sí una aproximada, buscamos el call y el mov nuevamente. El mov esta después del call por lo que el leak estará antes del número hexadecimal del mov: mov 0x7a, %l1. El 0x7a en decimal es 122, entonces hay que buscar una llamada a la funcion pbo_decimal_add antes de la línea 122, en nuestro caso es la que se encuentra en la línea 120.
Ahora analicemos otro buffer (voy a utilizar uno que sí tenga la dirección exacta) el 13 de la lista arrojada por el ::findleaks. (dirección bufctl: 006355a8) con ::bufctl_audit
Aquí vemos que el leak esta en la función fm_inv_pol_demo_leaks_poid_t al invocar pin_poid_copy. La funcion fm_inv_pol_demo_leaks_poid_t es:
Analizaremos entonces fm_inv_pol_demo_leaks_poid_t+0x154 con ::dis
En este caso el ::dis nos está dando la línea exacta donde se encuentra el leak (vemos que las instrucciones están centradas), entonces la línea es la "mov 0x5f, %l1" que en decimal es 95. Efectivamente viendo el código se ve que en la línea 95 está la invocación al PIN_POID_COPY y ahí es donde se produce el leak (se debe liberar la memoria de la variable demo_copy_poidp).
Hasta aquí ya hemos visto como con el archivo core utilizando mdb podemos llegar a la línea del código fuente donde se está produciendo la asignación de memoria que luego no se libera. En el próximo post se tratará la detección de memory leaks en aplicaciones que utilizan el framework MTA de BRM.
Un par de tips antes de finalizar el post:
fm_inv_pol_demo_leaks.c (no hay chequeo de errores para que el código fuente no sea tan largo y no ocupe mucho lugar en el post)
#include "pcm.h" #include "pin_cust.h" #include "cm_fm.h" #include "pin_errs.h" #include "pinlog.h" #include "pbo_decimal.h" #include "fm_inv_pol_demo_leaks.h" void fm_inv_pol_demo_leaks_pin_flist_t( pcm_context_t *ctxp, pin_flist_t *i_flistp, pin_errbuf_t *ebufp); void fm_inv_pol_demo_leaks_poid_t( pcm_context_t *ctxp, pin_flist_t *i_flistp, pin_errbuf_t *ebufp); void fm_inv_pol_demo_leaks_pin_decimal_t( pin_flist_t *i_flistp, pin_errbuf_t *ebufp); void fm_inv_pol_demo_leaks_start_demo( pcm_context_t *ctxp, pin_errbuf_t *ebufp) { pin_flist_t *demo_flistp = NULL; pin_flist_t *tmp_flistp = NULL; //construyo el flist con el que hare la demostracion de como detectar los memory leaks demo_flistp = PIN_FLIST_CREATE(ebufp); PIN_FLIST_FLD_PUT(demo_flistp, PIN_FLD_POID, PIN_POID_CREATE(PCM_GET_CURRENT_DB_NO(ctxp), "/dummy", -1, ebufp), ebufp); PIN_FLIST_FLD_PUT(demo_flistp, PIN_FLD_ACCOUNT_OBJ, PIN_POID_CREATE(PCM_GET_CURRENT_DB_NO(ctxp), "/account", -1, ebufp), ebufp); PIN_FLIST_FLD_PUT(demo_flistp, PIN_FLD_AMOUNT, pbo_decimal_from_str("100", ebufp), ebufp); PIN_FLIST_FLD_PUT(demo_flistp, PIN_FLD_DUE, pbo_decimal_from_str("8", ebufp), ebufp); PIN_FLIST_FLD_PUT(demo_flistp, PIN_FLD_TOTAL_DUE, pbo_decimal_from_str("3", ebufp), ebufp); tmp_flistp = PIN_FLIST_ELEM_ADD(demo_flistp, PIN_FLD_BAL_IMPACTS, 0, ebufp); PIN_FLIST_FLD_PUT(tmp_flistp, PIN_FLD_POID, PIN_POID_CREATE(PCM_GET_CURRENT_DB_NO(ctxp), "/dummy", -1, ebufp), ebufp); PIN_FLIST_FLD_PUT(tmp_flistp, PIN_FLD_AMOUNT, pbo_decimal_from_str("100", ebufp), ebufp); tmp_flistp = PIN_FLIST_ELEM_ADD(demo_flistp, PIN_FLD_BAL_IMPACTS, 1, ebufp); PIN_FLIST_FLD_PUT(tmp_flistp, PIN_FLD_POID, PIN_POID_CREATE(PCM_GET_CURRENT_DB_NO(ctxp), "/dummy", -1, ebufp), ebufp); PIN_FLIST_FLD_PUT(tmp_flistp, PIN_FLD_AMOUNT, pbo_decimal_from_str("100", ebufp), ebufp); fm_inv_pol_demo_leaks_pin_flist_t(ctxp, demo_flistp, ebufp); fm_inv_pol_demo_leaks_poid_t(ctxp, demo_flistp, ebufp); fm_inv_pol_demo_leaks_pin_decimal_t(demo_flistp, ebufp); PIN_FLIST_DESTROY_EX(&demo_flistp, NULL); } void fm_inv_pol_demo_leaks_pin_flist_t( pcm_context_t *ctxp, pin_flist_t *i_flistp, pin_errbuf_t *ebufp) { pin_flist_t *demo_create_flistp = NULL; pin_flist_t *demo_take_flistp = NULL; pin_flist_t *demo_copy_flistp = NULL; pin_flist_t *demo_beid_flistp = NULL; demo_create_flistp = PIN_FLIST_CREATE(ebufp); PIN_FLIST_FLD_COPY(i_flistp, PIN_FLD_POID, demo_create_flistp, PIN_FLD_POID, ebufp); PIN_FLIST_FLD_COPY(i_flistp, PIN_FLD_AMOUNT, demo_create_flistp, PIN_FLD_AMOUNT, ebufp); demo_copy_flistp = PIN_FLIST_COPY(i_flistp, ebufp); demo_take_flistp = PIN_FLIST_ELEM_TAKE(i_flistp, PIN_FLD_BAL_IMPACTS, 0, 0, ebufp); demo_beid_flistp = pin_conf_beid(ctxp, ebufp); } void fm_inv_pol_demo_leaks_poid_t( pcm_context_t *ctxp, pin_flist_t *i_flistp, pin_errbuf_t *ebufp) { poid_t *poidp = NULL; poid_t *demo_create_poidp = NULL; poid_t *demo_copy_poidp = NULL; poid_t *demo_take_poidp = NULL; demo_create_poidp = PIN_POID_CREATE(PCM_GET_CURRENT_DB_NO(ctxp), "/account", -1, ebufp); poidp = PIN_FLIST_FLD_GET(i_flistp, PIN_FLD_POID, 0, ebufp); demo_copy_poidp = PIN_POID_COPY(poidp, ebufp); demo_take_poidp = PIN_FLIST_FLD_TAKE(i_flistp, PIN_FLD_ACCOUNT_OBJ, 0, ebufp); } void fm_inv_pol_demo_leaks_pin_decimal_t( pin_flist_t *i_flistp, pin_errbuf_t *ebufp) { pin_decimal_t *decimal1p = NULL; pin_decimal_t *decimal2p = NULL; pin_decimal_t *demo_create_decimalp = NULL; pin_decimal_t *demo_copy_decimalp = NULL; pin_decimal_t *demo_take_decimalp = NULL; pin_decimal_t *demo_add_decimalp = NULL; demo_create_decimalp = pbo_decimal_from_str("100", ebufp); decimal1p = PIN_FLIST_FLD_GET(i_flistp, PIN_FLD_AMOUNT, 0, ebufp); demo_copy_decimalp = pbo_decimal_copy(decimal1p, ebufp); decimal1p = PIN_FLIST_FLD_GET(i_flistp, PIN_FLD_DUE, 0, ebufp); decimal2p = PIN_FLIST_FLD_GET(i_flistp, PIN_FLD_TOTAL_DUE, 0, ebufp); demo_add_decimalp = pbo_decimal_add(decimal1p, decimal2p, ebufp); demo_take_decimalp = PIN_FLIST_FLD_TAKE(i_flistp, PIN_FLD_DUE, 0, ebufp); }
Luego de haber aplicado el método 3 para obtener el archivo core empezamos el análisis con mdb.
pin>mdb core.29470 Loading modules: [ libumem.so.1 libc.so.1 libuutil.so.1 ld.so.1 ] > ::findleaks mdb: [fc000000, fc564000): couldn't read 16384 bytes at fc000000: no mapping for address CACHE LEAKED BUFCTL CALLER 00062008 1 00454780 libc.so.1`strdup+0xc 00068008 1 0045a830 libcmpin.so`_alloc_result_header+0x18 00062008 1 004547e8 libcmpin.so`_pin_decimal_add_mag+0x224 000a8008 1 0040a3b0 libcmpin.so`pcmmem_malloc_flistflds+0x34 000a8008 1 0040a2e0 libcmpin.so`pcmmem_malloc_flistflds+0x34 000a8008 1 005e6220 libcmpin.so`pcmmem_malloc_flistflds+0x34 000a4008 1 004001a0 libcmpin.so`pcmmem_malloc_flisthdr+0x34 000a4008 1 0040ab68 libcmpin.so`pcmmem_malloc_flisthdr+0x34 000a4008 1 005e6970 libcmpin.so`pcmmem_malloc_flisthdr+0x34 0006c008 1 0064dd40 libcmpin.so`pcmmem_malloc_poid+0x34 0006c008 1 00635540 libcmpin.so`pcmmem_malloc_poid+0x34 0006c008 1 00635950 libcmpin.so`pcmmem_malloc_poid+0x34 0006c008 1 006355a8 libcmpin.so`pcmmem_malloc_poid+0x34 00062008 1 00454718 libcmpin.so`pin_decimal+0x358 00062008 1 00454578 libcmpin.so`pin_decimal+0x358 00068008 1 0045a5c0 libcmpin.so`pin_decimal+0x640 00068008 1 0045a760 libcmpin.so`pin_decimal+0x640 00068008 1 0045a7c8 libcmpin.so`pin_decimal_clone+0x50 ---------------------------------------------------------------------- Total 18 buffers, 2400 bytes >
Aquí vemos que hay 18 buffers con leaks, empezaremos el análisis del primero (dirección bufctl: 00454780) con ::bufctl_audit
> 00454780::bufctl_audit ADDR BUFADDR TIMESTAMP THREAD CACHE LASTLOG CONTENTS 454780 4528c0 9f4b67977cc06e 1 62008 0 0 libumem.so.1`umem_cache_alloc+0x13c libumem.so.1`umem_alloc+0x60 libumem.so.1`malloc+0x28 libc.so.1`strdup+0xc libcmpin.so`pin_decimal_clone+0x8c libcmpin.so`pbo_decimal_copy+0x14 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xc0 fm_inv_pol.so`fm_inv_pol_demo_leaks_start_demo+0xe44 fm_inv_pol.so`fm_inv_pol_prep_invoice+0x11c fm_inv_pol.so`op_inv_pol_prep_invoice+0xf4 libcmpin.so`cm_pre_pcm_op+0x14f4 libcmpin.so`pcm_op_ex+0x390 fm_inv.so`fm_inv_make_invoice_exec_opcode_prep_invoice+0xb4 fm_inv.so`fm_inv_make_invoice+0x1a38 fm_inv.so`op_inv_make_invoice+0xe4 >
Mirando el stack trace, la entrada libumem.so.1`malloc en cada stack es la función que esta asignando la memoria al buffer leakeado. Lo que estamos analizando es nuestro código que se utiliza dentro de la librería fm_inv_pol.so, entonces el leak está en la primer línea en la que aparece el string fm_inv_pol.so (en este caso es fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xc0) y la línea anterior es la función de la PIN library que se está invocando dentro de nuestro código (libcmpin.so`pbo_decimal_copy+0x14).
Entonces por lo dicho en el párrafo de arriba podemos ver que dentro de la función fm_inv_pol_demo_leaks_pin_decimal_t se esta llamando a la función pbo_decimal_copy y esa es la memoria que estamos leakeando (conjungando la palabra inventada).
Lo que queda por hacer ahora es buscar donde está la definición de la función fm_inv_pol_demo_leaks_pin_decimal_t entre los archivos que conforman la librería fm_inv_pol.so. En este caso el archivo es el fm_inv_pol_demo_leaks.c y la función fm_inv_pol_demo_leaks_pin_decimal_t es:
void fm_inv_pol_demo_leaks_pin_decimal_t( pin_flist_t *i_flistp, pin_errbuf_t *ebufp) { pin_decimal_t *decimal1p = NULL; pin_decimal_t *decimal2p = NULL; pin_decimal_t *demo_create_decimalp = NULL; pin_decimal_t *demo_copy_decimalp = NULL; pin_decimal_t *demo_take_decimalp = NULL; pin_decimal_t *demo_add_decimalp = NULL; demo_create_decimalp = pbo_decimal_from_str("100", ebufp); decimal1p = PIN_FLIST_FLD_GET(i_flistp, PIN_FLD_AMOUNT, 0, ebufp); demo_copy_decimalp = pbo_decimal_copy(decimal1p, ebufp); decimal1p = PIN_FLIST_FLD_GET(i_flistp, PIN_FLD_DUE, 0, ebufp); decimal2p = PIN_FLIST_FLD_GET(i_flistp, PIN_FLD_TOTAL_DUE, 0, ebufp); demo_add_decimalp = pbo_decimal_add(decimal1p, decimal2p, ebufp); demo_take_decimalp = PIN_FLIST_FLD_TAKE(i_flistp, PIN_FLD_DUE, 0, ebufp); }
En éste caso solamente hay una invocación a la función pbo_decimal_copy (línea 116) por lo que resulta fácil saber que hay que liberar la memoria de la variable demo_copy_decimalp. Pero.... que sucedería en caso de que la función fm_inv_pol_demo_leaks_pin_decimal_t no fuese tan pequeña y que hubiese más de una llamada a la función pbo_decimal_copy y que hubiese muchos for, while con if anidados y dentro de ellos varias veces la llamada a pbo_decimal_copy, ahí la cosa no estaría tan facil de detectar. Por suerte dentro de mdb tenemos más comandos dcmd para buscar nuestro memory leak en el código fuente. Utilizaremos ::dis para desensamblar instrucciones cercanas al puntero del código actual (nos dará una línea cercana a donde se está produciendo el leak, a veces nos da la línea exacta, más adelante en este post mostraré cuando sí podremos obtener la línea exacta y cuando no).
Vamos a aplicar ::dis en la línea donde aparece por primer vez la librería fm_inv_pol.so. Lo cual nos da: fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xc0::dis (también puede ser fm_inv_pol_demo_leaks_pin_decimal_t+0xc0::dis es lo mismo).
> fm_inv_pol_demo_leaks_pin_decimal_t+0xc0::dis fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x98: st %o0, [%fp - 0x20] fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x9c: ld [%fp - 0x20], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xa0: ba +0xc <fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xac> fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xa4: st %l0, [%fp - 0x1c] fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xa8: clr [%fp - 0x1c] fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xac: ld [%fp - 0x1c], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xb0: st %l0, [%fp - 0x4] fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xb4: ld [%fp - 0x4], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xb8: ld [%fp + 0x48], %l1 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xbc: or %l0, %g0, %o0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xc0: call +0x274b604 <libcmpin.so`pbo_decimal_copy> fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xc4: or %l1, %g0, %o1 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xc8: st %o0, [%fp - 0x10] fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xcc: ld [%fp + 0x48], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xd0: ld [%l0 + 0x8], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xd4: cmp %l0, 0x0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xd8: bne +0x5c <fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x134> fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xdc: nop fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xe0: mov 0x76, %l1 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xe4: ld [%fp + 0x48], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0xe8: st %l1, [%l0 + 0x18] >Viendo el resultado del comando no se va a poder obtener la línea exacta del leak (las instrucciones no están centradas), pero sí una aproximación. En donde vemos la instrucción call es el punto donde se está invocando a la función que produce el leak (en este caso el <libcmpin.so`pin_poid_copy>), buscamos alguna instrucción mov y vemos un número en hexadecimal, dicho número es un número de línea cercano a donde se está produciendo el leak. En este caso la linea con el mov es mov 0x76, %l1. El número que nos interesa es el 0x76 que al convertilo a decimal nos da 118, entonces el leak estará en una llamada a la función pbo_decimal_copy cercana a la línea 118. Como el call está antes del mov que estamos analizando, entonces el leak estará antes de la línea 118. En nuestro ejemplo está en la línea 116.
Listo, ya hemos encontrado donde esta el leak en nuestro código fuente. Ahora analicemos el segundo buffer que nos dio el ::findleaks.(dirección bufctl: 0045a830) con ::bufctl_audit
> 0045a830::bufctl_audit ADDR BUFADDR TIMESTAMP THREAD CACHE LASTLOG CONTENTS 45a830 45c748 9f4b67977ce785 1 68008 0 0 libumem.so.1`umem_cache_alloc+0x13c libumem.so.1`umem_alloc+0x60 libumem.so.1`malloc+0x28 libcmpin.so`_alloc_result_header+0x18 libcmpin.so`_pin_decimal_add_mag+0x4cc libcmpin.so`pin_decimal_add+0x210 libcmpin.so`pbo_decimal_add+0xc fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1c8 fm_inv_pol.so`fm_inv_pol_demo_leaks_start_demo+0xe44 fm_inv_pol.so`fm_inv_pol_prep_invoice+0x11c fm_inv_pol.so`op_inv_pol_prep_invoice+0xf4 libcmpin.so`cm_pre_pcm_op+0x14f4 libcmpin.so`pcm_op_ex+0x390 fm_inv.so`fm_inv_make_invoice_exec_opcode_prep_invoice+0xb4 fm_inv.so`fm_inv_make_invoice+0x1a38 >
Siguiendo los pasos del buffer analizado anteriormente vemos que el leak está en la función fm_inv_pol_demo_leaks_pin_decimal_t al ejecutar la función pbo_decimal_add. Le aplicamos el ::dis a fm_inv_pol_demo_leaks_pin_decimal_t+0x1c8
> fm_inv_pol_demo_leaks_pin_decimal_t+0x1c8::dis fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1a0:ba +0xc <fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1ac> fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1a4:st %l0, [%fp - 0x2c] fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1a8:clr [%fp - 0x2c] fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1ac:ld [%fp - 0x2c], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1b0:st %l0, [%fp - 0x8] fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1b4:ld [%fp - 0x4], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1b8:ld [%fp - 0x8], %l1 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1bc:ld [%fp + 0x48], %l2 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1c0:or %l0, %g0, %o0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1c4:or %l1, %g0, %o1 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1c8:call +0x274b6d0 <libcmpin.so`pbo_decimal_add> fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1cc:or %l2, %g0, %o2 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1d0:st %o0, [%fp - 0x18] fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1d4:ld [%fp + 0x48], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1d8:ld [%l0 + 0x8], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1dc:cmp %l0, 0x0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1e0:bne +0x5c <fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x23c> fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1e4:nop fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1e8:mov 0x7a, %l1 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1ec:ld [%fp + 0x48], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_pin_decimal_t+0x1f0:st %l1, [%l0 + 0x18] >
En este caso tampoco obtendremos la dirección exacta(las instrucciones no están centradas), pero sí una aproximada, buscamos el call y el mov nuevamente. El mov esta después del call por lo que el leak estará antes del número hexadecimal del mov: mov 0x7a, %l1. El 0x7a en decimal es 122, entonces hay que buscar una llamada a la funcion pbo_decimal_add antes de la línea 122, en nuestro caso es la que se encuentra en la línea 120.
Ahora analicemos otro buffer (voy a utilizar uno que sí tenga la dirección exacta) el 13 de la lista arrojada por el ::findleaks. (dirección bufctl: 006355a8) con ::bufctl_audit
> 006355a8::bufctl_audit ADDR BUFADDR TIMESTAMP THREAD CACHE LASTLOG CONTENTS 6355a8 631b90 9f4b67977c50b4 1 6c008 0 0 libumem.so.1`umem_cache_alloc+0x210 libumem.so.1`umem_alloc+0x60 libumem.so.1`malloc+0x28 libcmpin.so`pcmmem_malloc_poid+0x34 libcmpin.so`pin_poid_create+0x70 libcmpin.so`pin_poid_copy+0x78 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x154 fm_inv_pol.so`fm_inv_pol_demo_leaks_start_demo+0xe30 fm_inv_pol.so`fm_inv_pol_prep_invoice+0x11c fm_inv_pol.so`op_inv_pol_prep_invoice+0xf4 libcmpin.so`cm_pre_pcm_op+0x14f4 libcmpin.so`pcm_op_ex+0x390 fm_inv.so`fm_inv_make_invoice_exec_opcode_prep_invoice+0xb4 fm_inv.so`fm_inv_make_invoice+0x1a38 fm_inv.so`op_inv_make_invoice+0xe4 >
Aquí vemos que el leak esta en la función fm_inv_pol_demo_leaks_poid_t al invocar pin_poid_copy. La funcion fm_inv_pol_demo_leaks_poid_t es:
void fm_inv_pol_demo_leaks_poid_t( pcm_context_t *ctxp, pin_flist_t *i_flistp, pin_errbuf_t *ebufp) { poid_t *poidp = NULL; poid_t *demo_create_poidp = NULL; poid_t *demo_copy_poidp = NULL; poid_t *demo_take_poidp = NULL; demo_create_poidp = PIN_POID_CREATE(PCM_GET_CURRENT_DB_NO(ctxp), "/account", -1, ebufp); poidp = PIN_FLIST_FLD_GET(i_flistp, PIN_FLD_POID, 0, ebufp); demo_copy_poidp = PIN_POID_COPY(poidp, ebufp); demo_take_poidp = PIN_FLIST_FLD_TAKE(i_flistp, PIN_FLD_ACCOUNT_OBJ, 0, ebufp); }
Analizaremos entonces fm_inv_pol_demo_leaks_poid_t+0x154 con ::dis
> fm_inv_pol_demo_leaks_poid_t+0x154::dis fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x12c: mov 0x5f, %l1 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x130: ld [%fp + 0x4c], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x134: st %l1, [%l0 + 0x18] fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x138: sethi %hi(0xfb216c00), %l1 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x13c: or %l1, 0xf0, %l1 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x140: ld [%fp + 0x4c], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x144: st %l1, [%l0 + 0x1c] fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x148: ld [%fp - 0x4], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x14c: ld [%fp + 0x4c], %l1 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x150: or %l0, %g0, %o0 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x154: call +0x27445ac <libcmpin.so`pin_poid_copy> fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x158: or %l1, %g0, %o1 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x15c: st %o0, [%fp - 0x28] fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x160: ld [%fp - 0x28], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x164: ba +0xc <fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x170> fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x168: st %l0, [%fp - 0x24] fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x16c: clr [%fp - 0x24] fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x170: ld [%fp - 0x24], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x174: st %l0, [%fp - 0xc] fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x178: ld [%fp + 0x4c], %l0 fm_inv_pol.so`fm_inv_pol_demo_leaks_poid_t+0x17c: ld [%l0 + 0x8], %l0 >
En este caso el ::dis nos está dando la línea exacta donde se encuentra el leak (vemos que las instrucciones están centradas), entonces la línea es la "mov 0x5f, %l1" que en decimal es 95. Efectivamente viendo el código se ve que en la línea 95 está la invocación al PIN_POID_COPY y ahí es donde se produce el leak (se debe liberar la memoria de la variable demo_copy_poidp).
Hasta aquí ya hemos visto como con el archivo core utilizando mdb podemos llegar a la línea del código fuente donde se está produciendo la asignación de memoria que luego no se libera. En el próximo post se tratará la detección de memory leaks en aplicaciones que utilizan el framework MTA de BRM.
Un par de tips antes de finalizar el post:
- ::findleaks -dv es como ejecutar ::bufctl_audit para todos los buffers.
- Si tenemos nuestro archivo core para una inspección rápida sobre una librería o función en particular podemos utilizar el siguiente comando: echo "::findleaks -d " | mdb archivo_core | grep mi_libreria.so, para el caso del ejemplo que hemos visto en este post: echo "::findleaks -d " | mdb core.29470 | grep fm_inv_pol.so
- Cuando estemos utilizando el dcmd ::dis y no encontremos la instrucción mov, ejecutarlo nuevamente pero con los parametros -n cantidad_instrucciones. Ejemplo: fm_inv_pol_demo_leaks_pin_decimal_t+0xc0::dis -n 40 (mostrará 40 instrucciones antes de la dirección y 40 despues de la instrucción, por defaulr el dcmd ::dis muestra 10 instrucciones antes y 10 después de la dirección y no siempre traen el mov)
Martín Falconi
No hay comentarios.:
Publicar un comentario