lunes, 25 de marzo de 2013

A la caza de leaks en Embarcadero C++


Los desarrolladores que utilizamos Codegear RAD Studio no disponemos de tantas herramientas como nos gustaría, la mayor parte de éstas están orientadas a VisualStudio.
Recientemente he necesitado realizar tareas de debug intensivo en una aplicación servidor debido a la existencia de varios leaks de memoria difíciles de encontrar.  Hasta la fecha me había bastado con el socorrido CodeGuard que viene de serie con el IDE, pero al parecer esta herramienta tan sólo puede encontrar ciertos tipos concretos de fugas, inicializaciones erróneas o acceso a objetos de memoria inválidos.

En mi búsqueda de herramientas alternativas he topado con un código que puede ser de utilidad, lo he probado y funcionar funciona (aunque todavía no he hecho pruebas intensivas). Es una adaptación de la herramienta IMallocSpy escrita originalmente para MSVC, que encontré en este blog adaptada a BCB. He tenido que arreglar alguna función para que compilara. Aquí os la dejo, esperando que sea de utilidad y os ahorre algún  quebradero de cabeza. Estos son los pasos:

1) Necesitamos descargar los archivos stdafx.cpp y leaker.cpp de esta dirección: http://comcorba.tripod.com/comleaks.htm

2) Crea la unidad IMallocSpyUnit.h con el siguiente contenido:

#ifndef IMallocSpyUnit
#ifndef IMallocSpyUnit
#define IMallocSpyUnit

// IMallocSpyUnit.h
class CMallocSpy : public IMallocSpy
{
 public:
 CMallocSpy(void);
 ~CMallocSpy(void);
 virtual HRESULT STDMETHODCALLTYPE QueryInterface();
 virtual ULONG STDMETHODCALLTYPE AddRef( void);
 virtual ULONG STDMETHODCALLTYPE Release( void);

 // IMallocSpy methods
 virtual SIZE_T STDMETHODCALLTYPE PreAlloc(
 /* [in] */ SIZE_T cbRequest);
 virtual void *STDMETHODCALLTYPE PostAlloc(
 /* [in] */ void *pActual);
 virtual void *STDMETHODCALLTYPE PreFree(
 /* [in] */ void *pRequest,
 /* [in] */ BOOL fSpyed);
 virtual void STDMETHODCALLTYPE PostFree(
 /* [in] */ BOOL fSpyed);
 virtual SIZE_T STDMETHODCALLTYPE PreRealloc(
 /* [in] */ void *pRequest,
 /* [in] */ SIZE_T cbRequest,
 /* [out] */ void **ppNewRequest,
 /* [in] */ BOOL fSpyed);
 virtual void *STDMETHODCALLTYPE PostRealloc(
 /* [in] */ void *pActual,
 /* [in] */ BOOL fSpyed);
 virtual void *STDMETHODCALLTYPE PreGetSize(
 /* [in] */ void *pRequest,
 /* [in] */ BOOL fSpyed);
 virtual SIZE_T STDMETHODCALLTYPE PostGetSize(
 /* [in] */ SIZE_T cbActual,
 /* [in] */ BOOL fSpyed);
 virtual void *STDMETHODCALLTYPE PreDidAlloc(
 /* [in] */ void *pRequest,
 /* [in] */ BOOL fSpyed);
 virtual int STDMETHODCALLTYPE PostDidAlloc(
 /* [in] */ void *pRequest,
 /* [in] */ BOOL fSpyed,
 /* [in] */ int fActual)
 virtual void STDMETHODCALLTYPE PreHeapMinimize( void);
 virtual void STDMETHODCALLTYPE PostHeapMinimize( void);

 void Clear();
 void Dump();
 void SetBreakAlloc(int allocNum);

 protected:
 enum
 {
   HEADERSIZE = 4,
   MAX_ALLOCATIONS = 100000 // cannot handle more than max
 };

 ULONG m_cRef;
 ULONG m_cbRequest;
 int m_counter;
 int m_breakAlloc;
 char *m_map;
 size_t m_mapSize;
};

#endif
  
3) Añade este código donde quieras debuguear en busca de leaks:
 typedef void WINAPI (*SETOANOCACHE)();
 HINSTANCE hDLL = LoadLibrary(L"oleaut32.dll");
 if(!hDLL) throw Exception("Unable to load oleaut32.dll");
 SETOANOCACHE SetOaNoCachePtr = (SETOANOCACHE) GetProcAddress(hDLL,"SetOaNoCache");
 if (!SetOaNoCachePtr) throw Exception("Unable to get SetOaNoCache");
 SetOaNoCachePtr();
 // Initialize COM.
 ::CoInitialize(NULL);
 // Initialize the COM memory checker …
 CMallocSpy* pMallocSpy = new CMallocSpy;
 pMallocSpy->AddRef();
 ::CoRegisterMallocSpy(pMallocSpy);
 pMallocSpy->Clear();
 test_com_allocs(); // CAMBIA ESTO POR LA FUNCIÓN A TESTEAR
 pMallocSpy->Dump();
 ::CoRevokeMallocSpy();
 pMallocSpy->Release();
 ::CoUninitialize();
Si se da algún leak en el código contenido por la función de ejemplo "test_com_allocs()" nos aparecerá el aviso en el output del IDE.

No hay comentarios:

Publicar un comentario