1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21: #include <atomic.h>
22: #include <dlfcn.h>
23: #include <errno.h>
24: #include <fcntl.h>
25: #include <inttypes.h>
26: #include <signal.h>
27: #include <stdarg.h>
28: #include <stdbool.h>
29: #include <stdio.h>
30: #include <stdlib.h>
31: #include <string.h>
32: #include <unistd.h>
33: #include <sys/mman.h>
34: #include <sys/time.h>
35:
36: #include <memusage.h>
37:
38:
39:
40: static void *(*mallocp) (size_t);
41: static void *(*reallocp) (void *, size_t);
42: static void *(*callocp) (size_t, size_t);
43: static void (*freep) (void *);
44:
45: static void *(*mmapp) (void *, size_t, int, int, int, off_t);
46: static void *(*mmap64p) (void *, size_t, int, int, int, off64_t);
47: static int (*munmapp) (void *, size_t);
48: static void *(*mremapp) (void *, size_t, size_t, int, void *);
49:
50: enum
51: {
52: idx_malloc = 0,
53: idx_realloc,
54: idx_calloc,
55: idx_free,
56: idx_mmap_r,
57: idx_mmap_w,
58: idx_mmap_a,
59: idx_mremap,
60: idx_munmap,
61: idx_last
62: };
63:
64:
65: struct header
66: {
67: size_t length;
68: size_t magic;
69: };
70:
71: #define MAGIC 0xfeedbeaf
72:
73:
74: static memusage_cntr_t calls[idx_last];
75: static memusage_cntr_t failed[idx_last];
76: static memusage_size_t total[idx_last];
77: static memusage_size_t grand_total;
78: static memusage_cntr_t histogram[65536 / 16];
79: static memusage_cntr_t large;
80: static memusage_cntr_t calls_total;
81: static memusage_cntr_t inplace;
82: static memusage_cntr_t decreasing;
83: static memusage_cntr_t realloc_free;
84: static memusage_cntr_t inplace_mremap;
85: static memusage_cntr_t decreasing_mremap;
86: static memusage_size_t current_heap;
87: static memusage_size_t peak_use[3];
88: static __thread uintptr_t start_sp;
89:
90:
91: #define peak_heap peak_use[0]
92: #define peak_stack peak_use[1]
93: #define peak_total peak_use[2]
94:
95: #define DEFAULT_BUFFER_SIZE 1024
96: static size_t buffer_size;
97:
98: static int fd = -1;
99:
100: static bool not_me;
101: static int initialized;
102: static bool trace_mmap;
103: extern const char *__progname;
104:
105: struct entry
106: {
107: uint64_t heap;
108: uint64_t stack;
109: uint32_t time_low;
110: uint32_t time_high;
111: };
112:
113: static struct entry buffer[2 * DEFAULT_BUFFER_SIZE];
114: static uatomic32_t buffer_cnt;
115: static struct entry first;
116:
117:
118:
119: static void
120: update_data (struct header *result, size_t len, size_t old_len)
121: {
122: if (result != NULL)
123: {
124:
125:
126: result->length = len;
127: result->magic = MAGIC;
128: }
129:
130:
131: memusage_size_t heap
132: = catomic_exchange_and_add (¤t_heap, len - old_len) + len - old_len;
133: catomic_max (&peak_heap, heap);
134:
135:
136:
137:
138:
139: if (__builtin_expect (!start_sp, 0))
140: start_sp = GETSP ();
141:
142: uintptr_t sp = GETSP ();
143: #ifdef STACK_GROWS_UPWARD
144:
145:
146: if (__builtin_expect (sp < start_sp, 0))
147: start_sp = sp;
148: size_t current_stack = sp - start_sp;
149: #else
150:
151:
152: if (__builtin_expect (sp > start_sp, 0))
153: start_sp = sp;
154: size_t current_stack = start_sp - sp;
155: #endif
156: catomic_max (&peak_stack, current_stack);
157:
158:
159: catomic_max (&peak_total, heap + current_stack);
160:
161:
162: if (fd != -1)
163: {
164: uatomic32_t idx = catomic_exchange_and_add (&buffer_cnt, 1);
165: if (idx >= 2 * buffer_size)
166: {
167:
168:
169:
170:
171: unsigned int reset = idx - 2 * buffer_size;
172: catomic_compare_and_exchange_val_acq (&buffer_size, reset, idx);
173: idx = reset;
174: }
175:
176: buffer[idx].heap = current_heap;
177: buffer[idx].stack = current_stack;
178: GETTIME (buffer[idx].time_low, buffer[idx].time_high);
179:
180:
181: if (idx + 1 == buffer_size)
182: write (fd, buffer, buffer_size * sizeof (struct entry));
183: else if (idx + 1 == 2 * buffer_size)
184: write (fd, &buffer[buffer_size], buffer_size * sizeof (struct entry));
185: }
186: }
187:
188:
189:
190: static void
191: int_handler (int signo)
192: {
193:
194: update_data (NULL, 0, 0);
195: }
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214: static void
215: me (void)
216: {
217: const char *env = getenv ("MEMUSAGE_PROG_NAME");
218: size_t prog_len = strlen (__progname);
219:
220: initialized = -1;
221: mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
222: reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
223: callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
224: freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
225:
226: mmapp = (void *(*) (void *, size_t, int, int, int, off_t)) dlsym (RTLD_NEXT,
227: "mmap");
228: mmap64p =
229: (void *(*) (void *, size_t, int, int, int, off64_t)) dlsym (RTLD_NEXT,
230: "mmap64");
231: mremapp = (void *(*) (void *, size_t, size_t, int, void *)) dlsym (RTLD_NEXT,
232: "mremap");
233: munmapp = (int (*) (void *, size_t)) dlsym (RTLD_NEXT, "munmap");
234: initialized = 1;
235:
236: if (env != NULL)
237: {
238:
239: size_t len = strlen (env);
240: if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
241: || (prog_len != len && __progname[prog_len - len - 1] != '/'))
242: not_me = true;
243: }
244:
245:
246: if (!not_me && fd == -1)
247: {
248: const char *outname;
249:
250: if (!start_sp)
251: start_sp = GETSP ();
252:
253: outname = getenv ("MEMUSAGE_OUTPUT");
254: if (outname != NULL && outname[0] != '\0'
255: && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
256: {
257: fd = creat64 (outname, 0666);
258:
259: if (fd == -1)
260:
261:
262: not_me = true;
263: else
264: {
265:
266: first.heap = 0;
267: first.stack = 0;
268: GETTIME (first.time_low, first.time_high);
269:
270: write (fd, &first, sizeof (first));
271: write (fd, &first, sizeof (first));
272:
273:
274:
275: buffer_size = DEFAULT_BUFFER_SIZE;
276: if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
277: {
278: buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
279: if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
280: buffer_size = DEFAULT_BUFFER_SIZE;
281: }
282:
283:
284: if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
285: {
286: struct sigaction act;
287:
288: act.sa_handler = (sighandler_t) &int_handler;
289: act.sa_flags = SA_RESTART;
290: sigfillset (&act.sa_mask);
291:
292: if (sigaction (SIGPROF, &act, NULL) >= 0)
293: {
294: struct itimerval timer;
295:
296: timer.it_value.tv_sec = 0;
297: timer.it_value.tv_usec = 1;
298: timer.it_interval = timer.it_value;
299: setitimer (ITIMER_PROF, &timer, NULL);
300: }
301: }
302: }
303: }
304:
305: if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
306: trace_mmap = true;
307: }
308: }
309:
310:
311:
312: static void
313: __attribute__ ((constructor))
314: init (void)
315: {
316: start_sp = GETSP ();
317: if (! initialized)
318: me ();
319: }
320:
321:
322:
323:
324: void *
325: malloc (size_t len)
326: {
327: struct header *result = NULL;
328:
329:
330: if (__builtin_expect (initialized <= 0, 0))
331: {
332: if (initialized == -1)
333: return NULL;
334: me ();
335: }
336:
337:
338: if (not_me)
339: return (*mallocp) (len);
340:
341:
342: catomic_increment (&calls[idx_malloc]);
343:
344: catomic_add (&total[idx_malloc], len);
345:
346: catomic_add (&grand_total, len);
347:
348: if (len < 65536)
349: catomic_increment (&histogram[len / 16]);
350: else
351: catomic_increment (&large);
352:
353: catomic_increment (&calls_total);
354:
355:
356: result = (struct header *) (*mallocp) (len + sizeof (struct header));
357: if (result == NULL)
358: {
359: catomic_increment (&failed[idx_malloc]);
360: return NULL;
361: }
362:
363:
364: update_data (result, len, 0);
365:
366:
367: return (void *) (result + 1);
368: }
369:
370:
371:
372:
373: void *
374: realloc (void *old, size_t len)
375: {
376: struct header *result = NULL;
377: struct header *real;
378: size_t old_len;
379:
380:
381: if (__builtin_expect (initialized <= 0, 0))
382: {
383: if (initialized == -1)
384: return NULL;
385: me ();
386: }
387:
388:
389: if (not_me)
390: return (*reallocp) (old, len);
391:
392: if (old == NULL)
393: {
394:
395: real = NULL;
396: old_len = 0;
397: }
398: else
399: {
400: real = ((struct header *) old) - 1;
401: if (real->magic != MAGIC)
402:
403: return (*reallocp) (old, len);
404: old_len = real->length;
405: }
406:
407:
408: catomic_increment (&calls[idx_realloc]);
409: if (len > old_len)
410: {
411:
412: catomic_add (&total[idx_realloc], len - old_len);
413:
414: catomic_add (&grand_total, len - old_len);
415: }
416:
417: if (len == 0 && old != NULL)
418: {
419:
420: catomic_increment (&realloc_free);
421:
422: catomic_add (&total[idx_free], real->length);
423:
424:
425: update_data (NULL, 0, old_len);
426:
427:
428: (*freep) (real);
429:
430: return NULL;
431: }
432:
433:
434: if (len < 65536)
435: catomic_increment (&histogram[len / 16]);
436: else
437: catomic_increment (&large);
438:
439: catomic_increment (&calls_total);
440:
441:
442: result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
443: if (result == NULL)
444: {
445: catomic_increment (&failed[idx_realloc]);
446: return NULL;
447: }
448:
449:
450: if (real == result)
451: catomic_increment (&inplace);
452:
453: if (old_len > len)
454: catomic_increment (&decreasing);
455:
456:
457: update_data (result, len, old_len);
458:
459:
460: return (void *) (result + 1);
461: }
462:
463:
464:
465:
466: void *
467: calloc (size_t n, size_t len)
468: {
469: struct header *result;
470: size_t size = n * len;
471:
472:
473: if (__builtin_expect (initialized <= 0, 0))
474: {
475: if (initialized == -1)
476: return NULL;
477: me ();
478: }
479:
480:
481: if (not_me)
482: return (*callocp) (n, len);
483:
484:
485: catomic_increment (&calls[idx_calloc]);
486:
487: catomic_add (&total[idx_calloc], size);
488:
489: catomic_add (&grand_total, size);
490:
491: if (size < 65536)
492: catomic_increment (&histogram[size / 16]);
493: else
494: catomic_increment (&large);
495:
496: ++calls_total;
497:
498:
499: result = (struct header *) (*mallocp) (size + sizeof (struct header));
500: if (result == NULL)
501: {
502: catomic_increment (&failed[idx_calloc]);
503: return NULL;
504: }
505:
506:
507: update_data (result, size, 0);
508:
509:
510: return memset (result + 1, '\0', size);
511: }
512:
513:
514:
515:
516: void
517: free (void *ptr)
518: {
519: struct header *real;
520:
521:
522: if (__builtin_expect (initialized <= 0, 0))
523: {
524: if (initialized == -1)
525: return;
526: me ();
527: }
528:
529:
530: if (not_me)
531: {
532: (*freep) (ptr);
533: return;
534: }
535:
536:
537: if (ptr == NULL)
538: {
539: catomic_increment (&calls[idx_free]);
540: return;
541: }
542:
543:
544: real = ((struct header *) ptr) - 1;
545: if (real->magic != MAGIC)
546: {
547:
548: (*freep) (ptr);
549: return;
550: }
551:
552:
553: catomic_increment (&calls[idx_free]);
554:
555: catomic_add (&total[idx_free], real->length);
556:
557:
558: update_data (NULL, 0, real->length);
559:
560:
561: (*freep) (real);
562: }
563:
564:
565:
566:
567: void *
568: mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
569: {
570: void *result = NULL;
571:
572:
573: if (__builtin_expect (initialized <= 0, 0))
574: {
575: if (initialized == -1)
576: return NULL;
577: me ();
578: }
579:
580:
581: result = (*mmapp) (start, len, prot, flags, fd, offset);
582:
583: if (!not_me && trace_mmap)
584: {
585: int idx = (flags & MAP_ANON
586: ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
587:
588:
589: catomic_increment (&calls[idx]);
590:
591: catomic_add (&total[idx], len);
592: