memory.cpp
来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 1,171 行 · 第 1/3 页
CPP
1,171 行
/////////////////////////////////////////////////////////////////////////////
// Name: memory.cpp
// Purpose: Memory checking implementation
// Author: Arthur Seaton, Julian Smart
// Modified by:
// Created: 04/01/98
// RCS-ID: $Id: memory.cpp,v 1.73 2005/02/09 21:36:10 JS Exp $
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "memory.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/defs.h"
#endif
#if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
// #pragma implementation
#endif
#ifndef WX_PRECOMP
#include "wx/utils.h"
#include "wx/app.h"
#include "wx/hash.h"
#endif
#if wxUSE_THREADS
#include "wx/thread.h"
#endif
#include "wx/log.h"
#include <stdlib.h>
#include "wx/ioswrap.h"
#if !defined(__WATCOMC__) && !(defined(__VMS__) && ( __VMS_VER < 70000000 ) )\
&& !defined( __MWERKS__ ) && !defined(__SALFORDC__)
#include <memory.h>
#endif
#include <stdarg.h>
#include <string.h>
#ifdef __WXMSW__
#include "wx/msw/wrapwin.h"
#ifdef GetClassInfo
#undef GetClassInfo
#endif
#ifdef GetClassName
#undef GetClassName
#endif
#endif
#include "wx/memory.h"
#if wxUSE_THREADS && defined(__WXDEBUG__)
#define USE_THREADSAFE_MEMORY_ALLOCATION 1
#else
#define USE_THREADSAFE_MEMORY_ALLOCATION 0
#endif
#ifdef new
#undef new
#endif
// wxDebugContext wxTheDebugContext;
/*
Redefine new and delete so that we can pick up situations where:
- we overwrite or underwrite areas of malloc'd memory.
- we use uninitialise variables
Only do this in debug mode.
We change new to get enough memory to allocate a struct, followed
by the caller's requested memory, followed by a tag. The struct
is used to create a doubly linked list of these areas and also
contains another tag. The tags are used to determine when the area
has been over/under written.
*/
/*
Values which are used to set the markers which will be tested for
under/over write. There are 3 of these, one in the struct, one
immediately after the struct but before the caller requested memory and
one immediately after the requested memory.
*/
#define MemStartCheck 0x23A8
#define MemMidCheck 0xA328
#define MemEndCheck 0x8A32
#define MemFillChar 0xAF
#define MemStructId 0x666D
/*
External interface for the wxMemStruct class. Others are
defined inline within the class def. Here we only need to be able
to add and delete nodes from the list and handle errors in some way.
*/
/*
Used for internal "this shouldn't happen" type of errors.
*/
void wxMemStruct::ErrorMsg (const char * mesg)
{
wxLogMessage(wxT("wxWidgets memory checking error: %s"), mesg);
PrintNode ();
}
/*
Used when we find an overwrite or an underwrite error.
*/
void wxMemStruct::ErrorMsg ()
{
wxLogMessage(wxT("wxWidgets over/underwrite memory error:"));
PrintNode ();
}
/*
We want to find out if pointers have been overwritten as soon as is
possible, so test everything before we dereference it. Of course it's still
quite possible that, if things have been overwritten, this function will
fall over, but the only way of dealing with that would cost too much in terms
of time.
*/
int wxMemStruct::AssertList ()
{
if (wxDebugContext::GetHead () != 0 && ! (wxDebugContext::GetHead ())->AssertIt () ||
wxDebugContext::GetTail () != 0 && ! wxDebugContext::GetTail ()->AssertIt ()) {
ErrorMsg ("Head or tail pointers trashed");
return 0;
}
return 1;
}
/*
Check that the thing we're pointing to has the correct id for a wxMemStruct
object and also that it's previous and next pointers are pointing at objects
which have valid ids.
This is definitely not perfect since we could fall over just trying to access
any of the slots which we use here, but I think it's about the best that I
can do without doing something like taking all new wxMemStruct pointers and
comparing them against all known pointer within the list and then only
doing this sort of check _after_ you've found the pointer in the list. That
would be safer, but also much more time consuming.
*/
int wxMemStruct::AssertIt ()
{
return (m_id == MemStructId &&
(m_prev == 0 || m_prev->m_id == MemStructId) &&
(m_next == 0 || m_next->m_id == MemStructId));
}
/*
Additions are always at the tail of the list.
Returns 0 on error, non-zero on success.
*/
int wxMemStruct::Append ()
{
if (! AssertList ())
return 0;
if (wxDebugContext::GetHead () == 0) {
if (wxDebugContext::GetTail () != 0) {
ErrorMsg ("Null list should have a null tail pointer");
return 0;
}
(void) wxDebugContext::SetHead (this);
(void) wxDebugContext::SetTail (this);
} else {
wxDebugContext::GetTail ()->m_next = this;
this->m_prev = wxDebugContext::GetTail ();
(void) wxDebugContext::SetTail (this);
}
return 1;
}
/*
Don't actually free up anything here as the space which is used
by the node will be free'd up when the whole block is free'd.
Returns 0 on error, non-zero on success.
*/
int wxMemStruct::Unlink ()
{
if (! AssertList ())
return 0;
if (wxDebugContext::GetHead () == 0 || wxDebugContext::GetTail () == 0) {
ErrorMsg ("Trying to remove node from empty list");
return 0;
}
// Handle the part of the list before this node.
if (m_prev == 0) {
if (this != wxDebugContext::GetHead ()) {
ErrorMsg ("No previous node for non-head node");
return 0;
}
(void) wxDebugContext::SetHead (m_next);
} else {
if (! m_prev->AssertIt ()) {
ErrorMsg ("Trashed previous pointer");
return 0;
}
if (m_prev->m_next != this) {
ErrorMsg ("List is inconsistent");
return 0;
}
m_prev->m_next = m_next;
}
// Handle the part of the list after this node.
if (m_next == 0) {
if (this != wxDebugContext::GetTail ()) {
ErrorMsg ("No next node for non-tail node");
return 0;
}
(void) wxDebugContext::SetTail (m_prev);
} else {
if (! m_next->AssertIt ()) {
ErrorMsg ("Trashed next pointer");
return 0;
}
if (m_next->m_prev != this) {
ErrorMsg ("List is inconsistent");
return 0;
}
m_next->m_prev = m_prev;
}
return 1;
}
/*
Checks a node and block of memory to see that the markers are still
intact.
*/
int wxMemStruct::CheckBlock ()
{
int nFailures = 0;
if (m_firstMarker != MemStartCheck) {
nFailures++;
ErrorMsg ();
}
char * pointer = wxDebugContext::MidMarkerPos ((char *) this);
if (* (wxMarkerType *) pointer != MemMidCheck) {
nFailures++;
ErrorMsg ();
}
pointer = wxDebugContext::EndMarkerPos ((char *) this, RequestSize ());
if (* (wxMarkerType *) pointer != MemEndCheck) {
nFailures++;
ErrorMsg ();
}
return nFailures;
}
/*
Check the list of nodes to see if they are all ok.
*/
int wxMemStruct::CheckAllPrevious ()
{
int nFailures = 0;
for (wxMemStruct * st = this->m_prev; st != 0; st = st->m_prev) {
if (st->AssertIt ())
nFailures += st->CheckBlock ();
else
return -1;
}
return nFailures;
}
/*
When we delete a node we set the id slot to a specific value and then test
against this to see if a nodes have been deleted previously. I don't
just set the entire memory to the fillChar because then I'd be overwriting
useful stuff like the vtbl which may be needed to output the error message
including the file name and line numbers. Without this info the whole point
of this class is lost!
*/
void wxMemStruct::SetDeleted ()
{
m_id = MemFillChar;
}
int wxMemStruct::IsDeleted ()
{
return (m_id == MemFillChar);
}
/*
Print out a single node. There are many far better ways of doing this
but this will suffice for now.
*/
void wxMemStruct::PrintNode ()
{
if (m_isObject)
{
wxObject *obj = (wxObject *)m_actualData;
wxClassInfo *info = obj->GetClassInfo();
// Let's put this in standard form so IDEs can load the file at the appropriate
// line
wxString msg;
if (m_fileName)
msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum);
if (info && info->GetClassName())
msg += info->GetClassName();
else
msg += wxT("object");
wxString msg2;
msg2.Printf(wxT(" at 0x%lX, size %d"), (long)GetActualData(), (int)RequestSize());
msg += msg2;
wxLogMessage(msg);
}
else
{
wxString msg;
if (m_fileName)
msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum);
msg += wxT("non-object data");
wxString msg2;
msg2.Printf(wxT(" at 0x%lX, size %d\n"), (long)GetActualData(), (int)RequestSize());
msg += msg2;
wxLogMessage(msg);
}
}
void wxMemStruct::Dump ()
{
if (!ValidateNode()) return;
if (m_isObject)
{
wxObject *obj = (wxObject *)m_actualData;
wxString msg;
if (m_fileName)
msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum);
/* TODO: We no longer have a stream (using wxLogDebug) so we can't dump it.
* Instead, do what wxObject::Dump does.
* What should we do long-term, eliminate Dumping? Or specify
* that MyClass::Dump should use wxLogDebug? Ugh.
obj->Dump(wxDebugContext::GetStream());
*/
if (obj->GetClassInfo() && obj->GetClassInfo()->GetClassName())
msg += obj->GetClassInfo()->GetClassName();
else
msg += wxT("unknown object class");
wxString msg2;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?