2
0

FoxProfiler.Mod 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. MODULE FoxProfiler; (** AUTHOR "fof"; PURPOSE "minimal implementation of a compiler supported profiler"; *)
  2. IMPORT KernelLog,Objects,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 (moduleId < MaxModules) & (procedureId < MaxProcedures) THEN
  40. INC(modules[moduleId,procedureId].calls);
  41. END;
  42. IF stackPosition < MaxStackSize THEN
  43. correcture[stackPosition] := 0;
  44. (* debugging *)
  45. module[stackPosition] := moduleId;
  46. procedure[stackPosition] := procedureId;
  47. startTime[stackPosition] := GetTimer();
  48. (* book keeping for caller *)
  49. IF stackPosition > 0 THEN
  50. (* try to remove time spent in profiler *)
  51. INC(correcture[stackPosition-1], startTime[stackPosition] -enterTime);
  52. END;
  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;
  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. BEGIN
  159. RETURN Objects.CurrentProcessTime();
  160. END GetTimer;
  161. PROCEDURE EnterProcedure*(moduleId, procedureId: LONGINT);
  162. VAR time: HUGEINT; p: Process;
  163. BEGIN
  164. time:= GetTimer();
  165. IF TraceEnter THEN log.String("Enter procedure: "); log.Int(moduleId,1); log.String(", "); log.Int(procedureId,1); log.Ln; log.Update END;
  166. p := GetProcess();
  167. p.Enter(moduleId,procedureId,time);
  168. END EnterProcedure;
  169. PROCEDURE ExitProcedure*(moduleId, procedureId: LONGINT);
  170. VAR time: HUGEINT; p: Process;
  171. BEGIN
  172. time:= GetTimer();
  173. IF TraceEnter THEN log.String("Exit procedure: "); log.Int(moduleId,1); log.String(", "); log.Int(procedureId,1); log.Ln; log.Update END;
  174. p := GetProcess();
  175. p.Exit(moduleId, procedureId, time);
  176. END ExitProcedure;
  177. PROCEDURE Initialize*;
  178. VAR i: LONGINT;
  179. BEGIN
  180. FOR i := 0 TO LEN(table)-1 DO table[i].used := FALSE END;
  181. numberProcesses := 0;
  182. END Initialize;
  183. PROCEDURE Report*(context: Commands.Context);
  184. TYPE
  185. Record=RECORD
  186. name: ARRAY 256 OF CHAR;
  187. calls:LONGINT; time,brut: HUGEINT
  188. END;
  189. Records=POINTER TO ARRAY OF Record;
  190. VAR
  191. i,j,k: LONGINT; records: Records; time,brut: HUGEINT; calls: LONGINT; recordNumber: LONGINT;
  192. option: ARRAY 32 OF CHAR;
  193. log: Streams.Writer;
  194. all,done: BOOLEAN; sort: LONGINT;
  195. PROCEDURE Sort(id: LONGINT);
  196. VAR i,j: LONGINT;
  197. (* stupid bubblesort *)
  198. PROCEDURE Swap(VAR l,r: Record);
  199. VAR temp: Record;
  200. BEGIN
  201. temp := l; l := r; r := temp
  202. END Swap;
  203. BEGIN
  204. IF id <0 THEN RETURN END;
  205. FOR i := 0 TO recordNumber-1 DO
  206. FOR j := i TO recordNumber-1 DO
  207. IF (id=0) & (records[j].name < records[i].name) THEN Swap(records[i],records[j])
  208. ELSIF (id=1) & (records[j].calls > records[i].calls) THEN Swap(records[i],records[j])
  209. ELSIF (id=2) & (records[j].time >records[i].time) THEN Swap(records[i],records[j])
  210. ELSIF (id=3) & (records[j].brut > records[i].brut) THEN Swap(records[i],records[j])
  211. END;
  212. END;
  213. END;
  214. END Sort;
  215. PROCEDURE String(chars: LONGINT; CONST string: ARRAY OF CHAR);
  216. VAR i: LONGINT;
  217. BEGIN
  218. i := 0;
  219. WHILE (i<LEN(string)) & (i<chars) & (string[i] # 0X) DO
  220. log.Char(string[i]);INC(i);
  221. END;
  222. WHILE(i<chars) DO
  223. log.Char(" "); INC(i);
  224. END;
  225. (*log.Update;*)
  226. END String;
  227. PROCEDURE Percent(x: LONGREAL);
  228. BEGIN
  229. log.String("["); log.Int(ENTIER(x*100),2); log.String("."); log.Int(ENTIER(x*1000 +0.5) MOD 10, 1); log.String("]");
  230. END Percent;
  231. BEGIN
  232. sort := -1; all := FALSE; done := FALSE;
  233. WHILE context.arg.GetString(option) & ~done DO
  234. IF option = "name" THEN sort := 0
  235. ELSIF option = "calls" THEN sort := 1
  236. ELSIF option = "time" THEN sort := 2
  237. ELSIF option = "brut" THEN sort := 3
  238. ELSIF option = "all" THEN all := TRUE
  239. ELSE done := TRUE
  240. END;
  241. END;
  242. log := context.out;
  243. recordNumber := 0;
  244. FOR i := 0 TO numberModules-1 DO
  245. INC(recordNumber, LEN(modules[i]));
  246. END;
  247. NEW(records,recordNumber);
  248. recordNumber := 0;
  249. FOR i := 0 TO numberModules-1 DO
  250. FOR j := 0 TO LEN(modules[i])-1 DO
  251. time := 0; calls := 0; brut := 0;
  252. IF (i< LEN(processes[k].modules)) & (j<LEN(processes[k].modules[i])) THEN
  253. FOR k := 0 TO numberProcesses-1 DO
  254. IF processes[k] # NIL THEN
  255. INC(time, processes[k].modules[i,j].time);
  256. INC(calls, processes[k].modules[i,j].calls);
  257. INC(brut, processes[k].modules[i,j].brut);
  258. END;
  259. END;
  260. ELSE calls := -9999999
  261. END;
  262. IF (calls > 0) OR all THEN
  263. records[recordNumber].calls := calls;
  264. records[recordNumber].time := time;
  265. records[recordNumber].brut := brut;
  266. COPY(modules[i,j],records[recordNumber].name);
  267. INC(recordNumber)
  268. END;
  269. END;
  270. END;
  271. Sort(sort);
  272. log.Char(0EX);
  273. log.String("--- FoxProfiler timing report ----"); log.Ln;
  274. log.String("processes= "); log.Int(numberProcesses,1); log.Ln;
  275. String(80,"name"); log.Char(9X);
  276. String(10,"calls"); log.Char(9X);
  277. String(18,"time [%]"); log.Char(9X);
  278. String(18,"brut [%]"); log.Char(9X);
  279. String(10,"time/call");log.Char(9X);
  280. String(10,"brut/call"); log.Ln;
  281. time := 0;
  282. brut := 0;
  283. calls := 0;
  284. FOR i := 0 TO recordNumber-1 DO
  285. INC(time, records[i].time);
  286. INC(brut, records[i].brut);
  287. INC(calls, records[i].calls);
  288. END;
  289. FOR i := 0 TO recordNumber-1 DO
  290. String(80,records[i].name);
  291. log.Int(records[i].calls,10); log.Char(9X);
  292. log.Float(records[i].time / frequency,12);
  293. Percent(records[i].time / time);
  294. log.Char(9X);
  295. log.Float(records[i].brut / frequency,12);
  296. Percent(records[i].brut / brut);
  297. log.Char(9X);
  298. log.Float(records[i].time / frequency / records[i].calls,10);
  299. log.Char(9X);
  300. log.Float(records[i].brut / frequency / records[i].calls,10);
  301. log.Ln;
  302. END;
  303. log.Update;
  304. FOR k := 0 TO numberProcesses-1 DO
  305. IF processes[k].stackPosition # 0 THEN
  306. log.String("warning: process "); log.Int(k,1); log.String(" still running with a stack of "); log.Int(processes[k].stackPosition,1); log.Ln;
  307. END;
  308. END;
  309. log.String("---------------------------"); log.Ln;
  310. String(80,"SUM");
  311. log.Int(calls,10); log.Char(9X);
  312. log.Float(time / frequency,10); log.Char(9X);
  313. log.Float(brut / frequency,10); log.Char(9X);
  314. log.Float(time / frequency / calls,20);
  315. log.Float(brut / frequency / calls,20);
  316. log.Ln; log.Update;
  317. log.String("---------------------------"); log.Ln;
  318. log.Char(0FX);
  319. log.Update;
  320. END Report;
  321. PROCEDURE CalibrateProc;
  322. BEGIN
  323. EnterProcedure(0,0);
  324. ExitProcedure(0,0);
  325. END CalibrateProc;
  326. PROCEDURE Calibrate;
  327. VAR cal: LONGINT; i: SIZE; process: Process;
  328. BEGIN
  329. frequency := Objects.TimerFrequency();
  330. log.Ln; log.String( "Timer reported Frequency: " ); log.FloatFix( frequency, 5, 1,0 );
  331. log.Ln; log.Update;
  332. AddModule(cal,1,"@FoxProfiler");
  333. AddProcedure(cal,0,"@ProfilerDelta");
  334. process := GetProcess();
  335. FOR i := 1 TO 1000 DO
  336. CalibrateProc();
  337. END;
  338. END Calibrate;
  339. PROCEDURE Init;
  340. VAR i: LONGINT;
  341. BEGIN
  342. FOR i := 0 TO LEN(modules)-1 DO modules[i] := NIL END;
  343. FOR i := 0 TO LEN(table)-1 DO table[i].used := FALSE END;
  344. numberModules := 0;
  345. numberProcesses := 0;
  346. Calibrate;
  347. END Init;
  348. PROCEDURE Reset*;
  349. VAR i,j,k: LONGINT;
  350. BEGIN{EXCLUSIVE}
  351. FOR i := 0 TO numberModules-1 DO
  352. FOR j := 0 TO LEN(modules[i])-1 DO
  353. IF (i< LEN(processes[k].modules)) & (j<LEN(processes[k].modules[i])) THEN
  354. FOR k := 0 TO numberProcesses-1 DO
  355. processes[k].modules[i,j].time := 0;
  356. processes[k].modules[i,j].calls := 0;
  357. processes[k].modules[i,j].brut := 0;
  358. END;
  359. END;
  360. END;
  361. END;
  362. END Reset;
  363. BEGIN
  364. NEW(log,KernelLog.Send,1024*1024);
  365. Init;
  366. END FoxProfiler.
  367. WMUtilities.Call --font=VeraMo FoxProfiler.Report ~
  368. WMUtilities.Call --font=VeraMo FoxProfiler.Report time ~
  369. WMUtilities.Call --font=VeraMo FoxProfiler.Report calls ~
  370. WMUtilities.Call --font=VeraMo FoxProfiler.Report name ~
  371. WMUtilities.Call --font=VeraMo FoxProfiler.Report brut ~
  372. WMUtilities.Call --font=Courier FoxProfiler.Report time all ~
  373. Compiler.Compile --profile TuringCoatWnd.Mod ~
  374. TuringCoatWnd.Open
  375. System.Free TuringCoatWnd FoxProfiler ~
  376. FoxProfiler.Reset