source: memlog.cpp @ f715c76

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

add comment about lang. usage

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