123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646 |
- (** AUTHOR "Michael Szediwy"; PURPOSE "Debug Log";
- - Shares the interface with KernelLog
- -
- Todo:
- - Mulit-Line support via Enter & Exit
- *)
- 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; (* Color property *)
- date : BOOLEAN; (* Default setting whether the date is printed or not *)
- 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;
- (* From now on tracing is with or without the date string *)
- PROCEDURE SetDate*(new : BOOLEAN);
- BEGIN {EXCLUSIVE}
- date := new
- END SetDate;
- (* Get the current date property. *)
- 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(); (* Get a random color *)
- 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 : 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);
- (* sub must not be NIL *)
- IF sub.date # date THEN
- date := sub.date
- END;
- (* Find the caller outside of this module *)
- bp := SYSTEM.GetFramePointer ();
- REPEAT
- SYSTEM.GET(bp + SIZEOF (ADDRESS), pc);
- module := Modules.ThisModuleByAdr(pc);
- SYSTEM.GET(bp, bp);
- SYSTEM.GET(bp, nextbp)
- UNTIL (module # me) OR (nextbp = 0);
- (* IF bp = 0 the previous PC is kept. This is the PC of the last PAF. *)
- (* Compute module pc *)
- DEC(pc, ADDRESSOF(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;
- (* Trace a string *)
- 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;
- (** Write a block of memory in hex. *)
- PROCEDURE Memory*(adr: ADDRESS; size : SIZE);
- VAR i, j : ADDRESS; ch : CHAR; out : Streams.Writer;
- BEGIN {EXCLUSIVE}
- TraceIdString();
- out := logwindow.tw;
- out.Ln;
- out.Char(0EX); (* "fixed font" *)
- 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); (* "proportional font" *)
- out.Ln(); out.Update()
- END Memory;
- (** Write a buffer in hex. *)
- PROCEDURE Buffer*(VAR buf : ARRAY OF CHAR; ofs, len : LONGINT);
- BEGIN
- Memory(ADDRESSOF(buf[ofs]), len)
- END Buffer;
- (** Write "x" as a hexadecimal number. "w" is the field width. Always prints 16 digits. *)
- PROCEDURE HIntHex*(x : HUGEINT; w : LONGINT);
- VAR
- out : Streams.Writer;
- BEGIN {EXCLUSIVE}
- TraceIdString();
- out := logwindow.tw;
- out.Hex(SHORT (ASH(x, -32)), w-8);
- out.Hex(SHORT (x), 8);
- out.Ln(); out.Update();
- END HIntHex;
- (** Write "x" as a decimal number with a power-of-two multiplier (K, M or G), followed by "suffix". "w" is the field width, excluding "suffix". *)
- 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;
- (* UNSAFE *)
- (** Send the specified characters to the trace output (cf. Streams.Sender). *)
- PROCEDURE Send*(CONST buf : ARRAY OF CHAR; ofs, len : LONGINT; propagate : BOOLEAN; VAR res : WORD);
- 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;
- (* Outputs [name] = [value] *)
- 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;
- (* Trace no message only ID *)
- 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;
- (* Outputs [name] = [value] *)
- 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;
- (* Outputs [name] = [value] *)
- 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;
- (* Outputs [name] = [value] *)
- 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;
- (* Outputs [name] = [value] *)
- 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;
- (*
- These parameter overwrites the the parameter given in a trace procedure.
- Parameter:
- color: The print color for this process.
- date: TRUE, The date will be printed for this process.
- FALSE, The date won't be printed for this process.
- overwrite: TRUE, If the process is already subscribed this parameter indicates
- that the settings are ready to override.
- FALSE, The oposite of TRUE ;-)
- *)
- 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;
- (* Find a procedure in the reference block. Return index of name, or -1 if not found. *)
- PROCEDURE FindProc(refs : Modules.Bytes; modpc : 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 (* proc *)
- GetNum(refs, i, t); (* pofs *)
- IF t > modpc THEN (* previous procedure was the one *)
- ch := 0X (* stop search *)
- ELSE (* ~found *)
- IF ch = 0F9X THEN
- GetNum(refs, i, t); (* nofPars *)
- INC(i, 3) (* RetType, procLev, slFlag *)
- END;
- proc := i; (* remember this position, just before the name *)
- REPEAT ch := refs[i]; INC(i) UNTIL ch = 0X; (* pname *)
- IF i < m THEN
- ch := refs[i]; INC(i); (* 1X | 3X | 0F8X | 0F9X *)
- WHILE (i < m) & (ch >= 1X) & (ch <= 3X) DO (* var *)
- ch := refs[i]; INC(i); (* type *)
- IF (ch >= 81X) OR (ch = 16X) OR (ch = 1DX) THEN
- GetNum(refs, i, t) (* dim/tdadr *)
- END;
- GetNum(refs, i, t); (* vofs *)
- REPEAT ch := refs[i]; INC(i) UNTIL ch = 0X; (* vname *)
- IF i < m THEN ch := refs[i]; INC(i) END (* 1X | 3X | 0F8X | 0F9X *)
- END
- END
- END
- END;
- IF (proc = -1) & (i # 0) THEN proc := i END; (* first procedure *)
- RETURN proc
- END FindProc;
- (* Get a compressed refblk number. *)
- 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 ~
- System.Free DebugLog ~
|