profile.pp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1993,97 by Pierre Muller,
  5. member of the Free Pascal development team.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. Unit profile;
  13. {$I os.inc}
  14. interface
  15. uses go32,dpmiexcp;
  16. type header = record
  17. low,high,nbytes : longint;
  18. end;
  19. {/* entry of a GPROF type file
  20. */}
  21. type MTABE = record
  22. from,_to,count : longint;
  23. end;
  24. pMTABE = ^MTABE;
  25. ppMTABE = ^pMTABE;
  26. {/* internal form - sizeof(MTAB) is 4096 for efficiency
  27. */ }
  28. type
  29. PMTAB = ^M_TAB;
  30. M_TAB = record
  31. calls : array [0..340] of MTABE;
  32. prev : PMTAB;
  33. end;
  34. var
  35. h : header;
  36. histogram : ^integer;
  37. const
  38. mcount_skip : longint = 1;
  39. var
  40. histlen : longint;
  41. oldexitproc : pointer;
  42. const
  43. mtab : PMTAB = nil;
  44. {/* called by functions. Use the pointer it provides to cache
  45. ** the last used MTABE, so that repeated calls to/from the same
  46. ** pair works quickly - no lookup.
  47. */ }
  48. procedure mcount;
  49. implementation
  50. type plongint = ^longint;
  51. var starttext, endtext : longint;
  52. const cache : pMTABE = nil;
  53. { ebp contains the frame of mcount)
  54. (ebp) the frame of calling (to_)
  55. ((ebp)) the frame of from }
  56. { problem how to avoid mcount calling itself !! }
  57. procedure mcount; [public, alias : 'MCOUNT'];
  58. var
  59. m : pmtab;
  60. i,to_,ebp,from,mtabi : longint;
  61. begin
  62. { optimisation !! }
  63. asm
  64. pushal
  65. movl 4(%ebp),%eax
  66. movl %eax,to_
  67. movl (%ebp),%eax
  68. movl 4(%eax),%eax
  69. movl %eax,from
  70. end;
  71. if endtext=0 then
  72. asm
  73. popal
  74. leave
  75. ret
  76. end;
  77. mcount_skip := 1;
  78. if (to_ > endtext) or (from > endtext) then runerror(255);
  79. if ((cache<>nil) and
  80. (cache^.from=from) and
  81. (cache^._to=to_)) then
  82. begin
  83. {/* cache paid off - works quickly */}
  84. inc(cache^.count);
  85. mcount_skip:=0;
  86. asm
  87. popal
  88. leave
  89. ret
  90. end;
  91. end;
  92. {/* no cache hit - search all mtab tables for a match, or an empty slot */}
  93. mtabi := -1;
  94. m:=mtab;
  95. while m<>nil do
  96. begin
  97. for i:=0 to 340 do
  98. begin
  99. if m^.calls[i].from=0 then
  100. begin
  101. {/* empty slot - end of table */ }
  102. mtabi := i;
  103. break;
  104. end;
  105. if ((m^.calls[i].from = from) and
  106. (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 new_timer,old_timer : tseginfo;
  152. { from itimer.c
  153. /* Copyright (C) 1995 Charles Sandmann ([email protected])
  154. setitimer implmentation - used for profiling and alarm
  155. BUGS: ONLY ONE AT A TIME, first pass code
  156. This software may be freely distributed, no warranty. */ }
  157. { static void timer_action(int signum) }
  158. {
  159. if(reload)
  160. __djgpp_timer_countdown = reload;
  161. else
  162. stop_timer();
  163. raise(sigtype);
  164. }
  165. var reload : longint;
  166. const invalid_mcount_call : longint = 0;
  167. mcount_nb : longint = 0;
  168. doublecall : longint = 0;
  169. function mcount_tick(x : longint) : longint;forward;
  170. function timer(x : longint) : longint;
  171. begin
  172. if reload>0 then
  173. asm
  174. movl _RELOAD,%eax
  175. movl %eax,___djgpp_timer_countdown
  176. end;
  177. mcount_tick(x);
  178. { _raise(SIGPROF); }
  179. end;
  180. {/* this is called during program exit (installed by atexit). */}
  181. procedure mcount_write;
  182. var m : PMTAB;
  183. i : longint;
  184. f : file;
  185. {
  186. MTAB *m;
  187. int i, f;
  188. struct itimerval new_values;
  189. mcount_skip = 1;
  190. /* disable timer */
  191. new_values.it_value.tv_usec = new_values.it_interval.tv_usec = 0;
  192. new_values.it_value.tv_sec = new_values.it_interval.tv_sec = 0;
  193. setitimer(ITIMER_PROF, &new_values, NULL); }
  194. begin
  195. mcount_skip:=1;
  196. signal(SIGTIMR,@SIG_IGN);
  197. signal(SIGPROF,@SIG_IGN);
  198. set_pm_interrupt($8,old_timer);
  199. reload:=0;
  200. exitproc:=oldexitproc;
  201. writeln('Writing profile output');
  202. writeln('histogram length = ',histlen);
  203. writeln('Nb of double calls = ',doublecall);
  204. if invalid_mcount_call>0 then
  205. writeln('nb of invalid mcount : ',invalid_mcount_call,'/',mcount_nb)
  206. else
  207. writeln('nb of mcount : ',mcount_nb);
  208. assign(f,'gmon.out');
  209. rewrite(f,1);
  210. blockwrite(f, h, sizeof(header));
  211. blockwrite(f, histogram^, histlen);
  212. m:=mtab;
  213. while m<>nil do
  214. begin
  215. for i:=0 to 340 do
  216. begin
  217. if (m^.calls[i].from = 0) then
  218. break;
  219. blockwrite(f, m^.calls[i],sizeof(MTABE));
  220. {$ifdef DEBUG}
  221. if m^.calls[i].count>0 then
  222. writeln(' 0x',hexstr(m^.calls[i]._to,8),' called from ',hexstr(m^.calls[i].from,8),
  223. ' ',m^.calls[i].count,' times');
  224. {$endif DEBUG}
  225. end;
  226. m:=m^.prev;
  227. end;
  228. close(f);
  229. end;
  230. (* extern unsigned start __asm__ ("start");
  231. #define START (unsigned)&start
  232. extern int etext;
  233. /* ARGSUSED */
  234. static void *)
  235. function mcount_tick(x : longint) : longint;
  236. var bin : longint;
  237. begin
  238. if mcount_skip=0 then
  239. begin
  240. {bin = __djgpp_exception_state->__eip;}
  241. bin := djgpp_exception_state^.__eip;
  242. if (djgpp_exception_state^.__cs=get_cs) and
  243. (bin >= starttext) and (bin <= endtext) then
  244. begin
  245. {bin := (bin - starttext) div 4;} {/* 4 EIP's per bin */}
  246. bin := (bin - starttext) div 16;
  247. inc(histogram[bin]);
  248. end
  249. else
  250. inc(invalid_mcount_call);
  251. inc(mcount_nb);
  252. end
  253. else
  254. inc(doublecall);
  255. mcount_tick:=0;
  256. end;
  257. {/* this is called to initialize profiling before the program starts */}
  258. procedure _mcount_init;
  259. {struct itimerval new_values;}
  260. function djgpp_timer_hdlr : pointer;
  261. begin
  262. asm
  263. movl $___djgpp_timer_hdlr,%eax
  264. movl %eax,__RESULT
  265. end;
  266. end;
  267. procedure set_old_timer_handler;
  268. begin
  269. asm
  270. movl $_OLD_TIMER,%eax
  271. movl $___djgpp_old_timer,%ebx
  272. movl (%eax),%ecx
  273. movl %ecx,(%ebx)
  274. movw 4(%eax),%ax
  275. movw %ax,4(%ebx)
  276. end;
  277. end;
  278. begin
  279. asm
  280. movl $_etext,_ENDTEXT
  281. movl $start,_STARTTEXT
  282. end;
  283. h.low := starttext;
  284. h.high := endtext;
  285. histlen := ((h.high-h.low) div 16) * 2; { must be even }
  286. h.nbytes := sizeof(header) + histlen;
  287. getmem(histogram,histlen);
  288. fillchar(histogram^, histlen,#0);
  289. oldexitproc:=exitproc;
  290. exitproc:=@mcount_write;
  291. {/* here, do whatever it takes to initialize the timer interrupt */}
  292. signal(SIGPROF,@mcount_tick);
  293. signal(SIGTIMR,@timer);
  294. get_pm_interrupt($8,old_timer);
  295. set_old_timer_handler;
  296. {$ifdef DEBUG}
  297. writeln(stderr,'ori pm int8 '+hexstr(old_timer.segment,4)+':'
  298. +hexstr(longint(old_timer.offset),8));
  299. flush(stderr);
  300. {$endif DEBUG}
  301. new_timer.segment:=get_cs;
  302. new_timer.offset:=djgpp_timer_hdlr;
  303. reload:=3;
  304. {$ifdef DEBUG}
  305. writeln(stderr,'new pm int8 '+hexstr(new_timer.segment,4)+':'
  306. +hexstr(longint(new_timer.offset),8));
  307. flush(stderr);
  308. {$endif DEBUG}
  309. set_pm_interrupt($8,new_timer);
  310. reload:=1;
  311. asm
  312. movl _RELOAD,%eax
  313. movl %eax,___djgpp_timer_countdown
  314. end;
  315. mcount_skip := 0;
  316. end;
  317. begin
  318. _mcount_init;
  319. end.
  320. {
  321. $Log$
  322. Revision 1.1 1998-03-25 11:18:42 root
  323. Initial revision
  324. Revision 1.4 1998/01/26 11:57:39 michael
  325. + Added log at the end
  326. Revision 1.3 1998/01/16 16:54:22 pierre
  327. + logs added at end
  328. + dxeload and emu387 added in makefile
  329. }
  330. {
  331. $Log$
  332. Revision 1.1 1998-03-25 11:18:42 root
  333. Initial revision
  334. Revision 1.4 1998/01/26 11:57:39 michael
  335. + Added log at the end
  336. Working file: rtl/dos/go32v2/profile.pp
  337. description:
  338. ----------------------------
  339. revision 1.3
  340. date: 1998/01/16 16:54:22; author: pierre; state: Exp; lines: +5 -2
  341. + logs added at end
  342. + dxeload and emu387 added in makefile
  343. ----------------------------
  344. revision 1.2
  345. date: 1997/12/01 12:26:09; author: michael; state: Exp; lines: +14 -3
  346. + added copyright reference in header.
  347. ----------------------------
  348. revision 1.1
  349. date: 1997/11/27 08:33:52; author: michael; state: Exp;
  350. Initial revision
  351. ----------------------------
  352. revision 1.1.1.1
  353. date: 1997/11/27 08:33:52; author: michael; state: Exp; lines: +0 -0
  354. FPC RTL CVS start
  355. =============================================================================
  356. }