source: memlog.cpp @ ed8b809

Revision ed8b809, 11.5 KB checked in by Hal Finkel <hfinkel@…>, 9 years ago (diff)

fixup the wrapping of mmap, etc.

  • Property mode set to 100644
RevLine 
[0ec59c5]1#ifndef _GNU_SOURCE
2#define _GNU_SOURCE
3#endif
4
[1bd82e0]5#include <cstdlib>
6#include <cstdio>
7#include <cstring>
[21d3542]8#include <cstdint>
[1bd82e0]9
[f715c76]10// NOTE: This source makes very minimal use of C++11 features. It can still be
11// compiled by g++ 4.4.7 with -std=gnu++0x.
[1bd82e0]12#include <unordered_map>
13#include <utility>
[0ec59c5]14
[1bd82e0]15#include <limits.h>
[430548b]16#include <malloc.h>
[a736d81]17#include <execinfo.h>
[6444e03]18#include <sys/mman.h>
[134408c]19#include <sys/syscall.h>
[0a0ef57]20#include <sys/time.h>
21#include <sys/resource.h>
[0ec59c5]22#include <sys/types.h>
23#include <sys/stat.h>
24#include <sys/utsname.h>
25#include <fcntl.h>
26#include <unistd.h>
27
28#include <pthread.h>
29#include <dlfcn.h>
30
[a7b97b9]31#ifdef __bgq__
32#include <spi/include/kernel/location.h>
[21d3542]33#include <spi/include/kernel/memory.h>
[a7b97b9]34#endif
35
[1bd82e0]36using namespace std;
37
[493cb97]38// NOTE: When static linking, this depends on linker wrapping.
39// Add to your LDFLAGS:
40//   -Wl,--wrap,malloc,--wrap,free,--wrap,realloc,--wrap,calloc,--wrap,memalign /path/to/memlog_s.o -lpthread -ldl
41
[22f928f]42FILE *log_file = 0;
[0ec59c5]43static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
44
[69850c8]45// The malloc hook might use functions that call malloc, and we need to make
46// sure this does not cause an infinite loop.
47static __thread int in_malloc = 0;
[3105f50]48static char self_path[PATH_MAX+1] = { '\0' };
[69850c8]49
[21d3542]50#ifdef __bgq__
51int on_bgq = 0;
52#endif
53
54void *initial_brk = 0;
55
[0ec59c5]56__attribute__((__constructor__))
57static void record_init() {
58  struct utsname u;
59  uname(&u);
60
[a7b97b9]61  int id = (int) getpid();
62#ifdef __bgq__
63  // If we're really running on a BG/Q compute node, use the job rank instead
64  // of the pid because the node name might not really be globally unique.
[21d3542]65  if (!strcmp(u.sysname, "CNK") && !strcmp(u.machine, "BGQ")) {
[a7b97b9]66    id = (int) Kernel_GetRank();
[21d3542]67    on_bgq = 1;
68  }
[a7b97b9]69#endif
70
[22f928f]71  // If we're running under a common batch system, add the job id to the output
72  // file names (add it as a prefix so that sorting the files will sort by job
73  // first).
74  char *job_id = 0;
75  const char *job_id_vars[] =
76    { "COBALT_JOBID", "PBS_JOBID", "SLURM_JOB_ID", "JOB_ID" };
77  for (int i = 0; i < sizeof(job_id_vars)/sizeof(job_id_vars[0]); ++i) {
78    job_id = getenv(job_id_vars[i]);
79    if (job_id)
80      break;
81  }
82
[3105f50]83  char log_name[PATH_MAX+1];
[22f928f]84  if (job_id)
85    snprintf(log_name, PATH_MAX+1, "%s.%s.%d.memlog", job_id, u.nodename, id);
86  else
87    snprintf(log_name, PATH_MAX+1, "%s.%d.memlog", u.nodename, id);
[0ec59c5]88  log_file = fopen(log_name, "w");
[0a0ef57]89  if (!log_file)
90    fprintf(stderr, "fopen failed for '%s': %m\n", log_name);
[3105f50]91
92  const char *link_name = "/proc/self/exe";
93  readlink(link_name, self_path, PATH_MAX);
[21d3542]94
95  initial_brk = sbrk(0);
[0ec59c5]96}
97
98__attribute__((__destructor__))
99static void record_cleanup() {
100  if (!log_file)
101    return;
102
[69850c8]103  // These functions might call free, but we're shutting down, so don't try to
104  // unwind the stack from here...
105  in_malloc = 1;
106
[5a9481e]107  // Avoid any racing by obtaining the lock.
108  if (pthread_mutex_lock(&log_mutex))
109    return;
110
[0ec59c5]111  (void) fflush(log_file);
112  (void) fclose(log_file);
113}
114
[5a9481e]115// dladdr is, relatively, quit slow. For this to work on a large application,
116// we need to cache the lookup results.
[1bd82e0]117static int dladdr_cached(void * addr, Dl_info *info) {
118  static unordered_map<void *, Dl_info> dladdr_cache;
119
120  auto I = dladdr_cache.find(addr);
121  if (I == dladdr_cache.end()) {
122    int r;
123    if (!(r = dladdr(addr, info)))
124      memset(info, 0, sizeof(Dl_info));
125
126    dladdr_cache.insert(make_pair(addr, *info));
127    return r;
128  }
129
130  memcpy(info, &I->second, sizeof(Dl_info));
131  return 1;
[5a9481e]132}
133
[1e5cce6]134static void print_context(const void *caller, int show_backtrace) {
[0a0ef57]135  struct rusage usage;
136  if (getrusage(RUSAGE_SELF, &usage)) {
137    fprintf(stderr, "getrusage failed: %m\n");
[0ec59c5]138    return;
[0a0ef57]139  }
[0ec59c5]140
[134408c]141  fprintf(log_file, "\t%ld.%06ld %ld %ld", usage.ru_utime.tv_sec,
142          usage.ru_utime.tv_usec, usage.ru_maxrss, syscall(SYS_gettid));
[0a0ef57]143
[21d3542]144  // Some other memory stats (like with maxrss, report these in KB).
145  size_t arena_size = ((size_t) sbrk(0)) - (size_t) initial_brk;
146
147  uint64_t mmap_size = 0;
148#ifdef __bgq__
149  if (on_bgq)
150    (void) Kernel_GetMemorySize(KERNEL_MEMSIZE_MMAP, &mmap_size);
151#endif
152
153  fprintf(log_file, " %ld %ld", arena_size >> 10, mmap_size >> 10);
154
[1e5cce6]155  if (!show_backtrace)
156    return;
157
[a736d81]158  void *pcs[1024];
159  int num_pcs = backtrace(pcs, 1024);
[14e2ab9]160
161  int found_caller = 0;
[a736d81]162  for (int pci = 0; pci < num_pcs; ++pci) {
[14e2ab9]163    intptr_t pc = (intptr_t) pcs[pci];
[69850c8]164
165    if (!pc)
166      break;
167
[14e2ab9]168    if (!found_caller) {
169      if (pc != (intptr_t) caller)
170        continue;
171
172      found_caller = 1;
173    }
174
175    intptr_t off, relpc;
[69850c8]176    const char *proc_name;
177    const char *file_name;
178    Dl_info dlinfo;
[5a9481e]179    if (dladdr_cached((void *) pc, &dlinfo) && dlinfo.dli_fname &&
[69850c8]180        *dlinfo.dli_fname) {
[14e2ab9]181      intptr_t saddr = (intptr_t) dlinfo.dli_saddr;
[69850c8]182      if (saddr) {
183#if defined(__powerpc64__) && !defined(__powerpc64le__)
184        // On PPC64 ELFv1, the symbol address points to the function descriptor, not
185        // the actual starting address.
[14e2ab9]186        saddr = *(intptr_t*) saddr;
[69850c8]187#endif
188
189        off = pc - saddr;
[14e2ab9]190        relpc = pc - ((intptr_t) dlinfo.dli_fbase);
[69850c8]191      } else {
192        off = 0;
193        relpc = 0;
194      }
195
196      proc_name = dlinfo.dli_sname;
197      if (!proc_name)
198        proc_name = "?";
199
200      file_name = dlinfo.dli_fname;
201    } else {
[3105f50]202      // We don't know these...
203      off = 0;
204      relpc = 0;
[69850c8]205      proc_name = "?";
[3105f50]206
207      // If we can't determine the file, assume it is the base executable
208      // (which does the right thing for statically-linked binaries).
209      file_name = self_path;
[69850c8]210    }
211
212    fprintf(log_file, "\t%s (%s+0x%x) [0x%lx (0x%lx)]", file_name, proc_name, (int) off,
213            (long) pc, (long) relpc);
[0ec59c5]214  }
215}
216
[430548b]217static void record_malloc(size_t size, void *ptr, const void *caller) {
[0ec59c5]218  if (!log_file)
219    return;
220
221  if (pthread_mutex_lock(&log_mutex))
222    return;
223
224  fprintf(log_file, "M: %zd %p", size, ptr);
[1e5cce6]225  print_context(caller, 1);
[0ec59c5]226  fprintf(log_file, "\n");
227
228done:
229  pthread_mutex_unlock(&log_mutex);
230}
231
[430548b]232static void record_free(void *ptr, const void *caller) {
[0ec59c5]233  if (!log_file)
234    return;
235
236  if (pthread_mutex_lock(&log_mutex))
237    return;
238
239  fprintf(log_file, "F: %p", ptr);
[1e5cce6]240  print_context(caller, 0);
[0ec59c5]241  fprintf(log_file, "\n");
242
243done:
244  pthread_mutex_unlock(&log_mutex);
245}
246
[ed8b809]247#ifdef __PIC__
248int (*__real_posix_memalign)(void **memptr, size_t alignment, size_t size) = 0;
249
250void *(*__real_mmap)(void *addr, size_t length, int prot, int flags,
251                     int fd, off_t offset) = 0;
252void *(*__real_mmap64)(void *addr, size_t length, int prot, int flags,
253                       int fd, off64_t offset) = 0;
254int (*__real_munmap)(void *addr, size_t length) = 0;
255#else
256extern "C" {
257extern int __real_posix_memalign(void **memptr, size_t alignment, size_t size);
258
259extern void *__real_mmap(void *addr, size_t length, int prot, int flags,
260                         int fd, off_t offset);
261extern void *__real_mmap64(void *addr, size_t length, int prot, int flags,
262                           int fd, off64_t offset);
263extern int __real_munmap(void *addr, size_t length);
264}
265#endif
266
[0ec59c5]267// glibc exports its underlying malloc implementation under the name
268// __libc_malloc so that hooks like this can use it.
[1bd82e0]269extern "C" {
[0ec59c5]270extern void *__libc_malloc(size_t size);
[6444e03]271extern void *__libc_valloc(size_t size);
[0ec59c5]272extern void *__libc_realloc(void *ptr, size_t size);
273extern void *__libc_calloc(size_t nmemb, size_t size);
274extern void *__libc_memalign(size_t boundary, size_t size);
275extern void __libc_free(void *ptr);
276
[430548b]277#ifdef __PIC__
278#define FUNC(x) x
279#else
280#define FUNC(x) __wrap_ ## x
281#endif
282
283void *FUNC(malloc)(size_t size) {
[5a9481e]284  const void *caller =
285    __builtin_extract_return_addr(__builtin_return_address(0));
[430548b]286
[0ec59c5]287  if (in_malloc)
288    return __libc_malloc(size);
289
290  in_malloc = 1;
291
292  void *ptr = __libc_malloc(size);
[6444e03]293  if (ptr)
294    record_malloc(size, ptr, caller);
295
296  in_malloc = 0;
297  return ptr;
298}
299
300void *FUNC(valloc)(size_t size) {
301  const void *caller =
302    __builtin_extract_return_addr(__builtin_return_address(0));
[0ec59c5]303
[6444e03]304  if (in_malloc)
305    return __libc_valloc(size);
306
307  in_malloc = 1;
308
309  void *ptr = __libc_valloc(size);
310  if (ptr)
311    record_malloc(size, ptr, caller);
[0ec59c5]312
313  in_malloc = 0;
314  return ptr;
315}
316
[430548b]317void *FUNC(realloc)(void *ptr, size_t size) {
[5a9481e]318  const void *caller =
319    __builtin_extract_return_addr(__builtin_return_address(0));
[430548b]320
[0ec59c5]321  if (in_malloc)
322    return __libc_realloc(ptr, size);
323
324  in_malloc = 1;
325
326  void *nptr = __libc_realloc(ptr, size);
327
[14e2ab9]328  if (ptr)
329    record_free(ptr, caller);
[6444e03]330  if (nptr)
331    record_malloc(size, nptr, caller);
[0ec59c5]332
333  in_malloc = 0;
334
335  return nptr;
336}
337
[430548b]338void *FUNC(calloc)(size_t nmemb, size_t size) {
[5a9481e]339  const void *caller =
340    __builtin_extract_return_addr(__builtin_return_address(0));
[430548b]341
[0ec59c5]342  if (in_malloc)
343    return __libc_calloc(nmemb, size);
344
345  in_malloc = 1;
346
347  void *ptr = __libc_calloc(nmemb, size);
348
[6444e03]349  if (ptr)
350    record_malloc(nmemb*size, ptr, caller);
[0ec59c5]351
352  in_malloc = 0;
353
354  return ptr;
355}
356
[430548b]357void *FUNC(memalign)(size_t boundary, size_t size) {
[5a9481e]358  const void *caller =
359    __builtin_extract_return_addr(__builtin_return_address(0));
[430548b]360
[0ec59c5]361  if (in_malloc)
362    return __libc_memalign(boundary, size);
363
364  in_malloc = 1;
365
366  void *ptr = __libc_memalign(boundary, size);
367
[6444e03]368  if (ptr)
369    record_malloc(size, ptr, caller);
[0ec59c5]370
371  in_malloc = 0;
372
373  return ptr;
374}
375
[430548b]376void FUNC(free)(void *ptr) {
[5a9481e]377  const void *caller =
378    __builtin_extract_return_addr(__builtin_return_address(0));
[430548b]379
[14e2ab9]380  if (in_malloc || !ptr)
[0ec59c5]381    return __libc_free(ptr);
382
383  in_malloc = 1;
384
[14e2ab9]385  record_free(ptr, caller);
[0ec59c5]386
387  __libc_free(ptr);
388
389  in_malloc = 0;
390}
391
[6444e03]392int FUNC(posix_memalign)(void **memptr, size_t alignment, size_t size) {
393  const void *caller =
394    __builtin_extract_return_addr(__builtin_return_address(0));
395
396#ifdef __PIC__
397  if (!__real_posix_memalign)
[ed8b809]398    if (!(*(void **) (&__real_posix_memalign) =
399        dlsym(RTLD_NEXT, "posix_memalign"))) {
400      return ELIBACC;
401    }
[6444e03]402#endif
403
404  if (in_malloc)
405    return __real_posix_memalign(memptr, alignment, size);
406
407  in_malloc = 1;
408
409  int r = __real_posix_memalign(memptr, alignment, size);
410
411  if (!r)
412    record_malloc(size, *memptr, caller);
413
414  in_malloc = 0;
415
416  return r;
417}
418
419void *FUNC(mmap)(void *addr, size_t length, int prot, int flags,
420                 int fd, off_t offset) {
421  const void *caller =
422    __builtin_extract_return_addr(__builtin_return_address(0));
423
[ed8b809]424#ifdef __PIC__
425  if (!__real_mmap)
426    if (!(*(void **) (&__real_mmap) = dlsym(RTLD_NEXT, "mmap"))) {
427      errno = ELIBACC;
428      return MAP_FAILED;
429    }
430#endif
431
[6444e03]432  if (in_malloc)
[ed8b809]433    return __real_mmap(addr, length, prot, flags, fd, offset);
[6444e03]434
435  in_malloc = 1;
436
[ed8b809]437  void *ptr = __real_mmap(addr, length, prot, flags, fd, offset);
[6444e03]438
439  if (ptr != MAP_FAILED)
440    record_malloc(length, ptr, caller);
441
442  in_malloc = 0;
443
444  return ptr;
445}
446
447void *FUNC(mmap64)(void *addr, size_t length, int prot, int flags,
448                   int fd, off64_t offset) {
449  const void *caller =
450    __builtin_extract_return_addr(__builtin_return_address(0));
451
[ed8b809]452#ifdef __PIC__
453  if (!__real_mmap64)
454    if (!(*(void **) (&__real_mmap64) = dlsym(RTLD_NEXT, "mmap64"))) {
455      errno = ELIBACC;
456      return MAP_FAILED;
457    }
458#endif
459
[6444e03]460  if (in_malloc)
[ed8b809]461    return __real_mmap64(addr, length, prot, flags, fd, offset);
[6444e03]462
463  in_malloc = 1;
464
[ed8b809]465  void *ptr = __real_mmap64(addr, length, prot, flags, fd, offset);
[6444e03]466
467  if (ptr != MAP_FAILED)
468    record_malloc(length, ptr, caller);
469
470  in_malloc = 0;
471
472  return ptr;
473}
474
475int FUNC(munmap)(void *addr, size_t length) {
476  const void *caller =
477    __builtin_extract_return_addr(__builtin_return_address(0));
478
[ed8b809]479#ifdef __PIC__
480  if (!__real_munmap)
481    if (!(*(void **) (&__real_munmap) = dlsym(RTLD_NEXT, "munmap"))) {
482      errno = ELIBACC;
483      return -1;
484    }
485#endif
486
[6444e03]487  if (in_malloc)
[ed8b809]488    return __real_munmap(addr, length);
[6444e03]489
490  in_malloc = 1;
491
492  record_free(addr, caller);
493
[ed8b809]494  int r = __real_munmap(addr, length);
[6444e03]495
496  in_malloc = 0;
497
498  return r;
499}
500
[1bd82e0]501} // extern "C"
502
Note: See TracBrowser for help on using the repository browser.