source: memlog.c @ 134408c

Revision 134408c, 5.4 KB checked in by Hal Finkel <hfinkel@…>, 9 years ago (diff)

print tid; break on UNW_ENOINFO

  • Property mode set to 100644
RevLine 
[0ec59c5]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
[134408c]10#include <sys/syscall.h>
[0a0ef57]11#include <sys/time.h>
12#include <sys/resource.h>
[0ec59c5]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__attribute__((__constructor__))
30static void record_init() {
31  struct utsname u;
32  uname(&u);
33
34  char log_name[PATH_MAX];
35  snprintf(log_name, PATH_MAX, "%s.%d.memory.log_file", u.nodename, getpid());
36  log_file = fopen(log_name, "w");
[0a0ef57]37  if (!log_file)
38    fprintf(stderr, "fopen failed for '%s': %m\n", log_name);
[0ec59c5]39}
40
41__attribute__((__destructor__))
42static void record_cleanup() {
43  if (!log_file)
44    return;
45
46  (void) fflush(log_file);
47  (void) fclose(log_file);
48}
49
50static void print_context(unw_context_t *context) {
[0a0ef57]51  struct rusage usage;
52  if (getrusage(RUSAGE_SELF, &usage)) {
53    fprintf(stderr, "getrusage failed: %m\n");
[0ec59c5]54    return;
[0a0ef57]55  }
[0ec59c5]56
[134408c]57  fprintf(log_file, "\t%ld.%06ld %ld %ld", usage.ru_utime.tv_sec,
58          usage.ru_utime.tv_usec, usage.ru_maxrss, syscall(SYS_gettid));
[0a0ef57]59
60  int r;
61  unw_cursor_t cursor;
62  if ((r = unw_init_local(&cursor, context))) {
63    fprintf(stderr, "unw_init_local failed: %s [%d]\n",
64            unw_strerror(r), r);
65    return;
66  }
[0ec59c5]67
[0a0ef57]68  while ((r = unw_step(&cursor)) > 0) {
[0ec59c5]69    unw_word_t off;
70    char proc_name[PATH_MAX];
71    if (unw_get_proc_name(&cursor, proc_name, PATH_MAX, &off)) {
72      off = 0;
73      strcpy(proc_name, "?");
74    }
75
[0a0ef57]76    unw_proc_info_t pip;
77    if ((r = unw_get_proc_info(&cursor, &pip))) {
78      // unw_get_proc_info is not supported on some platforms (ppc and ppc64,
79      // for example), so we need to try harder...
80      if (r == -UNW_EINVAL) {
81        unw_word_t pc;
82        if ((r = unw_get_reg(&cursor, UNW_REG_IP, &pc))) {
83          fprintf(stderr, "unw_get_reg UNW_REG_IP failed: %s [%d]\n",
84                  unw_strerror(r), r);
85          return;
86        }
87
88        if ((r = unw_get_proc_info_by_ip(unw_local_addr_space, pc, &pip, NULL))) {
[134408c]89          if (r == -UNW_ENOINFO)
90            break; // the cursor is now invalid; must break here.
91
[0a0ef57]92          fprintf(stderr, "unw_get_proc_info_by_ip failed: %s [%d]\n",
93                  unw_strerror(r), r);
94          return;
95        }
96      } else {
[134408c]97        if (r == -UNW_ENOINFO)
98          break; // the cursor is now invalid; must break here.
99
[0a0ef57]100        fprintf(stderr, "unw_get_proc_info failed: %s [%d]\n",
101                unw_strerror(r), r);
102        return;
103      }
104    }
105
[0ec59c5]106    const char *file_name;
107    Dl_info dlinfo;
108    if (dladdr((void *)(pip.start_ip + off), &dlinfo) && dlinfo.dli_fname &&
109        *dlinfo.dli_fname)
110      file_name = dlinfo.dli_fname;
111    else
112      file_name = "?";
113
114    fprintf(log_file, "\t%s (%s+0x%x) [%p]", file_name, proc_name, (int) off,
115            (void *) (pip.start_ip + off));
116  }
[0a0ef57]117
[134408c]118  if (r < 0 && r != -UNW_ENOINFO)
[0a0ef57]119    fprintf(stderr, "unw_step failed: %s [%d]\n",
120            unw_strerror(r), r);
[0ec59c5]121}
122
123static void record_malloc(size_t size, void *ptr, unw_context_t *uc) {
124  if (!log_file)
125    return;
126
127  if (pthread_mutex_lock(&log_mutex))
128    return;
129
130  fprintf(log_file, "M: %zd %p", size, ptr);
131  print_context(uc);
132  fprintf(log_file, "\n");
133
134done:
135  pthread_mutex_unlock(&log_mutex);
136}
137
138static void record_free(void *ptr, unw_context_t *uc) {
139  if (!log_file)
140    return;
141
142  if (pthread_mutex_lock(&log_mutex))
143    return;
144
145  fprintf(log_file, "F: %p", ptr);
146  print_context(uc);
147  fprintf(log_file, "\n");
148
149done:
150  pthread_mutex_unlock(&log_mutex);
151}
152
153// glibc exports its underlying malloc implementation under the name
154// __libc_malloc so that hooks like this can use it.
155extern void *__libc_malloc(size_t size);
156extern void *__libc_realloc(void *ptr, size_t size);
157extern void *__libc_calloc(size_t nmemb, size_t size);
158extern void *__libc_memalign(size_t boundary, size_t size);
159extern void __libc_free(void *ptr);
160
161// The malloc hook might use functions that call malloc, and we need to make
162// sure this does not cause an infinite loop.
163static __thread int in_malloc = 0;
164
165void *malloc(size_t size) {
166  if (in_malloc)
167    return __libc_malloc(size);
168
169  in_malloc = 1;
170
171  void *ptr = __libc_malloc(size);
172
173  unw_context_t uc;
174  if (!unw_getcontext(&uc))
175    record_malloc(size, ptr, &uc);
176
177  in_malloc = 0;
178  return ptr;
179}
180
181void *realloc(void *ptr, size_t size) {
182  if (in_malloc)
183    return __libc_realloc(ptr, size);
184
185  in_malloc = 1;
186
187  void *nptr = __libc_realloc(ptr, size);
188
189  unw_context_t uc;
190  if (!unw_getcontext(&uc)) {
191    record_free(ptr, &uc);
192    record_malloc(size, nptr, &uc);
193  }
194
195  in_malloc = 0;
196
197  return nptr;
198}
199
200void *calloc(size_t nmemb, size_t size) {
201  if (in_malloc)
202    return __libc_calloc(nmemb, size);
203
204  in_malloc = 1;
205
206  void *ptr = __libc_calloc(nmemb, size);
207
208  unw_context_t uc;
209  if (!unw_getcontext(&uc))
210    record_malloc(nmemb*size, ptr, &uc);
211
212  in_malloc = 0;
213
214  return ptr;
215}
216
217void *memalign(size_t boundary, size_t size) {
218  if (in_malloc)
219    return __libc_memalign(boundary, size);
220
221  in_malloc = 1;
222
223  void *ptr = __libc_memalign(boundary, size);
224
225  unw_context_t uc;
226  if (!unw_getcontext(&uc))
227    record_malloc(size, ptr, &uc);
228
229  in_malloc = 0;
230
231  return ptr;
232}
233
234void free(void *ptr) {
235  if (in_malloc)
236    return __libc_free(ptr);
237
238  in_malloc = 1;
239
240  unw_context_t uc;
241  if (!unw_getcontext(&uc))
242    record_free(ptr, &uc);
243
244  __libc_free(ptr);
245
246  in_malloc = 0;
247}
248
Note: See TracBrowser for help on using the repository browser.