Seguimiento de las solicitudes de asignación en el montón
Este tema se aplica a:
Edición |
Visual Basic |
C# |
F# |
C++ |
Web Developer |
---|---|---|---|---|---|
Express |
Sólo para código nativo |
||||
Pro, Premium y Ultimate |
Sólo para código nativo |
Aunque la determinación exacta del nombre del archivo de código fuente y el número de línea en el que una aserción o una macro de informe se ejecuta suele ser muy útil para localizar la causa de un problema, esto no es aplicable siempre en el caso de funciones de asignación de memoria en el montón. Mientras que las macros se pueden insertar en muchos puntos apropiados del árbol lógico de una aplicación, una asignación de memoria se suele incluir en una rutina especial a la que se llama desde muchos sitios diferentes y en muchos momentos diferentes. Normalmente, la cuestión no es qué línea de código realizó una asignación incorrecta, sino cuál de las miles de asignaciones efectuadas por esa línea de código fue incorrecta y por qué.
Números de solicitud de asignación únicos y _crtBreakAlloc
La manera más simple de identificar la llamada de asignación incorrecta consiste en aprovechar el número de solicitud de asignación único asociado con cada bloque del montón de depuración. Cuando alguna de las funciones de volcado proporciona información sobre un bloque, este número de solicitud de asignación aparece encerrado entre llaves (por ejemplo, "{36}").
Una vez que se conoce el número de solicitud de asignación de un bloque asignado incorrectamente, se puede pasar este número a _CrtSetBreakAlloc para crear un punto de interrupción. La ejecución se interrumpe justo antes de asignar el bloque y se puede realizar un seguimiento hacia atrás para determinar qué rutina fue la responsable de la llamada errónea. Para evitar tener que volver a compilar, se puede conseguir lo mismo en el depurador si se asigna a _crtBreakAlloc el número de solicitud de asignación que interesa.
Crear versiones de depuración de las rutinas de asignación
Un enfoque algo más complicado consiste en crear versiones de depuración de las propias rutinas de asignación, comparables a las versiones _dbg de las funciones de asignación en el montón. A continuación, se pueden pasar como argumentos el archivo de código fuente y el número de línea a las rutinas subyacentes de asignación en el montón; entonces, se podrá ver inmediatamente dónde se originó cualquier asignación incorrecta.
Por ejemplo, suponga que la aplicación contiene una rutina utilizada habitualmente similar a la siguiente:
int addNewRecord(struct RecStruct * prevRecord,
int recType, int recAccess)
{
// ...code omitted through actual allocation...
if ((newRec = malloc(recSize)) == NULL)
// ... rest of routine omitted too ...
}
En un archivo de encabezado, se podría agregar código como el siguiente:
#ifdef _DEBUG
#define addNewRecord(p, t, a) \
addNewRecord(p, t, a, __FILE__, __LINE__)
#endif
A continuación, se podría cambiar la asignación en la rutina de creación de registros del siguiente modo:
int addNewRecord(struct RecStruct *prevRecord,
int recType, int recAccess
#ifdef _DEBUG
, const char *srcFile, int srcLine
#endif
)
{
/* ... code omitted through actual allocation ... */
if ((newRec = _malloc_dbg(recSize, _NORMAL_BLOCK,
srcFile, scrLine)) == NULL)
/* ... rest of routine omitted too ... */
}
Ahora, el nombre del archivo de código fuente y el número de línea donde se llamó a addNewRecord se almacenarán en cada bloque resultante asignado en el montón de depuración y se informará de ellos al examinar el bloque.