1 /************************************************/
4 /* Jean-Marc Pigeon <jmp@safe.ca> 2009 */
6 /************************************************/
7 /* This program is free software; you can */
8 /* redistribute it and/or modify it under the */
9 /* terms of the GNU General Public License as */
10 /* published by the Free Software Foundation */
11 /* version 2 of the License */
13 /* This program is distributed in the hope that */
14 /* it will be useful, but WITHOUT ANY WARRANTY; */
15 /* without even the implied warranty of */
16 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR */
17 /* PURPOSE. See the GNU General Public License */
18 /* for more details. */
20 /* You should have received a copy of the GNU */
21 /* General Public License along with this */
22 /* program; if not, write to the Free Software */
23 /* Foundation, Inc., 51 Franklin Street, */
24 /* Fifth Floor, Boston, MA 02110-1301, USA. */
25 /************************************************/
27 /* Implement very sub level procedure to */
28 /* debug potential memory leak (or other) */
31 /************************************************/
57 #define MXTB 10 /*max trace deep */
59 extern char **environ; /*application env varirable */
62 typedef struct timeval TIMEVAL;
64 typedef struct { /*leak detection structure */
65 void *ptr; /*allocated memory */
66 TIMEVAL date; /*allocation date */
67 size_t size; /*memory size */
68 void *tb[MXTB]; /*back trace */
69 int noleak; /*No leak consideration flag */
72 int memleak=DMFALSE; /*flag to check for memory leak */
74 static void *memory; /*allocated memory structure */
75 static long nummem; /*number of leak structure */
76 static char *cpath; /*Application curpath */
77 static char *cbase; /*Application base */
78 static int flgleak; /*leak mod flag */
79 static u_long collected;/*number of memory still open */
80 static LEAKTYP **memsort;
84 /************************************************/
86 /* Procedure to compare to "leaking" memory*/
87 /* ptr, used by search routine */
89 /************************************************/
90 static int cmpptr(const void *v1,const void *v2)
104 p1=(u_long)(((LEAKTYP *)v1)->ptr);
105 p2=(u_long)(((LEAKTYP *)v2)->ptr);
115 /************************************************/
117 /* Procedure to compare to flag the leak */
118 /* value withing collected open memory. */
120 /************************************************/
121 static void setleakflg(const void *v1,const VISIT which,const int depth)
125 case preorder: /*no remote dump */
128 case postorder: /*remote dump */
130 (*((LEAKTYP **)v1))->noleak=flgleak;
137 /************************************************/
139 /* Procedure to count collected "leaking" */
142 /************************************************/
143 static void collectleakptr(const void *v1,const VISIT which,const int depth)
147 case preorder: /*no remote dump */
150 case postorder: /*remote dump */
152 if ((*((LEAKTYP **)v1))->noleak==0) {
153 memsort[collected]=*((LEAKTYP **)v1);
162 /************************************************/
164 /* Procedure to compare to "leaking" memory*/
165 /* information, from the backtrace stand */
168 /************************************************/
169 static int sametrace(const LEAKTYP *l1,const LEAKTYP *l2)
177 while ((inx<MXTB)&&(diff==(long)0)) {
178 diff=(long)(l1->tb[inx])-(long)(l2->tb[inx]);
186 /************************************************/
188 /* Procedure to compare to "leaking" memory*/
191 /************************************************/
192 static int cmpleak(const void *p1,const void *p2)
199 l1=((LEAKTYP **)p1)[0];
200 l2=((LEAKTYP **)p2)[0];
201 if ((diff=sametrace(l1,l2))==0)
202 diff=l1->date.tv_sec-l2->date.tv_sec;
204 diff=l1->date.tv_usec-l2->date.tv_usec;
210 /************************************************/
212 /* Procedure to convert back trace address */
213 /* to sources line information. */
215 /************************************************/
216 static void disline(LEAKTYP *leak,const char *path,FILE *fichier)
219 #define ADDR2LINE "addr2line"
225 (void) sprintf(command,"%s -e %s",ADDR2LINE,path);
226 for (i=0;i<MXTB;i++) {
231 (void) sprintf(str," %08lx",(long)leak->tb[i]);
232 (void) strcat(command,str);
234 if ((canal=popen(command,"r"))!=(FILE *)0) {
237 while (fgets(line,sizeof(line)-1,canal)!=(char *)0) {
238 (void) fprintf(fichier,"%s",line);
240 (void) fclose(canal);
246 /************************************************/
248 /* Procedure to assigne a Backtrace stack */
249 /* (to trace back leak) to memory structure*/
251 /************************************************/
252 static void setbtrace(LEAKTYP *leak,int step)
262 (void) memset(zone,'\000',sizeof(zone));
263 num=backtrace(zone,MXSTEP);
264 for (j=0,i=step+1;(j<MXTB)&&(i<num);i++,j++) {
267 (void) gettimeofday(&(leak->date),&tz);
272 /************************************************/
274 /* Procedure to report backtrace via syslog*/
276 /************************************************/
277 static void logbacktrace(int loglev,int step)
286 (void) memset(zone,'\000',sizeof(zone));
287 num=backtrace(zone,MXSTEP);
288 for (i=step;i<num;i++) {
289 (void) syslog(loglev,"\tbt--> %08lx",(long)zone[i]);
295 /************************************************/
297 /* procedure to get a "leak proof" memory */
298 /* email to a shell script */
300 /************************************************/
301 static void *getmemory(size_t size,int step)
304 register LEAKTYP *leak;
305 register void **leakptr;
307 leak=(LEAKTYP *)calloc(1,sizeof(LEAKTYP));
309 leak->ptr=malloc(size);
310 (void) setbtrace(leak,step+1);
311 leakptr=tsearch((void *)leak,&memory,cmpptr);
312 if (*leakptr!=leak) {
313 (void) syslog(LOG_DEBUG,"getmemory trouble PTR already within tsearch");
321 /************************************************/
323 /* Procedure to memorise the current */
324 /* application path, will be used to */
325 /* dump the memory stat. */
327 /************************************************/
328 void dbg_setcurpath(char *path)
336 /************************************************/
338 /* Procedure to memorise the current */
339 /* application base, will be used to */
340 /* dump the memory stat. */
342 /************************************************/
343 void dbg_setcurbase(char *path)
351 /************************************************/
353 /* Procedure to assigne new memory area */
354 /* while checking (if needed) for memory */
357 /************************************************/
358 void *dbg_malloc(size_t size)
363 if (memleak==DMTRUE) {
364 ptr=getmemory(size,1);
373 /************************************************/
375 /* Procedure to assigne clean new memory */
376 /* while checking (if needed) for memory */
379 /************************************************/
380 void *dbg_calloc(size_t nmemb,size_t size)
385 if (memleak==DMTRUE) {
386 ptr=getmemory(nmemb*size,1);
387 (void) memset(ptr,'\000',nmemb*size);
390 ptr=calloc(nmemb,size);
396 /************************************************/
398 /* Procedure to free memory from memry pool*/
399 /* while checking (if needed) for memory */
402 /************************************************/
403 void dbg_free(void *ptr)
406 if (memleak==DMTRUE) {
411 if ((leakpt=(LEAKTYP **)tfind(&leak,&memory,cmpptr))!=(LEAKTYP **)0) {
415 (void) tdelete(*leakpt,&memory,cmpptr);
416 (void) free(lk->ptr);
421 char *strloc="subsys.c:dbg_free, Unable to find 'leak memory' "
422 "for ptr '%08lx'=<%s>\n";
424 (void) syslog(LOG_INFO,strloc,(long)ptr,(char *)ptr);
425 (void) setbtrace(&leak,0);
426 (void) fprintf(stderr,strloc,(long)ptr,(char *)ptr);
427 (void) disline(&leak,cpath,stderr);
428 (void) fprintf(stderr,"\n");
437 /************************************************/
439 /* Procedure to remove a variable from the */
440 /* environement list */
442 /************************************************/
443 int dbg_unsetenv(const char *name)
450 if (memleak==DMTRUE) {
451 if (environ!=(char **)0) {
454 for (i=0;environ[i]!=(char *)0;i++) {
457 if ((ptr=strstr(environ[i],name))==(char *)0)
459 if (*(environ[i]+strlen(name))=='=') {
460 (void) dbg_free(ptr);
462 environ[i]=environ[i+1];
465 while (environ[i]!=(char *)0);
473 status=unsetenv(name);
479 /************************************************/
481 /* Procedure to install variable within the*/
484 /************************************************/
485 int dbg_putenv(char *valeur)
491 if (memleak==DMTRUE) {
494 if ((ptr=strchr(valeur,'='))!=(char *)0) {
497 register int tocheck;
501 if ((tocheck=(ptr-valeur))<=0)
504 if (environ!=(char **)0) {
505 for (;environ[taille]!=(char *)0;taille++) {
506 if (strncmp(valeur,environ[taille],tocheck)==0) {
507 (void) dbg_free(environ[taille]);
508 environ[taille]=valeur;
515 environ=dbg_calloc(1,sizeof(char *));
517 environ=(char **)dbg_realloc((void *)environ,(taille+2)*sizeof(char *));
518 environ[taille]=valeur;
519 environ[taille+1]=(char *)0;
524 status=putenv(valeur);
531 /************************************************/
533 /* Procedure to set variable within the */
536 /************************************************/
537 int dbg_setenv(const char *name,const char *value,int overwrite)
543 if (memleak==DMTRUE) {
547 (void) dbg_asprintf(&newvar,"%s=%s",name,value);
548 if ((ptr=getenv(name))!=(char *)0) {
550 (void) dbg_unsetenv(name);
552 (void) dbg_free(newvar);
556 if (newvar!=(char *)0) {
557 (void) dbg_putenv(newvar);
561 status=setenv(name,value,overwrite);
568 /************************************************/
570 /* Procedure to reallocate memory from pole*/
571 /* while checking (if needed) for memory */
574 /************************************************/
575 void *dbg_realloc(void *ptr,size_t size)
578 if (memleak==DMTRUE) {
580 ptr=getmemory(size,1);
586 if ((leakpt=(LEAKTYP **)tfind(&leak,&memory,cmpptr))!=(LEAKTYP **)0) {
590 (void) tdelete(*leakpt,&memory,cmpptr);
591 ptr=realloc(ptr,size);
594 (void) setbtrace(lk,1);
595 (void) tsearch(lk,&memory,cmpptr);
598 char *strloc="subsys.c:dbg_realloc, Unable to find 'leak memory' "
599 "for ptr '%08lx'=<%s>\n";
601 (void) fprintf(stderr,strloc,(long)ptr,(char *)ptr);
602 (void) syslog(LOG_DEBUG,strloc,(long)ptr,(char *)ptr);
603 (void) logbacktrace(LOG_DEBUG,2);
609 ptr=realloc(ptr,size);
615 /************************************************/
617 /* Procedure to allocate memory to */
618 /* duplicate a string. */
620 /************************************************/
621 char *dbg_strdup(const char *str)
626 if (str==(char *)0) {
627 /*NULL PTR to duplicate?! crashing HARD */
628 (void) kill(getpid(),SIGSEGV);
630 if (memleak==DMTRUE) {
631 ptr=getmemory(strlen(str)+3,1);
632 (void) strcpy(ptr,str);
641 /************************************************/
643 /* Procedure to allocate a string within */
644 /* a dynamic memory using a va_list */
646 /************************************************/
647 int dbg_vasprintf(char **strp, const char *fmt,va_list args)
653 if (memleak==DMTRUE) {
656 count=vasprintf(&strloc,fmt,args);
657 *strp=dbg_strdup(strloc);
661 count=vasprintf(strp,fmt,args);
668 /************************************************/
670 /* Procedure to allocate a string within */
671 /* a dynamic memory */
673 /************************************************/
674 int dbg_asprintf(char **strp, const char *fmt, ...)
683 count=dbg_vasprintf(strp,fmt,args);
685 count=vasprintf(strp,fmt,args);
692 /************************************************/
694 /* Procedure to free memory allocated by */
695 /* a library procedure (as scandir) */
696 /* Memory allocated by such library routine*/
697 /* are not known by leak detection. */
699 /************************************************/
700 void dbg_sysfree(void *ptr)
708 /************************************************/
710 /* Procedure to flag current memory as not */
711 /* to be checked against memory leak */
712 /* Implementation is such memory can be */
713 /* flagged/unflagged as a no memory leak. */
714 /* noleak value should always be positive. */
716 /************************************************/
717 void dbg_setnoleak(register int value)
721 (void) twalk(memory,setleakflg);
726 /************************************************/
728 /* Procedure to dump current memory */
729 /* allocation and detect memory allocation */
731 /* Data will be dumped on /tmp under */
732 /* application name and process pid. */
734 /************************************************/
735 void dbg_dumpmem(const char *apname,const char *extension)
738 #define DUMPFRM "%s/var/tmp/%s%s%s.%05d"
740 if (memleak==DMTRUE) {
748 if ((extension==(char *)0)||(strlen(extension)==0)) {
752 (void) asprintf(&name,DUMPFRM,cbase,apname,sep,extension,getpid());
753 if ((fichier=fopen(name,"w"))==(FILE *)0) {
754 (void) fprintf(stderr,"dbg_dumpmem unable to open file <%s> (error=<%s>)\n",
755 name,strerror(errno));
758 memsort=(LEAKTYP **)0;
760 memsort=(LEAKTYP **)calloc(nummem,sizeof(LEAKTYP *));
761 (void) twalk(memory,collectleakptr);
766 (void) fprintf(fichier,"%05ld memory allocation still open\n",collected);
767 (void) qsort(memsort,collected,sizeof(LEAKTYP *),cmpleak);
769 for (i=0;i<collected;i++) {
770 #define STRTIME "%Y-%m-%d %H:%M:%S"
774 if ((leak==(LEAKTYP *)0)||(sametrace(leak,memsort[i])!=0)) {
775 (void) fprintf(fichier,"\n");
777 (void) disline(leak,cpath,fichier);
779 tm=localtime(&(memsort[i]->date.tv_sec));
780 (void) strftime(strtime,sizeof(strtime),STRTIME,tm);
781 (void) fprintf(fichier,"\t size=%05ld age='%s.%06ld' ptr=%08lx->'%s'\n",
782 (long)(memsort[i]->size),
783 strtime,memsort[i]->date.tv_usec,
784 (long)(memsort[i]->ptr),
785 (char *)(memsort[i]->ptr));
790 (void) fprintf(fichier,"No memory leak detected\n");
792 (void) fprintf(fichier,"No memory leak detector\n(comment in "
793 "'#define DEBUGMEM' within dbgmem.h)\n");
796 (void) fclose(fichier);