source: memlog.c @ 69850c8

Revision 69850c8, 6.8 KB checked in by Hal Finkel <hfinkel@…>, 9 years ago (diff)

trying to avoid using unw_get_proc_name, etc.

  • 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 <sys/syscall.h>
11#include <sys/time.h>
12#include <sys/resource.h>
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <sys/utsname.h>
16#include <fcntl.h>
17#include <unistd.h>
18
19#include <pthread.h>
20
21#define UNW_LOCAL_ONLY
22#include <libunwind.h>
23
24#include <dlfcn.h>
25
26FILE *log_file = NULL;
27static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
28
29// The malloc hook might use functions that call malloc, and we need to make
30// sure this does not cause an infinite loop.
31static __thread int in_malloc = 0;
32
33__attribute__((__constructor__))
34static void record_init() {
35  struct utsname u;
36  uname(&u);
37
38  char log_name[PATH_MAX];
39  snprintf(log_name, PATH_MAX, "%s.%d.memory.log_file", 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
45__attribute__((__destructor__))
46static void record_cleanup() {
47  if (!log_file)
48    return;
49
50  // These functions might call free, but we're shutting down, so don't try to
51  // unwind the stack from here...
52  in_malloc = 1;
53
54  (void) fflush(log_file);
55  (void) fclose(log_file);
56}
57
58static void print_context(unw_context_t *context) {
59  struct rusage usage;
60  if (getrusage(RUSAGE_SELF, &usage)) {
61    fprintf(stderr, "getrusage failed: %m\n");
62    return;
63  }
64
65  fprintf(log_file, "\t%ld.%06ld %ld %ld", usage.ru_utime.tv_sec,
66          usage.ru_utime.tv_usec, usage.ru_maxrss, syscall(SYS_gettid));
67
68  int r;
69  unw_cursor_t cursor;
70  if ((r = unw_init_local(&cursor, context))) {
71    fprintf(stderr, "unw_init_local failed: %s [%d]\n",
72            unw_strerror(r), r);
73    return;
74  }
75
76  while ((r = unw_step(&cursor)) > 0) {
77    unw_word_t pc;
78    if ((r = unw_get_reg(&cursor, UNW_REG_IP, &pc))) {
79      fprintf(stderr, "unw_get_reg UNW_REG_IP failed: %s [%d]\n",
80              unw_strerror(r), r);
81      return;
82    }
83
84    if (!pc)
85      break;
86
87    unw_word_t off, relpc;
88    const char *proc_name;
89    const char *file_name;
90    Dl_info dlinfo;
91    if (dladdr((void *) pc, &dlinfo) && dlinfo.dli_fname &&
92        *dlinfo.dli_fname) {
93      unw_word_t saddr = (unw_word_t) dlinfo.dli_saddr;
94      if (saddr) {
95#if defined(__powerpc64__) && !defined(__powerpc64le__)
96        // On PPC64 ELFv1, the symbol address points to the function descriptor, not
97        // the actual starting address.
98        saddr = *(unw_word_t*) saddr;
99#endif
100
101        off = pc - saddr;
102        relpc = pc - ((unw_word_t) dlinfo.dli_fbase);
103      } else {
104        off = 0;
105        relpc = 0;
106      }
107
108      proc_name = dlinfo.dli_sname;
109      if (!proc_name)
110        proc_name = "?";
111
112      file_name = dlinfo.dli_fname;
113    } else {
114      off = pc;
115      relpc = pc;
116      proc_name = "?";
117      file_name = "?";
118    }
119
120    fprintf(log_file, "\t%s (%s+0x%x) [0x%lx (0x%lx)]", file_name, proc_name, (int) off,
121            (long) pc, (long) relpc);
122
123#if 0
124    unw_word_t off;
125    char proc_name[PATH_MAX];
126    if (unw_get_proc_name(&cursor, proc_name, PATH_MAX, &off)) {
127      off = 0;
128      strcpy(proc_name, "?");
129    }
130
131    unw_proc_info_t pip;
132    if ((r = unw_get_proc_info(&cursor, &pip))) {
133      // unw_get_proc_info is not supported on some platforms (ppc and ppc64,
134      // for example), so we need to try harder...
135      if (r == -UNW_EINVAL) {
136        unw_word_t pc;
137        if ((r = unw_get_reg(&cursor, UNW_REG_IP, &pc))) {
138          fprintf(stderr, "unw_get_reg UNW_REG_IP failed: %s [%d]\n",
139                  unw_strerror(r), r);
140          return;
141        }
142
143        if ((r = unw_get_proc_info_by_ip(unw_local_addr_space, pc, &pip, NULL))) {
144          if (r == -UNW_ENOINFO)
145            break; // the cursor is now invalid; must break here.
146
147          fprintf(stderr, "unw_get_proc_info_by_ip failed: %s [%d]\n",
148                  unw_strerror(r), r);
149          return;
150        }
151      } else {
152        if (r == -UNW_ENOINFO)
153          break; // the cursor is now invalid; must break here.
154
155        fprintf(stderr, "unw_get_proc_info failed: %s [%d]\n",
156                unw_strerror(r), r);
157        return;
158      }
159    }
160
161    const char *file_name;
162    Dl_info dlinfo;
163    if (dladdr((void *)(pip.start_ip + off), &dlinfo) && dlinfo.dli_fname &&
164        *dlinfo.dli_fname)
165      file_name = dlinfo.dli_fname;
166    else
167      file_name = "?";
168
169    fprintf(log_file, "\t%s (%s+0x%x) [%p]", file_name, proc_name, (int) off,
170            (void *) (pip.start_ip + off));
171#endif
172  }
173
174  if (r < 0 && r != -UNW_ENOINFO)
175    fprintf(stderr, "unw_step failed: %s [%d]\n",
176            unw_strerror(r), r);
177}
178
179static void record_malloc(size_t size, void *ptr, unw_context_t *uc) {
180  if (!log_file)
181    return;
182
183  if (pthread_mutex_lock(&log_mutex))
184    return;
185
186  fprintf(log_file, "M: %zd %p", size, ptr);
187  print_context(uc);
188  fprintf(log_file, "\n");
189
190done:
191  pthread_mutex_unlock(&log_mutex);
192}
193
194static void record_free(void *ptr, unw_context_t *uc) {
195  if (!log_file)
196    return;
197
198  if (pthread_mutex_lock(&log_mutex))
199    return;
200
201  fprintf(log_file, "F: %p", ptr);
202  print_context(uc);
203  fprintf(log_file, "\n");
204
205done:
206  pthread_mutex_unlock(&log_mutex);
207}
208
209// glibc exports its underlying malloc implementation under the name
210// __libc_malloc so that hooks like this can use it.
211extern void *__libc_malloc(size_t size);
212extern void *__libc_realloc(void *ptr, size_t size);
213extern void *__libc_calloc(size_t nmemb, size_t size);
214extern void *__libc_memalign(size_t boundary, size_t size);
215extern void __libc_free(void *ptr);
216
217void *malloc(size_t size) {
218  if (in_malloc)
219    return __libc_malloc(size);
220
221  in_malloc = 1;
222
223  void *ptr = __libc_malloc(size);
224
225  unw_context_t uc;
226  if (!unw_getcontext(&uc))
227    record_malloc(size, ptr, &uc);
228
229  in_malloc = 0;
230  return ptr;
231}
232
233void *realloc(void *ptr, size_t size) {
234  if (in_malloc)
235    return __libc_realloc(ptr, size);
236
237  in_malloc = 1;
238
239  void *nptr = __libc_realloc(ptr, size);
240
241  unw_context_t uc;
242  if (!unw_getcontext(&uc)) {
243    record_free(ptr, &uc);
244    record_malloc(size, nptr, &uc);
245  }
246
247  in_malloc = 0;
248
249  return nptr;
250}
251
252void *calloc(size_t nmemb, size_t size) {
253  if (in_malloc)
254    return __libc_calloc(nmemb, size);
255
256  in_malloc = 1;
257
258  void *ptr = __libc_calloc(nmemb, size);
259
260  unw_context_t uc;
261  if (!unw_getcontext(&uc))
262    record_malloc(nmemb*size, ptr, &uc);
263
264  in_malloc = 0;
265
266  return ptr;
267}
268
269void *memalign(size_t boundary, size_t size) {
270  if (in_malloc)
271    return __libc_memalign(boundary, size);
272
273  in_malloc = 1;
274
275  void *ptr = __libc_memalign(boundary, size);
276
277  unw_context_t uc;
278  if (!unw_getcontext(&uc))
279    record_malloc(size, ptr, &uc);
280
281  in_malloc = 0;
282
283  return ptr;
284}
285
286void free(void *ptr) {
287  if (in_malloc)
288    return __libc_free(ptr);
289
290  in_malloc = 1;
291
292  unw_context_t uc;
293  if (!unw_getcontext(&uc))
294    record_free(ptr, &uc);
295
296  __libc_free(ptr);
297
298  in_malloc = 0;
299}
300
Note: See TracBrowser for help on using the repository browser.