3 #include "ircd_alloc.h"
11 /* #include <assert.h> -- Now using assert in ircd_log.h */
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
24 #include "ircd_string.h"
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;
32 /** Header block to track an allocated block's information. */
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.
48 memfrob(void *p, size_t len)
52 const char *pat = "\xde\xad\xbe\xef";
55 for (s = (char*)p, se = s + (len & ~3) - 4;
58 *(uint32_t*)s = *(uint32_t*)pat;
59 for (se = s; se < s; s++)
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. */
72 /** Check whether we should run garbage collection.
76 dbg_check_gcollect(void)
78 if (CurrentTime - last_gcollect < GC_FREQ)
81 last_gcollect = CurrentTime;
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.
92 dbg_malloc(size_t size, const char *type, const char *file, int line)
94 struct MemHeader *mh = GC_malloc(size + sizeof(*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;
103 mh->since = CurrentTime;
104 mdbg_bytes_allocated += size;
105 mdbg_blocks_allocated++;
106 dbg_check_gcollect();
107 return (void*)(mh + 1);
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.
118 dbg_malloc_zero(size_t size, const char *type, const char *file, int line)
120 struct MemHeader *mh = GC_malloc(size + sizeof(*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;
129 mdbg_bytes_allocated += size;
130 mdbg_blocks_allocated++;
131 dbg_check_gcollect();
132 return (void*)(mh + 1);
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.
143 dbg_realloc(void *ptr, size_t size, const char *file, int line)
145 struct MemHeader *mh, *mh2;
147 return dbg_malloc(size, "realloc", file, line);
148 mh = (struct MemHeader*)ptr - 1;
149 assert(mh->magic == 0xA110CA7E);
150 if (mh->length >= size)
152 mh2 = dbg_malloc(size, "realloc", file, line);
155 dbg_free(mh+1, file, line);
158 memcpy(mh2+1, mh+1, mh->length);
159 dbg_free(mh+1, file, line);
160 return (void*)(mh2+1);
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.
169 dbg_free(void *ptr, const char *file, int line)
171 struct MemHeader *mh = (struct MemHeader*)ptr - 1;
172 /* XXX but bison gives us NULLs */
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--;
181 dbg_check_gcollect();
184 /** Report number of bytes currently allocated.
185 * @return Number of bytes allocated.
188 fda_get_byte_count(void)
190 dbg_check_gcollect();
191 return mdbg_bytes_allocated;
194 /** Report number of blocks currently allocated.
195 * @return Number of blocks allocated.
198 fda_get_block_count(void)
200 return mdbg_blocks_allocated;
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.
210 dbg_memory_leaked(void *p, int sz)
212 struct MemHeader *mh;
213 /* We have to return because the gc "leaks". */
215 if (mh->magic != 0xA110CA7E)
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);
222 "%s leak at %s:%u(%u bytes for %u seconds)",
223 mh->type, mh->file, mh->line, mh->length,
224 CurrentTime - mh->since));
227 /** Initialize the memory debugging subsystem. */
229 mem_dbg_initialise(void)
232 GC_set_leak_handler(dbg_memory_leaked);