Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Si está depurando un programa MFC, estas técnicas de depuración pueden resultar de utilidad.
AfxDebugBreak
MFC proporciona una función AfxDebugBreak especial para puntos de interrupción de codificación rígida en el código fuente:
AfxDebugBreak( );
En las plataformas Intel, AfxDebugBreak
genera el código siguiente, que se interrumpe en el código fuente en lugar de en el código kernel:
_asm int 3
En otras plataformas, AfxDebugBreak
simplemente llama a DebugBreak
.
Asegúrese de quitar las instrucciones AfxDebugBreak
cuando genere una versión de lanzamiento, o bien utilice #ifdef _DEBUG
para encerrarlas.
La macro TRACE
Para mostrar mensajes del programa en la ventana Salida del depurador, puede usar la macro ATLTRACE o la macro TRACE de MFC. Al igual que las aserciones, las macros de seguimiento solo están activas en la versión de depuración del programa y desaparecen cuando se compilan en la versión de lanzamiento.
En los ejemplos siguientes se muestran algunas de las formas en que puede usar la macro TRACE . Al igual que printf
, la macro TRACE puede controlar varios argumentos.
int x = 1;
int y = 16;
float z = 32.0;
TRACE( "This is a TRACE statement\n" );
TRACE( "The value of x is %d\n", x );
TRACE( "x = %d and y = %d\n", x, y );
TRACE( "x = %d and y = %x and z = %f\n", x, y, z );
La macro TRACE controla correctamente los parámetros char* y wchar_t*. En los ejemplos siguientes se muestra el uso de la macro TRACE junto con distintos tipos de parámetros de cadena.
TRACE( "This is a test of the TRACE macro that uses an ANSI string: %s %d\n", "The number is:", 2);
TRACE( L"This is a test of the TRACE macro that uses a UNICODE string: %s %d\n", L"The number is:", 2);
TRACE( _T("This is a test of the TRACE macro that uses a TCHAR string: %s %d\n"), _T("The number is:"), 2);
Para obtener más información sobre la macro TRACE , vea Diagnostic Services.
Detección de fugas de memoria en MFC
MFC proporciona clases y funciones para detectar memoria que se ha asignado pero nunca se ha liberado.
Seguimiento de asignaciones de memoria
En MFC, puede usar la macro DEBUG_NEW en lugar del nuevo operador para ayudar a localizar fugas de memoria. En la versión de depuración del programa, DEBUG_NEW
realiza un seguimiento del nombre de archivo y el número de línea de cada objeto que asigna. Cuando se compila una versión de lanzamiento de programa, DEBUG_NEW
se resuelve como una simple operación new sin la información de nombre de archivo y número de línea. De este modo, el rendimiento de la versión de lanzamiento no disminuye.
Si no desea volver a escribir todo el programa para usarlo DEBUG_NEW
en lugar de nuevo, puede definir esta macro en los archivos de origen:
#define new DEBUG_NEW
Al realizar un volcado de objetos, cada objeto asignado con DEBUG_NEW
mostrará el archivo y el número de línea donde fue asignado, permitiéndole identificar las fuentes de pérdidas de memoria.
La versión de depuración del marco de trabajo de MFC utiliza DEBUG_NEW
automáticamente, pero el código no lo hace. Si desea las ventajas de DEBUG_NEW
, debe usar DEBUG_NEW
explícitamente o #define nuevo como se muestra anteriormente.
Habilitación de diagnósticos de memoria
Para poder utilizar los servicios de diagnóstico de memoria, se debe habilitar la traza con diagnósticos.
Para habilitar o deshabilitar diagnósticos de memoria
Llame a la función global AfxEnableMemoryTracking para habilitar o deshabilitar el asignador de memoria de diagnóstico. Dado que los diagnósticos de memoria están activados de forma predeterminada en la biblioteca de depuración, normalmente usará esta función para desactivarlos temporalmente, lo que aumenta la velocidad de ejecución del programa y reduce la salida de diagnóstico.
Para seleccionar características de diagnóstico de memoria específicas con afxMemDF
Si desea un control más preciso sobre las características de diagnóstico de memoria, puede activar y desactivar de forma selectiva las características de diagnóstico de memoria individuales estableciendo el valor de la variable global de MFC afxMemDF. Esta variable puede tener los siguientes valores según lo especificado por el tipo enumerado afxMemDF.
Importancia Descripción allocMemDF Active el asignador de memoria de diagnóstico (valor predeterminado). delayFreeMemDF Retrasar la liberación de memoria al llamar a delete
ofree
hasta que el programa salga. Esto hará que el programa asigne la cantidad máxima de memoria posible.checkAlwaysMemDF Llame a AfxCheckMemory cada vez que se asigne o libere memoria. Estos valores se pueden usar en combinación realizando una operación lógico-OR, como se muestra aquí:
afxMemDF = allocMemDF | delayFreeMemDF | checkAlwaysMemDF;
Tomar instantáneas de la memoria
Cree un objeto CMemoryState y llame a la función miembro CMemoryState::Checkpoint . Esto crea la primera instantánea de memoria.
Después de que el programa realice sus operaciones de asignación y desasignación de memoria, cree otro
CMemoryState
objeto y llameCheckpoint
a ese objeto. Esto obtiene una segunda instantánea del uso de memoria.Cree un tercer
CMemoryState
objeto y llame a su función miembro CMemoryState::Difference para proporcionar como argumentos los dos objetos anterioresCMemoryState
. Si hay una diferencia entre los dos estados de memoria, laDifference
función devuelve un valor distinto de cero. Esto indica que algunos bloques de memoria no se han desasignado.En este ejemplo se muestra el aspecto del código:
// Declare the variables needed #ifdef _DEBUG CMemoryState oldMemState, newMemState, diffMemState; oldMemState.Checkpoint(); #endif // Do your memory allocations and deallocations. CString s("This is a frame variable"); // The next object is a heap object. CPerson* p = new CPerson( "Smith", "Alan", "581-0215" ); #ifdef _DEBUG newMemState.Checkpoint(); if( diffMemState.Difference( oldMemState, newMemState ) ) { TRACE( "Memory leaked!\n" ); } #endif
Observe que las instrucciones de comprobación de memoria están enmarcadas por bloques #ifdef _DEBUG / #endif, de modo que solo se compilan en versiones de depuración del programa.
Ahora que sabe que existe una pérdida de memoria, puede usar otra función miembro, CMemoryState::DumpStatistics que le ayudará a localizarla.
Visualización de estadísticas de memoria
La función CMemoryState::Difference examina dos objetos de estado de memoria y detecta cualquier objeto no desasignado del montón entre los estados inicial y final. Después de tomar instantáneas de la memoria y compararlas mediante CMemoryState::Difference
, puede llamar a CMemoryState::DumpStatistics para obtener información sobre los objetos que no se han desasignado.
Considere el ejemplo siguiente:
if( diffMemState.Difference( oldMemState, newMemState ) )
{
TRACE( "Memory leaked!\n" );
diffMemState.DumpStatistics();
}
Un volcado de memoria de ejemplo tiene el siguiente aspecto:
0 bytes in 0 Free Blocks
22 bytes in 1 Object Blocks
45 bytes in 4 Non-Object Blocks
Largest number used: 67 bytes
Total allocations: 67 bytes
Los bloques libres son bloques cuya desasignación se retrasa si afxMemDF
se estableció en delayFreeMemDF
.
Los bloques de objetos ordinarios, que se muestran en la segunda línea, permanecen asignados en el montón.
Los bloques que no son objetos incluyen matrices y estructuras asignadas con new
. En este caso, se asignó memoria en el montón para cuatro bloques que no son objetos, pero esa memoria no se desasignó.
Largest number used
proporciona la memoria máxima utilizada por el programa en cualquier momento.
Total allocations
proporciona la cantidad total de memoria utilizada por el programa.
Realizar volcados de memoria de objetos
En un programa MFC, puede usar CMemoryState::DumpAllObjectsSince para volcar una descripción de todos los objetos en el montón que no se han desasignado.
DumpAllObjectsSince
vuelca todos los objetos asignados desde el último CMemoryState::Checkpoint. Si no se realizó ninguna llamada a Checkpoint
, DumpAllObjectsSince
produce un volcado de memoria de todos los objetos y elementos que no sean objetos actualmente en memoria.
Nota:
Para poder utilizar el volcado de objetos MFC, debe habilitar el seguimiento de diagnóstico.
Nota:
MFC produce automáticamente un volcado de memoria de todos los objetos no desasignados (pérdidas) cuando el programa termina, de modo que no es necesario crear código para volcar objetos en ese punto.
El código siguiente comprueba si hay una pérdida de memoria comparando dos estados de memoria y volca todos los objetos si se detecta una pérdida.
if( diffMemState.Difference( oldMemState, newMemState ) )
{
TRACE( "Memory leaked!\n" );
diffMemState.DumpAllObjectsSince();
}
El contenido del volcado de memoria presenta el siguiente aspecto:
Dumping objects ->
{5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long
{4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long
{3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long
{2} a CPerson at $51A4
Last Name: Smith
First Name: Alan
Phone #: 581-0215
{1} strcore.cpp(80) : non-object block at $00A7516E, 25 bytes long
Los números entre llaves al principio de la mayoría de las líneas especifican el orden en el que se asignaron los objetos. El objeto asignado más recientemente tiene el número más alto y aparece en la parte superior del volcado de memoria.
Para obtener la máxima información posible de un volcado de memoria de objetos, puede invalidar la función miembro Dump
de cualquier objeto derivado de CObject
para personalizar el volcado de memoria de objetos.
Puede establecer un punto de interrupción en una asignación de memoria específica configurando la variable global _afxBreakAlloc
al número que se muestra entre llaves. Si vuelve a ejecutar el programa, el depurador interrumpirá la ejecución cuando se produzca esa asignación. A continuación, puede ver la pila de llamadas para ver cómo el programa ha llegado a ese punto.
La biblioteca en tiempo de ejecución de C tiene una función similar, _CrtSetBreakAlloc, que puede usar para las asignaciones en tiempo de ejecución de C.
Interpretar volcados de memoria
Examine el siguiente volcado de memoria de objetos con mayor detalle:
{5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long
{4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long
{3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long
{2} a CPerson at $51A4
Last Name: Smith
First Name: Alan
Phone #: 581-0215
{1} strcore.cpp(80) : non-object block at $00A7516E, 25 bytes long
El programa que generó este volcado de memoria sólo tenía dos asignaciones de memoria explícitas: una en la pila y otra en el montón:
// Do your memory allocations and deallocations.
CString s("This is a frame variable");
// The next object is a heap object.
CPerson* p = new CPerson( "Smith", "Alan", "581-0215" );
El CPerson
constructor toma tres argumentos que son punteros a char
, los cuales se utilizan para inicializar las variables miembro CString
. En el volcado de memoria, se puede ver el objeto CPerson
junto con tres bloques que no corresponden a objetos (3, 4 y 5). Contienen los caracteres de las CString
variables miembro y no se eliminarán cuando se invoque el CPerson
destructor de objeto.
El bloque número 2 es el objeto CPerson
en sí.
$51A4
representa la dirección del bloque y es seguida por el contenido del objeto, que fue generado por CPerson
::Dump
cuando es llamado por DumpAllObjectsSince.
Puede adivinar que el número de bloque 1 está asociado a la CString
variable de marco debido a su número de secuencia y tamaño, que coincide con el número de caracteres de la variable de marco CString
. Las variables asignadas en el marco de trabajo se desasignan automáticamente cuando el marco se sale del ámbito.
Variables de marco
En general, no debería preocuparse de los objetos del montón asociados con variables de marco, ya que se desasignan automáticamente cuando las variables se salen de su ámbito. Para conseguir claridad y orden en los volcados de memoria, las llamadas a Checkpoint
se deberían colocar de modo que se encuentren fuera del ámbito de las variables de marco. Por ejemplo, coloque los corchetes de ámbito alrededor del código de asignación anterior, como se muestra aquí:
oldMemState.Checkpoint();
{
// Do your memory allocations and deallocations ...
CString s("This is a frame variable");
// The next object is a heap object.
CPerson* p = new CPerson( "Smith", "Alan", "581-0215" );
}
newMemState.Checkpoint();
Con las llaves de ámbito, el volcado de memoria quedaría así:
Dumping objects ->
{5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long
{4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long
{3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long
{2} a CPerson at $51A4
Last Name: Smith
First Name: Alan
Phone #: 581-0215
Asignaciones no-objetuales
Observe que algunas asignaciones son objetos (como CPerson
) y algunas son asignaciones que no son de objetos. "Asignaciones no de objetos" son asignaciones para objetos que no se derivan de CObject
o asignaciones de tipos primitivos de C como char
, int
o long
. Si la clase derivada de CObject asigna espacio adicional, como para los búferes internos, esos objetos mostrarán asignaciones de objetos y no objetos.
Prevención de pérdidas de memoria
Observe en el código anterior que el bloque de memoria asociado a la CString
variable de marco se ha desasignado automáticamente y no se muestra como una pérdida de memoria. La desasignación automática asociada a las reglas de ámbito se ocupa de la mayoría de las pérdidas de memoria relacionadas con variables de marco.
Sin embargo, para objetos asignados en el montón, se debe eliminar explícitamente cada objeto para evitar una pérdida de memoria. Para evitar la última pérdida de memoria del ejemplo anterior, elimine el objeto CPerson
asignado en el montón, como se indica a continuación:
{
// Do your memory allocations and deallocations.
CString s("This is a frame variable");
// The next object is a heap object.
CPerson* p = new CPerson( "Smith", "Alan", "581-0215" );
delete p;
}
Personalizar volcados de memoria de objetos
Si se deriva una clase de CObject, puede reemplazarse la función miembro Dump
para ofrecer información adicional cuando se utiliza DumpAllObjectsSince para realizar un volcado de memoria de objetos en la Ventana de salida.
La función Dump
escribe una representación textual de las variables miembro del objeto en un contexto de volcado de memoria (CDumpContext). El contexto de volcado de memoria es similar a una secuencia de E/S. Puede usar el operador de adjuntar (<<) para enviar datos a CDumpContext
.
Cuando se reemplaza la función Dump
, primero se debería llamar a la versión de la clase base de Dump
para realizar un volcado de memoria del contenido del objeto de la clase base. A continuación, genera una descripción textual y un valor para cada variable miembro de la clase derivada.
La declaración de la Dump
función tiene este aspecto:
class CPerson : public CObject
{
public:
#ifdef _DEBUG
virtual void Dump( CDumpContext& dc ) const;
#endif
CString m_firstName;
CString m_lastName;
// And so on...
};
Como el volcado de objetos sólo tiene sentido en la depuración del programa, la declaración de la función Dump
se encuentra encerrada en un bloque #ifdef _DEBUG / #endif .
En el ejemplo siguiente, la Dump
función llama primero a la Dump
función para su clase base. A continuación, escribe una breve descripción de cada variable miembro junto con el valor del miembro en el flujo de diagnóstico.
#ifdef _DEBUG
void CPerson::Dump( CDumpContext& dc ) const
{
// Call the base class function first.
CObject::Dump( dc );
// Now do the stuff for our specific class.
dc << "last name: " << m_lastName << "\n"
<< "first name: " << m_firstName << "\n";
}
#endif
Se debe suministrar un argumento CDumpContext
que especifique dónde se escribirá el resultado del volcado de memoria. La versión de depuración de MFC proporciona un objeto predefinido CDumpContext
denominado afxDump
que envía la salida al depurador.
CPerson* pMyPerson = new CPerson;
// Set some fields of the CPerson object.
//...
// Now dump the contents.
#ifdef _DEBUG
pMyPerson->Dump( afxDump );
#endif
Reducir el tamaño de una configuración de compilación de MFC
La información de depuración de una aplicación MFC grande puede ocupar mucho espacio en disco. Puede usar uno de estos procedimientos para reducir el tamaño:
Recompile las bibliotecas de MFC mediante la opción /Z7, /Zi, /ZI (Formato de información de depuración), en lugar de /Z7. Estas opciones crean un único archivo de base de datos de programa (PDB) que contiene información de depuración para toda la biblioteca, lo que reduce la redundancia y ahorra espacio.
Recompile las bibliotecas de MFC sin información de depuración (sin la opción /Z7, /Zi, /ZI (Formato de información de depuración). En este caso, la falta de información de depuración le impedirá usar la mayoría de las funciones del depurador dentro del código de la biblioteca MFC, pero dado que las bibliotecas MFC ya están depuradas exhaustivamente, esto podría no ser un problema.
Compile su propia aplicación con información de depuración solo para los módulos seleccionados como se describe a continuación.
Creación de una aplicación MFC con información de depuración para módulos seleccionados
Compilar módulos seleccionados con las bibliotecas de depuración de MFC permite utilizar la ejecución paso a paso y otros servicios de depuración en esos módulos. Este procedimiento hace uso de ambas configuraciones de depuración y lanzamiento del proyecto, lo que requiere los cambios descritos en los pasos siguientes (y también hace necesaria una "recompilación completa" cuando se requiere una compilación completa de lanzamiento).
En el Explorador de soluciones, seleccione el proyecto.
En el menú Ver , seleccione Páginas de propiedades.
En primer lugar, creará una nueva configuración de proyecto.
En el <cuadro de diálogo Páginas de propiedades del proyecto>, haga clic en el botón Configuration Manager.
En el cuadro de diálogo Administrador de configuración, busque el proyecto en la cuadrícula. En la columna Configuración , seleccione <Nuevo...>.
En el cuadro de diálogo Nueva configuración del proyecto, escriba un nombre para la nueva configuración, como "Depuración parcial", en el cuadro Nombre de configuración del proyecto .
En la lista Copiar configuración de , elija Liberar.
Haga clic en Aceptar para cerrar el cuadro de diálogo Nueva configuración del proyecto .
Cierre el cuadro de diálogo Administrador de configuración .
Ahora, establecerá opciones para todo el proyecto.
En el cuadro de diálogo Páginas de propiedades, en la carpeta Propiedades de configuración , seleccione la categoría General .
En la cuadrícula de configuración del proyecto, expanda Valores predeterminados del proyecto (si es necesario).
En Valores predeterminados del proyecto, busque Uso de MFC. La configuración actual aparece en la columna derecha de la cuadrícula. Haga clic en la configuración actual y cámbiela a Usar MFC en una biblioteca estática.
En el panel izquierdo del cuadro de diálogo Páginas de propiedades , abra la carpeta C/C++ y seleccione Preprocesador. En la cuadrícula de propiedades, busque Definiciones de preprocesador y reemplace "NDEBUG" por "_DEBUG".
En el panel izquierdo del cuadro de diálogo Páginas de propiedades , abra la carpeta Enlazador y seleccione la Categoría de entrada . En la cuadrícula de propiedades, busque Dependencias adicionales. En la configuración Dependencias adicionales, escriba "NAFXCWD.LIB" y "LIBCMT".
Haga clic en Aceptar para guardar las nuevas opciones de compilación y cerrar el cuadro de diálogo Páginas de propiedades .
En el menú Compilar , seleccione Recompilar. Este comando quita toda la información de depuración de los módulos, pero no afecta a la biblioteca MFC.
Ahora, se debe volver a agregar la información de depuración en los módulos seleccionados de la aplicación. Recuerde que puede establecer puntos de interrupción y realizar otras funciones del depurador solo en módulos que haya compilado con información de depuración. Para cada archivo de proyecto en el que desea incluir información de depuración, lleve a cabo los pasos siguientes:
En el Explorador de soluciones, abra la carpeta Archivos de código fuente ubicada en el proyecto.
Seleccione el archivo para el que desea establecer la información de depuración.
En el menú Ver , seleccione Páginas de propiedades.
En el cuadro de diálogo Páginas de propiedades , en la carpeta Configuración de configuración , abra la carpeta C/C++ y seleccione la categoría General .
En la cuadrícula de propiedades, busque Formato de la información de depuración.
Haga clic en la configuración Debug Information Format (Formato de información de depuración ) y seleccione la opción deseada (normalmente /ZI) para obtener información de depuración.
Si usa una aplicación generada por el asistente para aplicaciones o tiene encabezados precompilados, debe desactivar los encabezados precompilados o volver a compilarlos antes de compilar los demás módulos. De lo contrario, recibirá la advertencia C4650 y el mensaje de error C2855. Puede desactivar los encabezados precompilados cambiando la opción Crear o usar encabezados precompilados en el <cuadro de diálogo Propiedades del proyecto> (carpeta Propiedades de configuración, subcarpeta C/C++, categoría Encabezados precompilados).
En el menú Compilar , seleccione Compilar para recompilar los archivos del proyecto que no están actualizados.
Como alternativa a la técnica descrita en este tema, puede usar un archivo Make externo para definir opciones individuales para cada archivo. En ese caso, para vincular con las bibliotecas de depuración de MFC, deberá definir el marcador _DEBUG para cada módulo. Si desea usar bibliotecas de versiones de MFC, debe definir NDEBUG. Para obtener más información sobre cómo escribir archivos Make externos, consulte la referencia de NMAKE.