Corrupción de memoria?

Estoy intentando hacer un port de un juego de pc a la nintendo DS. El juego en pc funciona en perfectas condiciones pero evidentemente el port puede dar problemas debido a la limitación de la consola.

La situación es la siguiente. El juego carga y se inicia con normalidad. Al cabo de "un rato", concretamente suele pasar después de pasar la primera fase y cargar la segunda, se empiezan a producir petes por todos lados.

Hay dos tipos distintos y no se por qué se deben producir por el mismo motivo. Uno es en el malloc, concrentamente en _malloc_r, cosa muy pero que muy rara ya que es el código base de las librerías y que se produzca un error así. Este error suele aparecer al cargar el nivel. Hay control en toda la aplicación, así que en caso de que malloc devuelva NULL me mostraría un mensaje. Sin embargo la aplicación revienta y salta la excepción dentro del propio malloc.

La segunda se producía en un null pointer justo cuando tocaba una moneda (es decir, o al borrar, o al crear los efectos especiales). Al dibujar el dibujo, se accede a un puntero en la estructura que guarda la imagen. Lo gracioso es que no haya nada sin inicializar. Quiero recordar que la aplicación funciona perféctamente en pc y el código es el mismo.

Otra vez me salió un poco antes, en el método que recorre todos los objetos a dibujar. Pude capturar -por casualidad- el contenido del objeto corrupto. No tenía un dato bien! hay unos char que me sirven para indicar que animación debe tener. Pues en lugar de tener "22", que es el valor que le corresponde, tenía 255-22, como si estubiese invertido. Los otros valores estaban a su bola, con enteros de longitud infinita o negativa.

Lo problematico es que el resto de elementos está correctamente. Me da la impresión de que al corromper estos datos, los punteros apuntan a direcciones de memoria prohibidas o inexistentes y por eso casca aleatoriamente cuando cojo una moneda o cuando cambio de nivel.

Me gustaría aber si esto es posible. Es posible que llegue un momento en los que se apunte mal a la memoria, no se inicialice bien o cualquier motivo por un exceso de datos o un mal control de la vram o lo que sea? Es que estoy loco.
Vale, voy a responder, pero puede que lo que diga sea bastante trivial y ya lo hayas tenido en cuenta.

Con respecto a los malloc...¿haces malloc() sin parar? me explico, mi pregunta es si haces muchos malloc sin haber realidado un free entre algunas de esas llamadas, es probable que te hayas comido toda la memoria de la consola o que incluso tengas por algun lado alguna fuga de memoria y ya no puedas hacer mas malloc.

En cuanto a lo de los valores que comentas, no se si habrás tenido en cuenta que PC y DS son dos plataformas distintas y que pueden tener un endianness distinto, PC estoy seguro de que es little endian, pero DS no se si es big endian con lo cual ahi podrias tener tus problemas con los numeros si los usas de algun modo raro (como leer numeros de archivos generados en pc o algo asi)

Suerte y con el sueño que tengo espero no haberlo liado mas o haber dicho alguna barbaridad, disculpas por si acaso

Un saludo
Respecto a la memoria, no, uso 2,2 megas aproximadamente de los 4 disponibles. Al menos eso es lo que me dice el gestor de memoria. Es posible que se queden los datos dispersos y no encuentre espacio para guardar algo grande? Es posible, pero si pasa eso malloc devuelve NULL, no peta.

Y lo del endian, ds y pc usan el little endian, igualmente no es problema porque durante el primer momento del juego todo tiene su valor correcto. Es al cabo de un rato cuando todo se estropea. Creo que es por exceso de mallocs. Estoy por olvidarme de las listas de objetos dinámicas y hacerlas estáticas y recargar los datos directamente sobre la memoria ya reservada. Aun así la lectura de disco se realiza mediante mallocs.


Me he dado cuenta de que no es azaroso. Siempre ocurre después de liberar memoria:
-Si cargo un nuevo nivel, debido a que aún no controlo los cambios, me aparece la pantalla antigua y se ve como los tiles se recargan. Siempre que se bloquea lo hace antes de que se carguen los tiles, es decir, al borrar algo.

-Solo me ha pasado en dos situaciones distintas en medio del nivel, aunque se repiten constantemente. Estas no me dan en el malloc, me lo dan al intentar acceder a un puntero corrupo (porque según me ha demostrado la aplicación, el puntero a veces no es null, pero casca al acceder). Me sucedío en una sola ocasión al lanzar una bola de fuego, pero ya estaba lanzada, era más bien cuando las partículas del efecto especial de la estela empezaron a destruirse. En el resto de ocasiones me sucede al tocar una moneda.

Alguien sabe algo al respecto? Casos parecidos? Ideas de lo que puede o no puede estar pasando?
Puede que se deba a un fallo en el propio malloc, como pasaba por ejemplo, en PS2. Pero también puede deberse a una corrupción del montículo de memoria.

Muchas veces, este tipo de fallos, se deben al uso de la caché, sobre todo si un buffer se usa para transferencias DMA o interprocesador y no se ha tenido en cuenta, no solo la alineación del buffer, si no adaptar el tamaño del buffer para que las operaciones de cache no se solapen con otras cosas (por ejemplo, los ARM manejan la cache en bloques de 32 bytes).

Por lo general, cuando asignas un bloque de memoria, se suele añadir unos bytes antes, una estructura que sirve para apuntar al siguiente montículo (ya sea libre u ocupado). Si hay un puntero loco o que se pasa de límite, es probable que corrompas alguna de esas estructuras y se derrumbe todo el sistema de asignaciones, pero también es posible que haya un bug que haga que no se libere bien esa memoria, o que no trabaje bien cuando esta se fragmenta demasiado.

Si no das con el problema, tal vez te venga mejor administrar tu mismo la memoria: haces un malloc con la memoria que vayas a necesitar y tu mismo te ocupas de repartirla como mejor te parezca y cuando tengas que liberarla, no usas el free, si no que vuelves a utilizar la memoria desde el puntero original.

Vamos, algo así como un falso malloc que vaya asignando memoria de forma secuencial (además, ganaras algo de velocidad porque no habrá que ir recorriendo todas las estructuras de asignación buscando memoria libre cada vez que hagas "malloc")
Me parece que es posible que el fallo esté en lo que me dices. He cambiado la memoria dinámica por arrays estáticos y parece que en los niveles ha dejado de fallar, pero me sigue fallando al cambiar de nivel, donde leo fichero de disco y creo los nuevos elementos.

Lo extraño es que no utilizo estructuras mal alineadas durante la carga, es más, para comprobar que no es un error de pila, he sacado el objeto a memoria y al leer de fichero vuelco sobre el objeto ya existente.

El problema de encontrar el error es que la aplicación no falla hasta que no se intenta acceder a un trozo de memoria corrupta. Es posible que esté fallando a la hora de cargar elementos en la vram?
Ya te comenté que yo creo que es corrupción de la pila. El no$gba en uno de esos crasheos me dijo que la ejecución había pasado a la VRAM, seguramente por culpa de algún retorno de alguna función en el que pilla un puntero en la pila de por medio.

Y lo que dice Hermes de malloc, no me extrañaría nada. Yo por culpa del handler de interrupciones de libnds tenía fallos en malloc. Se ve que al interrumpirse malloc el handler hacía alguna cosa rara y me volvía locos los punteros. Para probar, desactivé las interrupciones antes de llamar a malloc y las activé al acabar y no me volvió a fallar. Si no me equivoco, Hermes, te oí hablar de un bug en ese handler alguna vez, puede que sea el que me hacia fallar a mi. No me sorprendería que malloc tuviera algún fallo también.

Prueba poniendo REG_IME = 0 antes de llamar a fread o similares y después pones REG_IME = 1.
ANTONIOND escribió:Si no me equivoco, Hermes, te oí hablar de un bug en ese handler alguna vez, puede que sea el que me hacia fallar a mi. No me sorprendería que malloc tuviera algún fallo también.


Pues si, hace la tira, cuando desarrollé la librería multithread para NDS, me dí cuenta que estos pollos, no dedicaban una pila exclusiva para las interrupciones, con lo que se podía corromper la pila de las aplicaciones.

Ese fallo lo reporté junto con la publicación de mis librerías, pero esos listos de Devkitpro, pasaron de la librería Multithread y probablemente, no hayan arreglado ese tema tampoco... por lo que como tu comentas, en el malloc por ejemplo, te pueden afectar las interrupciones (de hecho te puede afectar en cualquier función)

Ya sabes que yo llevo ya como un par de años fuera del tema, así que he supuesto que ese problema con las interrupciones, ya estaba resuelto y no he pensado en ello.
En conclusión: malloc no funciona bien. Alguna alternativa?
Blue escribió:En conclusión: malloc no funciona bien. Alguna alternativa?


Si es lo que dice ANTONIOND de las interrupciones, no es que malloc no funcione bien, es que las interrupciones te pueden corromper las variables de cualquier función que se alojen en el stack y la única forma de solventar eso, es crear una nueva función que trate las interrupciones asignándose una pila propia.

Yo hace eones que no trabajo para NDS así que no se que opciones hay para poder adaptar mis eLibs (soporte multithread y lo mas completo que había para trabajar con sonido/ capturar de micrófono en aquella época)
Hermes escribió:Yo hace eones que no trabajo para NDS así que no se que opciones hay para poder adaptar mis eLibs (soporte multithread y lo mas completo que había para trabajar con sonido/ capturar de micrófono en aquella época)

Yo lo he intentado un par de veces (sólamente la de multithreading, hay librerías de audio bastante mas avanzadas ahora mismo como maxmod, y libnds ya tiene soporte para micrófono y hasta para PSG) y comprobé que se fastidiaba el invento en cuanto asignaba el nuevo handler de interrupiones. Bastante frustrante, si... [tomaaa] Hay que hacer también un apaño para que funcione el sistema de IPC que usas en la lib, pero es cambiar un par de defines copiándolos de libnds, aunque se podría cambiar del todo para que use el sistema de FIFO de libnds... Por eso yo solamente intenté que funcionara el multithreading en el ARM9, y ni con esas conseguí avances importantes...
La cosa es que ya probé lo de REG_IME pero el resultado era el mismo. Realmente frustrante XD

Como se cuando se realizan solas esas interrupciones? A lo mejor me da una pista de lo que está fallando.
Solo tienes activa la interrupción de vblank. ¿Has probado con irqDisable(IRQ_VBLANK) / irqEnable(IRQ_VBLANK)? Igual es que al leer los datos activa las interrupciones...
Mira lo que dicen aquí:
http://forum.gbadev.org/viewtopic.php?t=16862

Confirma lo de que malloc es un asco, y que deberías implementar otro sistema.
13 respuestas