Global replace of Nicira Networks.
[sliver-openvswitch.git] / lib / leak-checker.c
1 /*
2  * Copyright (c) 2008, 2009, 2010 Nicira, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18 #include "leak-checker.h"
19 #include <inttypes.h>
20 #include "backtrace.h"
21 #include "vlog.h"
22
23 VLOG_DEFINE_THIS_MODULE(leak_checker);
24
25 #ifndef HAVE_MALLOC_HOOKS
26 void
27 leak_checker_start(const char *file_name OVS_UNUSED)
28 {
29     VLOG_WARN("not enabling leak checker because the libc in use does not "
30               "have the required hooks");
31 }
32
33 void
34 leak_checker_set_limit(off_t max_size OVS_UNUSED)
35 {
36 }
37
38 void
39 leak_checker_claim(const void *p OVS_UNUSED)
40 {
41 }
42
43 void
44 leak_checker_usage(void)
45 {
46     printf("  --check-leaks=FILE      (accepted but ignored in this build)\n");
47 }
48 #else /* HAVE_MALLOC_HOOKS */
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <malloc.h>
52 #include <sys/stat.h>
53
54 typedef void *malloc_hook_type(size_t, const void *);
55 typedef void *realloc_hook_type(void *, size_t, const void *);
56 typedef void free_hook_type(void *, const void *);
57
58 struct hooks {
59     malloc_hook_type *malloc_hook_func;
60     realloc_hook_type *realloc_hook_func;
61     free_hook_type *free_hook_func;
62 };
63
64 static malloc_hook_type hook_malloc;
65 static realloc_hook_type hook_realloc;
66 static free_hook_type hook_free;
67
68 static struct hooks libc_hooks;
69 static const struct hooks our_hooks = { hook_malloc, hook_realloc, hook_free };
70
71 static FILE *file;
72 static off_t limit = 10 * 1000 * 1000;
73
74 static void
75 get_hooks(struct hooks *hooks)
76 {
77     hooks->malloc_hook_func = __malloc_hook;
78     hooks->realloc_hook_func = __realloc_hook;
79     hooks->free_hook_func = __free_hook;
80 }
81
82 static void
83 set_hooks(const struct hooks *hooks)
84 {
85     __malloc_hook = hooks->malloc_hook_func;
86     __realloc_hook = hooks->realloc_hook_func;
87     __free_hook = hooks->free_hook_func;
88 }
89
90 void
91 leak_checker_start(const char *file_name)
92 {
93     if (!file) {
94         file = fopen(file_name, "w");
95         if (!file) {
96             VLOG_WARN("failed to create \"%s\": %s",
97                       file_name, strerror(errno));
98             return;
99         }
100         setvbuf(file, NULL, _IOLBF, 0);
101         VLOG_WARN("enabled memory leak logging to \"%s\"", file_name);
102         get_hooks(&libc_hooks);
103         set_hooks(&our_hooks);
104     }
105 }
106
107 void
108 leak_checker_stop(void)
109 {
110     if (file) {
111         fclose(file);
112         file = NULL;
113         set_hooks(&libc_hooks);
114         VLOG_WARN("disabled memory leak logging");
115     }
116 }
117
118 void
119 leak_checker_set_limit(off_t limit_)
120 {
121     limit = limit_;
122 }
123
124 void
125 leak_checker_usage(void)
126 {
127     printf("  --check-leaks=FILE      log malloc and free calls to FILE\n");
128 }
129
130 static void PRINTF_FORMAT(1, 2)
131 log_callers(const char *format, ...)
132 {
133     struct backtrace backtrace;
134     va_list args;
135     int i;
136
137     va_start(args, format);
138     vfprintf(file, format, args);
139     va_end(args);
140
141     putc(':', file);
142     backtrace_capture(&backtrace);
143     for (i = 0; i < backtrace.n_frames; i++) {
144         fprintf(file, " 0x%"PRIxPTR, backtrace.frames[i]);
145     }
146     putc('\n', file);
147 }
148
149 static void
150 reset_hooks(void)
151 {
152     static int count;
153
154     if (file) {
155         if (ferror(file)) {
156             VLOG_WARN("error writing leak checker log file");
157             leak_checker_stop();
158             return;
159         }
160
161         if (count++ >= 100 && limit) {
162             struct stat s;
163             count = 0;
164             if (fstat(fileno(file), &s) < 0) {
165                 VLOG_WARN("cannot fstat leak checker log file: %s",
166                           strerror(errno));
167                 leak_checker_stop();
168                 return;
169             }
170             if (s.st_size > limit) {
171                 VLOG_WARN("leak checker log file size exceeded limit");
172                 leak_checker_stop();
173                 return;
174             }
175         }
176     }
177     if (file) {
178         set_hooks(&our_hooks);
179     }
180 }
181
182 static void *
183 hook_malloc(size_t size, const void *caller OVS_UNUSED)
184 {
185     void *p;
186
187     set_hooks(&libc_hooks);
188     p = malloc(size);
189     get_hooks(&libc_hooks);
190
191     log_callers("malloc(%zu) -> %p", size, p);
192
193     reset_hooks();
194     return p;
195 }
196
197 void
198 leak_checker_claim(const void *p)
199 {
200     if (!file) {
201         return;
202     }
203
204     if (p) {
205         set_hooks(&libc_hooks);
206         log_callers("claim(%p)", p);
207         reset_hooks();
208     }
209 }
210
211 static void
212 hook_free(void *p, const void *caller OVS_UNUSED)
213 {
214     if (!p) {
215         return;
216     }
217
218     set_hooks(&libc_hooks);
219     log_callers("free(%p)", p);
220     free(p);
221     get_hooks(&libc_hooks);
222
223     reset_hooks();
224 }
225
226 static void *
227 hook_realloc(void *p, size_t size, const void *caller OVS_UNUSED)
228 {
229     void *q;
230
231     set_hooks(&libc_hooks);
232     q = realloc(p, size);
233     get_hooks(&libc_hooks);
234
235     if (p != q) {
236         log_callers("realloc(%p, %zu) -> %p", p, size, q);
237     }
238
239     reset_hooks();
240
241     return q;
242 }
243 #endif /* HAVE_MALLOC_HOOKS */