Inserting VZGOT development tree within GIT
[safe/jmp/vzgot] / lib / dbgmem.c
1 /************************************************/
2 /*                                              */
3 /*      Copyright:                              */
4 /*       Jean-Marc Pigeon <jmp@safe.ca>  2009   */
5 /*                                              */
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                     */
12 /*                                              */
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.                            */
19 /*                                              */
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 /************************************************/
26 /*                                              */
27 /*      Implement very sub level procedure to   */
28 /*      debug potential memory leak (or other)  */
29 /*      trouble.                                */
30 /*                                              */
31 /************************************************/
32 #include        <malloc.h>
33 #include        <signal.h>
34 #include        <errno.h>
35 #include        <search.h>
36 #include        <stdio.h>
37 #include        <unistd.h>
38 #include        <time.h>
39 #include        <syslog.h>
40 #include        <execinfo.h>
41 #include        <sys/time.h>
42 #include        <memory.h>
43 #include        "dbgmem.h"
44
45 #undef  calloc
46 #undef  malloc
47 #undef  realloc
48 #undef  free
49 #undef  putenv
50 #undef  setenv
51 #undef  unsetenv
52 #undef  strdup
53 #undef  asprintf
54 #undef  vasprintf
55
56
57 #define MXTB    10      /*max trace deep                */
58
59 extern char **environ;  /*application env varirable     */
60
61
62 typedef struct timeval  TIMEVAL;
63
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    */
70         }LEAKTYP;
71
72 int memleak=DMFALSE;    /*flag to check for memory leak */
73
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;
81 /*
82 \f
83 */
84 /************************************************/
85 /*                                              */
86 /*      Procedure to compare to "leaking" memory*/
87 /*      ptr, used by search routine             */
88 /*                                              */
89 /************************************************/
90 static int cmpptr(const void *v1,const void *v2)
91
92 {
93 register u_long p1;
94 register u_long p2;
95
96 if (v1==(void *)0) {
97   if (v2==(void *)0)
98     return 0;
99   else
100     return -1;
101   }
102 if (v2==(void *)0)
103   return 1;
104 p1=(u_long)(((LEAKTYP *)v1)->ptr);
105 p2=(u_long)(((LEAKTYP *)v2)->ptr);
106 if (p1>p2)
107   return 1;
108 if (p1<p2)
109   return -1;
110 return 0;
111 }
112 /*
113 \f
114 */
115 /************************************************/
116 /*                                              */
117 /*      Procedure to compare to flag the leak   */
118 /*      value withing collected open memory.    */
119 /*                                              */
120 /************************************************/
121 static void setleakflg(const void *v1,const VISIT which,const int depth)
122
123 {
124 switch (which) {
125   case preorder:        /*no remote dump        */
126   case endorder:
127     break;
128   case postorder:       /*remote dump           */
129   case leaf:
130     (*((LEAKTYP **)v1))->noleak=flgleak;
131     break;
132   }
133 }
134 /*
135 \f
136 */
137 /************************************************/
138 /*                                              */
139 /*      Procedure to count collected "leaking"  */
140 /*      memory.                                 */
141 /*                                              */
142 /************************************************/
143 static void collectleakptr(const void *v1,const VISIT which,const int depth)
144
145 {
146 switch (which) {
147   case preorder:        /*no remote dump        */
148   case endorder:
149     break;
150   case postorder:       /*remote dump           */
151   case leaf:
152     if ((*((LEAKTYP **)v1))->noleak==0) {
153       memsort[collected]=*((LEAKTYP **)v1);
154       collected++;
155       }
156     break;
157   }
158 }
159 /*
160 \f
161 */
162 /************************************************/
163 /*                                              */
164 /*      Procedure to compare to "leaking" memory*/
165 /*      information, from the backtrace stand   */
166 /*      point.                                  */
167 /*                                              */
168 /************************************************/
169 static int sametrace(const LEAKTYP *l1,const LEAKTYP *l2)
170
171 {
172 int inx;
173 long diff;
174
175 inx=0;
176 diff=0;
177 while ((inx<MXTB)&&(diff==(long)0)) {
178   diff=(long)(l1->tb[inx])-(long)(l2->tb[inx]);
179   inx++;
180   }
181 return diff;
182 }
183 /*
184 \f
185 */
186 /************************************************/
187 /*                                              */
188 /*      Procedure to compare to "leaking" memory*/
189 /*      information                             */
190 /*                                              */
191 /************************************************/
192 static int cmpleak(const void *p1,const void *p2)
193
194 {
195 int diff;
196 LEAKTYP *l1;
197 LEAKTYP *l2;
198
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;
203   if (diff==0)
204     diff=l1->date.tv_usec-l2->date.tv_usec;
205 return diff;
206 }
207 /*
208 \f
209 */
210 /************************************************/
211 /*                                              */
212 /*      Procedure to convert back trace address */
213 /*      to sources line information.            */
214 /*                                              */
215 /************************************************/
216 static void disline(LEAKTYP *leak,const char *path,FILE *fichier)
217
218 {
219 #define ADDR2LINE       "addr2line"
220
221 int i;
222 FILE *canal;
223 char command[1000];
224
225 (void) sprintf(command,"%s -e %s",ADDR2LINE,path);
226 for (i=0;i<MXTB;i++) {
227   char str[20];
228
229   if (leak->tb[i]==0)
230     break;
231   (void) sprintf(str," %08lx",(long)leak->tb[i]);
232   (void) strcat(command,str);
233   }
234 if ((canal=popen(command,"r"))!=(FILE *)0) {
235   char line[200];
236
237   while (fgets(line,sizeof(line)-1,canal)!=(char *)0) {
238     (void) fprintf(fichier,"%s",line);
239     }
240   (void) fclose(canal);
241   }
242 }
243 /*
244 \f
245 */
246 /************************************************/
247 /*                                              */
248 /*      Procedure to assigne a Backtrace stack  */
249 /*      (to trace back leak) to memory structure*/
250 /*                                              */
251 /************************************************/
252 static void setbtrace(LEAKTYP *leak,int step)
253
254 {
255 #define MXSTEP  10
256
257 void *zone[MXSTEP];
258 int num;
259 int i,j;
260 struct timezone tz;
261
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++) {
265   leak->tb[j]=zone[i];
266   }
267 (void) gettimeofday(&(leak->date),&tz);
268 }
269 /*
270 \f
271 */
272 /************************************************/
273 /*                                              */
274 /*      Procedure to report backtrace via syslog*/
275 /*                                              */
276 /************************************************/
277 static void logbacktrace(int loglev,int step)
278
279 {
280 #define MXSTEP  10
281
282 void *zone[MXSTEP];
283 int num;
284 int i;
285
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]);
290   }
291 }
292 /*
293 \f
294 */
295 /************************************************/
296 /*                                              */
297 /*      procedure to get a "leak proof" memory  */
298 /*      email to a shell script                 */
299 /*                                              */
300 /************************************************/
301 static void *getmemory(size_t size,int step)
302
303 {
304 register LEAKTYP *leak;
305 register void **leakptr;
306
307 leak=(LEAKTYP *)calloc(1,sizeof(LEAKTYP));
308 leak->size=size;
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");
314   }
315 nummem++;
316 return leak->ptr;
317 }
318 /*
319 \f
320 */
321 /************************************************/
322 /*                                              */
323 /*      Procedure to memorise the current       */
324 /*      application path, will be used to       */
325 /*      dump the memory stat.                   */
326 /*                                              */
327 /************************************************/
328 void dbg_setcurpath(char *path)
329
330 {
331 cpath=strdup(path);
332 }
333 /*
334 \f
335 */
336 /************************************************/
337 /*                                              */
338 /*      Procedure to memorise the current       */
339 /*      application base, will be used to       */
340 /*      dump the memory stat.                   */
341 /*                                              */
342 /************************************************/
343 void dbg_setcurbase(char *path)
344
345 {
346 cbase=strdup(path);
347 }
348 /*
349 \f
350 */
351 /************************************************/
352 /*                                              */
353 /*      Procedure to assigne new memory area    */
354 /*      while checking (if needed) for memory   */
355 /*      leakage.                                */
356 /*                                              */
357 /************************************************/
358 void *dbg_malloc(size_t size)
359
360 {
361 void *ptr;
362
363 if (memleak==DMTRUE) {
364   ptr=getmemory(size,1);
365   }
366 else
367   ptr=malloc(size);
368 return ptr;
369 }
370 /*
371 \f
372 */
373 /************************************************/
374 /*                                              */
375 /*      Procedure to assigne clean new memory   */
376 /*      while checking (if needed) for memory   */
377 /*      leakage.                                */
378 /*                                              */
379 /************************************************/
380 void *dbg_calloc(size_t nmemb,size_t size)
381
382 {
383 void *ptr;
384
385 if (memleak==DMTRUE) {
386   ptr=getmemory(nmemb*size,1);
387   (void) memset(ptr,'\000',nmemb*size);
388   }
389 else
390   ptr=calloc(nmemb,size);
391 return ptr;
392 }
393 /*
394 \f
395 */
396 /************************************************/
397 /*                                              */
398 /*      Procedure to free memory from memry pool*/
399 /*      while checking (if needed) for memory   */
400 /*      leakage.                                */
401 /*                                              */
402 /************************************************/
403 void dbg_free(void *ptr)
404
405 {
406 if (memleak==DMTRUE) {
407   LEAKTYP **leakpt;
408   LEAKTYP leak;
409
410   leak.ptr=ptr;
411   if ((leakpt=(LEAKTYP **)tfind(&leak,&memory,cmpptr))!=(LEAKTYP **)0) {
412     LEAKTYP *lk;
413
414     lk=*leakpt;
415     (void) tdelete(*leakpt,&memory,cmpptr);
416     (void) free(lk->ptr);
417     (void) free(lk);
418     nummem--;
419     }
420   else {
421     char *strloc="subsys.c:dbg_free, Unable to find 'leak memory' "
422                  "for ptr '%08lx'=<%s>\n";
423
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");
429     }
430   }
431 else
432   (void) free(ptr);
433 }
434 /*
435 \f
436 */
437 /************************************************/
438 /*                                              */
439 /*      Procedure to remove a variable from the */
440 /*      environement list                       */
441 /*                                              */
442 /************************************************/
443 int dbg_unsetenv(const char *name)
444
445 {
446 int status;
447
448 status=-1;
449 errno=EINVAL;
450 if (memleak==DMTRUE) {
451   if (environ!=(char **)0) {
452     int i;
453
454     for (i=0;environ[i]!=(char *)0;i++) {
455       char *ptr;
456
457       if ((ptr=strstr(environ[i],name))==(char *)0)
458         continue;
459       if (*(environ[i]+strlen(name))=='=') {
460         (void) dbg_free(ptr);
461         do {
462           environ[i]=environ[i+1];
463           i++;
464           }
465         while (environ[i]!=(char *)0);
466         status=0;
467         break;
468         }
469       }
470     }
471   }
472 else 
473   status=unsetenv(name);
474 return status;
475 }
476 /*
477 \f
478 */
479 /************************************************/
480 /*                                              */
481 /*      Procedure to install variable within the*/
482 /*      environnement                           */
483 /*                                              */
484 /************************************************/
485 int dbg_putenv(char *valeur)
486
487 {
488 int status;
489
490 status=-1;
491 if (memleak==DMTRUE) {
492   register char *ptr;
493  
494   if ((ptr=strchr(valeur,'='))!=(char *)0) {
495     register int found;
496     register int taille;
497     register int tocheck;
498
499     found=0;
500     taille=0;
501     if ((tocheck=(ptr-valeur))<=0)
502       return status;
503     status=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;
509           found=1;
510           break;
511           }
512         }
513       }
514     else
515       environ=dbg_calloc(1,sizeof(char *));
516     if (found==0) {
517       environ=(char **)dbg_realloc((void *)environ,(taille+2)*sizeof(char *));
518       environ[taille]=valeur;
519       environ[taille+1]=(char *)0;
520       }
521     }
522   }
523 else {
524   status=putenv(valeur);
525   }
526 return status;
527 }
528 /*
529 \f
530 */
531 /************************************************/
532 /*                                              */
533 /*      Procedure to set variable within the    */
534 /*      environement                            */
535 /*                                              */
536 /************************************************/
537 int dbg_setenv(const char *name,const char *value,int overwrite)
538
539 {
540 int status;
541
542 status=0;
543 if (memleak==DMTRUE) {
544   char *newvar;
545   char *ptr;
546
547   (void) dbg_asprintf(&newvar,"%s=%s",name,value);
548   if ((ptr=getenv(name))!=(char *)0) {
549     if (overwrite!=0) 
550       (void) dbg_unsetenv(name);
551     else {
552       (void) dbg_free(newvar);
553       newvar=(char *)0;
554       }
555     }
556   if (newvar!=(char *)0) {
557     (void) dbg_putenv(newvar);
558     }
559   }
560 else {
561   status=setenv(name,value,overwrite);
562   }
563 return status;
564 }
565 /*
566 \f
567 */
568 /************************************************/
569 /*                                              */
570 /*      Procedure to reallocate memory from pole*/
571 /*      while checking (if needed) for memory   */
572 /*      leakage.                                */
573 /*                                              */
574 /************************************************/
575 void *dbg_realloc(void *ptr,size_t size)
576
577 {
578 if (memleak==DMTRUE) {
579   if (ptr==(void *)0)
580     ptr=getmemory(size,1);
581   else {
582     LEAKTYP **leakpt;
583     LEAKTYP leak;
584
585     leak.ptr=ptr;
586     if ((leakpt=(LEAKTYP **)tfind(&leak,&memory,cmpptr))!=(LEAKTYP **)0) {
587       LEAKTYP *lk;
588      
589       lk=*leakpt; 
590       (void) tdelete(*leakpt,&memory,cmpptr);
591       ptr=realloc(ptr,size);
592       lk->ptr=ptr;
593       lk->size=size;
594       (void) setbtrace(lk,1);
595       (void) tsearch(lk,&memory,cmpptr);
596       }
597     else {
598       char *strloc="subsys.c:dbg_realloc, Unable to find 'leak memory' "
599                    "for ptr '%08lx'=<%s>\n";
600
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);
604       ptr=(void *)0;
605       }
606     }
607   }
608 else
609   ptr=realloc(ptr,size);
610 return ptr;
611 }
612 /*
613 \f
614 */
615 /************************************************/
616 /*                                              */
617 /*      Procedure to allocate memory to         */
618 /*      duplicate a string.                     */
619 /*                                              */
620 /************************************************/
621 char *dbg_strdup(const char *str)
622
623 {
624 char *ptr;
625
626 if (str==(char *)0) {
627   /*NULL PTR to duplicate?! crashing HARD       */
628   (void) kill(getpid(),SIGSEGV);
629   }
630 if (memleak==DMTRUE) {
631   ptr=getmemory(strlen(str)+3,1);
632   (void) strcpy(ptr,str);
633   }
634 else
635   ptr=strdup(str);
636 return ptr;
637 }
638 /*
639 \f
640 */
641 /************************************************/
642 /*                                              */
643 /*      Procedure to allocate a string within   */
644 /*      a dynamic memory using a va_list        */
645 /*                                              */
646 /************************************************/
647 int dbg_vasprintf(char **strp, const char *fmt,va_list args)
648
649 {
650 int count;
651
652 count=-1;
653 if (memleak==DMTRUE) {
654   char *strloc;
655
656   count=vasprintf(&strloc,fmt,args);
657   *strp=dbg_strdup(strloc);
658   (void) free(strloc);
659   }
660 else {
661   count=vasprintf(strp,fmt,args);
662   }
663 return count;
664 }
665 /*
666 \f
667 */
668 /************************************************/
669 /*                                              */
670 /*      Procedure to allocate a string within   */
671 /*      a dynamic memory                        */
672 /*                                              */
673 /************************************************/
674 int dbg_asprintf(char **strp, const char *fmt, ...)
675
676 {
677 int count;
678 va_list args;
679
680 count=-1;
681 va_start(args,fmt);
682 if (memleak==DMTRUE)
683   count=dbg_vasprintf(strp,fmt,args);
684 else 
685   count=vasprintf(strp,fmt,args);
686 va_end(args);
687 return count;
688 }
689 /*
690 \f
691 */
692 /************************************************/
693 /*                                              */
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.        */
698 /*                                              */
699 /************************************************/
700 void dbg_sysfree(void *ptr)
701
702 {
703 (void) free(ptr);
704 }
705 /*
706 \f
707 */
708 /************************************************/
709 /*                                              */
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. */
715 /*                                              */
716 /************************************************/
717 void dbg_setnoleak(register int value)
718
719 {
720 flgleak=value;
721 (void) twalk(memory,setleakflg);
722 }
723 /*
724 \f
725 */
726 /************************************************/
727 /*                                              */
728 /*      Procedure to dump current memory        */
729 /*      allocation and detect memory allocation */
730 /*                                              */
731 /*      Data will be dumped on /tmp under       */
732 /*      application name and process pid.       */
733 /*                                              */
734 /************************************************/
735 void dbg_dumpmem(const char *apname,const char *extension)
736
737 {
738 #define DUMPFRM "%s/var/tmp/%s%s%s.%05d"
739
740 if (memleak==DMTRUE) {
741   char *sep;
742   FILE *fichier;
743   char *name;
744   LEAKTYP *leak;
745
746   sep="-";
747   collected=0;
748   if ((extension==(char *)0)||(strlen(extension)==0)) {
749     sep="";
750     extension="";
751     }
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));
756     fichier=stderr;
757     }
758   memsort=(LEAKTYP **)0;
759   if (nummem>0) {
760     memsort=(LEAKTYP **)calloc(nummem,sizeof(LEAKTYP *));
761     (void) twalk(memory,collectleakptr);
762     }
763   if (collected>0) {
764     register int i;
765
766     (void) fprintf(fichier,"%05ld memory allocation still open\n",collected);
767     (void) qsort(memsort,collected,sizeof(LEAKTYP *),cmpleak);
768     leak=(LEAKTYP *)0;
769     for (i=0;i<collected;i++) {
770       #define STRTIME   "%Y-%m-%d %H:%M:%S"
771       struct tm *tm;
772       char strtime[40];
773
774       if ((leak==(LEAKTYP *)0)||(sametrace(leak,memsort[i])!=0)) {
775         (void) fprintf(fichier,"\n");
776         leak=memsort[i];
777         (void) disline(leak,cpath,fichier);
778         }
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));
786       }
787     }
788   else {
789 #ifdef DEBUGMEM
790     (void) fprintf(fichier,"No memory leak detected\n");
791 #else
792     (void) fprintf(fichier,"No memory leak detector\n(comment in "
793                            "'#define DEBUGMEM' within dbgmem.h)\n");
794 #endif
795     }
796   (void) fclose(fichier);
797   (void) free(name);
798   }
799 }