You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

670 lines
14 KiB

  1. /************************************************************************
  2. * *
  3. * Copyright (c) 1985 by *
  4. * Digital Equipment Corporation, Maynard, MA *
  5. * All rights reserved. *
  6. * *
  7. * The information in this software is subject to change without *
  8. * notice and should not be construed as a commitment by Digital *
  9. * Equipment Corporation. *
  10. * *
  11. * Digital assumes no responsibility for the use or reliability *
  12. * of its software on equipment which is not supplied by Digital. *
  13. * *
  14. * Redistribution and use in source and binary forms are permitted *
  15. * provided that the above copyright notice and this paragraph are *
  16. * duplicated in all such forms and that any documentation, *
  17. * advertising materials, and other materials related to such *
  18. * distribution and use acknowledge that the software was developed *
  19. * by Digital Equipment Corporation. The name of Digital Equipment *
  20. * Corporation may not be used to endorse or promote products derived *
  21. * from this software without specific prior written permission. *
  22. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR *
  23. * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED *
  24. * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.*
  25. * Do not take internally. In case of accidental ingestion, contact *
  26. * your physician immediately. *
  27. * *
  28. ************************************************************************/
  29. /* DO NOT INCLUDE "mnemosyne.h" !!! */
  30. #include <stdio.h>
  31. #include <sys/types.h>
  32. #include <sys/file.h>
  33. /* shared stuff - and decl of struct ptr */
  34. #include "mnemconf.h"
  35. static char rcsid[] = "/fats/tools/hsv/mnemosyne/mnemosyne.c,v 1.1.1.1 1995/06/06 18:18:28 fabio Exp";
  36. /*
  37. malloc() realloc() and family wrappers - these functions manage a set
  38. of data files that are updated whenever a pointer is allocated or freed,
  39. as well as gathering stats about dynamic memory use and leakage.
  40. Marcus J. Ranum, 1990. (mjr@decuac.dec.com)
  41. */
  42. /*
  43. there is some egregious use of globals, void functions, and whatnot
  44. in this code. it is mostly due to the constraint that nothing must
  45. be visible to the outside, and that the code must be structurally
  46. simple. error checking is pitched out the window in spots, since an
  47. error in the mnemosyne code should not affect whatever is being
  48. instrumented if at all possible. this means no errors, no signals,
  49. nothing. (this message brought to you by my ego, in case you think
  50. I don't know how to write better code than this) :)
  51. mjr, hacking on Christmas, 1990.
  52. */
  53. #define REC_UNINIT 000
  54. #define REC_INITTED 001
  55. #define REC_ERR 002
  56. #define REC_ON 010
  57. #define REC_ONOFF 020
  58. static int rec_state = REC_UNINIT;
  59. /*
  60. this method of storing the symbol maps is not the most efficient, but
  61. then that's not important here, since all we're trying to do is find
  62. memory leaks. if you choose to improve the symbol management to use
  63. bucketed hash tables, please contact the author and the code will be
  64. updated :) - besides, since we do file I/O every time you malloc or
  65. free something, there's no way in hell this is going to set any new
  66. records for speed.
  67. */
  68. /* storage for code/line # entry */
  69. struct sym {
  70. char *labl;
  71. int lineno;
  72. int mapno;
  73. int mallcnt;
  74. float avsiz;
  75. struct sym *next;
  76. };
  77. /* static symbol map */
  78. static struct {
  79. FILE *fp;
  80. FILE *log;
  81. int fd;
  82. long nalloc; /* count of allocations */
  83. long nrlloc; /* count of re-allocations */
  84. long nfree; /* count of frees */
  85. long nbfree; /* count of bad frees */
  86. long ninuse; /* known allocated memory in use */
  87. float avgsiz; /* average malloc size */
  88. /* one entry per pointer returned by malloc */
  89. int pmap; /* current next ptr map to alloc */
  90. struct ptr *phash[HASHSIZ];
  91. /* one entry per line of code that calls malloc/realloc, etc */
  92. int lmap; /* current next line map to alloc */
  93. struct sym *shash[HASHSIZ]; /* hash access */
  94. } map;
  95. /* print a locale record with checks for closed log file */
  96. static void
  97. ploc(lab,lin,siz)
  98. char *lab;
  99. int lin;
  100. int siz;
  101. {
  102. if(map.log == (FILE *)0)
  103. return;
  104. if(lab != (char *)0)
  105. (void)fprintf(map.log," \"%s\"",lab);
  106. else
  107. (void)fprintf(map.log," unknown");
  108. if(lin != -1)
  109. (void)fprintf(map.log," line:%d",lin);
  110. if(siz != -1)
  111. (void)fprintf(map.log," size:%d",siz);
  112. }
  113. /* print a symbol map entry with checks for closed log file */
  114. static void
  115. psym(s)
  116. struct sym *s;
  117. {
  118. if(map.log == (FILE *)0)
  119. return;
  120. (void)fprintf(map.log," \"%s\"",s->labl);
  121. if(s->lineno != -1)
  122. (void)fprintf(map.log," line:%d",s->lineno);
  123. }
  124. /* output a warning message with checks for closed log file */
  125. static void
  126. pmsg(s)
  127. char *s;
  128. {
  129. if(map.log == (FILE *)0)
  130. return;
  131. (void)fprintf(map.log,"%s",s);
  132. }
  133. /* save an entry to the .lines file */
  134. static void
  135. savesym(s)
  136. struct sym *s;
  137. {
  138. if(map.fp == (FILE *)0)
  139. return;
  140. (void)fprintf(map.fp,"%d\t%d\t%.1f\t%d\t%s\n",
  141. s->mapno,s->mallcnt,s->avsiz,s->lineno,s->labl);
  142. }
  143. /* save an entry in the pointer map file */
  144. static void
  145. saveptr(p)
  146. register struct ptr *p;
  147. {
  148. if(lseek(map.fd,(off_t)(p->map * sizeof(p->dsk)),0) !=
  149. (off_t)(p->map * sizeof(p->dsk))) {
  150. pmsg("mnemosyne: cannot seek in pointer map file\n");
  151. rec_state |= REC_ERR;
  152. return;
  153. }
  154. if(write(map.fd,(char *)&(p->dsk),sizeof(p->dsk)) != sizeof(p->dsk)) {
  155. pmsg("mnemosyne: cannot write in pointer map file\n");
  156. rec_state |= REC_ERR;
  157. return;
  158. }
  159. }
  160. /* initialize everything - symbol tables, files, and maps */
  161. static void
  162. initmap()
  163. {
  164. register int xp;
  165. if(rec_state & REC_INITTED)
  166. return;
  167. if((map.fp = fopen(LINESFILE,"w")) == (FILE *)0)
  168. return;
  169. if((map.fd = open(PTRFILE,O_RDWR|O_CREAT|O_TRUNC,0600)) < 0) {
  170. (void)fclose(map.fp);
  171. return;
  172. }
  173. map.log = stderr;
  174. map.lmap = map.pmap = 0;
  175. map.nalloc = map.nrlloc = map.nfree = map.nbfree = 0L;
  176. map.ninuse = 0L;
  177. map.avgsiz = 0.0;
  178. for(xp = 0; xp < HASHSIZ; xp++) {
  179. map.phash[xp] = (struct ptr *)0;
  180. map.shash[xp] = (struct sym *)0;
  181. }
  182. rec_state = REC_INITTED | REC_ON;
  183. }
  184. /* set logging to a FILE * */
  185. void
  186. mnem_setlog(fp)
  187. FILE *fp;
  188. {
  189. map.log = fp;
  190. }
  191. /* return state of the recorder */
  192. int
  193. mnem_recording()
  194. {
  195. return((rec_state & REC_ON) && !(rec_state & REC_ERR));
  196. }
  197. /* turn on or off recording */
  198. int
  199. mnem_setrecording(val)
  200. int val;
  201. {
  202. if(!(rec_state & REC_INITTED))
  203. initmap();
  204. if(val)
  205. rec_state |= REC_ON;
  206. else
  207. rec_state &= ~REC_ON;
  208. if(map.fp != (FILE *)0)
  209. (void)fflush(map.fp);
  210. rec_state |= REC_ONOFF;
  211. return(0);
  212. }
  213. /* lookup a pointer record - search pointer hash table */
  214. static struct ptr *
  215. lookupptr(ptr)
  216. mall_t ptr;
  217. {
  218. register struct ptr *p;
  219. /* this probably give simply terrible hash performance */
  220. p = map.phash[(unsigned long)ptr % HASHSIZ];
  221. while(p != (struct ptr *)0) {
  222. if(ptr == p->ptr)
  223. return(p);
  224. p = p->next;
  225. }
  226. return((struct ptr *)0);
  227. }
  228. /*
  229. * polynomial conversion ignoring overflows
  230. * [this seems to work remarkably well, in fact better
  231. * then the ndbm hash function. Replace at your own risk]
  232. * use: 65599 nice.
  233. * 65587 even better.
  234. * author: oz@nexus.yorku.ca
  235. */
  236. static unsigned int
  237. dbm_hash(str)
  238. register char *str;
  239. {
  240. register unsigned int n = 0;
  241. while(*str != '\0')
  242. n = *str++ + 65599 * n;
  243. return(n);
  244. }
  245. /* lookup a line/source entry by name (search hash table) */
  246. static struct sym *
  247. lookupsymbyname(nam,lin)
  248. char *nam;
  249. int lin;
  250. {
  251. register struct sym *s;
  252. char *p = nam;
  253. if(p == (char *)0)
  254. p = "unknown";
  255. s = map.shash[(dbm_hash(p) + lin) % HASHSIZ];
  256. while(s != (struct sym *)0) {
  257. if(!strcmp(s->labl,nam) && s->lineno == lin)
  258. return(s);
  259. s = s->next;
  260. }
  261. return((struct sym *)0);
  262. }
  263. /* lookup a line/source entry by number (exhaustively search hash table) */
  264. static struct sym *
  265. lookupsymbynum(num)
  266. int num;
  267. {
  268. register struct sym *s;
  269. register int x;
  270. for(x = 0; x < HASHSIZ; x++) {
  271. s = map.shash[x];
  272. while(s != (struct sym *)0) {
  273. if(s->mapno == num)
  274. return(s);
  275. s = s->next;
  276. }
  277. }
  278. return((struct sym *)0);
  279. }
  280. /* stuff a pointer's value in the pointer map table */
  281. static void
  282. storeptr(ptr,siz,lab,lin)
  283. mall_t ptr;
  284. int siz;
  285. char *lab;
  286. int lin;
  287. {
  288. register struct ptr *p;
  289. register struct sym *s;
  290. int hv;
  291. /*
  292. is there is no existing symbol entry for this line of code...
  293. we must needs make one - and painful it is...
  294. */
  295. if((s = lookupsymbyname(lab,lin)) == (struct sym *)0) {
  296. s = (struct sym *)malloc(sizeof(struct sym));
  297. if(s == (struct sym *)0) {
  298. pmsg("mnemosyne: cannot allocate sym entry\n");
  299. rec_state |= REC_ERR;
  300. return;
  301. }
  302. /*
  303. this is funky - since we know the label is (?)
  304. compiled-in, we can just keep a pointer to it,
  305. rather than copying our own version of it.
  306. */
  307. if(lab != (char *)0)
  308. s->labl = lab;
  309. else
  310. s->labl = "unknown";
  311. s->mapno = map.lmap++;
  312. /* add sym to hash table */
  313. s->next = map.shash[hv = ((dbm_hash(s->labl) + lin) % HASHSIZ)];
  314. map.shash[hv] = s;
  315. s->lineno = lin;
  316. s->mallcnt = 1;
  317. s->avsiz = siz;
  318. savesym(s);
  319. } else {
  320. /* found an already defined symbol. store some averages */
  321. s->avsiz = ((s->avsiz * s->mallcnt) + siz) / (s->mallcnt + 1);
  322. (s->mallcnt)++;
  323. }
  324. p = lookupptr(ptr);
  325. if(p != (struct ptr *)0 && p->dsk.siz != 0) {
  326. struct sym *x;
  327. pmsg("pointer re-allocated without being freed");
  328. ploc(lab,lin,(int)siz);
  329. if((x = lookupsymbynum(p->dsk.smap)) != (struct sym *)0) {
  330. pmsg(" last allocated ");
  331. psym(x);
  332. }
  333. pmsg("\n");
  334. }
  335. /* heavy sigh. no entry for this pointer. make one. */
  336. if(p == (struct ptr *)0) {
  337. p = (struct ptr *)malloc(sizeof(struct ptr));
  338. if(p == (struct ptr *)0) {
  339. pmsg("mnemosyne: cannot expand pointer table\n");
  340. rec_state |= REC_ERR;
  341. return;
  342. }
  343. /* link it in */
  344. p->next = map.phash[(unsigned long)ptr % HASHSIZ];
  345. map.phash[(unsigned long)ptr % HASHSIZ] = p;
  346. }
  347. /* if we get to here (hazoo! hazaa!) both 's' and 'p' are OK */
  348. p->ptr = ptr;
  349. p->dsk.siz = siz;
  350. p->dsk.smap = s->mapno;
  351. p->map = map.pmap++;
  352. /* store the size */
  353. map.ninuse += siz;
  354. saveptr(p);
  355. }
  356. /*
  357. mark a pointer as now being free. note that a 1 is returned IF
  358. the actual value should NOT be really passed to free()
  359. */
  360. static int
  361. freeptr(ptr,lab,lin)
  362. mall_t ptr;
  363. char *lab;
  364. int lin;
  365. {
  366. register struct ptr *p;
  367. p = lookupptr(ptr);
  368. if(p == (struct ptr *)0) {
  369. pmsg("pointer freed that was never allocated");
  370. ploc(lab,lin,-1);
  371. pmsg("\n");
  372. return(1);
  373. }
  374. if(p != (struct ptr *)0 && p->dsk.siz == 0) {
  375. struct sym *x;
  376. pmsg("pointer re-freed when already free");
  377. ploc(lab,lin,-1);
  378. if((x = lookupsymbynum(p->dsk.smap)) != (struct sym *)0) {
  379. pmsg(" last allocated:");
  380. psym(x);
  381. }
  382. pmsg("\n");
  383. return(1);
  384. }
  385. /* get some free */
  386. map.ninuse -= p->dsk.siz;
  387. /* write in the map that it is free */
  388. p->dsk.siz = 0;
  389. saveptr(p);
  390. return(0);
  391. }
  392. /* pretend we are malloc() */
  393. mall_t
  394. mnem_malloc(siz,lab,lin)
  395. unsigned siz;
  396. char *lab;
  397. int lin;
  398. {
  399. mall_t ret;
  400. if(!(rec_state & REC_INITTED))
  401. initmap();
  402. if((ret = malloc(siz)) == (mall_t)0) {
  403. pmsg("malloc returned null pointer at");
  404. ploc(lab,lin,(int)siz);
  405. pmsg("\n");
  406. return(ret);
  407. }
  408. if((rec_state & REC_ON) && !(rec_state & REC_ERR))
  409. storeptr(ret,(int)siz,lab,lin);
  410. map.avgsiz = ((map.avgsiz * map.nalloc) + siz) / (map.nalloc + 1);
  411. map.nalloc++;
  412. return(ret);
  413. }
  414. /* pretend we are calloc() */
  415. mall_t
  416. mnem_calloc(cnt,siz,lab,lin)
  417. unsigned cnt;
  418. unsigned siz;
  419. char *lab;
  420. int lin;
  421. {
  422. mall_t ret;
  423. if(!(rec_state & REC_INITTED))
  424. initmap();
  425. if((ret = calloc(cnt,siz)) == (mall_t)0) {
  426. pmsg("calloc returned null pointer at");
  427. ploc(lab,lin,(int)(siz * cnt));
  428. pmsg("\n");
  429. return(ret);
  430. }
  431. if((rec_state & REC_ON) && !(rec_state & REC_ERR))
  432. storeptr(ret,(int)(cnt * siz),lab,lin);
  433. map.avgsiz = ((map.avgsiz * map.nalloc) + siz) / (map.nalloc + 1);
  434. map.nalloc++;
  435. return(ret);
  436. }
  437. /* pretend we are realloc() */
  438. mall_t
  439. mnem_realloc(ptr,siz,lab,lin)
  440. mall_t ptr;
  441. unsigned siz;
  442. char *lab;
  443. int lin;
  444. {
  445. mall_t ret;
  446. if(!(rec_state & REC_INITTED))
  447. initmap();
  448. if((ret = realloc(ptr,siz)) == (mall_t)0) {
  449. pmsg("realloc returned null pointer at");
  450. ploc(lab,lin,(int)siz);
  451. pmsg("\n");
  452. return(ret);
  453. }
  454. if((rec_state & REC_ON) && !(rec_state & REC_ERR)) {
  455. if(!freeptr(ptr,lab,lin))
  456. storeptr(ret,(int)siz,lab,lin);
  457. }
  458. map.nrlloc++;
  459. return(ret);
  460. }
  461. /* pretend we are free() */
  462. void
  463. mnem_free(ptr,lab,lin)
  464. mall_t ptr;
  465. char *lab;
  466. int lin;
  467. {
  468. if(!(rec_state & REC_INITTED))
  469. initmap();
  470. if((rec_state & REC_ON) && !(rec_state & REC_ERR))
  471. if(freeptr(ptr,lab,lin) == 0) {
  472. (void)free(ptr);
  473. map.nfree++;
  474. } else
  475. map.nbfree++;
  476. }
  477. /* dump everything we know about nothing in particular */
  478. int
  479. mnem_writestats()
  480. {
  481. register struct sym *s;
  482. register int x;
  483. if(map.fp == (FILE *)0)
  484. return(-1);
  485. (void)fseek(map.fp,0L,0);
  486. /* dump our life's story */
  487. (void)fprintf(map.fp,"#total allocations:%ld\n",map.nalloc);
  488. (void)fprintf(map.fp,"#total re-allocations:%ld\n",map.nrlloc);
  489. (void)fprintf(map.fp,"#total frees:%ld\n",map.nfree);
  490. if(map.nbfree != 0L)
  491. (void)fprintf(map.fp,"#bad/dup frees:%ld\n",map.nbfree);
  492. (void)fprintf(map.fp,"#total allocated never freed:%ld\n",map.ninuse);
  493. (void)fprintf(map.fp,"#average size of allocations:%.1f\n",map.avgsiz);
  494. /* note if we detected an internal error */
  495. if(rec_state & REC_ERR)
  496. (void)fprintf(map.fp,
  497. "#(figures likely inaccurate due to error)\n");
  498. /* note if the system was on all the time ? */
  499. if(!(rec_state & REC_ON) || (rec_state & REC_ONOFF))
  500. (void)fprintf(map.fp,
  501. "#(figures likely inaccurate as recording was off)\n");
  502. /* write the legend */
  503. (void)fprintf(map.fp,"#map#\tcalls\tave\tline#\tfile\n");
  504. for(x = 0; x < HASHSIZ; x++) {
  505. s = map.shash[x];
  506. while(s != (struct sym *)0) {
  507. savesym(s);
  508. s = s->next;
  509. }
  510. }
  511. (void)fflush(map.fp);
  512. return(0);
  513. }