source: memlog.c @ 3105f50

Revision 3105f50, 5.5 KB checked in by Hal Finkel <hfinkel@…>, 9 years ago (diff)

use the self link for no file

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