FoxProfiler.Mod 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. MODULE FoxProfiler; (** AUTHOR "fof"; PURPOSE "minimal implementation of a compiler supported profiler"; *)
  2. IMPORT KernelLog,Objects,Machine,SYSTEM,Streams,Commands;
  3. CONST
  4. TraceAdd=FALSE;
  5. TraceEnter=FALSE;
  6. MaxModules=1024;
  7. MaxProcedures=1024;
  8. MaxProcesses=1024;
  9. MaxStackSize=1024;
  10. TYPE
  11. Name = ARRAY 128 OF CHAR;
  12. Procedures = POINTER TO ARRAY OF Name;
  13. Modules= ARRAY MaxModules OF Procedures;
  14. ProcedureTime= RECORD
  15. calls:LONGINT;
  16. time,brut: HUGEINT;
  17. END;
  18. ProcedureTimes= ARRAY MaxProcedures OF ProcedureTime;
  19. Process= OBJECT
  20. VAR
  21. stackPosition: LONGINT;
  22. startTime, correcture: ARRAY MaxStackSize OF HUGEINT; (* correcture: time taken for calls to other profiled procedures *)
  23. module, procedure: ARRAY MaxStackSize OF LONGINT;
  24. modules: ARRAY MaxModules OF ProcedureTimes;
  25. PROCEDURE &Init;
  26. VAR i,j: LONGINT;
  27. BEGIN
  28. stackPosition := 0;
  29. FOR i := 0 TO LEN(modules)-1 DO
  30. FOR j := 0 TO LEN(modules[i])-1 DO
  31. modules[i,j].calls := 0;
  32. modules[i,j].time := 0;
  33. END;
  34. END;
  35. END Init;
  36. PROCEDURE Enter(moduleId, procedureId: LONGINT; enterTime: HUGEINT);
  37. BEGIN
  38. IF TraceEnter THEN log.String("stack position "); log.Int(stackPosition,1); log.Ln; END;
  39. IF stackPosition < MaxStackSize THEN
  40. startTime[stackPosition] := GetTimer();
  41. correcture[stackPosition] := 0;
  42. (* debugging *)
  43. module[stackPosition] := moduleId;
  44. procedure[stackPosition] := procedureId;
  45. (* book keeping for caller *)
  46. IF stackPosition > 0 THEN
  47. (* try to remove time spent in profiler *)
  48. INC(correcture[stackPosition-1], startTime[stackPosition] -enterTime);
  49. END;
  50. END;
  51. IF (moduleId < MaxModules) & (procedureId < MaxProcedures) THEN
  52. INC(modules[moduleId,procedureId].calls);
  53. END;
  54. INC(stackPosition);
  55. END Enter;
  56. PROCEDURE Exit(moduleId, procedureId: LONGINT; enterTime: HUGEINT);
  57. BEGIN
  58. DEC(stackPosition);
  59. IF stackPosition < MaxStackSize THEN
  60. IF (moduleId < MaxModules) & (procedureId < MaxProcedures) THEN
  61. INC(modules[moduleId,procedureId].time,enterTime-startTime[stackPosition]-correcture[stackPosition]);
  62. INC(modules[moduleId,procedureId].brut,enterTime-startTime[stackPosition]);
  63. END;
  64. IF stackPosition > 0 THEN
  65. (* try to remove time spent in procedure plus time spent in profiler *)
  66. INC(correcture[stackPosition-1], GetTimer()-startTime[stackPosition]);
  67. END;
  68. ASSERT(stackPosition >= 0);
  69. ASSERT(module[stackPosition] = moduleId);
  70. ASSERT(procedure[stackPosition] = procedureId);
  71. END;
  72. IF TraceEnter THEN log.String("stack position "); log.Int(stackPosition,1); log.Ln; END;
  73. END Exit;
  74. END Process;
  75. HashEntryInt = RECORD
  76. used: BOOLEAN; key, value: SIZE;
  77. END;
  78. HashIntArray = ARRAY 2*MaxProcesses OF HashEntryInt;
  79. VAR
  80. (* modules *)
  81. modules:Modules;
  82. numberModules: LONGINT;
  83. (* process hash table *)
  84. table: HashIntArray;
  85. numberProcesses: LONGINT;
  86. processes: ARRAY MaxProcesses OF Process;
  87. (* logging *)
  88. log: Streams.Writer;
  89. (* timing *)
  90. frequency: LONGREAL; n: LONGINT;
  91. PROCEDURE Put*(key, value: SIZE);
  92. VAR hash: SIZE;
  93. BEGIN
  94. ASSERT(numberProcesses < LEN(table),5000);
  95. hash := HashValue(key);
  96. IF table[hash].used THEN
  97. ASSERT(table[hash].key = key,5001);
  98. END;
  99. table[hash].key := key;
  100. table[hash].value := value;
  101. table[hash].used := TRUE;
  102. END Put;
  103. PROCEDURE Get*(key: SIZE):SIZE;
  104. BEGIN
  105. RETURN table[HashValue(key)].value;
  106. END Get;
  107. PROCEDURE Has*(key: SIZE):BOOLEAN;
  108. BEGIN
  109. RETURN table[HashValue(key)].used;
  110. END Has;
  111. PROCEDURE HashValue(key: SIZE):SIZE;
  112. VAR value, h1, h2, i: SIZE;
  113. BEGIN
  114. value :=key;
  115. i := 0;
  116. h1 := value MOD LEN(table);
  117. h2 := 1; (* Linear probing *)
  118. REPEAT
  119. value := (h1 + i*h2) MOD LEN(table);
  120. INC(i);
  121. UNTIL((~table[value].used) OR (table[value].key = key) OR (i >= LEN(table)));
  122. ASSERT(i<LEN(table),5002);
  123. RETURN value;
  124. END HashValue;
  125. PROCEDURE GetProcess(): Process;
  126. VAR process: ANY; value: SIZE; key: ADDRESS;
  127. BEGIN
  128. process := Objects.ActiveObject();
  129. key := SYSTEM.VAL(ADDRESS,process) DIV SIZEOF(ADDRESS);
  130. IF Has(key) THEN
  131. value := Get(key);
  132. ELSE
  133. BEGIN{EXCLUSIVE}
  134. value := numberProcesses; INC(numberProcesses);
  135. NEW(processes[value]);
  136. Put(key,value);
  137. END;
  138. END;
  139. RETURN processes[value]
  140. END GetProcess;
  141. PROCEDURE AddModule*(VAR moduleId: LONGINT; procedures: LONGINT; CONST name: ARRAY OF CHAR);
  142. BEGIN{EXCLUSIVE}
  143. IF TraceAdd THEN
  144. log.String("Add Module: "); log.String(name); log.String(", #procs: "); log.Int(procedures,1);
  145. log.String(", id: "); log.Int(numberModules,1); log.Ln; log.Update;
  146. END;
  147. moduleId := numberModules; NEW(modules[moduleId],procedures);
  148. INC(numberModules);
  149. END AddModule;
  150. PROCEDURE AddProcedure*(moduleId, procedureId: LONGINT; CONST name: ARRAY OF CHAR);
  151. BEGIN
  152. IF TraceAdd THEN
  153. log.String("Add procedure: "); log.String(name); log.String(": "); log.Int(moduleId,1); log.String(","); log.Int(procedureId,1); log.Ln; log.Update;
  154. END;
  155. COPY(name,modules[moduleId,procedureId]);
  156. END AddProcedure;
  157. PROCEDURE GetTimer(): HUGEINT;
  158. VAR cpuCycles: Objects.CpuCyclesArray;
  159. BEGIN
  160. RETURN Objects.CurrentProcessTime();
  161. END GetTimer;
  162. PROCEDURE EnterProcedure*(moduleId, procedureId: LONGINT);
  163. VAR time: HUGEINT; p: Process;
  164. BEGIN
  165. time:= GetTimer();
  166. IF TraceEnter THEN log.String("Enter procedure: "); log.Int(moduleId,1); log.String(", "); log.Int(procedureId,1); log.Ln; log.Update END;
  167. p := GetProcess();
  168. p.Enter(moduleId,procedureId,time);
  169. END EnterProcedure;
  170. PROCEDURE ExitProcedure*(moduleId, procedureId: LONGINT);
  171. VAR time: HUGEINT; p: Process;
  172. BEGIN
  173. time:= GetTimer();
  174. IF TraceEnter THEN log.String("Exit procedure: "); log.Int(moduleId,1); log.String(", "); log.Int(procedureId,1); log.Ln; log.Update END;
  175. p := GetProcess();
  176. p.Exit(moduleId, procedureId, time);
  177. END ExitProcedure;
  178. PROCEDURE Initialize*;
  179. VAR i: LONGINT;
  180. BEGIN
  181. FOR i := 0 TO LEN(table)-1 DO table[i].used := FALSE END;
  182. numberProcesses := 0;
  183. END Initialize;
  184. PROCEDURE Report*(context: Commands.Context);
  185. TYPE
  186. Record=RECORD
  187. name: ARRAY 256 OF CHAR;
  188. calls:LONGINT; time,brut: HUGEINT
  189. END;
  190. Records=POINTER TO ARRAY OF Record;
  191. VAR
  192. i,j,k: LONGINT; records: Records; time,brut: HUGEINT; calls: LONGINT; recordNumber: LONGINT;
  193. option: ARRAY 32 OF CHAR;
  194. log: Streams.Writer;
  195. all,done: BOOLEAN; sort: LONGINT;
  196. PROCEDURE Sort(id: LONGINT);
  197. VAR i,j: LONGINT;
  198. (* stupid bubblesort *)
  199. PROCEDURE Swap(VAR l,r: Record);
  200. VAR temp: Record;
  201. BEGIN
  202. temp := l; l := r; r := temp
  203. END Swap;
  204. BEGIN
  205. IF id <0 THEN RETURN END;
  206. FOR i := 0 TO recordNumber-1 DO
  207. FOR j := i TO recordNumber-1 DO
  208. IF (id=0) & (records[j].name < records[i].name) THEN Swap(records[i],records[j])
  209. ELSIF (id=1) & (records[j].calls > records[i].calls) THEN Swap(records[i],records[j])
  210. ELSIF (id=2) & (records[j].time >records[i].time) THEN Swap(records[i],records[j])
  211. ELSIF (id=3) & (records[j].brut > records[i].brut) THEN Swap(records[i],records[j])
  212. END;
  213. END;
  214. END;
  215. END Sort;
  216. PROCEDURE String(chars: LONGINT; CONST string: ARRAY OF CHAR);
  217. VAR i: LONGINT;
  218. BEGIN
  219. i := 0;
  220. WHILE (i<LEN(string)) & (i<chars) & (string[i] # 0X) DO
  221. log.Char(string[i]);INC(i);
  222. END;
  223. WHILE(i<chars) DO
  224. log.Char(" "); INC(i);
  225. END;
  226. (*log.Update;*)
  227. END String;
  228. PROCEDURE Percent(x: LONGREAL);
  229. BEGIN
  230. log.String("["); log.Int(ENTIER(x*100),2); log.String("."); log.Int(ENTIER(x*1000 +0.5) MOD 10, 1); log.String("]");
  231. END Percent;
  232. BEGIN
  233. sort := -1; all := FALSE; done := FALSE;
  234. WHILE context.arg.GetString(option) & ~done DO
  235. IF option = "name" THEN sort := 0
  236. ELSIF option = "calls" THEN sort := 1
  237. ELSIF option = "time" THEN sort := 2
  238. ELSIF option = "brut" THEN sort := 3
  239. ELSIF option = "all" THEN all := TRUE
  240. ELSE done := TRUE
  241. END;
  242. END;
  243. log := context.out;
  244. recordNumber := 0;
  245. FOR i := 0 TO numberModules-1 DO
  246. INC(recordNumber, LEN(modules[i]));
  247. END;
  248. NEW(records,recordNumber);
  249. recordNumber := 0;
  250. FOR i := 0 TO numberModules-1 DO
  251. FOR j := 0 TO LEN(modules[i])-1 DO
  252. time := 0; calls := 0; brut := 0;
  253. IF (i< LEN(processes[k].modules)) & (j<LEN(processes[k].modules[i])) THEN
  254. FOR k := 0 TO numberProcesses-1 DO
  255. IF processes[k] # NIL THEN
  256. INC(time, processes[k].modules[i,j].time);
  257. INC(calls, processes[k].modules[i,j].calls);
  258. INC(brut, processes[k].modules[i,j].brut);
  259. END;
  260. END;
  261. ELSE calls := -9999999
  262. END;
  263. IF (calls > 0) OR all THEN
  264. records[recordNumber].calls := calls;
  265. records[recordNumber].time := time;
  266. records[recordNumber].brut := brut;
  267. COPY(modules[i,j],records[recordNumber].name);
  268. INC(recordNumber)
  269. END;
  270. END;
  271. END;
  272. Sort(sort);
  273. log.Char(0EX);
  274. log.String("--- FoxProfiler timing report ----"); log.Ln;
  275. log.String("processes= "); log.Int(numberProcesses,1); log.Ln;
  276. String(80,"name"); log.Char(9X);
  277. String(10,"calls"); log.Char(9X);
  278. String(18,"time [%]"); log.Char(9X);
  279. String(18,"brut [%]"); log.Char(9X);
  280. String(10,"time/call");log.Char(9X);
  281. String(10,"brut/call"); log.Ln;
  282. time := 0;
  283. brut := 0;
  284. calls := 0;
  285. FOR i := 0 TO recordNumber-1 DO
  286. INC(time, records[i].time);
  287. INC(brut, records[i].brut);
  288. INC(calls, records[i].calls);
  289. END;
  290. FOR i := 0 TO recordNumber-1 DO
  291. String(80,records[i].name);
  292. log.Int(records[i].calls,10); log.Char(9X);
  293. log.Float(records[i].time / frequency,12);
  294. Percent(records[i].time / time);
  295. log.Char(9X);
  296. log.Float(records[i].brut / frequency,12);
  297. Percent(records[i].brut / brut);
  298. log.Char(9X);
  299. log.Float(records[i].time / frequency / records[i].calls,10);
  300. log.Char(9X);
  301. log.Float(records[i].brut / frequency / records[i].calls,10);
  302. log.Ln;
  303. END;
  304. log.Update;
  305. FOR k := 0 TO numberProcesses-1 DO
  306. IF processes[k].stackPosition # 0 THEN
  307. log.String("warning: process "); log.Int(k,1); log.String(" still running with a stack of "); log.Int(processes[k].stackPosition,1); log.Ln;
  308. END;
  309. END;
  310. log.String("---------------------------"); log.Ln;
  311. String(80,"SUM");
  312. log.Int(calls,10); log.Char(9X);
  313. log.Float(time / frequency,10); log.Char(9X);
  314. log.Float(brut / frequency,10); log.Char(9X);
  315. log.Float(time / frequency / calls,20);
  316. log.Float(brut / frequency / calls,20);
  317. log.Ln; log.Update;
  318. log.String("---------------------------"); log.Ln;
  319. log.Char(0FX);
  320. log.Update;
  321. END Report;
  322. PROCEDURE Calibrate;
  323. BEGIN
  324. frequency := Objects.TimerFrequency();
  325. log.Ln; log.String( "Timer reported Frequency: " ); log.FloatFix( frequency, 5, 1,0 );
  326. log.Ln; log.Update;
  327. END Calibrate;
  328. PROCEDURE Init;
  329. VAR i: LONGINT;
  330. BEGIN{EXCLUSIVE}
  331. FOR i := 0 TO LEN(modules)-1 DO modules[i] := NIL END;
  332. FOR i := 0 TO LEN(table)-1 DO table[i].used := FALSE END;
  333. numberModules := 0;
  334. numberProcesses := 0;
  335. Calibrate;
  336. END Init;
  337. PROCEDURE Reset*;
  338. VAR i,j,k: LONGINT;
  339. BEGIN{EXCLUSIVE}
  340. FOR i := 0 TO numberModules-1 DO
  341. FOR j := 0 TO LEN(modules[i])-1 DO
  342. IF (i< LEN(processes[k].modules)) & (j<LEN(processes[k].modules[i])) THEN
  343. FOR k := 0 TO numberProcesses-1 DO
  344. processes[k].modules[i,j].time := 0;
  345. processes[k].modules[i,j].calls := 0;
  346. processes[k].modules[i,j].brut := 0;
  347. END;
  348. END;
  349. END;
  350. END;
  351. END Reset;
  352. BEGIN
  353. NEW(log,KernelLog.Send,1024*1024);
  354. Init;
  355. END FoxProfiler.
  356. WMUtilities.Call --font=Courier FoxProfiler.Report ~
  357. WMUtilities.Call --font=Courier FoxProfiler.Report time ~
  358. WMUtilities.Call --font=Courier FoxProfiler.Report calls ~
  359. WMUtilities.Call --font=Courier FoxProfiler.Report name ~
  360. WMUtilities.Call --font=Courier FoxProfiler.Report brut ~
  361. WMUtilities.Call --font=Courier FoxProfiler.Report time all ~
  362. Compiler.Compile --profile TuringCoatWnd.Mod ~
  363. TuringCoatWnd.Open
  364. SystemTools.Free TuringCoatWnd FoxProfiler ~
  365. FoxProfiler.Reset