Profiler.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. unit Profiler;
  2. interface
  3. uses
  4. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5. ComCtrls, Dialogs, JvComponent, JvDockControlForm, VirtualTrees, Gauges,
  6. PrecisionTimer, JvExControls, JvgProgress, JvSpecialProgress;
  7. type
  8. PProfilerCall = ^TProfilerCall;
  9. TProfilerCall = record
  10. EnterTime: Int64; // Time when function enter
  11. EnterTimeStr: String;
  12. ExitTime: Int64; // Time when function exit
  13. ExitTimeStr: String;
  14. FctName: String; // Function's name
  15. Source: String;
  16. FctPointer: Pointer; // Functions's pointer
  17. Line: Integer; // Function call line declaration
  18. MemUsage: Double; // Lua's memory usage at the call
  19. Parent: PVirtualNode; // Parent's pointer (pointer to caller's informations structure)
  20. DurationRGauge: TGauge; // Relative duration gauge
  21. DurationOGauge: TGauge; // Overall duration gauge
  22. end;
  23. TfrmProfiler = class(TForm)
  24. JvDockClient1: TJvDockClient;
  25. vstLuaProfiler: TVirtualStringTree;
  26. procedure vstLuaProfilerGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  27. procedure vstLuaProfilerGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
  28. procedure FormDestroy(Sender: TObject);
  29. procedure vstLuaProfilerAfterCellPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; CellRect: TRect);
  30. procedure vstLuaProfilerCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode; var Allowed: Boolean);
  31. procedure FormCreate(Sender: TObject);
  32. private
  33. { Private declarations }
  34. pCurrentCall: PVirtualNode;
  35. pTimer: TPrecisionTimer;
  36. public
  37. { Public declarations }
  38. procedure InitProfiler;
  39. procedure ComputeProfiler;
  40. function AddCall(FctPointer: Pointer; FctName: String; Line: Integer; Source: String; MemUsage: Double): PProfilerCall;
  41. procedure AddReturn(FctPointer: Pointer; FctName: String);
  42. end;
  43. var
  44. frmProfiler: TfrmProfiler;
  45. implementation
  46. uses Types;
  47. {$R *.dfm}
  48. procedure TfrmProfiler.InitProfiler;
  49. var
  50. VTVColumn: TVirtualTreeColumn;
  51. pNode: PVirtualNode;
  52. pNodeData: PProfilerCall;
  53. begin
  54. pCurrentCall := vstLuaProfiler.RootNode;
  55. vstLuaProfiler.BeginUpdate;
  56. pTimer.Init;
  57. pNode := vstLuaProfiler.GetLast();
  58. // Manually clear the tree to free gauges in node data
  59. while Assigned(pNode) do
  60. begin
  61. pNodeData := vstLuaProfiler.GetNodeData(pNode);
  62. FreeAndNil(pNodeData.DurationRGauge);
  63. FreeAndNil(pNodeData.DurationOGauge);
  64. vstLuaProfiler.DeleteNode(pNode);
  65. pNode := vstLuaProfiler.GetLast();
  66. end;
  67. end;
  68. procedure TfrmProfiler.ComputeProfiler;
  69. var
  70. pNode, pMainNode: PVirtualNode;
  71. pNodeData: PProfilerCall;
  72. lpResidualDuration: Int64;
  73. begin
  74. // Computing residual process duration
  75. // This englobe all lines in the script that were not function calls...
  76. pNode := vstLuaProfiler.GetFirstChild(vstLuaProfiler.RootNode);
  77. pMainNode := vstLuaProfiler.GetFirstChild(vstLuaProfiler.RootNode);
  78. pNodeData := vstLuaProfiler.GetNodeData(pNode);
  79. lpResidualDuration := pNodeData.ExitTime - pNodeData.EnterTime;
  80. pNode := vstLuaProfiler.GetNext(pNode);
  81. while pNode <> nil do
  82. begin
  83. pNodeData := vstLuaProfiler.GetNodeData(pNode);
  84. lpResidualDuration := lpResidualDuration - (pNodeData.ExitTime - pNodeData.EnterTime);
  85. pNode := vstLuaProfiler.GetNextSibling(pNode);
  86. end;
  87. // Adding residual process duration node
  88. pNode := vstLuaProfiler.AddChild(pMainNode);
  89. pNodeData := vstLuaProfiler.GetNodeData(pNode);
  90. pNodeData.Parent := pMainNode;
  91. pNodeData.EnterTime := 0;
  92. pNodeData.EnterTimeStr := '';
  93. pNodeData.ExitTime := lpResidualDuration;
  94. pNodeData.ExitTimeStr := '';
  95. pNodeData.Line := -1;
  96. pNodeData.Source := 'Lua';
  97. pNodeData.FctPointer := nil;
  98. pNodeData.MemUsage := -1;
  99. pNodeData.FctName := '[RESIDUAL PROCESSES]';
  100. pNodeData.DurationRGauge := TGauge.Create(Self);
  101. pNodeData.DurationRGauge.Parent := vstLuaProfiler;
  102. pNodeData.DurationRGauge.MinValue := 0;
  103. pNodeData.DurationRGauge.MaxValue := 100;
  104. pNodeData.DurationRGauge.Progress := 0;
  105. pNodeData.DurationRGauge.Visible := False;
  106. pNodeData.DurationOGauge := TGauge.Create(Self);
  107. pNodeData.DurationOGauge.Parent := vstLuaProfiler;
  108. pNodeData.DurationOGauge.MinValue := 0;
  109. pNodeData.DurationOGauge.MaxValue := 100;
  110. pNodeData.DurationOGauge.Progress := 0;
  111. pNodeData.DurationOGauge.Visible := False;
  112. // Display the profiler
  113. vstLuaProfiler.EndUpdate;
  114. end;
  115. function TfrmProfiler.AddCall(FctPointer: Pointer; FctName: String; Line: Integer; Source: String; MemUsage: Double): PProfilerCall;
  116. var
  117. StartedTime: Int64;
  118. pCallData: PProfilerCall;
  119. pCallNode: PVirtualNode;
  120. begin
  121. pTimer.GetCurrentTime(StartedTime);
  122. pCallNode := vstLuaProfiler.AddChild(pCurrentCall);
  123. pCallData := vstLuaProfiler.GetNodeData(pCallNode);
  124. pCallData.DurationRGauge := TGauge.Create(Self);
  125. pCallData.DurationRGauge.Parent := vstLuaProfiler;
  126. pCallData.DurationRGauge.MinValue := 0;
  127. pCallData.DurationRGauge.MaxValue := 100;
  128. pCallData.DurationRGauge.Progress := 0;
  129. pCallData.DurationRGauge.Visible := False;
  130. pCallData.DurationOGauge := TGauge.Create(Self);
  131. pCallData.DurationOGauge.Parent := vstLuaProfiler;
  132. pCallData.DurationOGauge.MinValue := 0;
  133. pCallData.DurationOGauge.MaxValue := 100;
  134. pCallData.DurationOGauge.Progress := 0;
  135. pCallData.DurationOGauge.Visible := False;
  136. pCallData.EnterTimeStr := FormatDateTime('hh:nn:ss:zzz', Time);
  137. pCallData.Parent := pCurrentCall;
  138. pCallData.FctName := FctName;
  139. pCallData.FctPointer := FctPointer;
  140. pCallData.Line := Line;
  141. pCallData.Source := Source;
  142. pCallData.EnterTime := StartedTime;
  143. pCallData.MemUsage := MemUsage;
  144. pCurrentCall := pCallNode;
  145. Result := pCallData;
  146. end;
  147. procedure TfrmProfiler.AddReturn(FctPointer: Pointer; FctName: String);
  148. var
  149. EndedTime: Int64;
  150. pCallData: PProfilerCall;
  151. begin
  152. pTimer.GetCurrentTime(EndedTime);
  153. pCallData := vstLuaProfiler.GetNodeData(pCurrentCall);
  154. pCallData.ExitTimeStr := FormatDateTime('hh:nn:ss:zzz', Time);
  155. pCurrentCall := pCallData.Parent;
  156. pCallData.ExitTime := EndedTime;
  157. end;
  158. procedure TfrmProfiler.vstLuaProfilerGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  159. var
  160. pData: PProfilerCall;
  161. begin
  162. // Set text to display for all nodes
  163. if TextType = ttNormal then
  164. begin
  165. case Column of
  166. 0: // Function Name
  167. begin
  168. pData := Sender.GetNodeData(Node);
  169. if pData.Parent = Sender.RootNode then
  170. CellText := '[MAIN]'
  171. else if pData.FctName = '' then
  172. CellText := '[UNKNOWN]'
  173. else
  174. CellText := pData.FctName;
  175. end;
  176. 1: // Line
  177. begin
  178. pData := Sender.GetNodeData(Node);
  179. if pData.Line = -1 then
  180. CellText := 'N/A'
  181. else
  182. CellText := IntToStr(pData.Line);
  183. end;
  184. 2: // Source
  185. begin
  186. pData := Sender.GetNodeData(Node);
  187. CellText := pData.Source;
  188. end;
  189. 5: // Duration (s)
  190. begin
  191. pData := Sender.GetNodeData(Node);
  192. CellText := FormatFloat('0.000000', (pData.ExitTime - pData.EnterTime) / pTimer.GetFrequency());
  193. end;
  194. 6: // Enter Time
  195. begin
  196. pData := Sender.GetNodeData(Node);
  197. if pData.EnterTimeStr = '' then
  198. CellText := 'N/A'
  199. else
  200. CellText := pData.EnterTimeStr;
  201. end;
  202. 7: // Exit Time
  203. begin
  204. pData := Sender.GetNodeData(Node);
  205. if pData.ExitTimeStr = '' then
  206. CellText := 'N/A'
  207. else
  208. CellText := pData.ExitTimeStr;
  209. end;
  210. 8: // Memory Usage
  211. begin
  212. pData := Sender.GetNodeData(Node);
  213. if (pData.Parent = Sender.RootNode) or (pData.MemUsage < 0) then
  214. CellText := 'N/A'
  215. else
  216. CellText := FloatToStr(pData.MemUsage) + ' kb';
  217. end;
  218. else
  219. CellText := '';
  220. end;
  221. end;
  222. end;
  223. procedure TfrmProfiler.vstLuaProfilerGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
  224. begin
  225. NodeDataSize := SizeOf(TProfilerCall);
  226. end;
  227. procedure TfrmProfiler.vstLuaProfilerAfterCellPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; CellRect: TRect);
  228. var
  229. pFirstNode: PVirtualNode;
  230. pFirstNodeData: PProfilerCall;
  231. pData, pParentData: PProfilerCall;
  232. pRect: TRect;
  233. begin
  234. case Column of
  235. 3: // Relative Usage
  236. begin
  237. pRect := Sender.GetDisplayRect(Node, Column, False);
  238. pData := Sender.GetNodeData(Node);
  239. pParentData := Sender.GetNodeData(pData.Parent);
  240. if pData.Parent = Sender.RootNode then
  241. pData.DurationRGauge.Progress := 100
  242. else
  243. pData.DurationRGauge.Progress := Round(((pData.ExitTime - pData.EnterTime) / pTimer.GetFrequency()) / ((pParentData.ExitTime - pParentData.EnterTime) / pTimer.GetFrequency()) * 100);
  244. InflateRect(pRect, -1, -1);
  245. pData.DurationRGauge.BoundsRect := pRect;
  246. pData.DurationRGauge.Visible := True;
  247. end;
  248. 4: // Overall Usage
  249. begin
  250. pRect := Sender.GetDisplayRect(Node, Column, False);
  251. pFirstNode := Sender.GetFirstChild(vstLuaProfiler.RootNode);
  252. pFirstNodeData := Sender.GetNodeData(pFirstNode);
  253. pData := Sender.GetNodeData(Node);
  254. if pData.Parent = Sender.RootNode then
  255. pData.DurationOGauge.Progress := 100
  256. else
  257. pData.DurationOGauge.Progress := Round(((pData.ExitTime - pData.EnterTime) / pTimer.GetFrequency()) / ((pFirstNodeData.ExitTime - pFirstNodeData.EnterTime) / pTimer.GetFrequency()) * 100);
  258. InflateRect(pRect, -1, -1);
  259. pData.DurationOGauge.BoundsRect := pRect;
  260. pData.DurationOGauge.Visible := True;
  261. end;
  262. end;
  263. end;
  264. procedure TfrmProfiler.vstLuaProfilerCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode; var Allowed: Boolean);
  265. var
  266. pChildNode: PVirtualNode;
  267. pChildData: PProfilerCall;
  268. begin
  269. // Manually hidding gauges that shouldn't be visible anymore
  270. pChildNode := Sender.GetFirstChild(Node);
  271. while ((pChildNode <> nil) and (Node.Parent <> pChildNode.Parent)) do
  272. begin
  273. pChildData := Sender.GetNodeData(pChildNode);
  274. pChildData.DurationRGauge.Visible := False;
  275. pChildData.DurationOGauge.Visible := False;
  276. pChildNode := Sender.GetNext(pChildNode);
  277. end;
  278. end;
  279. procedure TfrmProfiler.FormCreate(Sender: TObject);
  280. begin
  281. pTimer := TPrecisionTimer.Create;
  282. end;
  283. procedure TfrmProfiler.FormDestroy(Sender: TObject);
  284. begin
  285. pTimer.Free;
  286. end;
  287. end.