source: memlog.cpp @ 1bd82e0

Revision 1bd82e0, 6.6 KB checked in by Hal Finkel <hfinkel@…>, 9 years ago (diff)

adjust for c++, use caching of dladdr, fixup for short names when making dot

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