Win32: Added stdout and stderr pipe support for the ConsoleLogger; such that plugins using printf or fprintf will have their messages show up in the new-style console log and in the emuLog.txt file.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1897 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-09-21 03:33:35 +00:00
parent fc623d0719
commit 8d91458c95
15 changed files with 354 additions and 72 deletions

View File

@ -41,7 +41,7 @@
# define DeclareNoncopyableObject(classname) \
private: \
explicit classname(const classname&); \
classname& operator=(const classname&);
classname& operator=(const classname&)
#endif
//////////////////////////////////////////////////////////////////////////////////////////

View File

@ -62,7 +62,7 @@ extern void pcsx2_aligned_free(void* pmem);
template< typename T >
class SafeArray
{
DeclareNoncopyableObject(SafeArray)
DeclareNoncopyableObject(SafeArray);
public:
static const int DefaultChunkSize = 0x1000 * sizeof(T);
@ -221,7 +221,7 @@ protected:
template< typename T >
class SafeList
{
DeclareNoncopyableObject(SafeList)
DeclareNoncopyableObject(SafeList);
public:
static const int DefaultChunkSize = 0x80 * sizeof(T);

View File

@ -117,7 +117,7 @@ namespace Threading
//
class PersistentThread
{
DeclareNoncopyableObject(PersistentThread)
DeclareNoncopyableObject(PersistentThread);
protected:
typedef int (*PlainJoeFP)();
@ -165,7 +165,7 @@ namespace Threading
//
class ScopedLock
{
DeclareNoncopyableObject(ScopedLock)
DeclareNoncopyableObject(ScopedLock);
protected:
MutexLock& m_lock;

View File

@ -635,7 +635,7 @@ __forceinline void xWrite( T val )
//
class xSmartJump
{
DeclareNoncopyableObject(xSmartJump)
DeclareNoncopyableObject(xSmartJump);
protected:
u8* m_baseptr; // base address of the instruction (passed to the instruction emitter)

View File

@ -5,7 +5,6 @@
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
// Returns a WXK_* keycode, given osome kinda GKT input mess!
int TranslateGDKtoWXK( u32 keysym )
{
@ -277,3 +276,12 @@ int TranslateGDKtoWXK( u32 keysym )
return key_code;
}
// NewPipeRedir .. Homeless function for now .. This is as good a spot as any.
// Eventually we might be so fancy as to have a linux console pipe to our own console
// window, same as the Win32 one. Not sure how doable it is, and it's not as urgent
// anyway since Linux has better generic console support and commandline piping.
//
PipeRedirectionBase* NewPipeRedir()
{
return NULL;
}

View File

@ -221,7 +221,7 @@ extern EmuPluginBindings EmuPlugins;
//
class PluginManagerBase
{
DeclareNoncopyableObject( PluginManagerBase )
DeclareNoncopyableObject( PluginManagerBase );
public:
PluginManagerBase() {}
@ -250,7 +250,7 @@ public:
//
class PluginManager : public PluginManagerBase
{
DeclareNoncopyableObject( PluginManager )
DeclareNoncopyableObject( PluginManager );
protected:
struct PluginStatus_t

View File

@ -16,9 +16,7 @@
#pragma once
// This shouldn't break Win compiles, but it does.
#ifdef __LINUX__
#include "PS2Edefs.h"
#endif
#include "System.h"
// Savestate Versioning!

View File

@ -296,10 +296,10 @@ protected:
wxScopedPtr<wxImageList> m_ToolbarImages;
wxScopedPtr<wxBitmap> m_Bitmap_Logo;
wxScopedPtr<SysCoreAllocations> m_CoreAllocs;
wxScopedPtr<PipeRedirectionBase>m_PipeRedirHandle;
public:
wxScopedPtr<SysCoreAllocations> m_CoreAllocs;
wxScopedPtr<PluginManager> m_CorePlugins;
wxScopedPtr<SysCoreThread> m_CoreThread;

View File

@ -393,7 +393,16 @@ bool Pcsx2App::OnInit()
g_Conf.reset( new AppConfig() );
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() );
try
{
m_PipeRedirHandle.reset( NewPipeRedir() );
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() );
}
catch( Exception::RuntimeError& ex )
{
// Entirely non-critical errors. Log 'em and move along.
Console::Error( ex.FormatDiagnosticMessage() );
}
#define pxMessageBoxEventThing(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxMessageBoxEventFunction, &func )
@ -420,6 +429,8 @@ bool Pcsx2App::OnInit()
try
{
InitDefaultGlobalAccelerators();
delete wxLog::SetActiveTarget( new pxLogConsole() );
ReadUserModeSettings();
@ -725,7 +736,6 @@ Pcsx2App::Pcsx2App() :
{
SetAppName( L"pcsx2" );
BuildCommandHash();
InitDefaultGlobalAccelerators();
}
Pcsx2App::~Pcsx2App()

View File

@ -22,7 +22,7 @@
#include <wx/file.h>
#include <wx/textfile.h>
// Custom ConsoleLogger, because the built-in wxWidgets one is poop.
using Console::Colors;
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE(wxEVT_LOG_Write, -1)
@ -37,7 +37,8 @@ DEFINE_EVENT_TYPE(wxEVT_SetTitleText)
DEFINE_EVENT_TYPE(wxEVT_DockConsole)
DEFINE_EVENT_TYPE(wxEVT_SemaphoreWait)
using Console::Colors;
// C++ requires abstract destructors to exist, even thought hey're abstract.
PipeRedirectionBase::~PipeRedirectionBase() throw() {}
// ----------------------------------------------------------------------------
//

View File

@ -26,11 +26,31 @@ using namespace Threading;
class LogWriteEvent;
// --------------------------------------------------------------------------------------
// PipeRedirectionBase
// --------------------------------------------------------------------------------------
// Implementations for this class are found in Win/Lnx specific modules. Class creation
// should be done using NewPipeRedir() only (hence the protected constructor in this class).
//
class PipeRedirectionBase
{
DeclareNoncopyableObject( PipeRedirectionBase );
//////////////////////////////////////////////////////////////////////////////////////////
// pxLogConsole
public:
virtual ~PipeRedirectionBase() throw()=0; // abstract destructor, forces abstract class behavior
protected:
PipeRedirectionBase() {}
};
extern PipeRedirectionBase* NewPipeRedir();
// --------------------------------------------------------------------------------------
// pxLogConsole
// --------------------------------------------------------------------------------------
// This is a custom logging facility that pipes wxLog messages to our very own console
// log window.
// log window. Useful for catching and redirecting wx's internal logs (although like
// 3/4ths of them are worthless and we would probably rather ignore them anyway).
//
class pxLogConsole : public wxLog
{
@ -41,10 +61,12 @@ protected:
virtual void DoLog(wxLogLevel level, const wxChar *szString, time_t t);
};
//////////////////////////////////////////////////////////////////////////////////////////
// ConsoleThreadTest -- useful class for unit testing the thread safety and general performance
// of the console logger.
//
// --------------------------------------------------------------------------------------
// ConsoleThreadTest -- useful class for unit testing the thread safety and general performance
// of the console logger.
// --------------------------------------------------------------------------------------
class ConsoleTestThread : public PersistentThread
{
protected:
@ -63,12 +85,12 @@ public:
}
};
//////////////////////////////////////////////////////////////////////////////////////////
//
// --------------------------------------------------------------------------------------
// ConsoleLogFrame -- Because the one built in wx is poop.
// --------------------------------------------------------------------------------------
class ConsoleLogFrame : public wxFrame
{
DeclareNoncopyableObject(ConsoleLogFrame)
DeclareNoncopyableObject(ConsoleLogFrame);
public:
typedef AppConfig::ConsoleLogOptions ConLogConfig;
@ -76,7 +98,7 @@ public:
protected:
class ColorArray
{
DeclareNoncopyableObject(ColorArray)
DeclareNoncopyableObject(ColorArray);
protected:
SafeArray<wxTextAttr> m_table;

View File

@ -1890,6 +1890,14 @@
</File>
</Filter>
</Filter>
<Filter
Name="Windows"
>
<File
RelativePath="..\WinSysExec.cpp"
>
</File>
</Filter>
</Filter>
<Filter
Name="AppHost"
@ -2528,7 +2536,7 @@
>
</File>
<File
RelativePath="..\WinSysExec.cpp"
RelativePath="..\WinConsolePipe.cpp"
>
</File>
</Filter>

View File

@ -0,0 +1,276 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2009 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/>.
*/
#include "PrecompiledHeader.h"
#include "Win32.h"
#include "App.h"
#include "ConsoleLogger.h"
// --------------------------------------------------------------------------------------
// Exception::Win32Error
// --------------------------------------------------------------------------------------
namespace Exception
{
class Win32Error : public RuntimeError
{
public:
int ErrorId;
public:
DEFINE_EXCEPTION_COPYTORS( Win32Error )
Win32Error( const char* msg="" )
{
ErrorId = GetLastError();
BaseException::InitBaseEx( msg );
}
wxString GetMsgFromWindows() const
{
if (!ErrorId)
return wxString();
const DWORD BUF_LEN = 2048;
TCHAR t_Msg[BUF_LEN];
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, ErrorId, 0, t_Msg, BUF_LEN, 0))
return wxsFormat( L"Win32 Error #%d: %s", ErrorId, t_Msg );
return wxsFormat( L"Win32 Error #%d (no text msg available)", ErrorId );
}
virtual wxString FormatDisplayMessage() const
{
return m_message_user + L"\n\n" + GetMsgFromWindows();
}
virtual wxString FormatDiagnosticMessage() const
{
return m_message_diag + L"\n\t" + GetMsgFromWindows();
}
};
}
// --------------------------------------------------------------------------------------
// Win32 Console Pipes
// As a courtesy and convenience, we redirect stdout/stderr to the console and logfile.
// --------------------------------------------------------------------------------------
using namespace Threading;
static void CreatePipe( HANDLE& ph_Pipe, HANDLE& ph_File )
{
// Create a threadsafe unique name for the Pipe
static int s32_Counter = 0;
wxString s_PipeName;
s_PipeName.Printf( L"\\\\.\\pipe\\pcsxPipe%X_%X_%X_%X",
GetCurrentProcessId(), GetCurrentThreadId(), GetTickCount(), s32_Counter++);
SECURITY_ATTRIBUTES k_Secur;
k_Secur.nLength = sizeof(SECURITY_ATTRIBUTES);
k_Secur.lpSecurityDescriptor = 0;
k_Secur.bInheritHandle = TRUE;
ph_Pipe = CreateNamedPipe(s_PipeName, PIPE_ACCESS_DUPLEX, 0, 1, 2048, 2048, 0, &k_Secur);
if (ph_Pipe == INVALID_HANDLE_VALUE)
throw Exception::Win32Error( "Error creating Named Pipe." );
ph_File = CreateFile(s_PipeName, GENERIC_READ|GENERIC_WRITE, 0, &k_Secur, OPEN_EXISTING, 0, NULL);
if (ph_File == INVALID_HANDLE_VALUE)
throw Exception::Win32Error( "Error creating Pipe Reader." );
if (!ConnectNamedPipe(ph_Pipe, NULL))
{
if (GetLastError() != ERROR_PIPE_CONNECTED)
throw Exception::Win32Error( "Error connecting Pipe." );
}
SetHandleInformation(ph_Pipe, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(ph_File, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
}
// Reads from the Pipe and appends the read data to ps_Data
// returns TRUE if something was printed to console, or false if the stdout/err were idle.
static bool ReadPipe(HANDLE h_Pipe, Console::Colors color )
{
// IMPORTANT: Check if there is data that can be read.
// The first console output will be lost if ReadFile() is called before data becomes available!
// It does not make any sense but the following 5 lines are indispensable!!
DWORD u32_Avail = 0;
if (!PeekNamedPipe(h_Pipe, 0, 0, 0, &u32_Avail, 0))
throw Exception::Win32Error( "Error peeking Pipe." );
if (!u32_Avail)
return false;
char s8_Buf[2049];
DWORD u32_Read = 0;
do
{
if (!ReadFile(h_Pipe, s8_Buf, sizeof(s8_Buf)-1, &u32_Read, NULL))
{
if (GetLastError() != ERROR_IO_PENDING)
throw Exception::Win32Error( "Error reading Pipe." );
}
// ATTENTION: The Console always prints ANSI to the pipe independent if compiled as UNICODE or MBCS!
s8_Buf[u32_Read] = 0;
OemToCharA(s8_Buf, s8_Buf); // convert DOS codepage -> ANSI
Console::Write( color, s8_Buf ); // convert ANSI -> Unicode if compiled as Unicode
}
while (u32_Read == sizeof(s8_Buf)-1);
return true;
}
class WinPipeThread : public PersistentThread
{
protected:
const HANDLE& mh_OutPipe;
const HANDLE& mh_ErrPipe;
public:
WinPipeThread( const HANDLE& outpipe, const HANDLE& errpipe ) :
mh_OutPipe( outpipe )
, mh_ErrPipe( errpipe )
//, mk_OverOut( overout )
//, mk_OverErr( overerr )
{
}
virtual ~WinPipeThread() throw()
{
PersistentThread::Cancel();
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
}
protected:
int ExecuteTask()
{
try
{
while( true )
{
Sleep( 100 );
pthread_testcancel();
ReadPipe(mh_OutPipe, Color_Black );
ReadPipe(mh_ErrPipe, Color_Red );
}
}
catch( Exception::Win32Error& ex )
{
// Log error, and fail silently. It's not really important if the
// pipe fails. PCSX2 will run fine without it in any case.
Console::Error( ex.FormatDiagnosticMessage() );
return -2;
}
}
};
class WinPipeRedirection : public PipeRedirectionBase
{
DeclareNoncopyableObject( WinPipeRedirection );
protected:
HANDLE mh_OutPipe;
HANDLE mh_ErrPipe;
HANDLE mh_OutFile;
HANDLE mh_ErrFile;
int m_hCrtOut;
int m_hCrtErr;
FILE* h_fpOut;
FILE* h_fpErr;
WinPipeThread m_Thread;
public:
WinPipeRedirection();
virtual ~WinPipeRedirection() throw();
};
WinPipeRedirection::WinPipeRedirection() :
mh_OutPipe(INVALID_HANDLE_VALUE)
, mh_ErrPipe(INVALID_HANDLE_VALUE)
, mh_OutFile(INVALID_HANDLE_VALUE)
, mh_ErrFile(INVALID_HANDLE_VALUE)
, m_hCrtOut(-1)
, m_hCrtErr(-1)
, h_fpOut(NULL)
, h_fpErr(NULL)
, m_Thread( mh_OutPipe, mh_ErrPipe )
{
CreatePipe(mh_OutPipe, mh_OutFile );
CreatePipe(mh_ErrPipe, mh_ErrFile );
SetStdHandle( STD_OUTPUT_HANDLE, mh_OutFile );
SetStdHandle( STD_ERROR_HANDLE, mh_ErrFile );
m_hCrtOut = _open_osfhandle( (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT );
m_hCrtErr = _open_osfhandle( (intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT );
h_fpOut = _fdopen( m_hCrtOut, "w" );
h_fpErr = _fdopen( m_hCrtErr, "w" );
*stdout = *h_fpOut;
*stderr = *h_fpErr;
setvbuf( stdout, NULL, _IONBF, 0 );
setvbuf( stderr, NULL, _IONBF, 0 );
m_Thread.Start();
}
WinPipeRedirection::~WinPipeRedirection() throw()
{
m_Thread.Cancel();
#define safe_CloseHandle( ptr ) \
((void) (( ( ptr != INVALID_HANDLE_VALUE ) && (!!CloseHandle( ptr ), !!0) ), ptr = INVALID_HANDLE_VALUE))
safe_CloseHandle(mh_OutPipe);
safe_CloseHandle(mh_ErrPipe);
if( h_fpOut != NULL )
{
fclose( h_fpOut );
h_fpOut = NULL;
}
if( h_fpErr != NULL )
{
fclose( h_fpErr );
h_fpErr = NULL;
}
#define safe_close( ptr ) \
((void) (( ( ptr != -1 ) && (!!_close( ptr ), !!0) ), ptr = -1))
// CrtOut and CrtErr are closed implicitly when closing fpOut/fpErr
// OutFile and ErrFile are closed implicitly when closing m_hCrtOut/Err
}
// The win32 specific implementation of PipeRedirection.
PipeRedirectionBase* NewPipeRedir()
{
return new WinPipeRedirection();
}

View File

@ -15,11 +15,9 @@
#include "PrecompiledHeader.h"
#include "Win32.h"
#include <winnt.h>
#include "Common.h"
#include "cdvd/CDVD.h"
#include "Common.h"
int SysPageFaultExceptionFilter( EXCEPTION_POINTERS* eps )
{
@ -40,41 +38,3 @@ int SysPageFaultExceptionFilter( EXCEPTION_POINTERS* eps )
return EXCEPTION_CONTINUE_EXECUTION;
}
namespace HostSys
{
void *Mmap(uptr base, u32 size)
{
return VirtualAlloc((void*)base, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
}
void Munmap(uptr base, u32 size)
{
if( base == NULL ) return;
VirtualFree((void*)base, size, MEM_DECOMMIT);
VirtualFree((void*)base, 0, MEM_RELEASE);
}
void MemProtect( void* baseaddr, size_t size, PageProtectionMode mode, bool allowExecution )
{
DWORD winmode = 0;
switch( mode )
{
case Protect_NoAccess:
winmode = ( allowExecution ) ? PAGE_EXECUTE : PAGE_NOACCESS;
break;
case Protect_ReadOnly:
winmode = ( allowExecution ) ? PAGE_EXECUTE_READ : PAGE_READONLY;
break;
case Protect_ReadWrite:
winmode = ( allowExecution ) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
break;
}
DWORD OldProtect; // enjoy my uselessness, yo!
VirtualProtect( baseaddr, size, winmode, &OldProtect );
}
}

View File

@ -1 +0,0 @@
#include "Win32.h"