MODULE DebugLog;
IMPORT SYSTEM, Objects, Machine, Streams, Modules, Random, TextUtilities,
Dates, Strings, WMComponents, WMEditors, WMGraphics, WMStandardComponents,
WM := WMWindowManager;
CONST
Title = "Debug Log";
InitListSize = 8;
TYPE
LogWindow = OBJECT(WMComponents.FormWindow)
VAR
tw- : TextUtilities.TextWriter;
panel : WMStandardComponents.Panel;
out- : WMEditors.Editor;
open : BOOLEAN;
PROCEDURE &New*(CONST title : ARRAY OF CHAR);
VAR toolbar : WMStandardComponents.Panel;
clear : WMStandardComponents.Button;
BEGIN
NEW(panel); panel.bounds.SetExtents(640, 420); panel.fillColor.Set(WMGraphics.RGBAToColor(255, 255, 255, 255));
NEW(toolbar);
toolbar.bounds.SetHeight(20);
toolbar.alignment.Set(WMComponents.AlignTop);
panel.AddContent(toolbar);
NEW(clear);
clear.alignment.Set(WMComponents.AlignLeft);
clear.SetCaption("Clear");
clear.onClick.Add(ClearText);
toolbar.AddContent(clear);
NEW(out); out.alignment.Set(WMComponents.AlignClient); out.tv.showBorder.Set(TRUE); panel.AddContent(out);
Init(panel.bounds.GetWidth(), panel.bounds.GetHeight(), FALSE);
SetContent(panel);
manager := WM.GetDefaultManager();
SetTitle(WMComponents.NewString(title));
WM.DefaultAddWindow(SELF);
NEW(tw, out.text);
open := TRUE
END New;
PROCEDURE Close;
BEGIN
open := FALSE;
Close^
END Close;
PROCEDURE ClearText(sender, data : ANY);
BEGIN
out.text.AcquireWrite();
out.text.Delete(0, out.text.GetLength());
out.tv.firstLine.Set(0); out.tv.cursor.SetPosition(0);
out.text.ReleaseWrite()
END ClearText;
END LogWindow;
TraceSubscriber = OBJECT
VAR
processID : LONGINT;
color : WMGraphics.Color;
date : BOOLEAN;
PROCEDURE &New*(processID : LONGINT; color : WMGraphics.Color; date : BOOLEAN);
BEGIN
SELF.processID := processID;
SELF.color := color;
SELF.date := date
END New;
END TraceSubscriber;
SubscriberList = POINTER TO ARRAY OF TraceSubscriber;
VAR
logwindow : LogWindow;
nrSubscriptions : LONGINT;
subscriptions : SubscriberList;
gen : Random.Sequence;
defaultColor : WMGraphics.Color;
me : Modules.Module;
date : BOOLEAN;
PROCEDURE AlreadySubscribed(processID : LONGINT; VAR sub : TraceSubscriber) : BOOLEAN;
VAR i : LONGINT;
BEGIN
FOR i := 0 TO nrSubscriptions - 1 DO
IF subscriptions[i].processID = processID THEN
IF sub # NIL THEN sub := subscriptions[i] END;
RETURN TRUE
END
END;
sub := NIL;
RETURN FALSE
END AlreadySubscribed;
PROCEDURE SetDate*(new : BOOLEAN);
BEGIN {EXCLUSIVE}
date := new
END SetDate;
PROCEDURE GetDate*() : BOOLEAN;
BEGIN {EXCLUSIVE}
RETURN date
END GetDate;
PROCEDURE GetNextColor() : WMGraphics.Color;
VAR r, g, b : LONGINT;
BEGIN
IF gen = NIL THEN
NEW(gen);
gen.InitSeed(1291)
END;
r := gen.Integer() MOD 100H;
g := gen.Integer() MOD 100H;
b := gen.Integer() MOD 100H;
RETURN WMGraphics.RGBAToColor(r,g,b,0FFH)
END GetNextColor;
PROCEDURE Grow;
VAR tmp : SubscriberList; i : LONGINT;
BEGIN
NEW(tmp, 2 * LEN(subscriptions));
FOR i := 0 TO LEN(subscriptions) - 1 DO
tmp[i] := subscriptions[i]
END;
subscriptions := tmp;
END Grow;
PROCEDURE Subscribe(processID : LONGINT);
VAR sub : TraceSubscriber; color : WMGraphics.Color;
BEGIN
color := GetNextColor();
NEW(sub, processID, color, date);
IF LEN(subscriptions) = nrSubscriptions THEN Grow END;
subscriptions[nrSubscriptions] := sub;
INC(nrSubscriptions)
END Subscribe;
PROCEDURE GetColor(processID : LONGINT) : WMGraphics.Color;
VAR i : LONGINT;
BEGIN
FOR i := 0 TO LEN(subscriptions) - 1 DO
IF subscriptions[i].processID = processID THEN
RETURN subscriptions[i].color
END
END;
RETURN defaultColor
END GetColor;
PROCEDURE GetSubscription(processID : LONGINT) : TraceSubscriber;
VAR i : LONGINT;
BEGIN
FOR i := 0 TO nrSubscriptions - 1 DO
IF subscriptions[i].processID = processID THEN
RETURN subscriptions[i]
END
END;
RETURN NIL
END GetSubscription;
PROCEDURE TraceIdString;
VAR
bp, pc, nextbp : SYSTEM.ADDRESS;
methadr, i : LONGINT;
module : Modules.Module;
process : Objects.Process;
now, name : ARRAY 128 OF CHAR;
ch : CHAR;
out : Streams.Writer;
sub : TraceSubscriber;
BEGIN
IF logwindow = NIL THEN NEW(logwindow, Title) END;
out := logwindow.tw;
process := Objects.CurrentProcess();
IF ~AlreadySubscribed(process.id, sub) THEN
Subscribe(process.id);
END;
sub := GetSubscription(process.id);
IF sub.date # date THEN
date := sub.date
END;
bp := Machine.CurrentBP ();
REPEAT
SYSTEM.GET(bp + SYSTEM.SIZEOF (SYSTEM.ADDRESS), pc);
module := Modules.ThisModuleByAdr(pc);
SYSTEM.GET(bp, bp);
SYSTEM.GET(bp, nextbp)
UNTIL (module # me) OR (nextbp = 0);
DEC(pc, SYSTEM.ADR(module.code[0]));
methadr := FindProc(module.refs, pc);
IF methadr # -1 THEN
i := 0;
ch := module.refs[methadr]; INC(methadr);
WHILE ch # 0X DO
name[i] := ch;
ch := module.refs[methadr];
INC(methadr);
INC(i)
END
END;
name[i] := 0X;
logwindow.tw.SetFontColor(GetColor(process.id));
IF date THEN
Strings.FormatDateTime("yyyy.mm.dd hh.nn.ss ", Dates.Now(), now);
out.String(now);
out.String(" ")
END;
out.String("P"); out.Int(process.procID, 0);out.String(".");out.Int(process.id, 0);
out.Char(" ");out.String(module.name);out.Char(".");out.String(name);out.String("[");
out.Address(pc); out.String("]> ")
END TraceIdString;
PROCEDURE String*(CONST str : ARRAY OF CHAR);
VAR out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.String(str);
out.Ln();
out.Update()
END String;
PROCEDURE TwoStrings*(CONST str1, str2 : ARRAY OF CHAR);
VAR out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.String(str1);
out.String(str2);
out.Ln();
out.Update()
END TwoStrings;
PROCEDURE Boolean*(x : BOOLEAN);
BEGIN
IF x THEN String("TRUE") ELSE String("FALSE") END
END Boolean;
PROCEDURE TraceDebugBoolean*(CONST name : ARRAY OF CHAR; x : BOOLEAN);
BEGIN
IF x THEN
TraceDebugString(name, "TRUE")
ELSE
TraceDebugString(name, "FALSE")
END
END TraceDebugBoolean;
PROCEDURE Memory*(adr: SYSTEM.ADDRESS; size : SYSTEM.SIZE);
VAR i, j : SYSTEM.ADDRESS; ch : CHAR; out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.Ln;
out.Char(0EX);
size := adr+size-1;
FOR i := adr TO size BY 16 DO
out.Address(i);
FOR j := i TO i+15 DO
IF j <= size THEN
SYSTEM.GET(j, ch);
out.Hex(ORD(ch), -3)
ELSE
out.String(" ")
END
END;
out.String(" ");
FOR j := i TO i+15 DO
IF j <= size THEN
SYSTEM.GET(j, ch);
IF (ch < " ") OR (ch >= CHR(127)) THEN ch := "." END;
out.Char(ch)
END
END;
out.Ln
END;
out.Char(0FX);
out.Ln(); out.Update()
END Memory;
PROCEDURE Buffer*(VAR buf : ARRAY OF CHAR; ofs, len : LONGINT);
BEGIN
Memory(SYSTEM.ADR(buf[ofs]), len)
END Buffer;
PROCEDURE HIntHex*(x : HUGEINT; w : LONGINT);
VAR
out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.Hex(SHORT (Machine.ASHH(x, -32)), w-8);
out.Hex(SHORT (x), 8);
out.Ln(); out.Update();
END HIntHex;
PROCEDURE IntSuffix*(x, w : LONGINT; CONST suffix : ARRAY OF CHAR);
CONST K = 1024; M = K*K; G = K*M;
VAR mult : CHAR; out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
IF x MOD K # 0 THEN
out.Int(x, w)
ELSE
IF x MOD M # 0 THEN mult := "K"; x := x DIV K
ELSIF x MOD G # 0 THEN mult := "M"; x := x DIV M
ELSE mult := "G"; x := x DIV G
END;
out.Int(x, w-1); out.Char(mult)
END;
out.String(suffix);
out.Ln(); out.Update()
END IntSuffix;
PROCEDURE Enter*;
END Enter;
PROCEDURE Exit*;
END Exit;
PROCEDURE GetWriter*() : Streams.Writer;
VAR x : Streams.Writer;
BEGIN
NEW(x, Send, 128);
RETURN x
END GetWriter;
PROCEDURE Send*(CONST buf : ARRAY OF CHAR; ofs, len : LONGINT; propagate : BOOLEAN; VAR res : LONGINT);
VAR i : LONGINT; str : POINTER TO ARRAY OF CHAR;
BEGIN
NEW(str, len + 1);
FOR i := 0 TO len - 1 DO
str[i] := buf[ofs + i];
END;
String(str^);
res := Streams.Ok
END Send;
PROCEDURE TraceDebugString*(CONST name, value : ARRAY OF CHAR);
VAR out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.String(name);
out.String(" = ");
out.String(value);
out.Ln();
out.Update()
END TraceDebugString;
PROCEDURE Ln*;
VAR out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.Ln();
out.Update()
END Ln;
PROCEDURE Int*(x, w : LONGINT);
VAR out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.Int(x,w);
out.Ln();
out.Update()
END Int;
PROCEDURE TraceDebugInt*(CONST name : ARRAY OF CHAR; value, w : LONGINT);
VAR
out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.String(name);
out.String(" = ");
out.Int(value, w);
out.Ln();
out.Update();
END TraceDebugInt;
PROCEDURE Hex*(x, w : LONGINT);
VAR
out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.Hex(x,w);
out.Ln();
out.Update()
END Hex;
PROCEDURE TraceDebugHex*(CONST name : ARRAY OF CHAR; value, w : LONGINT);
VAR
out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.String(name);
out.String(" = ");
out.Hex(value,w);
out.Ln();
out.Update()
END TraceDebugHex;
PROCEDURE Char*(c : CHAR);
VAR
out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.Char(c);
out.Ln();
out.Update()
END Char;
PROCEDURE TraceDebugChar*(CONST name : ARRAY OF CHAR; c : CHAR);
VAR
out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.String(name);
out.String(" = ");
out.Char(c);
out.Ln();
out.Update();
END TraceDebugChar;
PROCEDURE Set*(s : SET);
VAR
out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.Set(s);
out.Ln();
out.Update()
END Set;
PROCEDURE TraceDebugSet*(CONST name : ARRAY OF CHAR; s : SET);
VAR
out : Streams.Writer;
BEGIN {EXCLUSIVE}
TraceIdString();
out := logwindow.tw;
out.String(name);
out.String(" = ");
out.Set(s);
out.Ln();
out.Update()
END TraceDebugSet;
PROCEDURE SubscribeProcess*(color : WMGraphics.Color; date, overwrite : BOOLEAN);
VAR
sub : TraceSubscriber;
processID : LONGINT;
p : Objects.Process;
BEGIN {EXCLUSIVE}
p := Objects.CurrentProcess();
processID := p.id;
IF (AlreadySubscribed(processID, sub)) & ~(overwrite) THEN
RETURN
ELSIF AlreadySubscribed(processID, sub) THEN
IF ~CheckColor(color) THEN
color := sub.color;
String("Invalid Color! Left old color.")
END;
sub.date := date
ELSE
IF ~CheckColor(color) THEN
color := GetNextColor();
String("Invalid Color! New color choosen.")
END;
NEW(sub, processID, color, date);
IF LEN(subscriptions) = nrSubscriptions THEN
Grow
END;
subscriptions[nrSubscriptions] := sub;
INC(nrSubscriptions)
END
END SubscribeProcess;
PROCEDURE CheckColor(color : WMGraphics.Color) : BOOLEAN;
VAR
r, g, b, a : LONGINT;
BEGIN
WMGraphics.ColorToRGBA(color, r, g, b, a);
RETURN
( r >= 0 )
& ( g >= 0)
& ( b >= 0)
& ( r <= 255)
& ( g <= 255)
& ( b <= 255)
& ( a = 0FFH);
END CheckColor;
PROCEDURE FindProc(refs : Modules.Bytes; modpc : SYSTEM.ADDRESS) : LONGINT;
VAR i, m, t, proc : LONGINT; ch : CHAR;
BEGIN
proc := -1; i := 0; m := LEN(refs^);
ch := refs[i]; INC(i);
WHILE (i < m) & ((ch = 0F8X) OR (ch = 0F9X)) DO
GetNum(refs, i, t);
IF t > modpc THEN
ch := 0X
ELSE
IF ch = 0F9X THEN
GetNum(refs, i, t);
INC(i, 3)
END;
proc := i;
REPEAT ch := refs[i]; INC(i) UNTIL ch = 0X;
IF i < m THEN
ch := refs[i]; INC(i);
WHILE (i < m) & (ch >= 1X) & (ch <= 3X) DO
ch := refs[i]; INC(i);
IF (ch >= 81X) OR (ch = 16X) OR (ch = 1DX) THEN
GetNum(refs, i, t)
END;
GetNum(refs, i, t);
REPEAT ch := refs[i]; INC(i) UNTIL ch = 0X;
IF i < m THEN ch := refs[i]; INC(i) END
END
END
END
END;
IF (proc = -1) & (i # 0) THEN proc := i END;
RETURN proc
END FindProc;
PROCEDURE GetNum(refs : Modules.Bytes; VAR i, num : LONGINT);
VAR n, s : LONGINT; x : CHAR;
BEGIN
s := 0; n := 0;
x := refs[i]; INC(i);
WHILE ORD(x) >= 128 DO
INC(n, ASH(ORD(x) - 128, s));
INC(s, 7);
x := refs[i]; INC(i)
END;
num := n + ASH(ORD(x) MOD 64 - ORD(x) DIV 64 * 64, s)
END GetNum;
PROCEDURE Open*;
BEGIN
IntOpen();
END Open;
PROCEDURE IntOpen;
BEGIN {EXCLUSIVE}
IF logwindow # NIL THEN
IF ~logwindow.open THEN
WM.DefaultAddWindow(logwindow);
ELSE
WM.DefaultBringToView(logwindow, TRUE)
END
ELSE
NEW(logwindow, Title)
END
END IntOpen;
PROCEDURE Close;
BEGIN {EXCLUSIVE}
IF (logwindow # NIL) & (logwindow.open) THEN
logwindow.Close();
END;
END Close;
BEGIN
date := FALSE;
nrSubscriptions := 0;
NEW(subscriptions, InitListSize);
defaultColor := WMGraphics.RGBAToColor(0,0,0,255);
me := Modules.ThisModuleByAdr(Machine.CurrentPC());
Modules.InstallTermHandler(Close)
END DebugLog.
DebugLog.Open ~
SystemTools.Free DebugLog ~