3 #include "ircd_alloc.h"
12 /* To use this you need to get gc6.0 from:
13 * http://www.hpl.hp.com/personal/Hans_Boehm/gc/
14 * and you need to apply the patch in
15 * doc/debug_memleak_gc.patch to your gc6.0 tree, and reconfigure your ircd using
16 --with-leak-detect=path-to-gc6.0/.lib/
17 * You should only do this for debugging builds as it can slow things down
21 #include "ircd_string.h"
23 void *GC_malloc(size_t size);
24 void GC_free(void *ptr);
25 void GC_set_leak_handler(void (*)(void*, int));
26 void GC_gcollect(void);
27 extern int GC_find_leak;
29 /** Header block to track an allocated block's information. */
40 /** Overwrite \a len byte at \a p with 0xDEADBEEF.
41 * @param[out] p Memory buffer to overwrite.
42 * @param[in] len Number of bytes to overwrite.
45 memfrob(void *p, size_t len)
49 const char *pat = "\xde\xad\xbe\xef";
52 for (s = (char*)p, se = s + (len & ~3) - 4;
55 *(uint32_t*)s = *(uint32_t*)pat;
56 for (se = s; se < s; s++)
60 /** Total number of bytes allocated. */
61 static size_t mdbg_bytes_allocated = 0;
62 /** Total number of blocks allocated. */
63 static size_t mdbg_blocks_allocated = 0;
64 /** Last Unix time that we ran garbage collection. */
65 static time_t last_gcollect = 0;
66 /** Minimum interval for garbage collection. */
69 /** Check whether we should run garbage collection.
73 dbg_check_gcollect(void)
75 if (CurrentTime - last_gcollect < GC_FREQ)
78 last_gcollect = CurrentTime;
81 /** Allocate a block of memory.
82 * @param[in] size Number of bytes needed.
83 * @param[in] type Memory operation for block.
84 * @param[in] file File name of allocating function.
85 * @param[in] line Line number of allocating function.
86 * @return Pointer to the newly allocated block.
89 dbg_malloc(size_t size, const char *type, const char *file, int line)
91 struct MemHeader *mh = GC_malloc(size + sizeof(*mh));
94 memfrob((void*)(mh + 1), size);
95 mh->magic = 0xA110CA7E;
96 ircd_strncpy(mh->type, type, sizeof(mh->type) - 1)[sizeof(mh->type) - 1] = 0;
97 ircd_strncpy(mh->file, file, sizeof(mh->file) - 1)[sizeof(mh->file) - 1] = 0;
100 mh->since = CurrentTime;
101 mdbg_bytes_allocated += size;
102 mdbg_blocks_allocated++;
103 dbg_check_gcollect();
104 return (void*)(mh + 1);
107 /** Allocate a zero-initialized block of memory.
108 * @param[in] size Number of bytes needed.
109 * @param[in] type Memory operation for block.
110 * @param[in] file File name of allocating function.
111 * @param[in] line Line number of allocating function.
112 * @return Pointer to the newly allocated block.
115 dbg_malloc_zero(size_t size, const char *type, const char *file, int line)
117 struct MemHeader *mh = GC_malloc(size + sizeof(*mh));
120 memset((void*)(mh + 1), 0, size);
121 mh->magic = 0xA110CA7E;
122 ircd_strncpy(mh->type, type, sizeof(mh->type) - 1)[sizeof(mh->type) - 1] = 0;
123 ircd_strncpy(mh->file, file, sizeof(mh->file) - 1)[sizeof(mh->file) - 1] = 0;
126 mdbg_bytes_allocated += size;
127 mdbg_blocks_allocated++;
128 dbg_check_gcollect();
129 return (void*)(mh + 1);
132 /** Extend an allocated block of memory.
133 * @param[in] ptr Pointer to currently allocated block of memory (may be NULL).
134 * @param[in] size Minimum number of bytes for new block.
135 * @param[in] file File name of allocating function.
136 * @param[in] line Line number of allocating function.
137 * @return Pointer to the extended block of memory.
140 dbg_realloc(void *ptr, size_t size, const char *file, int line)
142 struct MemHeader *mh, *mh2;
144 return dbg_malloc(size, "realloc", file, line);
145 mh = (struct MemHeader*)ptr - 1;
146 assert(mh->magic == 0xA110CA7E);
147 if (mh->length >= size)
149 mh2 = dbg_malloc(size, "realloc", file, line);
152 dbg_free(mh+1, file, line);
155 memcpy(mh2+1, mh+1, mh->length);
156 dbg_free(mh+1, file, line);
157 return (void*)(mh2+1);
160 /** Deallocate a block of memory.
161 * @param[in] ptr Pointer to currently allocated block of memory (may be NULL).
162 * @param[in] file File name of deallocating function.
163 * @param[in] line Line number of deallocating function.
166 dbg_free(void *ptr, const char *file, int line)
168 struct MemHeader *mh = (struct MemHeader*)ptr - 1;
169 /* XXX but bison gives us NULLs */
172 assert(mh->magic == 0xA110CA7E);
173 /* XXX can we get boehmgc to check for references to it? */
174 memfrob(mh, mh->length + sizeof(*mh));
175 mdbg_bytes_allocated -= mh->length;
176 mdbg_blocks_allocated--;
178 dbg_check_gcollect();
181 /** Report number of bytes currently allocated.
182 * @return Number of bytes allocated.
185 fda_get_byte_count(void)
187 dbg_check_gcollect();
188 return mdbg_bytes_allocated;
191 /** Report number of blocks currently allocated.
192 * @return Number of blocks allocated.
195 fda_get_block_count(void)
197 return mdbg_blocks_allocated;
202 /** Callback for when the garbage collector detects a memory leak.
203 * @param[in] p Pointer to leaked memory.
204 * @param[in] sz Length of the block.
207 dbg_memory_leaked(void *p, int sz)
209 struct MemHeader *mh;
210 /* We have to return because the gc "leaks". */
212 if (mh->magic != 0xA110CA7E)
214 sendto_opmask_butone(NULL, SNO_OLDSNO,
215 "%s leak at %s:%u(%u bytes for %u seconds)",
216 mh->type, mh->file, mh->line, mh->length,
217 CurrentTime - mh->since);
219 "%s leak at %s:%u(%u bytes for %u seconds)",
220 mh->type, mh->file, mh->line, mh->length,
221 CurrentTime - mh->since));
224 /** Initialize the memory debugging subsystem. */
226 mem_dbg_initialise(void)
229 GC_set_leak_handler(dbg_memory_leaked);