Como puedo depurar un fallo de asignacion de memoria en C++???

Iniciado por kafok, 9 Noviembre 2014, 19:55 PM

0 Miembros y 1 Visitante están viendo este tema.

kafok

Bueno llevo unos dias investigando a tope, he cambiado de IDE ya que la ultima me estaba dando muchos problemas y tambien llevo estos dias testeando las nuevas cosas. Pues resulta que cuando copilo en este nuevo entorno resulta que se rompe pero en el delete no en el new, y digo yo ¿Por que se rompe cuando llamo a los metodos desde una dll (importada estaticamente) y no si cojo el mismo codigo y lo copilo dentro de un proyecto independiente?

[b]Nota:[/b] he sobrecargado los operadores new y delete para guardar los punteros de los nuevos objeto en un set de la api std. Esto lo hice como una especie de recolector de basura con el fin de que me avisara si se me olvida un delete porque me hace mas los test de las clases

Despues de mucho tocar el codigo, depurar, etc... resulta que es que al borrar un elemento del set es cuando rompe, new y delete funcionan bien, solo falla si quito del set el elemento que reciba delete. Pero claro, el codigo esta bien pero al aislarlo en una dll y cargarlo desde otro proyecto sin ni siquiera tocar una linea de codigo me desconcierta, ¿Por que falla en un sitio si y en otro no (reitero)?

PD: Puede que depurar esto sea facil, pero es la primera vez que me enfrento a algo que hay que mirar en tan bajo nivel :(

Por cierto, el debbuger de ahora me dice que el procesador tira la señal SIGSEV, por si sirve de algo

Eternal Idol

Con la informacion que tenemos solo podemos adivinar. Puede ser que uses diferentes heaps para reservar y liberar. ¿Estas haciendo el new desde la DLL y el delete desde el programa (o viceversa)?
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

kafok

Haber, creo un objeto de mi clase con new desde el codigo de mi programa, vamos que no esta llamado desde ningun metodo ni nada por el estilo, el codigo seria tal que asi:

Código (cpp) [Seleccionar]
MiClase *c = new MiClase();

He de decir que new y delete son sobrecargados en una clase padre que llame Object y que debe heredarse en todas las clases hijas que quieran implementar esto, es decir "MiClase" es hija de Object. No se si esa informacion es de interes, pero la cuento por si acaso.

La intencion de esto es que pueda dejar sin liberar la memoria que yo quiera y luego cuando copile se me informe de que hay memoria no liberada que libera otra clase que es la que contiene el set. Esta clase es un singleton y tiene varios metodos entre ellos añadir y eleminar elementos del set, y uno en especial que recorre el set eleminando sus elementos y llamando al delete del objeto. Este metodo esta pensado para llamarlo al final del programa y que lo que no se haya liberado (que siga en el set) lo elimine y lo imprima por pantalla.

Si llamas al delete desde el codigo de la aplicacion se elimina el elemento del set. En la implementacion de esto cuando se llama a delete este llama a "erase" y "erase" a delete, lo se, ese problema de recursividad esta solucionado.

Entonces delete es llamado desde ese metodo y desde la aplicacion, es decir los que no llame la aplicacion los llamara el metodo. El problema que ambos tienen que hacer un al metodo erase de set del std y es esa linea la que falla.

Ahora, te pregunto una cosa, lo que he encontrado en internet todos dicen que es que el set se hayan rotos los punteros interno que usa el arbol binario de la implementacion (o por lo menos creo que funciona asi), y tu me has hablado algo de heaps, no se... me da que puede haber hay un hilo por donde tirar, ¿Que son heaps? Detallame que es lo que puede pasar con ellos.

PD: ahora espero haberte hecho una descripcion mas profunda del problema y que a si podamos llegar a una solucion



Ante todo, muchisimas gracias por la ayuda, no se que hubiera hecho yo solo...  :huh:

Eternal Idol

#13
No respondiste mi pregunta concretamente: ¿Estas haciendo el new desde la DLL y el delete desde el programa (o viceversa)? Se que llamas a new desde tu programa pero no si en algun caso el delete correspondiente lo llamas desde la DLL.

Allocating and freeing memory across module boundaries.

Code, Stack, Data y Heap en la ejecución de programas.

Lo que puede pasar es que el heap del cual reserves no sea el mismo del cual liberes, por ejemplo si tu programa llama a new y una DLL llamada a delete.

Heap Functions.




Otra vez vuelvo a los metodos sencillos de analizar el problema que te deje:

1) Enabling Postmortem Debugging

2) Application Verifier puede ayudar mucho a simplificar la investigacion, dependieno del problema de origen es capaz de detectarlo incluso antes de que termine por explotar.

PD. Liberar la memoria al terminar el programa es una absoluta perdida de tiempo de procesamiento, el S.O. se encarga de esa tarea, solamente sirve para diagnosticar memory leaks (es decir para que uno vea que se olvido de liberar en su momento y lo libere justo cuando ya no sera mas usadoq, no cuando ya no sirve de nada hacerlo).
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

kafok

Haber estuve leyendo y estudiando los links. Yo sabia que los programas utilizaban un espacio de memoria especial para la asignacion dinamica, vamos que eso es el heap. Pero vamos, entonces cada modulo tiene un heap distinto? Es decir, si cargo una dll todos los new dentro de los metodos se crearan en su heap y los new de mis programas en su propio heap? y el heap de la dll es compartido por todas las aplicaciones que utilicen la dll?

Entonces ¿Que limitaciones tengo al usar memoria dinamica al usar una dll? No me quedo muy claro en esas paginas.

Tu pregunta: los new los llamo desde la aplicacion y los delete desde la dll, aunque eso puede cambiar, porque esta pensado que los delete que no diga yo, los haga la dll al final, aunque de momento solo lo hace la dll.

Otra cuestion: Lo que yo tengo entendio es que la memoria que no liberes se queda como basura en el sistema hasta que reinicies, eso es lo que tengo entendido como memory leak. Pero tu que dices, que la memoria que no haya liberado la libera el sistema operativo cuando termina el programa? es decir, que la memoria que se libere tiene que ser liberada en el momento en el que deja de utilizarse, que si se elimina toda al final del programa es trabajar de mas porque el sistema operativo ya los hace?

Otra vez, muchas gracias por tu tiempo

Eternal Idol

Cita de: kafok en 15 Noviembre 2014, 03:58 AM
Haber estuve leyendo y estudiando los links. Yo sabia que los programas utilizaban un espacio de memoria especial para la asignacion dinamica, vamos que eso es el heap. Pero vamos, entonces cada modulo tiene un heap distinto? Es decir, si cargo una dll todos los new dentro de los metodos se crearan en su heap y los new de mis programas en su propio heap? y el heap de la dll es compartido por todas las aplicaciones que utilicen la dll?

Si, si la DLL ofrece un metodo de reserva y de liberacion usara su heap.

Cita de: kafok en 15 Noviembre 2014, 03:58 AMEntonces ¿Que limitaciones tengo al usar memoria dinamica al usar una dll? No me quedo muy claro en esas paginas.

Se libera donde se reserva, si lo vas a hacer en la DLL exporta dos funciones para hacer ambas cosas.

Cita de: kafok en 15 Noviembre 2014, 03:58 AMTu pregunta: los new los llamo desde la aplicacion y los delete desde la dll, aunque eso puede cambiar, porque esta pensado que los delete que no diga yo, los haga la dll al final, aunque de momento solo lo hace la dll.

No, si el programa reserva en su heap tiene que liberar en su heap, no puede liberar la DLL desde el suyo o hay problemas como el que estas viendo ahora mismo ...

Cita de: kafok en 15 Noviembre 2014, 03:58 AMOtra cuestion: Lo que yo tengo entendio es que la memoria que no liberes se queda como basura en el sistema hasta que reinicies, eso es lo que tengo entendido como memory leak. Pero tu que dices, que la memoria que no haya liberado la libera el sistema operativo cuando termina el programa? es decir, que la memoria que se libere tiene que ser liberada en el momento en el que deja de utilizarse, que si se elimina toda al final del programa es trabajar de mas porque el sistema operativo ya los hace?

Si, es exactamente como te dije. Lo que tenes que hacer es liberar la memoria cuando corresponde y ese momento es exactamente cuando deja de ser necesaria, cuando ya no sera mas accedida hay que liberarla, no esperar hasta el fin de la ejecucion del proceso.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

kafok

Perdon por tardar en responder, he estado ocupado con los estudios  :-(

He estado haciendo mil pruebas, haber como reacciona las dll a algunas situaciones. Quiero destacar que mi dll es cargada estaticamente. He probado new y delete en distinto sitios (tanto en el exe como en la dll) y aunque han sido inicializadas en un sitio y liberado en otro la aplicacion no presento en ningún momento anomalias, funcionó bien, se creo y liberó memoria correctamente aun siendo declarados en distintos heaps. En la documentacion de windows solo nos ofrecen la funcion GetProcessHeap() que llamada desde la dll y el exe devuelve el mismo valor, porque es el mismo proceso. Pero no hay nada relacionado con el heap de cada modulo (el heap del exe y el de la dll) lo que da a entender que cada proceso tiene su propio heap, y este es compartido por la dll y el exe. Aqui hay una contradiccion, ¿Qué he dejado pasar por alto para haber obtenido estos resultados?

He leido que si sobrecargo los operadores new y delete y customizo la asignacion de memoria en un heap que haya creado o:

Código (cpp) [Seleccionar]
HeapAlloc(GetProcessHeap(), 0, size);

para el heap del proceso me curo en salud porque digo exactamente donde se guarda, pero... Sí guardo en un heap que no tengo acceso, ¿La aplicacion crashearia? Es decir, si estoy accediendo al heap del proceso, si no tenia permisos para borrar nada creado alli tampoco los tengo para crearlo, digo yo.

Por favor, que alguien arregle las barbaridades que estoy soltando

Eternal Idol

El tema es que ademas de GetProcessHeap existe HeapCreate y a esta funcion llama la RTL cuando se inicializa (un proceso puede tener mas de un heap). No, si hubieran estado en diferentes heaps pasaria lo que pasa con con el siguiente codigo:

Código (c++) [Seleccionar]
HANDLE anotherHeap = HeapCreate(0, 0x1000, 0x1000 * 1024);
LPVOID memoryBlock = HeapAlloc(anotherHeap, HEAP_ZERO_MEMORY, 0x1000);
HeapFree(GetProcessHeap(), 0, memoryBlock);


HEAP[heapcrash.exe]: Invalid address specified to RtlFreeHeap( 000000F1B8E00000, 000000F1B9170720 )
(de6c.ed7c): Break instruction exception - code 80000003 (first chance)

0:000> kb
RetAddr           : Args to Child                                                           : Call Site
00007fff`6ad05a8e : 00007fff`6acef650 00007fff`6acef520 000000f1`b8e00000 000000f1`b9170720 : ntdll!RtlpBreakPointHeap+0x1d
00007fff`6ad419e5 : 000000f1`b8e00000 00000000`00000000 00000000`00000000 01000000`00000080 : ntdll!RtlpValidateHeapEntry+0x5429a
00007fff`6acfbdaf : 000000f1`b8e00000 00000000`50000063 000000f1`b9170000 00007fff`6ad41706 : ntdll!RtlDebugFreeHeap+0xb9
00007fff`6ac829f8 : 000000f1`b8e00000 000000f1`b9170720 000000f1`b9170710 000000f1`b9170710 : ntdll!RtlpFreeHeap+0x7908f
*** WARNING: Unable to verify checksum for heapcrash.exe
00007ff7`9749105d : 00000000`00000001 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlFreeHeap+0x428
00007ff7`974911b8 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : heapcrash!main+0x4d [d:\src\heapcrash.cpp @ 9]
00007fff`6a8016ad : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : heapcrash!__tmainCRTStartup+0x144 [f:\dd\vctools\crt\crtw32\startup\crt0.c @ 255]
00007fff`6aca4409 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0xd
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d


Se produce una excepcion no controlada; si liberaramos en anotherHeap no habria ninguna excepcion.

Y si, por logica si sobrecargas de esa manera (HeapFree tambien con GetProcessHeap por supuesto) no vas a tener problemas, siempre que se use el mismo heap para reservar y liberar un determinado bloque de memoria.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

kafok

Entonces, si sobrecargo el new y delete y utilizo el GetProcessHeap() por ejemplo, para asignar la memoria y liberarla, aunque llame a new desde el exe podre liberarlo desde el dll, y viceversa? De esa forma, me ahorraria todos los poblemas que me han surgido asignando memoria. Y si creo un heap para la dll seria exactamente igual solo que podria personalizar algunas cosas no?

Muchas gracias, me estas ayudando un monton

Eternal Idol

Si; siempre que se use el mismo heap - no importa cual - para reservar y liberar un bloque de memoria determinado no habra problemas.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón