MODULE WMProfiler;
IMPORT
Modules, Kernel, Strings, HierarchicalProfiler, WMGraphics, WMMessages, WMRestorable,
WMWindowManager, WMComponents, WMStandardComponents, WMEditors, WMTrees, WMErrors, WMProgressComponents;
CONST
DefaultTime = 30;
TYPE
KillerMsg = OBJECT
END KillerMsg;
Window* = OBJECT (WMComponents.FormWindow)
VAR
startBtn, stopBtn,continueBtn, getProfileBtn, flattenBtn, filterBtn : WMStandardComponents.Button;
timeEdit, typeEdit, infoEdit, filterMaskEdit, filterCountEdit : WMEditors.Editor;
tree : WMTrees.Tree;
treeView : WMTrees.TreeView;
lastSelectedNode : HierarchicalProfiler.Node;
lastState : LONGINT;
statusLabel : WMStandardComponents.Label;
progressBar : WMProgressComponents.ProgressBar;
profile : HierarchicalProfiler.Profile;
alive, dead : BOOLEAN;
timer : Kernel.Timer;
PROCEDURE HasVisibleChildren(node : HierarchicalProfiler.Node) : BOOLEAN;
VAR child : HierarchicalProfiler.Node;
BEGIN
ASSERT(node # NIL);
child := node.child;
WHILE (child # NIL) & (child.marked # TRUE) DO child := child.sibling; END;
RETURN (child # NIL);
END HasVisibleChildren;
PROCEDURE AddChildNodes(parent : WMTrees.TreeNode; node : HierarchicalProfiler.Node; expand : BOOLEAN);
VAR n : WMTrees.TreeNode; temp : HierarchicalProfiler.Node;
BEGIN
ASSERT(parent # NIL);
temp := node.child;
node.extern := TRUE;
WHILE (temp # NIL) DO
NEW(n);
tree.AddChildNode(parent, n);
tree.SetNodeCaption(n, temp.GetCaption());
tree.SetNodeData(n, temp);
IF (temp.marked) THEN
tree.ExclNodeState(n, WMTrees.NodeHidden);
IF HasVisibleChildren(temp) THEN tree.InclNodeState(n, WMTrees.NodeSubnodesOnExpand); END;
ELSE
tree.InclNodeState(n, WMTrees.NodeHidden);
END;
IF expand THEN
tree.ExpandToRoot(n);
AddChildNodes(n, temp, FALSE);
END;
temp := temp.sibling;
END;
END AddChildNodes;
PROCEDURE ClearExternField(node : HierarchicalProfiler.Node);
BEGIN
ASSERT(node # NIL);
node.extern := FALSE;
END ClearExternField;
PROCEDURE UpdateTree;
VAR root : WMTrees.TreeNode;
BEGIN
tree.Acquire;
NEW(root);
tree.SetRoot(root);
IF (profile # NIL) THEN
profile.Visit(ClearExternField);
tree.SetNodeCaption(root, profile.nodes.GetCaption());
tree.SetNodeData(root, profile);
AddChildNodes(root, profile.nodes, TRUE);
ELSE
tree.SetNodeCaption(root, Strings.NewString("No profile data"));
END;
tree.Release;
treeView.SetFirstLine(0, TRUE);
END UpdateTree;
PROCEDURE UpdateStatusBar(forceUpdate : BOOLEAN);
VAR
state, currentNofSamples, maxNofSamples : LONGINT;
caption : ARRAY 256 OF CHAR; number : ARRAY 16 OF CHAR;
BEGIN
state := HierarchicalProfiler.GetState(currentNofSamples, maxNofSamples);
IF (state # lastState) OR forceUpdate THEN
IF (state = HierarchicalProfiler.Running) THEN
progressBar.SetRange(0, maxNofSamples);
END;
CASE state OF
|HierarchicalProfiler.Running: caption := "Profiler is running...";
|HierarchicalProfiler.NotRunningNoDataAvailable: caption := "Profiler is not running. No profile data available.";
|HierarchicalProfiler.NotRunningDataAvailable: caption := "Profiler is not running.";
ELSE
caption := "Profiler is in undefined state.";
END;
IF (profile # NIL) THEN
Strings.Append(caption, " [Loaded profile: ");
Strings.IntToStr(profile.nofSamples, number); Strings.Append(caption, number);
Strings.Append(caption, " samples on ");
Strings.IntToStr(profile.nofProcessors, number); Strings.Append(caption, number);
Strings.Append(caption, " processor(s), ");
Strings.IntToStr(profile.nofSamplesNotStored, number); Strings.Append(caption, number);
Strings.Append(caption, " discarded, ");
Strings.IntToStr(profile.nofRunsTooDeep, number); Strings.Append(caption, number);
Strings.Append(caption, " call chain too deep]");
ELSE
Strings.Append(caption, " [No profile loaded]");
END;
statusLabel.caption.SetAOC(caption);
lastState := state;
END;
IF (state = HierarchicalProfiler.Running) THEN
progressBar.SetCurrent(currentNofSamples);
END;
END UpdateStatusBar;
PROCEDURE HandleNodeClicked(sender, data : ANY);
VAR treeNode : WMTrees.TreeNode; node : HierarchicalProfiler.Node; ptr : ANY;
BEGIN
IF (data # NIL) & (data IS WMTrees.TreeNode) THEN
tree.Acquire;
treeNode := data (WMTrees.TreeNode);
ptr := tree.GetNodeData(treeNode);
IF (ptr # NIL) & (ptr IS HierarchicalProfiler.Node) THEN
node := ptr (HierarchicalProfiler.Node);
IF ~node.extern THEN
AddChildNodes(treeNode, node, FALSE);
END;
END;
tree.Release;
END;
END HandleNodeClicked;
PROCEDURE HandleNodeSelected(sender, data : ANY);
VAR ptr : ANY;
BEGIN
IF (data # NIL) & (data IS WMTrees.TreeNode) THEN
tree.Acquire;
ptr := tree.GetNodeData(data(WMTrees.TreeNode));
tree.Release;
IF (ptr # NIL) & (ptr IS HierarchicalProfiler.Node) THEN
lastSelectedNode := ptr (HierarchicalProfiler.Node);
END;
END;
END HandleNodeSelected;
PROCEDURE GetTypeAndInfo(VAR type, info : LONGINT);
VAR string : ARRAY 16 OF CHAR;
BEGIN
type := HierarchicalProfiler.Hierarchical;
typeEdit.GetAsString(string);
Strings.StrToInt(string, type);
info := HierarchicalProfiler.None;
infoEdit.GetAsString(string);
Strings.StrToInt(string, info);
END GetTypeAndInfo;
PROCEDURE GetFilterMask(VAR mask : ARRAY OF CHAR; VAR minPercent : LONGINT);
VAR number : ARRAY 16 OF CHAR;
BEGIN
filterMaskEdit.GetAsString(mask);
filterCountEdit.GetAsString(number);
Strings.StrToInt(number, minPercent);
END GetFilterMask;
PROCEDURE HandleButtons(sender, data : ANY);
VAR
tempProfile : HierarchicalProfiler.Profile;
mask : ARRAY 128 OF CHAR; timeStr : ARRAY 16 OF CHAR;
minPercent : LONGINT;
time, type, info, res : LONGINT;
root : WMTrees.TreeNode;
BEGIN
IF (sender = startBtn) THEN
time := 0;
timeEdit.GetAsString(timeStr);
Strings.StrToInt(timeStr, time);
IF (time > 0) THEN
HierarchicalProfiler.StartProfiling(time, res);
IF (res # HierarchicalProfiler.Ok) THEN WMErrors.Show(res); END;
ELSE
WMErrors.ShowMessage("Invalid sampling time");
END;
ELSIF (sender = stopBtn) THEN
HierarchicalProfiler.StopProfiling(res);
IF (res # HierarchicalProfiler.Ok) THEN WMErrors.Show(res); END;
ELSIF (sender = continueBtn) THEN
HierarchicalProfiler.ContinueProfiling(res);
IF (res # HierarchicalProfiler.Ok) THEN WMErrors.Show(res); END;
ELSIF (sender = flattenBtn) THEN
IF (lastSelectedNode # NIL) THEN
IF (profile # NIL) THEN
profile.Flatten(lastSelectedNode);
UpdateTree;
ELSE
WMErrors.ShowMessage("No profile");
END;
ELSE
WMErrors.ShowMessage("No node selected");
END;
ELSIF (sender = getProfileBtn) THEN
GetTypeAndInfo(type, info);
HierarchicalProfiler.GetProfile(type, info, tempProfile, res);
IF (res = HierarchicalProfiler.Ok) THEN
profile := tempProfile;
UpdateTree;
UpdateStatusBar(TRUE);
ELSE
WMErrors.Show(res);
END;
ELSIF (sender = filterBtn) THEN
IF (profile # NIL) THEN
GetFilterMask(mask, minPercent);
profile.Mark(mask, minPercent);
tree.Acquire;
root := tree.GetRoot();
tree.Release;
UpdateTree;
ELSE
WMErrors.ShowMessage("No profile");
END;
END;
END HandleButtons;
PROCEDURE CreateForm() : WMComponents.VisualComponent;
VAR
panel, toolbar, filterbar, statusbar : WMStandardComponents.Panel;
label : WMStandardComponents.Label;
timeStr : ARRAY 16 OF CHAR;
BEGIN
NEW(panel); panel.alignment.Set(WMComponents.AlignClient); panel.fillColor.Set(WMGraphics.White);
NEW(toolbar); toolbar.alignment.Set(WMComponents.AlignTop); toolbar.bounds.SetHeight(20);
panel.AddContent(toolbar);
NEW(startBtn); startBtn.alignment.Set(WMComponents.AlignLeft);
startBtn.caption.SetAOC("Start");
startBtn.onClick.Add(HandleButtons);
toolbar.AddContent(startBtn);
NEW(stopBtn); stopBtn.alignment.Set(WMComponents.AlignLeft);
stopBtn.caption.SetAOC("Stop");
stopBtn.onClick.Add(HandleButtons);
toolbar.AddContent(stopBtn);
NEW(continueBtn); continueBtn.alignment.Set(WMComponents.AlignLeft);
continueBtn.caption.SetAOC("Continue");
continueBtn.onClick.Add(HandleButtons);
toolbar.AddContent(continueBtn);
NEW(getProfileBtn); getProfileBtn.alignment.Set(WMComponents.AlignLeft);
getProfileBtn.bounds.SetWidth(80);
getProfileBtn.caption.SetAOC("Get Profile");
getProfileBtn.onClick.Add(HandleButtons);
toolbar.AddContent(getProfileBtn);
NEW(label); label.alignment.Set(WMComponents.AlignLeft); label.bounds.SetWidth(120);
label.caption.SetAOC(" Max. sampling time: ");
toolbar.AddContent(label);
NEW(timeEdit); timeEdit.alignment.Set(WMComponents.AlignLeft); timeEdit.bounds.SetWidth(40);
timeEdit.multiLine.Set(FALSE);
timeEdit.tv.showBorder.Set(TRUE);
Strings.IntToStr(DefaultTime, timeStr);
timeEdit.SetAsString(timeStr);
toolbar.AddContent(timeEdit);
NEW(label); label.alignment.Set(WMComponents.AlignLeft); label.bounds.SetWidth(45);
label.caption.SetAOC(" seconds");
toolbar.AddContent(label);
NEW(label); label.alignment.Set(WMComponents.AlignLeft); label.bounds.SetWidth(35);
label.caption.SetAOC(" Type: ");
toolbar.AddContent(label);
NEW(typeEdit); typeEdit.alignment.Set(WMComponents.AlignLeft); typeEdit.bounds.SetWidth(40);
typeEdit.multiLine.Set(FALSE);
typeEdit.tv.showBorder.Set(TRUE);
Strings.IntToStr(HierarchicalProfiler.Hierarchical, timeStr);
typeEdit.SetAsString(timeStr);
toolbar.AddContent(typeEdit);
NEW(label); label.alignment.Set(WMComponents.AlignLeft); label.bounds.SetWidth(30);
label.caption.SetAOC(" Info: ");
toolbar.AddContent(label);
NEW(infoEdit); infoEdit.alignment.Set(WMComponents.AlignLeft); infoEdit.bounds.SetWidth(40);
infoEdit.multiLine.Set(FALSE);
infoEdit.tv.showBorder.Set(TRUE);
Strings.IntToStr(0, timeStr);
infoEdit.SetAsString(timeStr);
toolbar.AddContent(infoEdit);
NEW(flattenBtn); flattenBtn.alignment.Set(WMComponents.AlignRight);
flattenBtn.caption.SetAOC("Flatten");
flattenBtn.onClick.Add(HandleButtons);
toolbar.AddContent(flattenBtn);
NEW(statusbar); statusbar.alignment.Set(WMComponents.AlignBottom); statusbar.bounds.SetHeight(20);
statusbar.fillColor.Set(0CCCCCCFFH);
panel.AddContent(statusbar);
NEW(progressBar); progressBar.alignment.Set(WMComponents.AlignRight);
progressBar.bounds.SetWidth(80);
progressBar.color.Set(WMGraphics.Blue);
progressBar.textColor.Set(WMGraphics.White);
progressBar.SetRange(0, 100);
progressBar.SetCurrent(0);
progressBar.showPercents.Set(TRUE);
statusbar.AddContent(progressBar);
NEW(statusLabel); statusLabel.alignment.Set(WMComponents.AlignClient);
statusbar.AddContent(statusLabel);
NEW(filterbar); filterbar.alignment.Set(WMComponents.AlignBottom); filterbar.bounds.SetHeight(20);
panel.AddContent(filterbar);
NEW(label); label.alignment.Set(WMComponents.AlignLeft); label.bounds.SetWidth(110);
label.caption.SetAOC(" Filter mask: Name: ");
filterbar.AddContent(label);
NEW(filterBtn); filterBtn.alignment.Set(WMComponents.AlignRight);
filterBtn.caption.SetAOC("Apply");
filterBtn.onClick.Add(HandleButtons);
filterbar.AddContent(filterBtn);
NEW(filterCountEdit); filterCountEdit.alignment.Set(WMComponents.AlignRight); filterCountEdit.bounds.SetWidth(40);
filterCountEdit.multiLine.Set(FALSE);
filterCountEdit.tv.showBorder.Set(TRUE);
filterCountEdit.SetAsString("0");
filterbar.AddContent(filterCountEdit);
NEW(label); label.alignment.Set(WMComponents.AlignRight); label.bounds.SetWidth(40);
label.caption.SetAOC(" Min %: ");
filterbar.AddContent(label);
NEW(filterMaskEdit); filterMaskEdit.alignment.Set(WMComponents.AlignClient);
filterMaskEdit.multiLine.Set(FALSE);
filterMaskEdit.tv.showBorder.Set(TRUE);
filterMaskEdit.SetAsString("*");
filterbar.AddContent(filterMaskEdit);
NEW(treeView); treeView.alignment.Set(WMComponents.AlignClient);
treeView.onSelectNode.Add(HandleNodeSelected);
tree := treeView.GetTree();
tree.beforeExpand.Add(HandleNodeClicked);
panel.AddContent(treeView);
RETURN panel;
END CreateForm;
PROCEDURE &New*(context : WMRestorable.Context);
BEGIN
NEW(timer);
alive := TRUE; dead := FALSE;
lastSelectedNode := NIL;
lastState := -1;
profile := NIL;
Init(640, 480, FALSE);
SetContent(CreateForm());
UpdateStatusBar(TRUE);
SetTitle(Strings.NewString("Profiler"));
IF (context # NIL) THEN
WMRestorable.AddByContext(SELF, context);
Resized(context.r - context.l, context.b - context.t);
ELSE
WMWindowManager.DefaultAddWindow(SELF)
END;
IncCount;
END New;
PROCEDURE Handle(VAR x : WMMessages.Message);
BEGIN
IF (x.msgType = WMMessages.MsgExt) & (x.ext # NIL) THEN
IF (x.ext IS KillerMsg) THEN Close
ELSIF (x.ext IS WMRestorable.Storage) THEN
x.ext(WMRestorable.Storage).Add("WMProfiler", "WMProfiler.Restore", SELF, NIL);
ELSE Handle^(x)
END
ELSE Handle^(x)
END
END Handle;
PROCEDURE Close;
BEGIN
Close^;
alive := FALSE;
timer.Wakeup;
BEGIN {EXCLUSIVE} AWAIT(dead); END;
DecCount;
END Close;
BEGIN {ACTIVE}
WHILE alive DO
UpdateStatusBar(FALSE);
timer.Sleep(500);
END;
BEGIN {EXCLUSIVE} dead := TRUE; END;
END Window;
VAR
nofWindows : LONGINT;
PROCEDURE Open*;
VAR window : Window;
BEGIN
NEW(window, NIL);
END Open;
PROCEDURE Restore*(context : WMRestorable.Context);
VAR window : Window;
BEGIN
NEW(window, context);
END Restore;
PROCEDURE IncCount;
BEGIN {EXCLUSIVE}
INC(nofWindows)
END IncCount;
PROCEDURE DecCount;
BEGIN {EXCLUSIVE}
DEC(nofWindows)
END DecCount;
PROCEDURE Cleanup;
VAR die : KillerMsg;
msg : WMMessages.Message;
m : WMWindowManager.WindowManager;
BEGIN {EXCLUSIVE}
NEW(die);
msg.ext := die;
msg.msgType := WMMessages.MsgExt;
m := WMWindowManager.GetDefaultManager();
m.Broadcast(msg);
AWAIT(nofWindows = 0)
END Cleanup;
BEGIN
Modules.InstallTermHandler(Cleanup)
END WMProfiler.
WMProfiler.Open ~
SystemTools.Free WMProfiler ~