obs/OBSApi/Utility/XT.cpp
palana 0204fa906c Disable FastAlloc for 64 bit builds
FastAlloc only uses the lower 32 bits of a pointer to index its internal
structures which is a problem for allocations with size >= 0x8001,
because multiple big allocations can have the same values for bit 16 to
bit 32, which causes FastAlloc to discard its info about the previous
allocation
2014-08-17 20:16:03 +02:00

497 lines
11 KiB
C++

/********************************************************************************
Copyright (C) 2001-2012 Hugh Bailey <obs.jim@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
********************************************************************************/
#include "XT.h"
#include <memory>
namespace
{
struct XStringLog
{
XStringLog() : stopped(false) { Reset(); }
void Append(String const &string, bool linefeed=true);
void Append(CTSTR str, UINT len, bool linefeed=true);
void Read(String &str);
void Read(String &str, unsigned &start, unsigned length);
void Stop();
void Clear();
void Reset();
private:
void Process();
String log;
StringList unprocessed, processing;
std::unique_ptr<void, MutexDeleter> append_mutex, process_mutex, read_mutex;
bool stopped;
};
}
////////////////
//globals (oh deary me. if there were a convention convention, I most certainly wouldn't be invited.)
SafeList<DWORD> TickList;
Alloc *MainAllocator = NULL;
BOOL bBaseLoaded = FALSE;
BOOL bDebugBreak = TRUE;
TCHAR lpLogFileName[260] = TEXT("XT.log");
XFile LogFile;
XStringLog StringLog;
LogUpdateCallback LogUpdateProc;
StringList TraceFuncList;
BOOL bLogStarted = FALSE;
void STDCALL CriticalExit();
void STDCALL OpenLogFile();
void STDCALL CloseLogFile();
void STDCALL OSInit();
void STDCALL OSExit();
BOOL STDCALL InitXT(CTSTR logFile, CTSTR allocatorName)
{
if(!bBaseLoaded)
{
if(logFile)
scpy(lpLogFileName, logFile);
OSInit();
ResetXTAllocator(allocatorName);
bBaseLoaded = 1;
}
return TRUE;
}
void STDCALL InitXTLog(CTSTR logFile)
{
if(logFile)
scpy(lpLogFileName, logFile);
}
void STDCALL ResetXTAllocator(CTSTR lpAllocator)
{
StringLog.Stop();
StringLog.Clear();
delete locale;
delete MainAllocator;
if(scmpi(lpAllocator, TEXT("DebugAlloc")) == 0)
MainAllocator = new DebugAlloc;
else if (scmpi(lpAllocator, TEXT("DefaultAlloc")) == 0)
MainAllocator = new DefaultAlloc;
else if (scmpi(lpAllocator, TEXT("SeriousMemoryDebuggingAlloc")) == 0)
MainAllocator = new SeriousMemoryDebuggingAlloc;
else
#if defined(_M_X64) || defined(__amd64__)
MainAllocator = new DefaultAlloc;
#else
MainAllocator = new FastAlloc;
#endif
locale = new LocaleStringLookup;
StringLog.Reset();
}
void STDCALL TerminateXT()
{
if(bBaseLoaded)
{
StringLog.Stop();
FreeProfileData();
delete locale;
locale = NULL;
StringLog.Clear();
if(MainAllocator)
{
Alloc *curAlloc = MainAllocator;
MainAllocator = new DefaultAlloc;
delete curAlloc;
delete MainAllocator;
MainAllocator = NULL;
}
bBaseLoaded = 0;
if(LogFile.IsOpen())
LogFile.Close();
OSExit();
}
}
void STDCALL CriticalExit()
{
if(bBaseLoaded)
{
bBaseLoaded = 0;
if(LogFile.IsOpen())
LogFile.Close();
OSCriticalExit();
}
}
#define MAX_STACK_TRACE 1000
void STDCALL TraceCrash(const TCHAR *trackName)
{
TraceFuncList.Insert(0, trackName);
if(TraceFuncList.Num() > MAX_STACK_TRACE)
TraceFuncList.SetSize(MAX_STACK_TRACE);
}
void STDCALL TraceCrashEnd()
{
String strStackTrace = TEXT("\r\nException Fault - Stack Trace:");
for(unsigned int i=0; i<TraceFuncList.Num(); i++)
{
if(i) strStackTrace << TEXT(" -> ");
if(!(i%10)) strStackTrace << TEXT("\r\n ");
strStackTrace << TraceFuncList[i];
}
if(TraceFuncList.Num() == MAX_STACK_TRACE)
strStackTrace << TEXT(" -> ...");
String strOut = FormattedString(TEXT("%s\r\n"), strStackTrace.Array());
OpenLogFile();
LogFile.WriteAsUTF8(strOut, strOut.Length());
LogFile.WriteAsUTF8(TEXT("\r\n"));
CloseLogFile();
OSMessageBox(TEXT("Error: Exception fault - More info in the log file.\r\n\r\nMake sure you're using the latest verison, otherwise send your log to obs.jim@gmail.com"));
TraceFuncList.Clear();
CriticalExit();
}
void STDCALL OpenLogFile()
{
if(LogFile.IsOpen())
return;
if(!bLogStarted)
{
LogFile.Open(lpLogFileName, XFILE_READ|XFILE_WRITE, XFILE_CREATEALWAYS);
LogFile.Write("\xEF\xBB\xBF", 3);
bLogStarted = TRUE;
}
else
{
//LogFile.Open(lpLogFileName, XFILE_WRITE, XFILE_OPENEXISTING);
//LogFile.SetPos(0, XFILE_END);
}
}
void STDCALL CloseLogFile()
{
//LogFile.Close();
}
void __cdecl LogRaw(const TCHAR *text, UINT len)
{
if(!text) return;
if (!len)
len = slen(text);
OpenLogFile();
LogFile.WriteAsUTF8(text, len);
LogFile.WriteAsUTF8(TEXT("\r\n"));
CloseLogFile();
StringLog.Append(text, len);
}
void __cdecl Logva(const TCHAR *format, va_list argptr)
{
if(!format) return;
#ifdef _DEBUG
OSDebugOutva(format, argptr);
OSDebugOut(L"\n");
#endif
String strCurTime = CurrentTimeString();
strCurTime << TEXT(": ");
String strOut = strCurTime;
strOut << FormattedStringva(format, argptr);
strOut.FindReplace(TEXT("\n"), String() << TEXT("\n") << strCurTime);
OpenLogFile();
LogFile.WriteAsUTF8(strOut, strOut.Length());
LogFile.WriteAsUTF8(TEXT("\r\n"));
CloseLogFile();
StringLog.Append(strOut);
}
void __cdecl Log(const TCHAR *format, ...)
{
if(!format) return;
va_list arglist;
va_start(arglist, format);
Logva(format, arglist);
}
void __cdecl AppWarning(const TCHAR *format, ...)
{
if(!format) return;
va_list arglist;
va_start(arglist, format);
String strOut(L"Warning -- ");
strOut << FormattedStringva(format, arglist);
if(bLogStarted)
{
OpenLogFile();
LogFile.WriteAsUTF8(strOut, strOut.Length());
LogFile.WriteAsUTF8(TEXT("\r\n"));
CloseLogFile();
}
OSDebugOut(TEXT("Warning -- "));
OSDebugOutva(format, arglist);
OSDebugOut(TEXT("\r\n"));
//------------------------------------------------------
// NOTE:
// If you're seeting this, you can safely continue running, but I recommend fixing whatever's causing this warning.
//
// The debug output window contains the warning that has occured.
//------------------------------------------------------
#if defined(_DEBUG) && defined(_WIN32)
if(bDebugBreak && OSDebuggerPresent())
{
ProgramBreak();
}
#endif
StringLog.Append(strOut);
}
//note to self: do try not to rely on this too much.
__declspec(noreturn) void __cdecl CrashError(const TCHAR *format, ...)
{
va_list arglist;
va_start(arglist, format);
String strOut(L"\r\nError: ");
strOut << FormattedStringva(format, arglist);
OpenLogFile();
LogFile.WriteAsUTF8(strOut);
LogFile.WriteStr(TEXT("\r\n"));
CloseLogFile();
OSMessageBoxva(format, arglist);
#if defined(_DEBUG) && defined(_WIN32)
if(bDebugBreak && OSDebuggerPresent())
ProgramBreak();
#endif
CriticalExit();
}
__declspec(noreturn) void __cdecl DumpError(const TCHAR *format, ...)
{
va_list arglist;
va_start(arglist, format);
String strOut(L"\r\nError: ");
strOut << FormattedStringva(format, arglist);
OpenLogFile();
LogFile.WriteAsUTF8(strOut);
LogFile.WriteStr(TEXT("\r\n"));
CloseLogFile();
OSMessageBoxva(format, arglist);
OSRaiseException(0xDEAD0B5);
}
String CurrentTimeString()
{
time_t now = time(0);
struct tm tstruct;
char buf[80];
tstruct = *localtime(&now);
strftime(buf, sizeof(buf), "%X", &tstruct);
return buf;
}
String CurrentDateTimeString()
{
time_t now = time(0);
struct tm tstruct;
char buf[80];
tstruct = *localtime(&now);
strftime(buf, sizeof(buf), "%Y-%m-%d, %X", &tstruct);
return buf;
}
String CurrentLogFilename()
{
return lpLogFileName;
}
void ReadLog(String &data)
{
StringLog.Read(data);
}
void ReadLogPartial(String &data, unsigned &start, unsigned maxLength)
{
StringLog.Read(data, start, maxLength);
}
void ResetLogUpdateCallback(LogUpdateCallback proc)
{
LogUpdateProc = proc;
}
void XStringLog::Append(String const &string, bool linefeed)
{
ScopedLock a(append_mutex);
if (stopped) return;
unprocessed << string;
if (linefeed) unprocessed << L"\r\n";
if (LogUpdateProc) LogUpdateProc();
}
void XStringLog::Append(CTSTR tstr, UINT len, bool linefeed)
{
if (stopped) return;
String str;
str.AppendString(tstr, len);
Append(str, linefeed);
}
void XStringLog::Read(String &str)
{
Process();
ScopedLock r(read_mutex);
str = log;
}
void XStringLog::Read(String &str, unsigned &start, unsigned length)
{
Process();
ScopedLock r(read_mutex);
if (log.Length() == 0 && start) start = 0;
if (start >= log.Length()) return;
if ((UINT_MAX - start) < length) length = UINT_MAX - start;
str = log.Mid(start, ((start+length) > log.Length()) ? log.Length() : (start+length));
start += str.Length();
}
void XStringLog::Process()
{
ScopedLock p(process_mutex, true);
if (!p.locked) return;
if (stopped) return;
{
ScopedLock a(append_mutex);
processing.TransferFrom(unprocessed);
}
String str;
for (unsigned int i = 0; i < processing.Num(); i++)
str << processing[i];
{
ScopedLock r(read_mutex);
log << str;
}
processing.Clear();
}
void XStringLog::Stop()
{
ScopedLock p(process_mutex, false, false); //stop processing
ScopedLock a(append_mutex);
stopped = true;
}
void XStringLog::Clear()
{
unprocessed.Clear();
processing.Clear();
log.Clear();
append_mutex.reset();
process_mutex.reset();
read_mutex.reset();
}
void XStringLog::Reset()
{
Clear();
stopped = false;
append_mutex.reset(OSCreateMutex());
process_mutex.reset(OSCreateMutex());
read_mutex.reset(OSCreateMutex());
}