source: memlog.c @ 625d5f9

Revision 625d5f9, 5.0 KB checked in by Hal Finkel <hfinkel@…>, 9 years ago (diff)

just use .memlog as the suffix

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