You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
463 lines
11 KiB
463 lines
11 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkDebugLeaks.cxx,v $
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
#include "vtkDebugLeaks.h"
|
|
|
|
#include "vtkCriticalSection.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkWindows.h"
|
|
|
|
#include <vtkstd/string>
|
|
|
|
static const char *vtkDebugLeaksIgnoreClasses[] = {
|
|
0
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// return 1 if the class should be ignored
|
|
int vtkDebugLeaksIgnoreClassesCheck(const char* s)
|
|
{
|
|
int i =0;
|
|
while(vtkDebugLeaksIgnoreClasses[i])
|
|
{
|
|
if(strcmp(s, vtkDebugLeaksIgnoreClasses[i]) == 0)
|
|
{
|
|
return 1;
|
|
}
|
|
i++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
vtkCxxRevisionMacro(vtkDebugLeaks, "$Revision: 1.41 $");
|
|
vtkStandardNewMacro(vtkDebugLeaks);
|
|
|
|
//----------------------------------------------------------------------------
|
|
// A hash function for converting a string to a long
|
|
inline size_t vtkHashString(const char* s)
|
|
{
|
|
unsigned long h = 0;
|
|
for ( ; *s; ++s)
|
|
{
|
|
h = 5*h + *s;
|
|
}
|
|
return size_t(h);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
class vtkDebugLeaksHashNode
|
|
{
|
|
public:
|
|
vtkDebugLeaksHashNode()
|
|
{
|
|
this->Count =1; // if it goes in, then there is one of them
|
|
this->Key = 0;
|
|
this->Next =0;
|
|
}
|
|
void Print(vtkstd::string& os)
|
|
{
|
|
if(this->Count)
|
|
{
|
|
char tmp[256];
|
|
sprintf(tmp,"\" has %i %s still around.\n",this->Count,
|
|
(this->Count == 1) ? "instance" : "instances");
|
|
os += "Class \"";
|
|
os += this->Key;
|
|
os += tmp;
|
|
}
|
|
}
|
|
~vtkDebugLeaksHashNode()
|
|
{
|
|
delete [] this->Key;
|
|
if(this->Next)
|
|
{
|
|
delete this->Next;
|
|
}
|
|
}
|
|
public:
|
|
vtkDebugLeaksHashNode *Next;
|
|
char *Key;
|
|
int Count;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
class vtkDebugLeaksHashTable
|
|
{
|
|
public:
|
|
vtkDebugLeaksHashTable();
|
|
vtkDebugLeaksHashNode* GetNode(const char* name);
|
|
void IncrementCount(const char *name);
|
|
unsigned int GetCount(const char *name);
|
|
int DecrementCount(const char* name);
|
|
void PrintTable(vtkstd::string &os);
|
|
int IsEmpty();
|
|
~vtkDebugLeaksHashTable()
|
|
{
|
|
for (int i = 0; i < 64; i++)
|
|
{
|
|
vtkDebugLeaksHashNode *pos = this->Nodes[i];
|
|
if(pos)
|
|
{
|
|
delete pos;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
vtkDebugLeaksHashNode* Nodes[64];
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkDebugLeaksHashTable::vtkDebugLeaksHashTable()
|
|
{
|
|
int i;
|
|
for (i = 0; i < 64; i++)
|
|
{
|
|
this->Nodes[i] = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkDebugLeaksHashTable::IncrementCount(const char * name)
|
|
{
|
|
vtkDebugLeaksHashNode *pos;
|
|
vtkDebugLeaksHashNode *newpos;
|
|
int loc;
|
|
|
|
pos = this->GetNode(name);
|
|
if(pos)
|
|
{
|
|
pos->Count++;
|
|
return;
|
|
}
|
|
|
|
newpos = new vtkDebugLeaksHashNode;
|
|
newpos->Key = strcpy(new char[strlen(name)+1], name);
|
|
|
|
loc = (((unsigned long)vtkHashString(name)) & 0x03f0) / 16;
|
|
|
|
pos = this->Nodes[loc];
|
|
if (!pos)
|
|
{
|
|
this->Nodes[loc] = newpos;
|
|
return;
|
|
}
|
|
while (pos->Next)
|
|
{
|
|
pos = pos->Next;
|
|
}
|
|
pos->Next = newpos;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkDebugLeaksHashNode* vtkDebugLeaksHashTable::GetNode(const char* key)
|
|
{
|
|
vtkDebugLeaksHashNode *pos;
|
|
int loc = (((unsigned long)vtkHashString(key)) & 0x03f0) / 16;
|
|
|
|
pos = this->Nodes[loc];
|
|
|
|
if (!pos)
|
|
{
|
|
return NULL;
|
|
}
|
|
while ((pos) && (strcmp(pos->Key, key) != 0) )
|
|
{
|
|
pos = pos->Next;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
unsigned int vtkDebugLeaksHashTable::GetCount(const char* key)
|
|
{
|
|
vtkDebugLeaksHashNode *pos;
|
|
int loc = (((unsigned long)vtkHashString(key)) & 0x03f0) / 16;
|
|
|
|
pos = this->Nodes[loc];
|
|
|
|
if (!pos)
|
|
{
|
|
return 0;
|
|
}
|
|
while ((pos)&&(pos->Key != key))
|
|
{
|
|
pos = pos->Next;
|
|
}
|
|
if (pos)
|
|
{
|
|
return pos->Count;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkDebugLeaksHashTable::IsEmpty()
|
|
{
|
|
int count = 0;
|
|
for(int i =0; i < 64; i++)
|
|
{
|
|
vtkDebugLeaksHashNode *pos = this->Nodes[i];
|
|
if(pos)
|
|
{
|
|
if(!vtkDebugLeaksIgnoreClassesCheck(pos->Key))
|
|
{
|
|
count += pos->Count;
|
|
}
|
|
while(pos->Next)
|
|
{
|
|
pos = pos->Next;
|
|
if(!vtkDebugLeaksIgnoreClassesCheck(pos->Key))
|
|
{
|
|
count += pos->Count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return !count;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkDebugLeaksHashTable::DecrementCount(const char *key)
|
|
{
|
|
|
|
vtkDebugLeaksHashNode *pos = this->GetNode(key);
|
|
if(pos)
|
|
{
|
|
pos->Count--;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkDebugLeaksHashTable::PrintTable(vtkstd::string &os)
|
|
{
|
|
for(int i =0; i < 64; i++)
|
|
{
|
|
vtkDebugLeaksHashNode *pos = this->Nodes[i];
|
|
if(pos)
|
|
{
|
|
if(!vtkDebugLeaksIgnoreClassesCheck(pos->Key))
|
|
{
|
|
pos->Print(os);
|
|
}
|
|
while(pos->Next)
|
|
{
|
|
pos = pos->Next;
|
|
if(!vtkDebugLeaksIgnoreClassesCheck(pos->Key))
|
|
{
|
|
pos->Print(os);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
#ifdef VTK_DEBUG_LEAKS
|
|
void vtkDebugLeaks::ConstructClass(const char* name)
|
|
{
|
|
vtkDebugLeaks::CriticalSection->Lock();
|
|
vtkDebugLeaks::MemoryTable->IncrementCount(name);
|
|
vtkDebugLeaks::CriticalSection->Unlock();
|
|
}
|
|
#else
|
|
void vtkDebugLeaks::ConstructClass(const char*)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
#ifdef VTK_DEBUG_LEAKS
|
|
void vtkDebugLeaks::DestructClass(const char* p)
|
|
{
|
|
vtkDebugLeaks::CriticalSection->Lock();
|
|
// Due to globals being deleted, this table may already have
|
|
// been deleted.
|
|
if(vtkDebugLeaks::MemoryTable &&
|
|
!vtkDebugLeaks::MemoryTable->DecrementCount(p))
|
|
{
|
|
vtkDebugLeaks::CriticalSection->Unlock();
|
|
vtkGenericWarningMacro("Deleting unknown object: " << p);
|
|
}
|
|
else
|
|
{
|
|
vtkDebugLeaks::CriticalSection->Unlock();
|
|
}
|
|
}
|
|
#else
|
|
void vtkDebugLeaks::DestructClass(const char*)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkDebugLeaks::PrintCurrentLeaks()
|
|
{
|
|
#ifdef VTK_DEBUG_LEAKS
|
|
if(vtkDebugLeaks::MemoryTable->IsEmpty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// we must not use stringstream ior strstream due to problems with
|
|
// finalizers in MSVC
|
|
vtkstd::string leaks;
|
|
vtkDebugLeaks::MemoryTable->PrintTable(leaks);
|
|
#ifdef _WIN32
|
|
fprintf(stderr,"%s",leaks.c_str());
|
|
fflush(stderr);
|
|
int cancel=0;
|
|
if(getenv("DASHBOARD_TEST_FROM_CTEST") ||
|
|
getenv("DART_TEST_FROM_DART"))
|
|
{
|
|
// Skip dialogs when running on dashboard.
|
|
return 1;
|
|
}
|
|
vtkstd::string::size_type myPos = 0;
|
|
int count = 0;
|
|
vtkstd::string msg;
|
|
msg = "vtkDebugLeaks has detected LEAKS!\n";
|
|
while(!cancel && myPos != leaks.npos)
|
|
{
|
|
vtkstd::string::size_type newPos = leaks.find('\n',myPos);
|
|
if (newPos != leaks.npos)
|
|
{
|
|
msg += leaks.substr(myPos,newPos-myPos);
|
|
msg += "\n";
|
|
myPos = newPos;
|
|
myPos++;
|
|
}
|
|
else
|
|
{
|
|
myPos = newPos;
|
|
}
|
|
count++;
|
|
if (count == 10)
|
|
{
|
|
count = 0;
|
|
cancel = vtkDebugLeaks::DisplayMessageBox(msg.c_str());
|
|
msg = "";
|
|
}
|
|
}
|
|
if (!cancel && count > 0)
|
|
{
|
|
vtkDebugLeaks::DisplayMessageBox(msg.c_str());
|
|
}
|
|
#else
|
|
cout << "vtkDebugLeaks has detected LEAKS!\n";
|
|
cout << leaks.c_str() << endl;
|
|
#endif
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
#ifdef _WIN32
|
|
int vtkDebugLeaks::DisplayMessageBox(const char* msg)
|
|
{
|
|
#ifdef UNICODE
|
|
wchar_t *wmsg = new wchar_t [mbstowcs(NULL, msg, 32000)+1];
|
|
mbstowcs(wmsg, msg, 32000);
|
|
int result = (MessageBox(NULL, wmsg, L"Error",
|
|
MB_ICONERROR | MB_OKCANCEL) == IDCANCEL);
|
|
delete [] wmsg;
|
|
#else
|
|
int result = (MessageBox(NULL, msg, "Error",
|
|
MB_ICONERROR | MB_OKCANCEL) == IDCANCEL);
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
int vtkDebugLeaks::DisplayMessageBox(const char*)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkDebugLeaks::GetExitError()
|
|
{
|
|
return vtkDebugLeaks::ExitError;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkDebugLeaks::SetExitError(int flag)
|
|
{
|
|
vtkDebugLeaks::ExitError = flag;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkDebugLeaks::ClassInitialize()
|
|
{
|
|
#ifdef VTK_DEBUG_LEAKS
|
|
// Create the hash table.
|
|
vtkDebugLeaks::MemoryTable = new vtkDebugLeaksHashTable;
|
|
|
|
// Create the lock for the critical sections.
|
|
vtkDebugLeaks::CriticalSection = new vtkSimpleCriticalSection;
|
|
|
|
// Default to error when leaks occur while running tests.
|
|
vtkDebugLeaks::ExitError =
|
|
(getenv("DASHBOARD_TEST_FROM_CTEST") ||
|
|
getenv("DART_TEST_FROM_DART"))? 1:0;
|
|
#else
|
|
vtkDebugLeaks::MemoryTable = 0;
|
|
vtkDebugLeaks::CriticalSection = 0;
|
|
vtkDebugLeaks::ExitError = 0;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkDebugLeaks::ClassFinalize()
|
|
{
|
|
#ifdef VTK_DEBUG_LEAKS
|
|
// Report leaks.
|
|
int leaked = vtkDebugLeaks::PrintCurrentLeaks();
|
|
|
|
// Destroy the hash table.
|
|
delete vtkDebugLeaks::MemoryTable;
|
|
vtkDebugLeaks::MemoryTable = 0;
|
|
|
|
// Destroy the lock for the critical sections.
|
|
delete vtkDebugLeaks::CriticalSection;
|
|
vtkDebugLeaks::CriticalSection = 0;
|
|
|
|
// Exit with error if leaks occured and error mode is on.
|
|
if(leaked && vtkDebugLeaks::ExitError)
|
|
{
|
|
exit(1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef VTK_LEGACY_REMOVE
|
|
void vtkDebugLeaks::PromptUserOn() {}
|
|
void vtkDebugLeaks::PromptUserOff() {}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Purposely not initialized. ClassInitialize will handle it.
|
|
vtkDebugLeaksHashTable* vtkDebugLeaks::MemoryTable;
|
|
|
|
// Purposely not initialized. ClassInitialize will handle it.
|
|
vtkSimpleCriticalSection* vtkDebugLeaks::CriticalSection;
|
|
|
|
// Purposely not initialized. ClassInitialize will handle it.
|
|
int vtkDebugLeaks::ExitError;
|
|
|