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 }