source: memlog.c @ 966f5de

Revision 966f5de, 6.2 KB checked in by Hal Finkel <hfinkel@…>, 9 years ago (diff)

misc whitespace

  • Property mode set to 100644
RevLine 
[0ec59c5]1#ifndef _GNU_SOURCE
2#define _GNU_SOURCE
3#endif
4
5#include <stdlib.h>
6#include <stdio.h>
7#include <limits.h>
8#include <string.h>
9
[430548b]10#include <malloc.h>
[a736d81]11#include <execinfo.h>
[134408c]12#include <sys/syscall.h>
[0a0ef57]13#include <sys/time.h>
14#include <sys/resource.h>
[0ec59c5]15#include <sys/types.h>
16#include <sys/stat.h>
17#include <sys/utsname.h>
18#include <fcntl.h>
19#include <unistd.h>
20
21#include <pthread.h>
22#include <dlfcn.h>
23
[493cb97]24// NOTE: When static linking, this depends on linker wrapping.
25// Add to your LDFLAGS:
26//   -Wl,--wrap,malloc,--wrap,free,--wrap,realloc,--wrap,calloc,--wrap,memalign /path/to/memlog_s.o -lpthread -ldl
27
[0ec59c5]28FILE *log_file = NULL;
29static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
30
[69850c8]31// The malloc hook might use functions that call malloc, and we need to make
32// sure this does not cause an infinite loop.
33static __thread int in_malloc = 0;
[3105f50]34static char self_path[PATH_MAX+1] = { '\0' };
[69850c8]35
[0ec59c5]36__attribute__((__constructor__))
37static void record_init() {
38  struct utsname u;
39  uname(&u);
40
[3105f50]41  char log_name[PATH_MAX+1];
42  snprintf(log_name, PATH_MAX+1, "%s.%d.memlog", u.nodename, getpid());
[0ec59c5]43  log_file = fopen(log_name, "w");
[0a0ef57]44  if (!log_file)
45    fprintf(stderr, "fopen failed for '%s': %m\n", log_name);
[3105f50]46
47  const char *link_name = "/proc/self/exe";
48  readlink(link_name, self_path, PATH_MAX);
[0ec59c5]49}
50
51__attribute__((__destructor__))
52static void record_cleanup() {
53  if (!log_file)
54    return;
55
[69850c8]56  // These functions might call free, but we're shutting down, so don't try to
57  // unwind the stack from here...
58  in_malloc = 1;
59
[5a9481e]60  // Avoid any racing by obtaining the lock.
61  if (pthread_mutex_lock(&log_mutex))
62    return;
63
[0ec59c5]64  (void) fflush(log_file);
65  (void) fclose(log_file);
66}
67
[5a9481e]68// dladdr is, relatively, quit slow. For this to work on a large application,
69// we need to cache the lookup results.
70static int dladdr_cached(void *addr, Dl_info *info) {
71  return dladdr(addr, info);
72}
73
[1e5cce6]74static void print_context(const void *caller, int show_backtrace) {
[0a0ef57]75  struct rusage usage;
76  if (getrusage(RUSAGE_SELF, &usage)) {
77    fprintf(stderr, "getrusage failed: %m\n");
[0ec59c5]78    return;
[0a0ef57]79  }
[0ec59c5]80
[134408c]81  fprintf(log_file, "\t%ld.%06ld %ld %ld", usage.ru_utime.tv_sec,
82          usage.ru_utime.tv_usec, usage.ru_maxrss, syscall(SYS_gettid));
[0a0ef57]83
[1e5cce6]84  if (!show_backtrace)
85    return;
86
[a736d81]87  void *pcs[1024];
88  int num_pcs = backtrace(pcs, 1024);
[14e2ab9]89
90  int found_caller = 0;
[a736d81]91  for (int pci = 0; pci < num_pcs; ++pci) {
[14e2ab9]92    intptr_t pc = (intptr_t) pcs[pci];
[69850c8]93
94    if (!pc)
95      break;
96
[14e2ab9]97    if (!found_caller) {
98      if (pc != (intptr_t) caller)
99        continue;
100
101      found_caller = 1;
102    }
103
104    intptr_t off, relpc;
[69850c8]105    const char *proc_name;
106    const char *file_name;
107    Dl_info dlinfo;
[5a9481e]108    if (dladdr_cached((void *) pc, &dlinfo) && dlinfo.dli_fname &&
[69850c8]109        *dlinfo.dli_fname) {
[14e2ab9]110      intptr_t saddr = (intptr_t) dlinfo.dli_saddr;
[69850c8]111      if (saddr) {
112#if defined(__powerpc64__) && !defined(__powerpc64le__)
113        // On PPC64 ELFv1, the symbol address points to the function descriptor, not
114        // the actual starting address.
[14e2ab9]115        saddr = *(intptr_t*) saddr;
[69850c8]116#endif
117
118        off = pc - saddr;
[14e2ab9]119        relpc = pc - ((intptr_t) dlinfo.dli_fbase);
[69850c8]120      } else {
121        off = 0;
122        relpc = 0;
123      }
124
125      proc_name = dlinfo.dli_sname;
126      if (!proc_name)
127        proc_name = "?";
128
129      file_name = dlinfo.dli_fname;
130    } else {
[3105f50]131      // We don't know these...
132      off = 0;
133      relpc = 0;
[69850c8]134      proc_name = "?";
[3105f50]135
136      // If we can't determine the file, assume it is the base executable
137      // (which does the right thing for statically-linked binaries).
138      file_name = self_path;
[69850c8]139    }
140
141    fprintf(log_file, "\t%s (%s+0x%x) [0x%lx (0x%lx)]", file_name, proc_name, (int) off,
142            (long) pc, (long) relpc);
[0ec59c5]143  }
144}
145
[430548b]146static void record_malloc(size_t size, void *ptr, const void *caller) {
[0ec59c5]147  if (!log_file)
148    return;
149
150  if (pthread_mutex_lock(&log_mutex))
151    return;
152
153  fprintf(log_file, "M: %zd %p", size, ptr);
[1e5cce6]154  print_context(caller, 1);
[0ec59c5]155  fprintf(log_file, "\n");
156
157done:
158  pthread_mutex_unlock(&log_mutex);
159}
160
[430548b]161static void record_free(void *ptr, const void *caller) {
[0ec59c5]162  if (!log_file)
163    return;
164
165  if (pthread_mutex_lock(&log_mutex))
166    return;
167
168  fprintf(log_file, "F: %p", ptr);
[1e5cce6]169  print_context(caller, 0);
[0ec59c5]170  fprintf(log_file, "\n");
171
172done:
173  pthread_mutex_unlock(&log_mutex);
174}
175
176// glibc exports its underlying malloc implementation under the name
177// __libc_malloc so that hooks like this can use it.
178extern void *__libc_malloc(size_t size);
179extern void *__libc_realloc(void *ptr, size_t size);
180extern void *__libc_calloc(size_t nmemb, size_t size);
181extern void *__libc_memalign(size_t boundary, size_t size);
182extern void __libc_free(void *ptr);
183
[430548b]184#ifdef __PIC__
185#define FUNC(x) x
186#else
187#define FUNC(x) __wrap_ ## x
188#endif
189
190void *FUNC(malloc)(size_t size) {
[5a9481e]191  const void *caller =
192    __builtin_extract_return_addr(__builtin_return_address(0));
[430548b]193
[0ec59c5]194  if (in_malloc)
195    return __libc_malloc(size);
196
197  in_malloc = 1;
198
199  void *ptr = __libc_malloc(size);
200
[14e2ab9]201  record_malloc(size, ptr, caller);
[0ec59c5]202
203  in_malloc = 0;
204  return ptr;
205}
206
[430548b]207void *FUNC(realloc)(void *ptr, size_t size) {
[5a9481e]208  const void *caller =
209    __builtin_extract_return_addr(__builtin_return_address(0));
[430548b]210
[0ec59c5]211  if (in_malloc)
212    return __libc_realloc(ptr, size);
213
214  in_malloc = 1;
215
216  void *nptr = __libc_realloc(ptr, size);
217
[14e2ab9]218  if (ptr)
219    record_free(ptr, caller);
220  record_malloc(size, nptr, caller);
[0ec59c5]221
222  in_malloc = 0;
223
224  return nptr;
225}
226
[430548b]227void *FUNC(calloc)(size_t nmemb, size_t size) {
[5a9481e]228  const void *caller =
229    __builtin_extract_return_addr(__builtin_return_address(0));
[430548b]230
[0ec59c5]231  if (in_malloc)
232    return __libc_calloc(nmemb, size);
233
234  in_malloc = 1;
235
236  void *ptr = __libc_calloc(nmemb, size);
237
[14e2ab9]238  record_malloc(nmemb*size, ptr, caller);
[0ec59c5]239
240  in_malloc = 0;
241
242  return ptr;
243}
244
[430548b]245void *FUNC(memalign)(size_t boundary, size_t size) {
[5a9481e]246  const void *caller =
247    __builtin_extract_return_addr(__builtin_return_address(0));
[430548b]248
[0ec59c5]249  if (in_malloc)
250    return __libc_memalign(boundary, size);
251
252  in_malloc = 1;
253
254  void *ptr = __libc_memalign(boundary, size);
255
[14e2ab9]256  record_malloc(size, ptr, caller);
[0ec59c5]257
258  in_malloc = 0;
259
260  return ptr;
261}
262
[430548b]263void FUNC(free)(void *ptr) {
[5a9481e]264  const void *caller =
265    __builtin_extract_return_addr(__builtin_return_address(0));
[430548b]266
[14e2ab9]267  if (in_malloc || !ptr)
[0ec59c5]268    return __libc_free(ptr);
269
270  in_malloc = 1;
271
[14e2ab9]272  record_free(ptr, caller);
[0ec59c5]273
274  __libc_free(ptr);
275
276  in_malloc = 0;
277}
278
Note: See TracBrowser for help on using the repository browser.