newHostVM: More exception / error handling mess.

git-svn-id: http://pcsx2.googlecode.com/svn/branches/newHostVM@3994 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-11-05 01:33:01 +00:00
parent dc7f00d05e
commit a7726871dc
19 changed files with 411 additions and 202 deletions

View File

@ -259,43 +259,40 @@ public: \
virtual classname& SetStreamName( const wxString& name ) { StreamName = name; return *this; } \
virtual classname& SetStreamName( const char* name ) { StreamName = fromUTF8(name); return *this; }
#define DEFINE_STREAM_EXCEPTION( classname, parent, message ) \
DEFINE_RUNTIME_EXCEPTION( classname, parent, message ) \
#define DEFINE_STREAM_EXCEPTION( classname, parent ) \
DEFINE_RUNTIME_EXCEPTION( classname, parent, wxEmptyString ) \
classname( const wxString& filename ) { \
StreamName = filename; \
SetBothMsgs(message); \
} \
DEFINE_STREAM_EXCEPTION_ACCESSORS( classname )
// Generic stream error. Contains the name of the stream and a message.
// This exception is usually thrown via derived classes, except in the (rare) case of a
// generic / unknown error.
// A generic base error class for bad streams -- corrupted data, sudden closures, loss of
// connection, or anything else that would indicate a failure to open a stream or read the
// data after the stream was successfully opened.
//
class Stream : public RuntimeError
class BadStream : public RuntimeError
{
DEFINE_STREAM_EXCEPTION( Stream, RuntimeError, wxLt("General file operation error.") )
DEFINE_STREAM_EXCEPTION( BadStream, RuntimeError )
public:
wxString StreamName; // name of the stream (if applicable)
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
// A generic base error class for bad streams -- corrupted data, sudden closures, loss of
// connection, or anything else that would indicate a failure to read the data after the
// stream was successfully opened.
//
class BadStream : public Stream
{
DEFINE_STREAM_EXCEPTION( BadStream, Stream, wxLt("File data is corrupted or incomplete, or the stream connection closed unexpectedly.") )
protected:
void _formatDiagMsg( FastFormatUnicode& dest ) const;
void _formatUserMsg( FastFormatUnicode& dest ) const;
};
// A generic exception for odd-ball stream creation errors.
//
class CannotCreateStream : public Stream
class CannotCreateStream : public BadStream
{
DEFINE_STREAM_EXCEPTION( CannotCreateStream, Stream, wxLt("File could not be created or opened.") )
DEFINE_STREAM_EXCEPTION( CannotCreateStream, BadStream )
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
// Exception thrown when an attempt to open a non-existent file is made.
@ -304,22 +301,31 @@ public: \
class FileNotFound : public CannotCreateStream
{
public:
DEFINE_STREAM_EXCEPTION( FileNotFound, CannotCreateStream, wxLt("File not found.") )
DEFINE_STREAM_EXCEPTION( FileNotFound, CannotCreateStream )
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
class AccessDenied : public CannotCreateStream
{
public:
DEFINE_STREAM_EXCEPTION( AccessDenied, CannotCreateStream, wxLt("Permission denied to file.") )
DEFINE_STREAM_EXCEPTION( AccessDenied, CannotCreateStream )
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
// EndOfStream can be used either as an error, or used just as a shortcut for manual
// feof checks.
//
class EndOfStream : public Stream
class EndOfStream : public BadStream
{
public:
DEFINE_STREAM_EXCEPTION( EndOfStream, Stream, wxLt("Unexpected end of file or stream.") );
DEFINE_STREAM_EXCEPTION( EndOfStream, BadStream )
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
#ifdef __WXMSW__

View File

@ -219,11 +219,11 @@ static __fi PageProtectionMode PageAccess_Any()
namespace HostSys
{
void* MmapReserve(uptr base, size_t size);
void MmapCommit(uptr base, size_t size, const PageProtectionMode& mode);
bool MmapCommit(uptr base, size_t size, const PageProtectionMode& mode);
void MmapReset(uptr base, size_t size);
void* MmapReservePtr(void* base, size_t size);
void MmapCommitPtr(void* base, size_t size, const PageProtectionMode& mode);
bool MmapCommitPtr(void* base, size_t size, const PageProtectionMode& mode);
void MmapResetPtr(void* base, size_t size);
// Maps a block of memory for use as a recompiled code buffer.

View File

@ -163,10 +163,6 @@ protected:
// This function is called for every committed block.
virtual void OnCommittedBlock( void* block )=0;
virtual void OnOutOfMemory( const Exception::OutOfMemory& ex, void* blockptr, bool& handled )
{
throw;
}
};
// --------------------------------------------------------------------------------------
@ -212,9 +208,9 @@ public:
virtual void* Reserve( uint size, uptr base = 0, uptr upper_bounds = 0 );
virtual void Reset();
virtual bool TryResize( uint newsize );
void OnCommittedBlock( void* block );
void OnOutOfMemory( const Exception::OutOfMemory& ex, void* blockptr, bool& handled );
SpatialArrayReserve& SetBlockCount( uint blocks );
SpatialArrayReserve& SetBlockSizeInPages( uint bytes );

View File

@ -60,6 +60,26 @@ extern void pcsx2_aligned_free(void* pmem);
# define _aligned_realloc pcsx2_aligned_realloc
#endif
// --------------------------------------------------------------------------------------
// pxDoOutOfMemory
// --------------------------------------------------------------------------------------
typedef void FnType_OutOfMemory( uptr blocksize );
typedef FnType_OutOfMemory* Fnptr_OutOfMemory;
// This method is meant to be assigned by applications that link against pxWex. It is called
// (invoked) prior to most pxWex built-in memory/array classes throwing exceptions, and can be
// used by an application to remove unneeded memory allocations and/or reduce internal cache
// reserves.
//
// Example: PCSX2 uses several bloated recompiler code caches. Larger caches improve performance,
// however a rouge cache growth could cause memory constraints in the operating system. If an out-
// of-memory error occurs, PCSX2's implementation of this function attempts to reset all internal
// recompiler caches. This can typically free up 100-150 megs of memory, and will allow the app
// to continue running without crashing or hanging the operating system, etc.
//
extern Fnptr_OutOfMemory pxDoOutOfMemory;
// --------------------------------------------------------------------------------------
// BaseScopedAlloc

View File

@ -23,6 +23,9 @@
#include <signal.h>
#endif
// for lack of a better place...
Fnptr_OutOfMemory pxDoOutOfMemory = NULL;
static wxString GetTranslation( const wxChar* msg )
{
return msg ? wxGetTranslation( msg ) : wxEmptyString;
@ -195,22 +198,30 @@ Exception::RuntimeError::RuntimeError( const std::exception& ex, const wxString&
Exception::OutOfMemory::OutOfMemory( const wxString& allocdesc )
{
AllocDescription = allocdesc;
m_message_user = _("Memory allocation failure! Your system has insufficient memory or resources to meet PCSX2's lofty needs.");
}
wxString Exception::OutOfMemory::FormatDiagnosticMessage() const
{
FastFormatUnicode details;
if (!m_message_diag.IsEmpty())
details.Write(":\n%s",m_message_diag.c_str());
FastFormatUnicode retmsg;
retmsg.Write(L"Out of memory");
if (!AllocDescription.IsEmpty())
retmsg.Write(L" while allocating '%s'", AllocDescription.c_str());
return pxsFmt(L"Out of memory while allocating '%s'%s", AllocDescription.c_str(), details.c_str());
if (!m_message_diag.IsEmpty())
retmsg.Write(L":\n%s", m_message_diag.c_str());
return retmsg;
}
wxString Exception::OutOfMemory::FormatDisplayMessage() const
{
if (m_message_user.IsEmpty()) return FormatDisplayMessage();
return m_message_user;
FastFormatUnicode retmsg;
retmsg.Write( L"%s", _("Oh noes! Out of memory!") );
if (!m_message_diag.IsEmpty())
retmsg.Write(L"\n\n%s", m_message_diag.c_str());
return retmsg;
}
@ -225,17 +236,31 @@ Exception::VirtualMemoryMapConflict::VirtualMemoryMapConflict( const wxString& a
wxString Exception::VirtualMemoryMapConflict::FormatDiagnosticMessage() const
{
FastFormatUnicode details;
if (!m_message_diag.IsEmpty())
details.Write(":\n%s",m_message_diag.c_str());
FastFormatUnicode retmsg;
retmsg.Write(L"Virtual memory map failed");
if (!AllocDescription.IsEmpty())
retmsg.Write(L" while reserving '%s'", AllocDescription.c_str());
return pxsFmt(L"Out of virtual memory while reserving '%s'%s", AllocDescription.c_str(), details.c_str());
if (!m_message_diag.IsEmpty())
retmsg.Write(L":\n%s", m_message_diag.c_str());
return retmsg;
}
wxString Exception::VirtualMemoryMapConflict::FormatDisplayMessage() const
{
if (m_message_user.IsEmpty()) return FormatDisplayMessage();
return m_message_user;
FastFormatUnicode retmsg;
retmsg.Write( L"%s",
pxE( ".Error:VirtualMemoryMap",
L"There is not enough virtual memory available, or necessary virtual memory "
L"mappings have already been reserved by other processes, services, or DLLs."
)
);
if (!m_message_diag.IsEmpty())
retmsg.Write(L"\n\n%s", m_message_diag.c_str());
return retmsg;
}
@ -250,20 +275,120 @@ wxString Exception::CancelEvent::FormatDisplayMessage() const
return L"Action canceled: " + m_message_diag;
}
wxString Exception::Stream::FormatDiagnosticMessage() const
// --------------------------------------------------------------------------------------
// Exception::BadStream (implementations)
// --------------------------------------------------------------------------------------
wxString Exception::BadStream::FormatDiagnosticMessage() const
{
return pxsFmt(
L"%s\n\tFile/Object: %s",
m_message_diag.c_str(), StreamName.c_str()
);
FastFormatUnicode retval;
_formatDiagMsg(retval);
return retval;
}
wxString Exception::Stream::FormatDisplayMessage() const
wxString Exception::BadStream::FormatDisplayMessage() const
{
wxString retval( m_message_user );
if (!StreamName.IsEmpty())
retval += L"\n\n" + pxsFmt( _("Path: %s"), StreamName.c_str() );
FastFormatUnicode retval;
_formatUserMsg(retval);
return retval;
}
void Exception::BadStream::_formatDiagMsg( FastFormatUnicode& dest ) const
{
dest.Write( L"Path: " );
if (!StreamName.IsEmpty())
dest.Write( L"%s", StreamName.c_str() );
else
dest.Write( L"[Unnamed or unknown]" );
if (!m_message_diag.IsEmpty())
dest.Write(L"\n%s", m_message_diag.c_str());
}
void Exception::BadStream::_formatUserMsg( FastFormatUnicode& dest ) const
{
dest.Write( _("Path: ") );
if (!StreamName.IsEmpty())
dest.Write( L"%s", StreamName.c_str() );
else
dest.Write( _("[Unnamed or unknown]") );
if (!m_message_user.IsEmpty())
dest.Write(L"\n%s", m_message_user.c_str());
}
// --------------------------------------------------------------------------------------
// Exception::CannotCreateStream (implementations)
// --------------------------------------------------------------------------------------
wxString Exception::CannotCreateStream::FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("File could not be created.");
_formatDiagMsg(retval);
return retval;
}
wxString Exception::CannotCreateStream::FormatDisplayMessage() const
{
FastFormatUnicode retval;
retval.Write(_("A file could not be created."));
_formatUserMsg(retval);
return retval;
}
// --------------------------------------------------------------------------------------
// Exception::FileNotFound (implementations)
// --------------------------------------------------------------------------------------
wxString Exception::FileNotFound::FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("File not found.");
_formatDiagMsg(retval);
return retval;
}
wxString Exception::FileNotFound::FormatDisplayMessage() const
{
FastFormatUnicode retval;
retval.Write(_("File not found."));
_formatUserMsg(retval);
return retval;
}
// --------------------------------------------------------------------------------------
// Exception::AccessDenied (implementations)
// --------------------------------------------------------------------------------------
wxString Exception::AccessDenied::FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("Permission denied to file.");
_formatDiagMsg(retval);
return retval;
}
wxString Exception::AccessDenied::FormatDisplayMessage() const
{
FastFormatUnicode retval;
retval.Write(_("Permission denied while trying to open file, likely due to insufficient user account rights."));
_formatUserMsg(retval);
return retval;
}
// --------------------------------------------------------------------------------------
// Exception::EndOfStream (implementations)
// --------------------------------------------------------------------------------------
wxString Exception::EndOfStream::FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("Unexpected end of file or stream.");
_formatDiagMsg(retval);
return retval;
}
wxString Exception::EndOfStream::FormatDisplayMessage() const
{
FastFormatUnicode retval;
retval.Write(_("Unexpected end of file or stream encountered. File is probably truncated or corrupted."));
_formatUserMsg(retval);
return retval;
}
@ -281,7 +406,7 @@ BaseException* Exception::FromErrno( const wxString& streamname, int errcode )
{
case EINVAL:
pxFailDev( L"Invalid argument" );
return &(new Exception::Stream( streamname ))->SetDiagMsg(L"Invalid argument? (likely caused by an unforgivable programmer error!)" );
return &(new Exception::BadStream( streamname ))->SetDiagMsg(L"Invalid argument? (likely caused by an unforgivable programmer error!)" );
case EACCES: // Access denied!
return new Exception::AccessDenied( streamname );
@ -302,6 +427,6 @@ BaseException* Exception::FromErrno( const wxString& streamname, int errcode )
return &(new Exception::BadStream( streamname ))->SetDiagMsg(L"Bad file number");
default:
return &(new Exception::Stream( streamname ))->SetDiagMsg(pxsFmt( L"General file/stream error [errno: %d]", errcode ));
return &(new Exception::BadStream( streamname ))->SetDiagMsg(pxsFmt( L"General file/stream error [errno: %d]", errcode ));
}
}

View File

@ -35,6 +35,44 @@ static __ri void PageSizeAssertionTest( size_t size )
);
}
// returns FALSE if the mprotect call fails with an ENOMEM.
// Raises assertions on other types of POSIX errors (since those typically reflect invalid object
// or memory states).
static bool _memprotect( void* baseaddr, size_t size, const PageProtectionMode& mode )
{
PageSizeAssertionTest(size);
uint lnxmode = 0;
if (mode.CanWrite()) lnxmode |= PROT_WRITE;
if (mode.CanRead()) lnxmode |= PROT_READ;
if (mode.CanExecute()) lnxmode |= PROT_EXEC | PROT_READ;
const int result = mprotect( baseaddr, size, lnxmode );
if (result == 0) return true;
switch(errno)
{
case EINVAL:
pxFailDev(pxsFmt(L"mprotect returned EINVAL @ 0x%08X -> 0x%08X (mode=%s)",
baseaddr, (uptr)baseaddr+size, mode.ToString().c_str())
);
break;
case EACCES:
pxFailDev(pxsFmt(L"mprotect returned EACCES @ 0x%08X -> 0x%08X (mode=%s)",
baseaddr, (uptr)baseaddr+size, mode.ToString().c_str())
);
break;
case ENOMEM:
// caller handles assertion or exception, or whatever.
break;
}
return false;
}
void* HostSys::MmapReservePtr(void* base, size_t size)
{
PageSizeAssertionTest(size);
@ -46,7 +84,7 @@ void* HostSys::MmapReservePtr(void* base, size_t size)
return mmap(base, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
void HostSys::MmapCommitPtr(void* base, size_t size, const PageProtectionMode& mode)
bool HostSys::MmapCommitPtr(void* base, size_t size, const PageProtectionMode& mode)
{
// In linux, reserved memory is automatically committed when its permissions are
// changed to something other than PROT_NONE. If the user is committing memory
@ -54,7 +92,12 @@ void HostSys::MmapCommitPtr(void* base, size_t size, const PageProtectionMode& m
// later when the user changes permissions to something useful via calls to MemProtect).
if (mode.IsNone()) return;
MemProtect( base, size, mode );
if (_memprotect( base, size, mode )) return true;
if (!pxDoOutOfMemory) return false;
pxDoOutOfMemory(size);
return _memprotect( base, size, mode );
}
void HostSys::MmapResetPtr(void* base, size_t size)
@ -82,9 +125,9 @@ void* HostSys::MmapReserve(uptr base, size_t size)
return MmapReservePtr((void*)base, size);
}
void HostSys::MmapCommit(uptr base, size_t size, const PageProtectionMode& mode)
bool HostSys::MmapCommit(uptr base, size_t size, const PageProtectionMode& mode)
{
MmapCommitPtr( (void*)base, size, mode );
return MmapCommitPtr( (void*)base, size, mode );
}
void HostSys::MmapReset(uptr base, size_t size)
@ -108,35 +151,12 @@ void HostSys::Munmap(uptr base, size_t size)
void HostSys::MemProtect( void* baseaddr, size_t size, const PageProtectionMode& mode )
{
PageSizeAssertionTest(size);
uint lnxmode = 0;
if (mode.CanWrite()) lnxmode |= PROT_WRITE;
if (mode.CanRead()) lnxmode |= PROT_READ;
if (mode.CanExecute()) lnxmode |= PROT_EXEC | PROT_READ;
int result = mprotect( baseaddr, size, lnxmode );
if (result != 0)
if (!_memprotect(baseaddr, size, mode))
{
switch(errno)
{
case EINVAL:
pxFailDev(pxsFmt(L"mprotect returned EINVAL @ 0x%08X -> 0x%08X (mode=%s)",
baseaddr, (uptr)baseaddr+size, mode.ToString().c_str())
throw Exception::OutOfMemory( "MemProtect" )
.SetDiagMsg(pxsFmt( L"mprotect failed @ 0x%08X -> 0x%08X (mode=%s)",
baseaddr, (uptr)baseaddr+size, mode.ToString().c_str()
)
);
break;
case ENOMEM:
throw Exception::OutOfMemory( pxsFmt( L"mprotect failed @ 0x%08X -> 0x%08X (mode=%s)",
baseaddr, (uptr)baseaddr+size, mode.ToString().c_str())
);
break;
case EACCES:
break;
}
throw Exception::OutOfMemory();
}
}

View File

@ -167,14 +167,19 @@ bool BaseVirtualMemoryReserve::TryResize( uint newsize )
return true;
}
void BaseVirtualMemoryReserve::CommitBlocks( uptr page, uint blocks )
{
const uint blocksbytes = blocks * m_blocksize * __pagesize;
const uptr blocksbytes = blocks * m_blocksize * __pagesize;
void* blockptr = (u8*)m_baseptr + (page * __pagesize);
// Depending on the operating system, one or both of these could fail if the system
// is low on either physical ram or virtual memory.
HostSys::MmapCommitPtr(blockptr, blocksbytes, m_prot_mode);
// Depending on the operating system, this call could fail if the system is low on either
// physical ram or virtual memory.
if (!HostSys::MmapCommitPtr(blockptr, blocksbytes, m_prot_mode))
{
throw Exception::OutOfMemory(Name)
.SetDiagMsg(pxsFmt("An additional %u blocks @ 0x%08x were requested, but could not be committed!", blocks, blockptr));
}
u8* init = (u8*)blockptr;
u8* endpos = init + blocksbytes;
@ -189,29 +194,38 @@ void BaseVirtualMemoryReserve::OnPageFaultEvent(const PageFaultInfo& info, bool&
sptr offset = (info.addr - (uptr)m_baseptr) / __pagesize;
if ((offset < 0) || ((uptr)offset >= m_reserved)) return;
try {
DoCommitAndProtect( offset );
handled = true;
}
catch (Exception::OutOfMemory& ex)
{
handled = false;
OnOutOfMemory( ex, (u8*)m_baseptr + (offset * __pagesize), handled );
}
#ifndef __WXMSW__
// Linux Note! the SIGNAL handler is very limited in what it can do, and not only can't
// we let the C++ exception try to unwind the stack, we may not be able to log it either.
// (but we might as well try -- kernel/posix rules says not to do it, but Linux kernel
// implementations seem to support it).
// Note also that logging the exception and/or issuing an assertion dialog are always
// possible if the thread handling the signal is not the main thread.
// In windows we can let exceptions bubble out of the page fault handler. SEH will more
// or less handle them in a semi-expected way, and might even avoid a GPF long enough
// for the system to log the error or something.
// In Linux, however, the SIGNAL handler is very limited in what it can do, and not only
// can't we let the C++ exception try to unwind the stack, we can't really log it either.
// We can't issue a proper assertion (requires user popup). We can't do jack or shit,
// *unless* its attached to a debugger; then we can, at a bare minimum, trap it.
#ifndef __WXMSW__
try {
#endif
throw Exception::OutOfMemory( L"Right Here" );
DoCommitAndProtect( offset );
handled = true;
#ifndef __WXMSW__
}
catch (Exception::BaseException& ex)
{
handled = false;
if (!wxThread::IsMain())
{
pxFailRel( ex.FormatDiagnosticMessage() );
}
else
{
wxTrap();
}
}
#endif
}
@ -237,6 +251,33 @@ void SpatialArrayReserve::Reset()
memzero_sse_a(m_blockbits.GetPtr(), _calcBlockBitArrayLength());
}
// Important! The number of blocks of the array will be altered when using this method.
//
bool SpatialArrayReserve::TryResize( uint newsize )
{
uint newpages = (newsize + __pagesize - 1) / __pagesize;
// find the last allocated block -- we cannot be allowed to resize any smaller than that:
uint i;
for (i=m_numblocks-1; i; --i)
{
uint bit = i & 7;
if (m_blockbits[i / 8] & bit) break;
}
uint pages_in_use = i * m_blocksize;
if (newpages < pages_in_use) return false;
if (!__parent::TryResize( newsize )) return false;
// On success, we must re-calibrate the internal blockbits array.
m_blockbits.Resize( (m_numblocks + 7) / 8 );
return true;
}
// This method allows the programmer to specify the block size of the array as a function
// of its reserved size. This function *must* be called *after* the reserve has been made,
// and *before* the array contents have been accessed.
@ -272,7 +313,7 @@ SpatialArrayReserve& SpatialArrayReserve::SetBlockSizeInPages( uint pages )
return *this;
}
// This method assigns the block size of the spatial array, in bytes. The actual size of
// SetBlockSize assigns the block size of the spatial array, in bytes. The actual size of
// each block will be rounded up to the nearest page size. The resulting size is returned.
//
// This method must be called prior to accessing or modifying the array contents. Calls to
@ -295,11 +336,6 @@ void SpatialArrayReserve::OnCommittedBlock( void* block )
m_commited += m_blocksize;
}
void SpatialArrayReserve::OnOutOfMemory( const Exception::OutOfMemory& ex, void* blockptr, bool& handled )
{
}
// --------------------------------------------------------------------------------------
// PageProtectionMode (implementations)

View File

@ -42,10 +42,26 @@ void* HostSys::MmapReservePtr(void* base, size_t size)
return VirtualAlloc(base, size, MEM_RESERVE, PAGE_NOACCESS);
}
void HostSys::MmapCommitPtr(void* base, size_t size, const PageProtectionMode& mode)
bool HostSys::MmapCommitPtr(void* base, size_t size, const PageProtectionMode& mode)
{
void* result = VirtualAlloc(base, size, MEM_COMMIT, ConvertToWinApi(mode));
pxAssumeDev(result, L"VirtualAlloc COMMIT failed: " + Exception::WinApiError().GetMsgFromWindows());
if (result) return true;
const DWORD errcode = GetLastError();
if (errcode == ERROR_COMMITMENT_MINIMUM)
{
Console.Warning("(MmapCommit) Received windows error %u {Virtual Memory Minimum Too Low}.", ERROR_COMMITMENT_MINIMUM);
Sleep(1000); // Cut windows some time to rework its memory...
}
else if (errcode != ERROR_NOT_ENOUGH_MEMORY && errcode != ERROR_OUTOFMEMORY)
{
pxFailDev(L"VirtualAlloc COMMIT failed: " + Exception::WinApiError().GetMsgFromWindows());
return false;
}
if (!pxDoOutOfMemory) return false;
pxDoOutOfMemory(size);
return VirtualAlloc(base, size, MEM_COMMIT, ConvertToWinApi(mode)) != NULL;
}
void HostSys::MmapResetPtr(void* base, size_t size)
@ -59,9 +75,9 @@ void* HostSys::MmapReserve(uptr base, size_t size)
return MmapReservePtr((void*)base, size);
}
void HostSys::MmapCommit(uptr base, size_t size, const PageProtectionMode& mode)
bool HostSys::MmapCommit(uptr base, size_t size, const PageProtectionMode& mode)
{
MmapCommitPtr( (void*)base, size, mode );
return MmapCommitPtr( (void*)base, size, mode );
}
void HostSys::MmapReset(uptr base, size_t size)

View File

@ -472,16 +472,16 @@ int GetPS2ElfName( wxString& name )
return 0;
}
}
catch (Exception::BadStream& ex)
{
Console.Error(ex.FormatDiagnosticMessage());
return 0; // ISO error
}
catch( Exception::FileNotFound& )
{
//Console.Warning(ex.FormatDiagnosticMessage());
return 0; // no SYSTEM.CNF, not a PS1/PS2 disc.
}
catch (Exception::BadStream& ex)
{
Console.Error(ex.FormatDiagnosticMessage());
return 0; // ISO error
}
#ifdef PCSX2_DEVBUILD
FILE *fp;

View File

@ -674,18 +674,34 @@ SysCorePlugins *g_plugins = NULL;
// Plugin-related Exception Implementations
// ---------------------------------------------------------------------------------
wxString Exception::SaveStateLoadError::FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("Savestate is corrupt or incomplete!");
_formatDiagMsg(retval);
return retval;
}
wxString Exception::SaveStateLoadError::FormatDisplayMessage() const
{
FastFormatUnicode retval;
retval.Write(_("The savestate cannot be loaded, as it appears to be corrupt or incomplete."));
_formatUserMsg(retval);
return retval;
}
Exception::PluginOpenError::PluginOpenError( PluginsEnum_t pid )
{
PluginId = pid;
m_message_diag = L"%s plugin failed to open!";
m_message_user = L"%s plugin failed to open. Your computer may have insufficient resources, or incompatible hardware/drivers.";
m_message_user = _("%s plugin failed to open. Your computer may have insufficient resources, or incompatible hardware/drivers.");
}
Exception::PluginInitError::PluginInitError( PluginsEnum_t pid )
{
PluginId = pid;
m_message_diag = L"%s plugin initialization failed!";
m_message_user = L"%s plugin failed to initialize. Your system may have insufficient memory or resources needed.";
m_message_user = _("%s plugin failed to initialize. Your system may have insufficient memory or resources needed.");
}
Exception::PluginLoadError::PluginLoadError( PluginsEnum_t pid )
@ -695,29 +711,29 @@ Exception::PluginLoadError::PluginLoadError( PluginsEnum_t pid )
wxString Exception::PluginLoadError::FormatDiagnosticMessage() const
{
return wxsFormat( m_message_diag, tbl_PluginInfo[PluginId].GetShortname().c_str() ) +
return pxsFmt( m_message_diag, tbl_PluginInfo[PluginId].GetShortname().c_str() ) +
L"\n\n" + StreamName;
}
wxString Exception::PluginLoadError::FormatDisplayMessage() const
{
return wxsFormat( m_message_user, tbl_PluginInfo[PluginId].GetShortname().c_str() ) +
return pxsFmt( m_message_user, tbl_PluginInfo[PluginId].GetShortname().c_str() ) +
L"\n\n" + StreamName;
}
wxString Exception::PluginError::FormatDiagnosticMessage() const
{
return wxsFormat( m_message_diag, tbl_PluginInfo[PluginId].GetShortname().c_str() );
return pxsFmt( m_message_diag, tbl_PluginInfo[PluginId].GetShortname().c_str() );
}
wxString Exception::PluginError::FormatDisplayMessage() const
{
return wxsFormat( m_message_user, tbl_PluginInfo[PluginId].GetShortname().c_str() );
return pxsFmt( m_message_user, tbl_PluginInfo[PluginId].GetShortname().c_str() );
}
wxString Exception::FreezePluginFailure::FormatDiagnosticMessage() const
{
return wxsFormat(
return pxsFmt(
L"%s plugin returned an error while saving the state.\n\n",
tbl_PluginInfo[PluginId].shortname
);
@ -731,7 +747,7 @@ wxString Exception::FreezePluginFailure::FormatDisplayMessage() const
wxString Exception::ThawPluginFailure::FormatDiagnosticMessage() const
{
return wxsFormat(
return pxsFmt(
L"%s plugin returned an error while loading the state.\n\n",
tbl_PluginInfo[PluginId].shortname
);

View File

@ -57,12 +57,15 @@ namespace Exception
// Exception thrown when a corrupted or truncated savestate is encountered.
class SaveStateLoadError : public BadStream
{
DEFINE_STREAM_EXCEPTION( SaveStateLoadError, BadStream, wxLt("The savestate appears to be corrupt or incomplete.") )
DEFINE_STREAM_EXCEPTION( SaveStateLoadError, BadStream )
virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const;
};
class PluginError : public RuntimeError
{
DEFINE_RUNTIME_EXCEPTION( PluginError, RuntimeError, L"Generic plugin error")
DEFINE_RUNTIME_EXCEPTION( PluginError, RuntimeError, L"Generic plugin error!" )
public:
PluginsEnum_t PluginId;

View File

@ -108,57 +108,39 @@ void RecompiledCodeReserve::OnCommittedBlock( void* block )
}
}
void RecompiledCodeReserve::ResetProcessReserves() const
void SysOutOfMemory_EmergencyResponse(uptr blocksize)
{
// An out of memory error occurred. All we can try to do in response is reset the various
// recompiler caches (which can sometimes total over 120megs, so it can be quite helpful).
// If the user is using interpreters, or if the memory allocation failure was on a very small
// allocation, then this code could fail; but that's fine. We're already trying harder than
// 99.995% of all programs ever written. -- air
if (Cpu)
{
Cpu->SetCacheReserve( (Cpu->GetCacheReserve() * 3) / 2 );
Cpu->Reset();
}
if (CpuVU0)
{
CpuVU0->SetCacheReserve( (CpuVU0->GetCacheReserve() * 3) / 2 );
CpuVU0->Reset();
}
if (CpuVU1)
{
CpuVU1->SetCacheReserve( (CpuVU1->GetCacheReserve() * 3) / 2 );
CpuVU1->Reset();
}
if (psxCpu)
{
psxCpu->SetCacheReserve( (psxCpu->GetCacheReserve() * 3) / 2 );
psxCpu->Reset();
}
// Default behavior for out of memory: the
void RecompiledCodeReserve::OnOutOfMemory( const Exception::OutOfMemory& ex, void* blockptr, bool& handled )
{
// Since the recompiler is happy writing away to memory, we have to truncate the reserve
// to include the page currently being accessed, and cannot go any smaller. This will
// allow the rec to finish emitting the current block of instructions, detect that it has
// exceeded the threshold buffer, and reset the buffer on its own.
// Note: We attempt to commit multiple pages first, since a single block of recompiled
// code can pretty easily surpass 4k. We should have enough for this, since we just
// cleared the other rec caches above -- but who knows what could happen if the user
// has another process sucking up RAM or if the operating system is fickle. If even
// that fails, give up and kill the process.
try
{
// Truncate and reset reserves of all other in-use recompiler caches, as this should
// help free up quite a bit of emergency memory.
ResetProcessReserves();
uint cusion = std::min<uint>( m_blocksize, 4 );
HostSys::MmapCommitPtr((u8*)blockptr, cusion * __pagesize, m_prot_mode);
handled = true;
}
catch (Exception::BaseException&)
{
// Fickle has become our reality. By setting handled to FALSE, the OS should kill
// the process for us. No point trying to log anything; this is a super-awesomely
// serious condition that likely means the system is hosed. ;)
handled = false;
}
}
#if _MSC_VER
# include "svnrev.h"

View File

@ -134,6 +134,7 @@ extern SysCpuProviderPack& GetCpuProviders();
extern void SysLogMachineCaps(); // Detects cpu type and fills cpuInfo structs.
extern void SysClearExecutionCache(); // clears recompiled execution caches!
extern void SysOutOfMemory_EmergencyResponse(uptr blocksize);
extern u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller="Unnamed");
extern void vSyncDebugStuff( uint frame );

View File

@ -21,9 +21,7 @@
// RecompiledCodeReserve
// --------------------------------------------------------------------------------------
// A recompiled code reserve is a simple sequential-growth block of memory which is auto-
// cleared to INT 3 (0xcc) as needed. When using this class, care should be take to re-
// implement the provided OnOutOfMemory handler so that it clears other recompiled memory
// reserves that are known to be attached to the process.
// cleared to INT 3 (0xcc) as needed.
//
class RecompiledCodeReserve : public BaseVirtualMemoryReserve
{
@ -44,7 +42,6 @@ public:
virtual void* Reserve( uint size, uptr base=0, uptr upper_bounds=0 );
virtual void OnCommittedBlock( void* block );
virtual void OnOutOfMemory( const Exception::OutOfMemory& ex, void* blockptr, bool& handled );
virtual RecompiledCodeReserve& SetProfilerName( const wxString& shortname );
virtual RecompiledCodeReserve& SetProfilerName( const char* shortname )

View File

@ -528,6 +528,8 @@ bool Pcsx2App::OnInit()
InitCPUTicks();
pxDoAssert = AppDoAssert;
pxDoOutOfMemory = SysOutOfMemory_EmergencyResponse;
g_Conf = new AppConfig();
wxInitAllImageHandlers();

View File

@ -59,7 +59,15 @@ namespace Exception
class NotEnumerablePlugin : public BadStream
{
public:
DEFINE_STREAM_EXCEPTION( NotEnumerablePlugin, BadStream, wxLt("File is not a PCSX2 plugin") );
DEFINE_STREAM_EXCEPTION( NotEnumerablePlugin, BadStream );
wxString FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("File is not a PCSX2 plugin");
_formatDiagMsg(retval);
return retval;
}
};
}

View File

@ -62,21 +62,6 @@ static void SaveStateFile_ReadHeader( IStreamReader& thr )
.SetUserMsg(_("Cannot load this savestate. The state is an unsupported version, likely created by a newer edition of PCSX2."));
};
class gzError : public Exception::BadStream
{
DEFINE_STREAM_EXCEPTION( gzError, BadStream, wxLt("Invalid or corrupted gzip archive") )
};
class gzReadError : public gzError
{
};
class gzWriteError : public gzError
{
};
// --------------------------------------------------------------------------------------
// gzipReader
// --------------------------------------------------------------------------------------

View File

@ -451,10 +451,6 @@
RelativePath="..\..\NakedAsm.h"
>
</File>
<File
RelativePath="..\..\System\PageFaultSource.h"
>
</File>
<File
RelativePath="..\..\Plugins.h"
>

View File

@ -46,7 +46,7 @@ void StreamException_ThrowLastError( const wxString& streamname, HANDLE result )
default:
{
throw Exception::Stream( streamname ).SetDiagMsg(wxsFormat( L"General Win32 File/stream error [GetLastError: %d]", error ));
throw Exception::BadStream( streamname ).SetDiagMsg(pxsFmt( L"General Win32 File/stream error [GetLastError: %d]", error ));
}
}
}
@ -58,7 +58,7 @@ bool StreamException_LogLastError( const wxString& streamname, const wxChar* act
{
StreamException_ThrowLastError( streamname, result );
}
catch( Exception::Stream& ex )
catch( Exception::BadStream& ex )
{
Console.WriteLn( Color_Yellow, L"%s: %s", action, ex.FormatDiagnosticMessage().c_str() );
return true;