123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632 |
- MODULE WMProcessInfo; (** AUTHOR "tf/staubesv"; PURPOSE "Components for process visualization"; *)
- IMPORT
- SYSTEM, KernelLog,
- Streams, Machine, Modules, Objects, Kernel, Reflection, Strings, ProcessInfo, XML, Commands,
- WMGraphics, WMProperties, WMComponents, WMStandardComponents, WMGrids, WMStringGrids,
- WMPopups, WMDialogs;
- CONST
- (* ProcessSelector.sort *)
- None* = 0;
- ID* = 1;
- Priority* = 2;
- Mode* = 3;
- (* States *)
- Paused = 0; Running = 3; RunningRefresh = 4; Terminating = 99; Terminated = 100;
- DefaultRefreshInterval = 500;
- DefaultInterleave = 2;
- MaxNofProcesses = 1000;
- TYPE
- Selection* = POINTER TO ARRAY OF Objects.Process;
- ProcessSelector* = OBJECT (WMComponents.VisualComponent)
- VAR
- sort- : WMProperties.Int32Property;
- sortI : LONGINT;
- processes : ARRAY MaxNofProcesses OF Objects.Process;
- nofProcesses : LONGINT;
- colWidth : WMGrids.Spacings;
- grid : WMStringGrids.StringGrid;
- lastProcTime : HUGEINT;
- sw : Streams.StringWriter; (* Only use in procedure Update *)
- nofUpdates, interval, interleave : LONGINT;
- timer : Kernel.Timer;
- state, currentState : LONGINT;
- PROCEDURE &Init*;
- BEGIN
- Init^;
- NEW(sort, PrototypeSort, NIL, NIL); properties.Add(sort);
- sortI := sort.Get();
- ProcessInfo.Clear(processes);
- nofProcesses := 0;
- NEW(colWidth, 11);
- grid := CreateGrid();
- AddContent(grid);
- lastProcTime := Machine.GetTimer();
- NEW(sw, 128);
- nofUpdates := 0;
- interval := DefaultRefreshInterval;
- interleave := DefaultInterleave;
- NEW(timer);
- state := Paused;
- END Init;
- PROCEDURE GetSelection*() : Selection;
- VAR
- processes : ARRAY ProcessInfo.MaxNofProcesses OF Objects.Process;
- nofProcesses : LONGINT;
- scol, srow, ecol, erow, id, i : LONGINT;
- selection : Selection;
- str : Strings.String;
- BEGIN
- selection := NIL;
- grid.Acquire;
- grid.model.Acquire;
- grid.GetSelection(scol, srow, ecol, erow);
- IF (srow >= 0) & (srow <= erow) THEN
- ProcessInfo.GetProcesses(processes, nofProcesses);
- NEW(selection, erow - srow + 1);
- FOR i := srow TO erow DO
- str := grid.model.GetCellText(0, i); (* Get the Process ID *)
- Strings.StrToInt(str^, id);
- IF id > 0 THEN
- selection[i - srow] := ProcessInfo.Find(processes, id); (* may be NIL *)
- ELSE
- selection[i - srow] := NIL;
- END;
- END;
- END;
- grid.model.Release;
- grid.Release;
- RETURN selection;
- END GetSelection;
- PROCEDURE PropertyChanged*(sender, property : ANY);
- BEGIN
- IF (property = sort) THEN
- CheckSort;
- ELSIF (property = visible) THEN
- PropertyChanged^(sender, property);
- CheckVisibility;
- ELSE
- PropertyChanged^(sender, property);
- END;
- END PropertyChanged;
- PROCEDURE RecacheProperties*;
- BEGIN
- RecacheProperties^;
- CheckVisibility;
- CheckSort;
- END RecacheProperties;
- PROCEDURE CheckVisibility;
- BEGIN
- IF visible.Get() THEN Start;
- ELSE Pause;
- END;
- END CheckVisibility;
- PROCEDURE CheckSort;
- BEGIN {EXCLUSIVE}
- sortI := sort.Get();
- IF (state = Running) THEN
- state := RunningRefresh;
- END;
- timer.Wakeup;
- END CheckSort;
- PROCEDURE CreateGrid() : WMStringGrids.StringGrid;
- VAR
- grid : WMStringGrids.StringGrid;
- str : ARRAY 256 OF CHAR;
- i, dx, dy, minWidth : LONGINT;
- f : WMGraphics.Font;
- BEGIN
- NEW(grid); grid.alignment.Set(WMComponents.AlignClient);
- f := WMGraphics.GetFont("Oberon", 12, {});
- grid.fixedCols.Set(2); grid.fixedRows.Set(1);
- grid.SetSelectionMode(WMGrids.GridSelectRows);
- grid.Acquire;
- grid.model.Acquire;
- grid.model.SetNofCols(11);
- grid.model.SetNofRows(2);
- f.GetStringSize("-999999999", minWidth, dy);
- FOR i := 0 TO 5 DO
- GetTitleStr(i, str);
- f.GetStringSize(str, dx, dy);
- colWidth[i] := MAX(dx + 4, 30);
- grid.model.SetCellText(i, 0, Strings.NewString(str));
- grid.model.SetTextAlign(i, 0, WMGraphics.AlignCenter)
- END;
- FOR i := 6 TO 11 - 1 DO
- GetTitleStr(i, str);
- f.GetStringSize(str, dx, dy);
- colWidth[i] := MAX(dx + 4, minWidth+ 40);
- grid.model.SetCellText(i, 0, Strings.NewString(str));
- grid.model.SetTextAlign(i, 0, WMGraphics.AlignCenter)
- END;
- grid.SetColSpacings(colWidth);
- grid.model.Release;
- grid.Release;
- RETURN grid;
- END CreateGrid;
- PROCEDURE Start;
- BEGIN {EXCLUSIVE}
- IF (state < Terminating) THEN state := Running; END;
- END Start;
- PROCEDURE Pause;
- BEGIN {EXCLUSIVE}
- IF (state < Terminating) THEN
- state := Paused;
- END;
- END Pause;
- PROCEDURE Resized*;
- VAR width, height, w, add, i : LONGINT; newColWidth : WMGrids.Spacings;
- BEGIN
- width := bounds.GetWidth(); height := bounds.GetHeight();
- NEW(newColWidth, LEN(colWidth));
- FOR i := 0 TO LEN(colWidth)-1 DO
- w := w + colWidth[i];
- newColWidth[i] := colWidth[i];
- END;
- IF w < width THEN
- add := (width - w) DIV 3;
- INC(newColWidth[6], add);
- INC(newColWidth[8], add);
- INC(newColWidth[9], add);
- colWidth := newColWidth;
- grid.SetColSpacings(colWidth);
- END;
- Resized^;
- END Resized;
- PROCEDURE Update;
- VAR
- cycles : Objects.CpuCyclesArray;
- i, posP, beg : LONGINT;
- mod : Modules.Module;
- str : ARRAY 256 OF CHAR;
- t0, t1 : HUGEINT;
-
- pc, relpc : ADDRESS;
- mode: LONGINT;
- PROCEDURE SetText(line, cell : LONGINT; CONST str : ARRAY OF CHAR);
- VAR s : Strings.String;
- BEGIN
- s := grid.model.GetCellText(cell, line + 1); (* recycle the string *)
- IF s = NIL THEN NEW(s, 64) END;
- COPY(str, s^);
- grid.model.SetTextAlign(cell, line + 1, GetAlign(cell));
- grid.model.SetCellText(cell, line + 1, s)
- END SetText;
-
- PROCEDURE GetValues( p: Objects.Process );
- VAR bp: ADDRESS;
- BEGIN
- Objects.UpdateProcessState( p );
- mode := p.mode;
- pc := p.state.PC;
- bp := p.state.BP;
- mod := Modules.ThisModuleByAdr( pc );
- WHILE (mod = NIL) & (bp # 0) DO
- bp := CheckBP( p, bp );
- IF bp # 0 THEN
- SYSTEM.GET( bp + SIZEOF(ADDRESS), pc ); (* return addr from stack *)
- mod := Modules.ThisModuleByAdr( pc );
- SYSTEM.GET( bp, bp ); (* follow dynamic link *)
- END
- END
- END GetValues;
-
- BEGIN
- t1 := Machine.GetTimer() - lastProcTime;
- lastProcTime := Machine.GetTimer();
- grid.model.Acquire;
- grid.model.SetNofRows(nofProcesses + 1);
- FOR i := 0 TO nofProcesses - 1 DO
- ASSERT(processes[i] # NIL);
- GetValues( processes[i] );
-
- (* PID Process ID - 0 *)
- Strings.IntToStr(processes[i].id, str); SetText(i, 0, str);
- (* CPU - processor number - 1 *)
- Strings.IntToStr(processes[i].procID, str); SetText(i, 1, str);
- (* CPU% - 2 *)
- Objects.GetCpuCycles(processes[i], cycles, FALSE);
- t0 := cycles[0];
- Strings.IntToStr(SHORT ((t0 * 100) DIV t1), str);SetText(i, 2, str);
- (* priority - 3 *)
- Strings.IntToStr(processes[i].priority, str); SetText(i, 3, str);
- (* mode - 4 *)
- sw.Reset; ProcessInfo.WriteMode( mode, sw ); sw.Get(str); SetText(i, 4, str);
- (* PC - 5 *)
- relpc := pc;
- IF (mod # NIL) & (mod.code # NIL) THEN DEC(relpc, ADDRESSOF(mod.code[0])) END;
- Strings.IntToStr(SYSTEM.VAL (LONGINT, relpc), str); SetText(i, 5, str);
- (* active object type - 6 *)
- sw.Reset; ProcessInfo.WriteActiveObject(processes[i], sw); sw.Get(str);
- SetText(i, 6, str);
- (* Module - 7 *)
- IF mod # NIL THEN SetText(i, 7, mod.name)
- ELSE str := "Unknown"; SetText(i, 7, str);
- END;
- (* Procedure - 8 - Module name (prefix) and "pc=xyz" (suffix) suppressed *)
- sw.Reset; Reflection.WriteProc( sw, pc ); sw.Get(str);
- IF (str # "NIL") & (mod#NIL) THEN
- posP := 0;
- REPEAT INC(posP) UNTIL (posP=LEN(str)) OR (str[posP]=0X) OR (str[posP] = "."); (* Skip prefix *)
- INC(posP);
- beg := 0;
- REPEAT
- str[beg] := str[posP];
- INC(beg); INC(posP);
- UNTIL (posP=LEN(str)) OR (str[posP]=0X) OR (str[posP] = " "); (* Here follows "pc=xyz" now suppressed *)
- str[beg] := 0X
- END;
- SetText(i, 8, str);
- (* Waiting on condition - 9 *)
- sw.Reset; ProcessInfo.WriteWaitingOn(processes[i], sw); sw.Get(str);
- SetText(i, 9, str);
- (* Flags - 10 *)
- sw.Reset; ProcessInfo.WriteFlags(processes[i].flags, sw); sw.Get(str);
- SetText(i, 10, str);
- END;
- grid.model.Release;
- lastProcTime := Machine.GetTimer();
- END Update;
- PROCEDURE Refresh;
- VAR proc : ProcessInfo.IsGreaterThanProc;
- BEGIN
- Acquire;
- ProcessInfo.GetProcesses(processes, nofProcesses);
- CASE sortI OF
- | ID: proc := ProcessInfo.SortByID;
- | Priority : proc := ProcessInfo.SortByPriority;
- | Mode : proc := ProcessInfo.SortByMode;
- ELSE
- proc := NIL;
- END;
- IF (proc # NIL) THEN
- ProcessInfo.Sort(processes, nofProcesses, proc);
- END;
- Release;
- END Refresh;
- PROCEDURE Finalize*; (* override *)
- BEGIN
- Finalize^;
- BEGIN {EXCLUSIVE}
- state := Terminating;
- timer.Wakeup
- END;
- END Finalize;
- BEGIN {ACTIVE}
- WHILE (state < Terminating) DO
- BEGIN {EXCLUSIVE}
- AWAIT(state # Paused);
- IF (state = RunningRefresh) THEN
- nofUpdates := 0; (* forces Refresh *)
- state := Running;
- END;
- currentState := state;
- END;
- IF (currentState = Running) THEN
- IF (nofUpdates MOD interleave = 0) THEN
- Refresh;
- END;
- Update;
- INC(nofUpdates);
- timer.Sleep(interval);
- ELSIF (currentState = Paused) THEN
- nofProcesses := 0; ProcessInfo.Clear(processes);
- END;
- END;
- nofProcesses := 0; ProcessInfo.Clear(processes);
- BEGIN {EXCLUSIVE} state := Terminated; END
- END ProcessSelector;
- TYPE
- SortInfo = OBJECT
- VAR
- mode : LONGINT;
- name : ARRAY 64 OF CHAR;
- PROCEDURE &New*(mode : LONGINT; CONST name : ARRAY OF CHAR);
- BEGIN
- SELF.mode := mode;
- COPY(name, SELF.name);
- END New;
- END SortInfo;
- TYPE
- ProcessManager* = OBJECT (WMComponents.VisualComponent)
- VAR
- processSelector : ProcessSelector;
- haltBtn, unbreakHaltBtn, sortBtn, showBtn, cpuLoadBtn : WMStandardComponents.Button;
- sortPopup : WMPopups.Popup;
- nbrOfProcessesLabel : WMStandardComponents.Label;
- toolbar- : WMStandardComponents.Panel;
- PROCEDURE &Init*;
- VAR font : WMGraphics.Font; dx, dy : LONGINT; sortInfo : SortInfo;
- BEGIN
- Init^;
- SetNameAsString(StrProcessManager);
- NEW(toolbar);
- toolbar.bounds.SetHeight(20);
- toolbar.alignment.Set(WMComponents.AlignBottom);
- AddContent(toolbar);
- NEW(haltBtn);
- haltBtn.alignment.Set(WMComponents.AlignLeft);
- haltBtn.SetCaption("Halt process");
- haltBtn.onClick.Add(HandleHalt);
- toolbar.AddContent(haltBtn);
- font := haltBtn.GetFont();
- font.GetStringSize(" Halt process ", dx, dy);
- haltBtn.bounds.SetWidth(dx);
- NEW(unbreakHaltBtn);
- unbreakHaltBtn.alignment.Set(WMComponents.AlignLeft);
- unbreakHaltBtn.SetCaption("Halt process unbreakable");
- unbreakHaltBtn.onClick.Add(HandleUnbreakableHalt);
- toolbar.AddContent(unbreakHaltBtn);
- font := unbreakHaltBtn.GetFont();
- font.GetStringSize(" Halt process unbreakable ", dx, dy);
- unbreakHaltBtn.bounds.SetWidth(dx);
- NEW(sortBtn);
- sortBtn.bounds.SetWidth(80); sortBtn.alignment.Set(WMComponents.AlignLeft);
- sortBtn.caption.SetAOC("SortBy:PID");
- sortBtn.onClick.Add(HandleSort);
- toolbar.AddContent(sortBtn);
- NEW(sortPopup);
- NEW(sortInfo, None, "SortBy:None"); sortPopup.AddParButton("None", HandleSortPopup, sortInfo);
- NEW(sortInfo, ID, "SortBy:ID"); sortPopup.AddParButton("ID", HandleSortPopup, sortInfo);
- NEW(sortInfo, Priority, "SortBy:Priority"); sortPopup.AddParButton("Priority", HandleSortPopup, sortInfo);
- NEW(sortInfo, Mode, "SortBy:Mode"); sortPopup.AddParButton("Mode", HandleSortPopup, sortInfo);
- NEW(showBtn);
- showBtn.alignment.Set(WMComponents.AlignLeft);
- showBtn.SetCaption(" Show Stack ");
- showBtn.onClick.Add(HandleShowStack);
- toolbar.AddContent(showBtn);
- font := showBtn.GetFont();
- font.GetStringSize(" Show Stack ", dx, dy);
- showBtn.bounds.SetWidth(dx);
- NEW(cpuLoadBtn); cpuLoadBtn.alignment.Set(WMComponents.AlignLeft);
- cpuLoadBtn.SetCaption("CPU Load");
- cpuLoadBtn.onClick.Add(HandleCpuLoad);
- toolbar.AddContent(cpuLoadBtn);
- NEW(nbrOfProcessesLabel);
- nbrOfProcessesLabel.alignment.Set(WMComponents.AlignClient);
- nbrOfProcessesLabel.textColor.Set(WMGraphics.White);
- toolbar.AddContent(nbrOfProcessesLabel);
- NEW(processSelector); processSelector.alignment.Set(WMComponents.AlignClient);
- AddContent(processSelector);
- END Init;
- PROCEDURE Decision(CONST message : ARRAY OF CHAR) : BOOLEAN;
- BEGIN
- RETURN WMDialogs.Confirmation("Confirm terminating process", message) = WMDialogs.ResYes;
- END Decision;
- PROCEDURE HaltThread(unbreakable : BOOLEAN);
- VAR selection : Selection; i : LONGINT;
- BEGIN
- selection := processSelector.GetSelection();
- IF (selection # NIL) THEN
- FOR i := 0 TO LEN(selection)-1 DO
- IF (selection[i] # NIL) THEN
- (* Make these checks before acquiring the locks *)
- IF (Objects.Resistant IN selection[i].flags) THEN
- IF Decision("Teminate a resistant process") THEN
- Objects.TerminateThis(selection[i], unbreakable);
- END
- ELSE
- Objects.TerminateThis(selection[i], unbreakable);
- END;
- END;
- END;
- END;
- END HaltThread;
- PROCEDURE HandleHalt(sender, data : ANY);
- BEGIN
- HaltThread(FALSE);
- END HandleHalt;
- PROCEDURE HandleUnbreakableHalt(sender, data : ANY);
- BEGIN
- HaltThread(TRUE);
- END HandleUnbreakableHalt;
- PROCEDURE HandleSort(sender, data : ANY);
- VAR gx, gy : LONGINT;
- BEGIN
- sortBtn.ToWMCoordinates(0, sortBtn.bounds.GetBottom(), gx, gy);
- sortPopup.Popup(gx, gy);
- END HandleSort;
- PROCEDURE HandleSortPopup(sender, data : ANY);
- VAR sortInfo : SortInfo;
- BEGIN
- IF (data # NIL) & (data IS SortInfo) THEN
- sortPopup.Close;
- sortInfo := data(SortInfo);
- sortBtn.caption.SetAOC(sortInfo.name);
- processSelector.sort.Set(sortInfo.mode);
- END;
- END HandleSortPopup;
- PROCEDURE HandleShowStack(sender, data : ANY);
- VAR selection : Selection; w: Streams.Writer; i : LONGINT;
- BEGIN
- selection := processSelector.GetSelection();
- IF (selection # NIL) THEN
- Streams.OpenWriter(w, KernelLog.Send);
- FOR i := 0 TO LEN(selection)-1 DO
- IF (selection[i] # NIL) THEN
- ProcessInfo.ShowStack(selection[i], w);
- w.Ln;
- END;
- END;
- END;
- END HandleShowStack;
- PROCEDURE HandleCpuLoad(sender, data : ANY);
- VAR selection : Selection; i : LONGINT;
- BEGIN
- selection := processSelector.GetSelection();
- IF (selection # NIL) THEN
- FOR i := 0 TO LEN(selection) - 1 DO
- IF (selection[i] # NIL) THEN
- OpenCpuLoadWindow(selection[i].id);
- END;
- END;
- END;
- END HandleCpuLoad;
- END ProcessManager;
- VAR
- StrProcessSelector, StrProcessManager : Strings.String;
- PrototypeSort : WMProperties.Int32Property;
- PROCEDURE GetTitleStr(col : LONGINT; VAR x : ARRAY OF CHAR);
- BEGIN
- CASE col OF
- | 0 : COPY("PID", x)
- | 1 : COPY("CPU #", x)
- | 2 : COPY("CPU %", x)
- | 3 : COPY("Prio", x)
- | 4 : COPY("Mode", x)
- | 5 : COPY("PC", x)
- | 6 : COPY("Active Object", x)
- | 7 : COPY("Module", x)
- | 8 : COPY("Procedure", x)
- | 9 : COPY("Await condition", x)
- | 10 : COPY("Flags", x);
- ELSE COPY("", x);
- END
- END GetTitleStr;
- PROCEDURE GetAlign(col : LONGINT) : LONGINT;
- BEGIN
- CASE col OF
- | 6, 7, 8, 9, 10 : RETURN WMGraphics.AlignLeft;
- | 3, 1 : RETURN WMGraphics.AlignCenter;
- | 0, 2, 4, 5 : RETURN WMGraphics.AlignRight;
- ELSE RETURN WMGraphics.AlignRight
- END
- END GetAlign;
- PROCEDURE CheckBP( p: Objects.Process; bp: ADDRESS ): ADDRESS;
- VAR n: ADDRESS;
- BEGIN
- IF (bp < Objects.GetStackBottom(p)) & (bp > Objects.GetStackBottom(p) - 256*1024) THEN
- SYSTEM.GET(bp, n);
- IF ODD(n) THEN INC(bp, SIZEOF(ADDRESS)) END;
- RETURN bp
- ELSE
- RETURN 0
- END;
- END CheckBP;
- PROCEDURE OpenCpuLoadWindow(pid : LONGINT);
- VAR commandString, msg : ARRAY 128 OF CHAR; nbr : ARRAY 16 OF CHAR; res : WORD;
- BEGIN
- commandString := "WMPerfMonPluginProcesses.Install ";
- Strings.IntToStr(pid, nbr);
- Strings.Append(commandString, nbr);
- Commands.Call(commandString, {}, res, msg);
- IF (res # Commands.Ok) THEN
- KernelLog.String("WMProcessInfo.OpenCpuLoad: Command call failed, res = "); KernelLog.Int(res, 0);
- KernelLog.String(" ("); KernelLog.String(msg); KernelLog.String(")"); KernelLog.Ln;
- END;
- END OpenCpuLoadWindow;
- PROCEDURE InitStrings;
- BEGIN
- StrProcessSelector := Strings.NewString("ProcessSelector");
- StrProcessManager := Strings.NewString("ProcessManager");
- END InitStrings;
- PROCEDURE InitPrototypes;
- BEGIN
- NEW(PrototypeSort, NIL, Strings.NewString("sort"), Strings.NewString("Sort process list by 0: None, 1: ID, 2: Priority, 3: Mode"));
- PrototypeSort.Set(ID);
- END InitPrototypes;
- PROCEDURE GenProcessSelector*() : XML.Element;
- VAR ps : ProcessSelector;
- BEGIN
- NEW(ps); RETURN ps;
- END GenProcessSelector;
- PROCEDURE GenProcessManager*() : XML.Element;
- VAR pm : ProcessManager;
- BEGIN
- NEW(pm); RETURN pm;
- END GenProcessManager;
- BEGIN
- InitStrings;
- InitPrototypes;
- END WMProcessInfo.
- System.Free WMProcessInfo ~
|