unit CustomDebugOut;
/// Simple override for Windows.OutputDebugString which adds a timestamp, thread id and memory
/// Written by Lars Fosdal, Tine SA, 01 FEB 2010 http://plus.lars.fosdal.com
interface
uses
Windows, SysUtils, DateUtils, SyncObjs;
const
/// For significant system events
SysEvent = '### ';
/// For serious low level error log entries
BadShit = '#BAD: ';
/// For unexpected error log entries
WTF = '#WTF: ';
{$ifdef CI} // Defined in the project options - Should be set if app is Continuous Integration Tests
type
TOnDebugOutput = reference to procedure(const aThreadId: Cardinal; const aStr: pChar);
function HookOutputDebug(const aOnDebugOutput: TOnDebugOutput): TOnDebugOutput;
procedure UnhookOutputDebug;
{$endif}
procedure OutputDebugString(aStr:PChar); {$ifndef CI} {Inline;} {$endif} overload;
procedure OutputDebugString(const aStr:String); {$ifndef CI} Inline; {$endif} overload;
implementation
var
CustomDebugCriticalSection: TCriticalSection;
{$ifdef CI}
threadvar
FOnOutputDebug: TOnDebugOutput;
function HookOutputDebug(const aOnDebugOutput: TOnDebugOutput): TOnDebugOutput;
begin
Result := FOnOutputDebug;
FOnOutputDebug := aOnDebugOutput;
end;
procedure UnhookOutputDebug;
begin
FOnOutputDebug := nil;
end;
procedure CallOutputDebugHook(const aThreadId: Cardinal; const aStr: pChar);
begin
if Assigned(FOnOutputDebug)
then FOnOutputDebug(aThreadId, aStr);
end;
{$endif}
function MemoryUsed: cardinal; Inline;
var
MMS: TMemoryManagerState;
Block: TSmallBlockTypeState;
begin
GetMemoryManagerState(MMS);
Result := MMS.TotalAllocatedMediumBlockSize + MMS.TotalAllocatedLargeBlockSize;
for Block in MMS.SmallBlockTypeStates
do Result := Result + (Block.UseableBlockSize * Block.AllocatedBlockCount);
Result := Result DIV 1024;
end;
function MemoryUsedInK:String; Inline;
begin
Result := ' '+ IntToStr(MemoryUsed) +'k';
end;
procedure OutputDebugString(aStr:PChar); {$ifndef CI} {Inline;} {$endif}
var
tid: Cardinal;
begin
CustomDebugCriticalSection.Acquire;
try
tid := GetCurrentThreadId;
Windows.OutputDebugString(pChar('{'
+ FormatDateTime('hh:nn:ss,zzz ', Now) // time
+ '('+IntToStr(tid)+')' // thread
+ MemoryUsedInK // memory in k
+ '} ' + aStr));
{$ifdef CI}
CallOutputDebugHook(tid, aStr);
{$endif}
finally
CustomDebugCriticalSection.Release;
end;
end;
procedure OutputDebugString(const aStr:String); {$ifndef CI} Inline; {$endif}
begin
OutputDebugString(pChar(aStr));
end;
initialization
CustomDebugCriticalSection := TCriticalSection.Create;
{$ifdef CI}
FOnOutputDebug := nil;
{$endif}
OutputDebugString(SysEvent + 'START' + FormatDateTime(' dd mmm yyyy ', Now) + ParamStr(0));
finalization
{$ifdef CI}
FOnOutputDebug := nil;
{$endif}
OutputDebugString(SysEvent + 'STOP' + FormatDateTime(' dd mmm yyyy ', Now) + ParamStr(0));
CustomDebugCriticalSection.Free;
end.