mirror of https://github.com/PCSX2/pcsx2.git
456 lines
14 KiB
C++
456 lines
14 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||
*
|
||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||
* ation, either version 3 of the License, or (at your option) any later version.
|
||
*
|
||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||
* PURPOSE. See the GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||
* If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
// Avoid the following gcc error:
|
||
// Exceptions.cpp:133: error: inlining failed in call to always_inline ‘void pxOnAssert(const DiagnosticOrigin&, const char*)’: function not considered for inlining
|
||
// DEVASSERT_INLINE void pxOnAssert( const DiagnosticOrigin& origin, const char* msg)
|
||
// Exceptions.cpp:141: error: called from here
|
||
// pxOnAssert( origin, WX_STR(msg) ); // wc_str ???
|
||
//
|
||
// Feel free to provide a better fix
|
||
#if defined(__linux__) && defined(NDEBUG)
|
||
#undef NDEBUG
|
||
#endif
|
||
|
||
#include "PrecompiledHeader.h"
|
||
|
||
#include <wx/app.h>
|
||
#include "Threading.h"
|
||
#include "TlsVariable.inl"
|
||
|
||
#if defined(__UNIX__)
|
||
#include <signal.h>
|
||
#endif
|
||
|
||
// for lack of a better place...
|
||
Fnptr_OutOfMemory pxDoOutOfMemory = NULL;
|
||
|
||
// ------------------------------------------------------------------------
|
||
// Force DevAssert to *not* inline for devel builds (allows using breakpoints to trap assertions,
|
||
// and force it to inline for release builds (optimizes it out completely since IsDevBuild is a
|
||
// const false).
|
||
//
|
||
#ifdef PCSX2_DEVBUILD
|
||
# define DEVASSERT_INLINE __noinline
|
||
#else
|
||
# define DEVASSERT_INLINE __fi
|
||
#endif
|
||
|
||
// Using a threadlocal assertion guard. Separate threads can assert at the same time.
|
||
// That's ok. What we don't want is the *same* thread recurse-asserting.
|
||
static DeclareTls(int) s_assert_guard( 0 );
|
||
|
||
pxDoAssertFnType* pxDoAssert = pxAssertImpl_LogIt;
|
||
|
||
// make life easier for people using VC++ IDE by using this format, which allows double-click
|
||
// response times from the Output window...
|
||
wxString DiagnosticOrigin::ToString( const wxChar* msg ) const
|
||
{
|
||
FastFormatUnicode message;
|
||
|
||
message.Write( L"%ls(%d) : assertion failed:\n", srcfile, line );
|
||
|
||
if( function != NULL )
|
||
message.Write( " Function: %s\n", function );
|
||
|
||
message.Write(L" Thread: %s\n", WX_STR(Threading::pxGetCurrentThreadName()) );
|
||
|
||
if( condition != NULL )
|
||
message.Write(L" Condition: %ls\n", condition);
|
||
|
||
if( msg != NULL )
|
||
message.Write(L" Message: %ls\n", msg);
|
||
|
||
return message;
|
||
}
|
||
|
||
|
||
// Because wxTrap isn't available on Linux builds of wxWidgets (non-Debug, typically)
|
||
void pxTrap()
|
||
{
|
||
#if defined(__WXMSW__) && !defined(__WXMICROWIN__)
|
||
__debugbreak();
|
||
#elif defined(__WXMAC__) && !defined(__DARWIN__)
|
||
#if __powerc
|
||
Debugger();
|
||
#else
|
||
SysBreak();
|
||
#endif
|
||
#elif defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
|
||
Debugger();
|
||
#elif defined(__UNIX__)
|
||
raise(SIGTRAP);
|
||
#else
|
||
// TODO
|
||
#endif // Win/Unix
|
||
}
|
||
|
||
|
||
bool pxAssertImpl_LogIt( const DiagnosticOrigin& origin, const wxChar *msg )
|
||
{
|
||
//wxLogError( L"%s", origin.ToString( msg ).c_str() );
|
||
wxMessageOutputDebug().Printf( L"%s", origin.ToString( msg ).c_str() );
|
||
pxTrap();
|
||
return false;
|
||
}
|
||
|
||
|
||
DEVASSERT_INLINE void pxOnAssert( const DiagnosticOrigin& origin, const wxChar* msg )
|
||
{
|
||
// Recursion guard: Allow at least one recursive call. This is useful because sometimes
|
||
// we get meaningless assertions while unwinding stack traces after exceptions have occurred.
|
||
|
||
RecursionGuard guard( s_assert_guard );
|
||
if (guard.Counter > 2) { return pxTrap(); }
|
||
|
||
// wxWidgets doesn't come with debug builds on some Linux distros, and other distros make
|
||
// it difficult to use the debug build (compilation failures). To handle these I've had to
|
||
// bypass the internal wxWidgets assertion handler entirely, since it may not exist even if
|
||
// PCSX2 itself is compiled in debug mode (assertions enabled).
|
||
|
||
bool trapit;
|
||
|
||
if( pxDoAssert == NULL )
|
||
{
|
||
// Note: Format uses MSVC's syntax for output window hotlinking.
|
||
trapit = pxAssertImpl_LogIt( origin, msg );
|
||
}
|
||
else
|
||
{
|
||
trapit = pxDoAssert( origin, msg );
|
||
}
|
||
|
||
if( trapit ) { pxTrap(); }
|
||
}
|
||
|
||
__fi void pxOnAssert( const DiagnosticOrigin& origin, const char* msg)
|
||
{
|
||
pxOnAssert( origin, fromUTF8(msg) );
|
||
}
|
||
|
||
#if wxMAJOR_VERSION >= 3
|
||
__fi void pxOnAssert( const DiagnosticOrigin& origin, const wxString& msg)
|
||
{
|
||
pxOnAssert( origin, WX_STR(msg) ); // wc_str ???
|
||
}
|
||
|
||
__fi void pxOnAssert( const DiagnosticOrigin& origin, const FastFormatUnicode& msg)
|
||
{
|
||
pxOnAssert( origin, msg.c_str());
|
||
}
|
||
#endif
|
||
|
||
// --------------------------------------------------------------------------------------
|
||
// BaseException (implementations)
|
||
// --------------------------------------------------------------------------------------
|
||
|
||
BaseException::~BaseException() throw() {}
|
||
|
||
BaseException& BaseException::SetBothMsgs( const wxChar* msg_diag )
|
||
{
|
||
#if wxMAJOR_VERSION >= 3
|
||
m_message_user = msg_diag ? wxString(wxGetTranslation( msg_diag )) : wxString("");
|
||
#else
|
||
m_message_user = msg_diag ? wxGetTranslation( msg_diag ) : wxEmptyString;
|
||
#endif
|
||
return SetDiagMsg( msg_diag );
|
||
}
|
||
|
||
BaseException& BaseException::SetDiagMsg( const wxString& msg_diag )
|
||
{
|
||
m_message_diag = msg_diag;
|
||
return *this;
|
||
}
|
||
|
||
BaseException& BaseException::SetUserMsg( const wxString& msg_user )
|
||
{
|
||
m_message_user = msg_user;
|
||
return *this;
|
||
}
|
||
|
||
wxString BaseException::FormatDiagnosticMessage() const
|
||
{
|
||
return m_message_diag;
|
||
}
|
||
|
||
wxString BaseException::FormatDisplayMessage() const
|
||
{
|
||
return m_message_user.IsEmpty() ? m_message_diag : m_message_user;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------------------
|
||
// Exception::RuntimeError (implementations)
|
||
// --------------------------------------------------------------------------------------
|
||
Exception::RuntimeError::RuntimeError( const std::runtime_error& ex, const wxString& prefix )
|
||
{
|
||
IsSilent = false;
|
||
|
||
SetDiagMsg( pxsFmt( L"STL Runtime Error%s: %s",
|
||
(prefix.IsEmpty() ? L"" : pxsFmt(L" (%s)", WX_STR(prefix)).c_str()),
|
||
WX_STR(fromUTF8( ex.what() ))
|
||
) );
|
||
}
|
||
|
||
Exception::RuntimeError::RuntimeError( const std::exception& ex, const wxString& prefix )
|
||
{
|
||
IsSilent = false;
|
||
|
||
SetDiagMsg( pxsFmt( L"STL Exception%s: %s",
|
||
(prefix.IsEmpty() ? L"" : pxsFmt(L" (%s)", WX_STR(prefix)).c_str()),
|
||
WX_STR(fromUTF8( ex.what() ))
|
||
) );
|
||
}
|
||
|
||
// --------------------------------------------------------------------------------------
|
||
// Exception::OutOfMemory (implementations)
|
||
// --------------------------------------------------------------------------------------
|
||
Exception::OutOfMemory::OutOfMemory( const wxString& allocdesc )
|
||
{
|
||
AllocDescription = allocdesc;
|
||
}
|
||
|
||
wxString Exception::OutOfMemory::FormatDiagnosticMessage() const
|
||
{
|
||
FastFormatUnicode retmsg;
|
||
retmsg.Write(L"Out of memory");
|
||
if (!AllocDescription.IsEmpty())
|
||
retmsg.Write(L" while allocating '%s'", WX_STR(AllocDescription));
|
||
|
||
if (!m_message_diag.IsEmpty())
|
||
retmsg.Write(L":\n%s", WX_STR(m_message_diag));
|
||
|
||
return retmsg;
|
||
}
|
||
|
||
wxString Exception::OutOfMemory::FormatDisplayMessage() const
|
||
{
|
||
FastFormatUnicode retmsg;
|
||
retmsg.Write( L"%s", _("Oh noes! Out of memory!") );
|
||
|
||
if (!m_message_user.IsEmpty())
|
||
retmsg.Write(L"\n\n%s", WX_STR(m_message_user));
|
||
|
||
return retmsg;
|
||
}
|
||
|
||
|
||
// --------------------------------------------------------------------------------------
|
||
// Exception::VirtualMemoryMapConflict (implementations)
|
||
// --------------------------------------------------------------------------------------
|
||
Exception::VirtualMemoryMapConflict::VirtualMemoryMapConflict( const wxString& allocdesc )
|
||
{
|
||
AllocDescription = allocdesc;
|
||
m_message_user = _("Virtual memory mapping failure! Your system may have conflicting device drivers, services, or may simply have insufficient memory or resources to meet PCSX2's lofty needs.");
|
||
}
|
||
|
||
wxString Exception::VirtualMemoryMapConflict::FormatDiagnosticMessage() const
|
||
{
|
||
FastFormatUnicode retmsg;
|
||
retmsg.Write(L"Virtual memory map failed");
|
||
if (!AllocDescription.IsEmpty())
|
||
retmsg.Write(L" while reserving '%s'", WX_STR(AllocDescription));
|
||
|
||
if (!m_message_diag.IsEmpty())
|
||
retmsg.Write(L":\n%s", WX_STR(m_message_diag));
|
||
|
||
return retmsg;
|
||
}
|
||
|
||
wxString Exception::VirtualMemoryMapConflict::FormatDisplayMessage() const
|
||
{
|
||
FastFormatUnicode retmsg;
|
||
retmsg.Write( L"%s",
|
||
pxE( L"There is not enough virtual memory available, or necessary virtual memory mappings have already been reserved by other processes, services, or DLLs."
|
||
)
|
||
);
|
||
|
||
if (!m_message_diag.IsEmpty())
|
||
retmsg.Write(L"\n\n%s", WX_STR(m_message_diag));
|
||
|
||
return retmsg;
|
||
}
|
||
|
||
|
||
// ------------------------------------------------------------------------
|
||
wxString Exception::CancelEvent::FormatDiagnosticMessage() const
|
||
{
|
||
return L"Action canceled: " + m_message_diag;
|
||
}
|
||
|
||
wxString Exception::CancelEvent::FormatDisplayMessage() const
|
||
{
|
||
return L"Action canceled: " + m_message_diag;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------------------
|
||
// Exception::BadStream (implementations)
|
||
// --------------------------------------------------------------------------------------
|
||
wxString Exception::BadStream::FormatDiagnosticMessage() const
|
||
{
|
||
FastFormatUnicode retval;
|
||
_formatDiagMsg(retval);
|
||
return retval;
|
||
}
|
||
|
||
wxString Exception::BadStream::FormatDisplayMessage() const
|
||
{
|
||
FastFormatUnicode retval;
|
||
_formatUserMsg(retval);
|
||
return retval;
|
||
}
|
||
|
||
void Exception::BadStream::_formatDiagMsg( FastFormatUnicode& dest ) const
|
||
{
|
||
dest.Write( L"Path: " );
|
||
if (!StreamName.IsEmpty())
|
||
dest.Write( L"%s", WX_STR(StreamName) );
|
||
else
|
||
dest.Write( L"[Unnamed or unknown]" );
|
||
|
||
if (!m_message_diag.IsEmpty())
|
||
dest.Write(L"\n%s", WX_STR(m_message_diag));
|
||
}
|
||
|
||
void Exception::BadStream::_formatUserMsg( FastFormatUnicode& dest ) const
|
||
{
|
||
dest.Write( _("Path: ") );
|
||
if (!StreamName.IsEmpty())
|
||
dest.Write( L"%s", WX_STR(StreamName) );
|
||
else
|
||
dest.Write( _("[Unnamed or unknown]") );
|
||
|
||
if (!m_message_user.IsEmpty())
|
||
dest.Write(L"\n%s", WX_STR(m_message_user));
|
||
}
|
||
|
||
// --------------------------------------------------------------------------------------
|
||
// 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."));
|
||
retval.Write("\n");
|
||
_formatUserMsg(retval);
|
||
return retval;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------------------
|
||
// Exception::FileNotFound (implementations)
|
||
// --------------------------------------------------------------------------------------
|
||
wxString Exception::FileNotFound::FormatDiagnosticMessage() const
|
||
{
|
||
FastFormatUnicode retval;
|
||
retval.Write("File not found.\n");
|
||
_formatDiagMsg(retval);
|
||
return retval;
|
||
}
|
||
|
||
wxString Exception::FileNotFound::FormatDisplayMessage() const
|
||
{
|
||
FastFormatUnicode retval;
|
||
retval.Write(_("File not found."));
|
||
retval.Write("\n");
|
||
_formatUserMsg(retval);
|
||
return retval;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------------------
|
||
// Exception::AccessDenied (implementations)
|
||
// --------------------------------------------------------------------------------------
|
||
wxString Exception::AccessDenied::FormatDiagnosticMessage() const
|
||
{
|
||
FastFormatUnicode retval;
|
||
retval.Write("Permission denied to file.\n");
|
||
_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."));
|
||
retval.Write("\n");
|
||
_formatUserMsg(retval);
|
||
return retval;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------------------
|
||
// Exception::EndOfStream (implementations)
|
||
// --------------------------------------------------------------------------------------
|
||
wxString Exception::EndOfStream::FormatDiagnosticMessage() const
|
||
{
|
||
FastFormatUnicode retval;
|
||
retval.Write("Unexpected end of file or stream.\n");
|
||
_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."));
|
||
retval.Write("\n");
|
||
_formatUserMsg(retval);
|
||
return retval;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------------------
|
||
// Exceptions from Errno (POSIX)
|
||
// --------------------------------------------------------------------------------------
|
||
|
||
// Translates an Errno code into an exception.
|
||
// Throws an exception based on the given error code (usually taken from ANSI C's errno)
|
||
BaseException* Exception::FromErrno( const wxString& streamname, int errcode )
|
||
{
|
||
pxAssumeDev( errcode != 0, "Invalid NULL error code? (errno)" );
|
||
|
||
switch( errcode )
|
||
{
|
||
case EINVAL:
|
||
pxFailDev( L"Invalid argument" );
|
||
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 );
|
||
|
||
case EMFILE: // Too many open files!
|
||
return &(new Exception::CannotCreateStream( streamname ))->SetDiagMsg(L"Too many open files"); // File handle allocation failure
|
||
|
||
case EEXIST:
|
||
return &(new Exception::CannotCreateStream( streamname ))->SetDiagMsg(L"File already exists");
|
||
|
||
case ENOENT: // File not found!
|
||
return new Exception::FileNotFound( streamname );
|
||
|
||
case EPIPE:
|
||
return &(new Exception::BadStream( streamname ))->SetDiagMsg(L"Broken pipe");
|
||
|
||
case EBADF:
|
||
return &(new Exception::BadStream( streamname ))->SetDiagMsg(L"Bad file number");
|
||
|
||
default:
|
||
return &(new Exception::BadStream( streamname ))->SetDiagMsg(pxsFmt( L"General file/stream error [errno: %d]", errcode ));
|
||
}
|
||
}
|