profile.pp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1993-98 by Pierre Muller,
  5. member of the Free Pascal development team.
  6. Profiling support for Go32V2
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************
  13. }
  14. Unit profile;
  15. interface
  16. type
  17. header = record
  18. low,high,nbytes : longint;
  19. end;
  20. { entry of a GPROF type file }
  21. ppMTABE = ^pMTABE;
  22. pMTABE = ^MTABE;
  23. MTABE = record
  24. from,_to,count : longint;
  25. end;
  26. { internal form - sizeof(MTAB) is 4096 for efficiency }
  27. PMTAB = ^M_TAB;
  28. M_TAB = record
  29. calls : array [0..340] of MTABE;
  30. prev : PMTAB;
  31. end;
  32. const
  33. mcount_skip : longint = 1;
  34. mtab : PMTAB = nil;
  35. var
  36. h : header;
  37. histogram : ^integer;
  38. histlen : longint;
  39. oldexitproc : pointer;
  40. { called by functions. Use the pointer it provides to cache the last used
  41. MTABE, so that repeated calls to/from the same pair works quickly -
  42. no lookup. }
  43. procedure mcount;
  44. implementation
  45. uses
  46. go32,dpmiexcp;
  47. {$ASMMODE ATT}
  48. type
  49. plongint = ^longint;
  50. var
  51. starttext, endtext : longint;
  52. const
  53. cache : pMTABE = nil;
  54. { problem how to avoid mcount calling itself !! }
  55. procedure mcount; [public, alias : 'MCOUNT'];
  56. {
  57. ebp contains the frame of mcount (ebp) the frame of calling (to_)
  58. ((ebp)) the frame of from
  59. }
  60. var
  61. m : pmtab;
  62. i,to_,ebp,from,mtabi : longint;
  63. begin
  64. { optimisation !! }
  65. asm
  66. pushal
  67. movl 4(%ebp),%eax
  68. movl %eax,to_
  69. movl (%ebp),%eax
  70. movl 4(%eax),%eax
  71. movl %eax,from
  72. end;
  73. if endtext=0 then
  74. asm
  75. popal
  76. leave
  77. ret
  78. end;
  79. mcount_skip := 1;
  80. if (to_ > endtext) or (from > endtext) then
  81. runerror(255);
  82. if ((cache<>nil) and (cache^.from=from) and (cache^._to=to_)) then
  83. begin
  84. { cache paid off - works quickly }
  85. inc(cache^.count);
  86. mcount_skip:=0;
  87. asm
  88. popal
  89. leave
  90. ret
  91. end;
  92. end;
  93. { no cache hit - search all mtab tables for a match, or an empty slot }
  94. mtabi := -1;
  95. m:=mtab;
  96. while m<>nil do
  97. begin
  98. for i:=0 to 340 do
  99. begin
  100. if m^.calls[i].from=0 then
  101. begin
  102. { empty slot - end of table }
  103. mtabi := i;
  104. break;
  105. end;
  106. if ((m^.calls[i].from = from) and (m^.calls[i]._to = to_)) then
  107. begin
  108. { found a match - bump count and return }
  109. inc(m^.calls[i].count);
  110. cache:=@(m^.calls[i]);
  111. mcount_skip:=0;
  112. asm
  113. popal
  114. leave
  115. ret
  116. end;
  117. end;
  118. end;
  119. m:=m^.prev;
  120. end;
  121. if (mtabi<>-1) then
  122. begin
  123. { found an empty - fill it in }
  124. mtab^.calls[mtabi].from := from;
  125. mtab^.calls[mtabi]._to := to_;
  126. mtab^.calls[mtabi].count := 1;
  127. cache := @(mtab^.calls[mtabi]);
  128. mcount_skip := 0;
  129. asm
  130. popal
  131. leave
  132. ret
  133. end;
  134. end;
  135. { lob off another page of memory and initialize the new table }
  136. getmem(m,sizeof(M_TAB));
  137. fillchar(m^, sizeof(M_TAB),#0);
  138. m^.prev := mtab;
  139. mtab := m;
  140. m^.calls[0].from := from;
  141. m^.calls[0]._to := to_;
  142. m^.calls[0].count := 1;
  143. cache := @(m^.calls[0]);
  144. mcount_skip := 0;
  145. asm
  146. popal
  147. leave
  148. ret
  149. end;
  150. end;
  151. var
  152. new_timer,
  153. old_timer : tseginfo;
  154. invalid_mcount_call,
  155. mcount_nb,
  156. doublecall,
  157. reload : longint; {=0}
  158. function mcount_tick(x : longint) : longint;
  159. var
  160. bin : longint;
  161. begin
  162. if mcount_skip=0 then
  163. begin
  164. bin := djgpp_exception_state^.__eip;
  165. if (djgpp_exception_state^.__cs=get_cs) and (bin >= starttext) and (bin <= endtext) then
  166. begin
  167. bin := (bin - starttext) div 16;
  168. inc(histogram[bin]);
  169. end
  170. else
  171. inc(invalid_mcount_call);
  172. inc(mcount_nb);
  173. end
  174. else
  175. inc(doublecall);
  176. mcount_tick:=0;
  177. end;
  178. {$ASMMODE DIRECT}
  179. function timer(x : longint) : longint;
  180. begin
  181. if reload>0 then
  182. asm
  183. movl _RELOAD,%eax
  184. movl %eax,___djgpp_timer_countdown
  185. end;
  186. mcount_tick(x);
  187. { _raise(SIGPROF); }
  188. end;
  189. procedure mcount_write;
  190. {
  191. this is called during program exit
  192. }
  193. var
  194. m : PMTAB;
  195. i : longint;
  196. f : file;
  197. begin
  198. mcount_skip:=1;
  199. signal(SIGTIMR,@SIG_IGN);
  200. signal(SIGPROF,@SIG_IGN);
  201. set_pm_interrupt($8,old_timer);
  202. reload:=0;
  203. exitproc:=oldexitproc;
  204. writeln('Writing profile output');
  205. writeln('histogram length = ',histlen);
  206. writeln('Nb of double calls = ',doublecall);
  207. if invalid_mcount_call>0 then
  208. writeln('nb of invalid mcount : ',invalid_mcount_call,'/',mcount_nb)
  209. else
  210. writeln('nb of mcount : ',mcount_nb);
  211. assign(f,'gmon.out');
  212. rewrite(f,1);
  213. blockwrite(f, h, sizeof(header));
  214. blockwrite(f, histogram^, histlen);
  215. m:=mtab;
  216. while m<>nil do
  217. begin
  218. for i:=0 to 340 do
  219. begin
  220. if (m^.calls[i].from = 0) then
  221. break;
  222. blockwrite(f, m^.calls[i],sizeof(MTABE));
  223. {$ifdef DEBUG}
  224. if m^.calls[i].count>0 then
  225. writeln(' 0x',hexstr(m^.calls[i]._to,8),' called from ',hexstr(m^.calls[i].from,8),
  226. ' ',m^.calls[i].count,' times');
  227. {$endif DEBUG}
  228. end;
  229. m:=m^.prev;
  230. end;
  231. close(f);
  232. end;
  233. procedure mcount_init;
  234. {
  235. this is called to initialize profiling before the program starts
  236. }
  237. function djgpp_timer_hdlr : pointer;
  238. begin
  239. asm
  240. movl $___djgpp_timer_hdlr,%eax
  241. movl %eax,__RESULT
  242. end;
  243. end;
  244. procedure set_old_timer_handler;
  245. begin
  246. asm
  247. movl $_OLD_TIMER,%eax
  248. movl $___djgpp_old_timer,%ebx
  249. movl (%eax),%ecx
  250. movl %ecx,(%ebx)
  251. movw 4(%eax),%ax
  252. movw %ax,4(%ebx)
  253. end;
  254. end;
  255. begin
  256. asm
  257. movl $_etext,_ENDTEXT
  258. movl $start,_STARTTEXT
  259. end;
  260. h.low := starttext;
  261. h.high := endtext;
  262. histlen := ((h.high-h.low) div 16) * 2; { must be even }
  263. h.nbytes := sizeof(header) + histlen;
  264. getmem(histogram,histlen);
  265. fillchar(histogram^, histlen,#0);
  266. oldexitproc:=exitproc;
  267. exitproc:=@mcount_write;
  268. { here, do whatever it takes to initialize the timer interrupt }
  269. signal(SIGPROF,@mcount_tick);
  270. signal(SIGTIMR,@timer);
  271. get_pm_interrupt($8,old_timer);
  272. set_old_timer_handler;
  273. {$ifdef DEBUG}
  274. writeln(stderr,'ori pm int8 '+hexstr(old_timer.segment,4)+':'+hexstr(longint(old_timer.offset),8));
  275. flush(stderr);
  276. {$endif DEBUG}
  277. new_timer.segment:=get_cs;
  278. new_timer.offset:=djgpp_timer_hdlr;
  279. reload:=3;
  280. {$ifdef DEBUG}
  281. writeln(stderr,'new pm int8 '+hexstr(new_timer.segment,4)+':'+hexstr(longint(new_timer.offset),8));
  282. flush(stderr);
  283. {$endif DEBUG}
  284. set_pm_interrupt($8,new_timer);
  285. reload:=1;
  286. asm
  287. movl _RELOAD,%eax
  288. movl %eax,___djgpp_timer_countdown
  289. end;
  290. mcount_skip := 0;
  291. end;
  292. {$ASMMODE ATT}
  293. begin
  294. mcount_init;
  295. end.
  296. {
  297. $Log$
  298. Revision 1.2 1998-05-31 14:18:28 peter
  299. * force att or direct assembling
  300. * cleanup of some files
  301. }