memdebug.c - randomcrap - random crap programs of varying quality
 (HTM) git clone git://git.codemadness.org/randomcrap
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       memdebug.c (5181B)
       ---
            1 /*
            2 Ugly shared library to intercept some alloc calls writing a debug line to some
            3 fd.  Useful for debugging simple (single-threaded) programs.
            4 
            5 Tested on OpenBSD and Void Linux gcc+glibc.
            6 
            7 usage:
            8 
            9 Void Linux (glibc):
           10 
           11         cc -D_GNU_SOURCE -fPIC -shared -o memdebug.so memdebug.c -ldl
           12 
           13 OpenBSD:
           14         cc -fPIC -shared -o memdebug.so memdebug.c
           15 
           16 LD_PRELOAD=./memdebug.so ./program 3>/tmp/debug
           17 
           18 envs:
           19 MAX_ALLOCCOUNT:    maximum amount of allocations.
           20 MAX_ALLOCSIZE:     maximum size of allocations (not counting reallocations or frees).
           21 */
           22 
           23 #include <sys/types.h>
           24 
           25 #include <dlfcn.h>
           26 #include <errno.h>
           27 #include <limits.h>
           28 #include <stdint.h>
           29 #include <stdio.h>
           30 #include <stdlib.h>
           31 #include <string.h>
           32 #include <unistd.h>
           33 
           34 /* string and byte-length */
           35 #define STRP(s) s,sizeof(s)-1
           36 
           37 /* POSIX defines shell redirection should support atleast upto 9, ksh supports up to 9 */
           38 #define DEBUG_FD 3
           39 
           40 #define INIT_FN() do { \
           41         int saved_errno; \
           42         saved_errno = errno; \
           43         alloccount++;
           44 
           45 #define EXIT_FN() \
           46                 errno = saved_errno; \
           47         } while(0)
           48 
           49 #define INIT_ALLOCFN() do { \
           50         int saved_errno; \
           51         saved_errno = errno; \
           52         alloccount++;
           53 
           54 #define EXIT_ALLOCFN() \
           55                 errno = saved_errno; \
           56                 char *p; \
           57                 if ((p = getenv("MAX_ALLOCCOUNT")) && alloccount >= strtoll(p, NULL, 0)) { \
           58                         errno = ENOMEM; \
           59                         return NULL; \
           60                 } \
           61                 if ((p = getenv("MAX_ALLOCSIZE")) && allocsize >= strtoll(p, NULL, 0)) { \
           62                         errno = ENOMEM; \
           63                         return NULL; \
           64                 } \
           65         } while(0)
           66 
           67 #define RESOLV(s) dlsym(RTLD_NEXT, (s))
           68 
           69 static size_t alloccount, allocsize;
           70 
           71 char * (*old_strdup)(const char *s);
           72 char * (*old_strndup)(const char *s, size_t maxlen);
           73 void * (*old_malloc)(size_t size);
           74 void * (*old_calloc)(size_t nmemb, size_t size);
           75 void * (*old_realloc)(void *ptr, size_t size);
           76 
           77 /* helper print functions because some libc use malloc in printf() */
           78 static const char xdigits[16] = { "0123456789ABCDEF" };
           79 
           80 static char *
           81 fmt_x(uintmax_t x, char *s, int lower)
           82 {
           83         for (; x; x >>= 4)
           84                 *--s = xdigits[(x & 15)];
           85         return s;
           86 }
           87 
           88 static char *
           89 fmt_u(uintmax_t x, char *s)
           90 {
           91         unsigned long y;
           92 
           93         for (; x > ULONG_MAX; x /= 10)
           94                 *--s = '0' + x % 10;
           95         for (y = x; y; y /= 10)
           96                 *--s = '0' + y % 10;
           97 
           98         return s;
           99 }
          100 
          101 void
          102 printaddr(int fd, void *p)
          103 {
          104         char buf[64], *s;
          105         unsigned long long l;
          106 
          107         l = (unsigned long long)p;
          108         write(fd, "0x", 2);
          109         if (!l) {
          110                 write(fd, "0", 1);
          111                 return;
          112         }
          113         s = fmt_x(l, buf + sizeof(buf) - 1, 0);
          114         write(fd, s, buf + sizeof(buf) - 1 - s);
          115 }
          116 
          117 void
          118 printsize(int fd, long long l)
          119 {
          120         char buf[64], *s;
          121 
          122         if (l < 0) {
          123                 write(fd, "-", 1);
          124                 l = -l;
          125         } else if (!l) {
          126                 write(fd, "0", 1);
          127                 return;
          128         }
          129         s = fmt_u(l, buf + sizeof(buf) - 1);
          130         write(fd, s, buf + sizeof(buf) - 1 - s);
          131 }
          132 /* /helper print functions */
          133 
          134 char *
          135 strdup(const char *s)
          136 {
          137         INIT_ALLOCFN();
          138         allocsize += strlen(s);
          139 
          140         write(DEBUG_FD, STRP("["));
          141         printsize(DEBUG_FD, alloccount);
          142         write(DEBUG_FD, STRP("] "));
          143         write(DEBUG_FD, STRP(__func__));
          144         write(DEBUG_FD, STRP("(s="));
          145         printaddr(DEBUG_FD, (void *)s);
          146         write(DEBUG_FD, STRP("), len of s="));
          147         printsize(DEBUG_FD, strlen(s));
          148         write(DEBUG_FD, STRP("\n"));
          149 
          150         EXIT_ALLOCFN();
          151         return old_strdup(s);
          152 }
          153 
          154 char *
          155 strndup(const char *s, size_t maxlen)
          156 {
          157         size_t slen;
          158 
          159         INIT_ALLOCFN();
          160 
          161         slen = strlen(s);
          162         allocsize += (slen > maxlen ? maxlen : slen);
          163 
          164         write(DEBUG_FD, STRP("["));
          165         printsize(DEBUG_FD, alloccount);
          166         write(DEBUG_FD, STRP("] "));
          167         write(DEBUG_FD, STRP(__func__));
          168         write(DEBUG_FD, STRP("(s="));
          169         printaddr(DEBUG_FD, (void *)s);
          170         write(DEBUG_FD, STRP(", maxlen="));
          171         printsize(DEBUG_FD, maxlen);
          172         write(DEBUG_FD, STRP("), len of s="));
          173         printsize(DEBUG_FD, strlen(s));
          174         write(DEBUG_FD, STRP("\n"));
          175 
          176         EXIT_ALLOCFN();
          177         return old_strndup(s, maxlen);
          178 }
          179 
          180 void *
          181 malloc(size_t size)
          182 {
          183         INIT_ALLOCFN();
          184         allocsize += size;
          185 
          186         write(DEBUG_FD, STRP("["));
          187         printsize(DEBUG_FD, alloccount);
          188         write(DEBUG_FD, STRP("] "));
          189         write(DEBUG_FD, STRP(__func__));
          190         write(DEBUG_FD, STRP("(size="));
          191         printsize(DEBUG_FD, size);
          192         write(DEBUG_FD, STRP(")\n"));
          193 
          194         EXIT_ALLOCFN();
          195 
          196         return old_malloc(size);
          197 }
          198 
          199 void *
          200 calloc(size_t nmemb, size_t size)
          201 {
          202         INIT_ALLOCFN();
          203         allocsize += size;
          204 
          205         write(DEBUG_FD, STRP("["));
          206         printsize(DEBUG_FD, alloccount);
          207         write(DEBUG_FD, STRP("] "));
          208         write(DEBUG_FD, STRP(__func__));
          209         write(DEBUG_FD, STRP("(nmemb="));
          210         printsize(DEBUG_FD, nmemb);
          211         write(DEBUG_FD, STRP(", size="));
          212         printsize(DEBUG_FD, size);
          213         write(DEBUG_FD, STRP(")\n"));
          214 
          215         EXIT_ALLOCFN();
          216         return old_calloc(nmemb, size);
          217 }
          218 
          219 void *
          220 realloc(void *ptr, size_t size)
          221 {
          222         INIT_ALLOCFN();
          223         allocsize += size; /* incorrect but just add it */
          224 
          225         write(DEBUG_FD, STRP("["));
          226         printsize(DEBUG_FD, alloccount);
          227         write(DEBUG_FD, STRP("] "));
          228         write(DEBUG_FD, STRP(__func__));
          229         write(DEBUG_FD, STRP("(ptr="));
          230         printaddr(DEBUG_FD, ptr);
          231         write(DEBUG_FD, STRP(", size="));
          232         printsize(DEBUG_FD, size);
          233         write(DEBUG_FD, STRP(")\n"));
          234 
          235         EXIT_ALLOCFN();
          236         return old_realloc(ptr, size);
          237 }
          238 
          239 /* gcc extension, works on clang too, executed before dlopen() returns. */
          240 static void construct() __attribute__((constructor));
          241 
          242 void
          243 construct(void)
          244 {
          245         old_strdup = RESOLV("strdup");
          246         old_strndup = RESOLV("strndup");
          247         old_malloc = RESOLV("malloc");
          248         old_realloc = RESOLV("realloc");
          249         old_calloc = RESOLV("calloc");
          250 }