source: memlog.c @ 493cb97

Revision 493cb97, 5.7 KB checked in by Hal Finkel <hfinkel@…>, 9 years ago (diff)

static linking comment; make static .o memlog_s.o

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