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