]> jfr.im git - irc/quakenet/snircd.git/blob - ircd/memdebug.c
Initial import of 2.10.12.01
[irc/quakenet/snircd.git] / ircd / memdebug.c
1 #include <sys/types.h>
2 #include "ircd.h"
3 #include "ircd_alloc.h"
4 #include "ircd_log.h"
5 #include "client.h"
6 #include "s_debug.h"
7 #include "send.h"
8 #include <stdlib.h>
9 #include <string.h>
10
11 /* #include <assert.h> -- Now using assert in ircd_log.h */
12
13 #ifdef MDEBUG
14
15 /* To use this you need to get gc6.0 from:
16 * http://www.hpl.hp.com/personal/Hans_Boehm/gc/
17 * and you need to apply the patch in
18 * doc/debug_memleak_gc.patch to your gc6.0 tree, and reconfigure your ircd using
19 --with-leak-detect=path-to-gc6.0/.lib/
20 * You should only do this for debugging builds as it can slow things down
21 * a bit.
22 */
23
24 #include "ircd_string.h"
25
26 void *GC_malloc(size_t size);
27 void GC_free(void *ptr);
28 void GC_set_leak_handler(void (*)(void*, int));
29 void GC_gcollect(void);
30 extern int GC_find_leak;
31
32 /** Header block to track an allocated block's information. */
33 struct MemHeader
34 {
35 uint32_t magic;
36 char type[32];
37 char file[32];
38 int line;
39 size_t length;
40 time_t since;
41 };
42
43 /** Overwrite \a len byte at \a p with 0xDEADBEEF.
44 * @param[out] p Memory buffer to overwrite.
45 * @param[in] len Number of bytes to overwrite.
46 */
47 void
48 memfrob(void *p, size_t len)
49 {
50 /* deadbeef */
51 int i = 0;
52 const char *pat = "\xde\xad\xbe\xef";
53 char *s, *se;
54
55 for (s = (char*)p, se = s + (len & ~3) - 4;
56 s <= se;
57 s += 4)
58 *(uint32_t*)s = *(uint32_t*)pat;
59 for (se = s; se < s; s++)
60 *s = pat[i++];
61 }
62
63 /** Total number of bytes allocated. */
64 static size_t mdbg_bytes_allocated = 0;
65 /** Total number of blocks allocated. */
66 static size_t mdbg_blocks_allocated = 0;
67 /** Last Unix time that we ran garbage collection. */
68 static time_t last_gcollect = 0;
69 /** Minimum interval for garbage collection. */
70 #define GC_FREQ 5
71
72 /** Check whether we should run garbage collection.
73 * If so, do it.
74 */
75 void
76 dbg_check_gcollect(void)
77 {
78 if (CurrentTime - last_gcollect < GC_FREQ)
79 return;
80 GC_gcollect();
81 last_gcollect = CurrentTime;
82 }
83
84 /** Allocate a block of memory.
85 * @param[in] size Number of bytes needed.
86 * @param[in] type Memory operation for block.
87 * @param[in] file File name of allocating function.
88 * @param[in] line Line number of allocating function.
89 * @return Pointer to the newly allocated block.
90 */
91 void*
92 dbg_malloc(size_t size, const char *type, const char *file, int line)
93 {
94 struct MemHeader *mh = GC_malloc(size + sizeof(*mh));
95 if (mh == NULL)
96 return mh;
97 memfrob((void*)(mh + 1), size);
98 mh->magic = 0xA110CA7E;
99 ircd_strncpy(mh->type, type, sizeof(mh->type) - 1)[sizeof(mh->type) - 1] = 0;
100 ircd_strncpy(mh->file, file, sizeof(mh->file) - 1)[sizeof(mh->file) - 1] = 0;
101 mh->line = line;
102 mh->length = size;
103 mh->since = CurrentTime;
104 mdbg_bytes_allocated += size;
105 mdbg_blocks_allocated++;
106 dbg_check_gcollect();
107 return (void*)(mh + 1);
108 }
109
110 /** Allocate a zero-initialized block of memory.
111 * @param[in] size Number of bytes needed.
112 * @param[in] type Memory operation for block.
113 * @param[in] file File name of allocating function.
114 * @param[in] line Line number of allocating function.
115 * @return Pointer to the newly allocated block.
116 */
117 void*
118 dbg_malloc_zero(size_t size, const char *type, const char *file, int line)
119 {
120 struct MemHeader *mh = GC_malloc(size + sizeof(*mh));
121 if (mh == NULL)
122 return mh;
123 memset((void*)(mh + 1), 0, size);
124 mh->magic = 0xA110CA7E;
125 ircd_strncpy(mh->type, type, sizeof(mh->type) - 1)[sizeof(mh->type) - 1] = 0;
126 ircd_strncpy(mh->file, file, sizeof(mh->file) - 1)[sizeof(mh->file) - 1] = 0;
127 mh->line = line;
128 mh->length = size;
129 mdbg_bytes_allocated += size;
130 mdbg_blocks_allocated++;
131 dbg_check_gcollect();
132 return (void*)(mh + 1);
133 }
134
135 /** Extend an allocated block of memory.
136 * @param[in] ptr Pointer to currently allocated block of memory (may be NULL).
137 * @param[in] size Minimum number of bytes for new block.
138 * @param[in] file File name of allocating function.
139 * @param[in] line Line number of allocating function.
140 * @return Pointer to the extended block of memory.
141 */
142 void*
143 dbg_realloc(void *ptr, size_t size, const char *file, int line)
144 {
145 struct MemHeader *mh, *mh2;
146 if (ptr == NULL)
147 return dbg_malloc(size, "realloc", file, line);
148 mh = (struct MemHeader*)ptr - 1;
149 assert(mh->magic == 0xA110CA7E);
150 if (mh->length >= size)
151 return mh;
152 mh2 = dbg_malloc(size, "realloc", file, line);
153 if (mh2 == NULL)
154 {
155 dbg_free(mh+1, file, line);
156 return NULL;
157 }
158 memcpy(mh2+1, mh+1, mh->length);
159 dbg_free(mh+1, file, line);
160 return (void*)(mh2+1);
161 }
162
163 /** Deallocate a block of memory.
164 * @param[in] ptr Pointer to currently allocated block of memory (may be NULL).
165 * @param[in] file File name of deallocating function.
166 * @param[in] line Line number of deallocating function.
167 */
168 void
169 dbg_free(void *ptr, const char *file, int line)
170 {
171 struct MemHeader *mh = (struct MemHeader*)ptr - 1;
172 /* XXX but bison gives us NULLs */
173 if (ptr == NULL)
174 return;
175 assert(mh->magic == 0xA110CA7E);
176 /* XXX can we get boehmgc to check for references to it? */
177 memfrob(mh, mh->length + sizeof(*mh));
178 mdbg_bytes_allocated -= mh->length;
179 mdbg_blocks_allocated--;
180 GC_free(mh);
181 dbg_check_gcollect();
182 }
183
184 /** Report number of bytes currently allocated.
185 * @return Number of bytes allocated.
186 */
187 size_t
188 fda_get_byte_count(void)
189 {
190 dbg_check_gcollect();
191 return mdbg_bytes_allocated;
192 }
193
194 /** Report number of blocks currently allocated.
195 * @return Number of blocks allocated.
196 */
197 size_t
198 fda_get_block_count(void)
199 {
200 return mdbg_blocks_allocated;
201 }
202
203 #include <stdio.h>
204
205 /** Callback for when the garbage collector detects a memory leak.
206 * @param[in] p Pointer to leaked memory.
207 * @param[in] sz Length of the block.
208 */
209 void
210 dbg_memory_leaked(void *p, int sz)
211 {
212 struct MemHeader *mh;
213 /* We have to return because the gc "leaks". */
214 mh = p;
215 if (mh->magic != 0xA110CA7E)
216 return;
217 sendto_opmask_butone(NULL, SNO_OLDSNO,
218 "%s leak at %s:%u(%u bytes for %u seconds)",
219 mh->type, mh->file, mh->line, mh->length,
220 CurrentTime - mh->since);
221 Debug((DEBUG_ERROR,
222 "%s leak at %s:%u(%u bytes for %u seconds)",
223 mh->type, mh->file, mh->line, mh->length,
224 CurrentTime - mh->since));
225 }
226
227 /** Initialize the memory debugging subsystem. */
228 void
229 mem_dbg_initialise(void)
230 {
231 GC_find_leak = 1;
232 GC_set_leak_handler(dbg_memory_leaked);
233 }
234
235 #endif