Files
yapetavi/udebug.pas
2022-10-24 22:07:20 +02:00

359 lines
9.9 KiB
ObjectPascal

{
********************************************************************************
YaPeTaVi - Yet another Periodic Table Viewer
Copyright (C) 2000-2012 Geoffray Levasseur <geoffray.levasseurbrandin@numericable.fr>.
All rights reserved.
http://www.geoffray-levasseur.org/
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 3 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.
********************************************************************************
Description:
Console debuging facilities
}
unit uDebug;
{$mode objfpc}{$H+}
interface
uses
Classes;
type
TVerboseLevel = (vlFull, vlHigh, vlLow, vlNone);
var
DebugLevel: Integer;
VerboseLevel: TVerboseLevel;
LogError: Boolean;
sLogFileName: string;
FLogFile: Text;
// init logging facilities
function InitDbg(const FileName: string): Boolean;
// close logging file
procedure TerminateDbg;
// print and log messages
procedure PrintDbg(Mes: string; Verbosity: TVerboseLevel);
procedure PrintDbg(Mes: string);
procedure PrintLnDbg(Mes: string; Verbosity: TVerboseLevel);
procedure PrintLnDbg(Mes: TStringList; Verbosity: TVerboseLevel);
procedure PrintLnDbg(Verbosity: TVerboseLevel);
procedure PrintLnDbg(Mes: string);
procedure PrintDbgStat(Stat: Boolean; Verbosity: TVerboseLevel);
procedure PrintDbgResult(Res: Boolean; Verbosity: TVerboseLevel);
procedure PrintDbgResult(Res: string; Verbosity: TVerboseLevel);
// put full informations about heap inside a string
function GetHeapStatus(aHS: THeapStatus): string;
implementation
uses
Crt, SysUtils;
const
dbgStatOk = '-';
dbgStatWarn = '!';
dbgOk = 'Ok';
dbgFail = 'Fail';
sTimeJump = ' '; //number of space = time length with braces
resourcestring
rsErrorBase = '*** ERROR: ';
rsWarningBase = '! WARNING: ';
rsInfoBase = 'Info: ';
rsDebugBase = '[Debug]: ';
rsErrorLogrotate = 'E logrotate failed on file %s';
rsErrorButLogrotate = 'E logrotate worked but the log file %s still exists';
rsErrorCreatingLogfile = 'E could not create log file %s';
rsErrorWritingLogFile = 'E unable to write in logfile %s';
rsWarningNoLogfile = 'W no log file will be created, ' +
'logging on stdout (console) only';
rsInfoLoggingStarted = 'Logging started: %s';
rsInfoLoggingTerminated = 'Closing log file %s: application terminated';
procedure LoggingError(Msg: string);
begin
LogError := True;
PrintLnDbg(Msg, vlLow);
PrintLnDbg(rsWarningNoLogfile, vlLow);
end;
//create logfile and rotate old ones (10 max)
function InitDbg(const FileName: string): Boolean;
var
Count: Byte;
I: Integer;
F: File;
Orig, Dest: string;
begin
{$I-}
//init some vars
Count := 1;
Result := True;
//first step: do the logrotate (10 logfile max fixed)
if FileExists(Trim(FileName)) then
while FileExists(Trim(FileName) + '.' + IntToStr(Count)) do
Inc(Count)
else
Count := 0;
if Count > 0 then
for I := Count downto 1 do
begin
Assign(F, Trim(FileName) + '.' + IntToStr(I - 1));
if I = 1 then
Orig := Trim(FileName)
else
Orig := Trim(FileName) + '.' + IntToStr(I - 1);
Dest := Trim(FileName) + '.' + IntToStr(I);
if I >= 10 then
if not FileIsReadOnly(Orig) then
Erase(F)
else
begin
LoggingError(Format(rsErrorLogrotate, [Trim(FileName)]));
Result := False;
Exit;
end
else
if (not FileIsReadOnly(Orig)) and (not FileExists(Dest)) then
RenameFile(Orig, Dest)
else
begin
LoggingError(Format(rsErrorLogrotate, [Trim(FileName)]));
Result := False;
Exit;
end;
end;
{$I+}
//second step: check if file can be safely created and create it
if FileExists(Trim(FileName)) then
begin
LoggingError(Format(rsErrorButLogrotate, [Trim(FileName)]));
Result := False;
Exit;
end;
Assign(FLogFile, Trim(FileName));
{$I-}
Rewrite(FLogFile);
{$I+}
if IOResult <> 0 then
begin
LoggingError(Format(rsErrorCreatingLogfile, [Trim(FileName)]));
Result := False;
Exit;
end;
//third step: write header and assign vars
LogError := False;
sLogFileName := Trim(FileName);
PrintLnDbg(Format(rsInfoLoggingStarted, [sLogFileName]), vlLow);
end;
procedure TerminateDbg;
begin
PrintLnDbg(Format(rsInfoLoggingTerminated, [sLogFileName]), vlLow);
if (not LogError) and (Trim(sLogFileName) <> '') then
CloseFile(FLogFile);
end;
function VerbosityToInt(V: TVerboseLevel): integer;
begin
Result := 4; //default is all the messages to be shown
case V of
vlFull: Result := 4;
vlHigh: Result := 2;
vlLow: Result := 1;
vlNone: Result := 0; //nothing is displayed
end;
end;
procedure PrintDbg(Mes: string; Verbosity: TVerboseLevel);
var
I: integer;
Err: Boolean;
C: Char;
LineBegining: Boolean;
begin
//showing debugging message only if Debug is defined
{$IFNDEF Debug}
if LineBegining and (Length(Mes) > 2) and (Mes[2] = ' ') and
(Mes[1] = 'D') then
Exit;
{$ENDIF}
//if it's not meant to be displayed get out of here
if VerbosityToInt(Verbosity) > VerbosityToInt(VerboseLevel) then
Exit;
//init error state
{$I-}
Err := False;
//display time only if we are on a new line
try
LineBegining := WhereX <= 1;
if LineBegining or ((Length(Mes) > Length(sTimeJump)) and
(Copy(Mes, 1, Length(sTimeJump)) = sTimeJump)) then
begin
Write('[' + TimeToStr(Time) + '] ');
if (not LogError) and (Trim(sLogFileName) <> '') then
begin
Write(FLogFile, '[' + TimeToStr(Time) + '] ');
Err := (IOResult <> 0) or Err;
end;
//this is used in case where indentation is needed in log
if DebugLevel > 0 then
for I := 0 to DebugLevel do
begin
Write(' '); //this is 3 spaces
if (not LogError) and (Trim(sLogFileName) <> '') then
begin
Write(FLogFile, ' '); //this is 3 spaces
Err := (IOResult <> 0) or Err;
end;
end;
end;
// we use the first char to know if it's error warning or info
if LineBegining and (Length(Mes) > 2) and (Mes[2] = ' ') and
(Mes[1] in ['E', 'W', 'i', 'D']) then
begin
C := Mes[1];
Mes := Copy(Mes, 3, Length(Mes) - 2);
case C of
'E': Mes := rsErrorBase + Mes;
'W': Mes := rsWarningBase + Mes;
'i': Mes := rsInfoBase + Mes;
'D': Mes := rsDebugBase + Mes;
end;
end;
//write the message
Write(Mes);
if (not LogError) and (Trim(sLogFileName) <> '') then
begin
Write(FLogFile, Mes);
Err := (IOResult <> 0) or Err;
end;
except
Err := True;
end;
{$I+}
//force truely writing the file
if not LogError then
Flush(FLogFile);
//if error occurs somewhere, trigger an error
if Err then
LoggingError(Format(rsErrorWritingLogFile, [sLogFileName]));
end;
procedure PrintDbg(Mes: string);
begin
PrintDbg(Mes, vlFull);
end;
procedure PrintLnDbg(Mes: TStringList; Verbosity: TVerboseLevel);
var
I: Integer;
begin
if VerbosityToInt(Verbosity) > VerbosityToInt(VerboseLevel) then
Exit;
for I := 0 to Mes.Count - 1 do
if I = 0 then
PrintLnDbg(Mes[I], Verbosity)
else
PrintDbg(sTimeJump + Mes[I] + sLineBreak, Verbosity);
end;
procedure PrintLnDbg(Mes: string);
begin
PrintLnDbg(Mes, vlFull);
end;
procedure PrintLnDbg(Mes: string; Verbosity: TVerboseLevel);
begin
if VerbosityToInt(Verbosity) > VerbosityToInt(VerboseLevel) then
Exit;
PrintDbg(Mes + sLineBreak, Verbosity);
end;
procedure PrintLnDbg(Verbosity: TVerboseLevel);
begin
if VerbosityToInt(Verbosity) > VerbosityToInt(VerboseLevel) then
Exit;
Writeln;
if (not LogError) and (Trim(sLogFileName) <> '') then
Writeln(FLogFile);
end;
procedure PrintDbgStat(Stat: Boolean; Verbosity: TVerboseLevel);
begin
if VerbosityToInt(Verbosity) > VerbosityToInt(VerboseLevel) then
Exit;
if Stat then
PrintDbg(dbgStatOk, Verbosity)
else
PrintDbg(dbgStatWarn, Verbosity);
end;
procedure PrintDbgResult(Res: Boolean; Verbosity: TVerboseLevel);
begin
if VerbosityToInt(Verbosity) > VerbosityToInt(VerboseLevel) then
Exit;
if Res then
PrintLnDbg(dbgOk, Verbosity)
else
PrintLnDbg(dbgFail, Verbosity);
end;
procedure PrintDbgResult(Res: string; Verbosity: TVerboseLevel);
begin
if VerbosityToInt(Verbosity) > VerbosityToInt(VerboseLevel) then
Exit;
PrintLnDbg(Res, Verbosity);
end;
function GetHeapStatus(aHS: THeapStatus): string;
begin
Result := Format(
'Heap Status: ' + LineEnding + LineEnding +
'Addr Space: ' + #9 + ' %d ' + LineEnding +
'Uncommitted: ' + #9 + ' %d ' + LineEnding +
'Committed: ' + #9 + ' %d ' + LineEnding +
'Allocated: ' + #9 + ' %d ' + LineEnding +
'Free: ' + #9 + ' %d ' + LineEnding +
'Free Small: ' + #9 + ' %d ' + LineEnding +
'Free Big: ' + #9 + ' %d ' + LineEnding +
'Unused: ' + #9 + ' %d ' + LineEnding +
'Overhead: ' + #9 + ' %d ' + LineEnding +
'Heap Errorcode:' + #9 + ' %d ',
[aHS.TotalAddrSpace, aHS.TotalUncommitted, aHS.TotalCommitted,
aHS.TotalAllocated, aHS.TotalFree, aHS.FreeSmall, aHS.FreeBig,
aHS.Unused, aHS.Overhead, aHS.HeapErrorCode]);
end;
initialization
{$IFDEF Debug}
VerboseLevel := vlFull;
{$ELSE}
VerboseLevel := vlLow;
{$ENDIF}
DebugLevel := 0;
LogError := True; //means that we can't write in the log file as it's not opened
end.