Doxyfy memdebug.c.
[ircu2.10.12-pk.git] / ircd / memdebug.c
1 #include <sys/types.h>
2 #include "ircd.h"
3 #include "ircd_alloc.h"
4 #include "client.h"
5 #include "s_debug.h"
6 #include <stdlib.h>
7
8 #include <assert.h>
9
10 #ifdef MDEBUG
11
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
18  * a bit.
19  */
20
21 #include "ircd_string.h"
22
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;
28
29 /** Header block to track an allocated block's information. */
30 struct MemHeader
31 {
32   uint32_t magic;
33   char type[32];
34   char file[32];
35   int line;
36   size_t length;
37   time_t since;
38 };
39
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.
43  */
44 void
45 memfrob(void *p, size_t len)
46 {
47   /* deadbeef */
48   int i = 0;
49   const char *pat = "\xde\xad\xbe\xef";
50   char *s, *se;
51
52   for (s = (char*)p, se = s + (len & ~3) - 4;
53        s <= se;
54        s += 4)
55     *(uint32_t*)s = *(uint32_t*)pat;
56   for (se = s; se < s; s++)
57     *s = pat[i++];
58 }
59
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. */
67 #define GC_FREQ 5
68
69 /** Check whether we should run garbage collection.
70  * If so, do it.
71  */
72 void
73 dbg_check_gcollect(void)
74 {
75   if (CurrentTime - last_gcollect < GC_FREQ)
76     return;
77   GC_gcollect();
78   last_gcollect = CurrentTime;
79 }
80
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.
87  */
88 void*
89 dbg_malloc(size_t size, const char *type, const char *file, int line)
90 {
91   struct MemHeader *mh = GC_malloc(size + sizeof(*mh));
92   if (mh == NULL)
93     return 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;
98   mh->line = line;
99   mh->length = size;
100   mh->since = CurrentTime;
101   mdbg_bytes_allocated += size;
102   mdbg_blocks_allocated++;
103   dbg_check_gcollect();
104   return (void*)(mh + 1);
105 }
106
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.
113  */
114 void*
115 dbg_malloc_zero(size_t size, const char *type, const char *file, int line)
116 {
117   struct MemHeader *mh = GC_malloc(size + sizeof(*mh));
118   if (mh == NULL)
119     return 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;
124   mh->line = line;
125   mh->length = size;
126   mdbg_bytes_allocated += size;
127   mdbg_blocks_allocated++;
128   dbg_check_gcollect();
129   return (void*)(mh + 1);
130 }
131
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.
138  */
139 void*
140 dbg_realloc(void *ptr, size_t size, const char *file, int line)
141 {
142   struct MemHeader *mh, *mh2;
143   if (ptr == NULL)
144     return dbg_malloc(size, "realloc", file, line);
145   mh = (struct MemHeader*)ptr - 1;
146   assert(mh->magic == 0xA110CA7E);
147   if (mh->length >= size)
148     return mh;
149   mh2 = dbg_malloc(size, "realloc", file, line);
150   if (mh2 == NULL)
151   {
152     dbg_free(mh+1, file, line);
153     return NULL;
154   }
155   memcpy(mh2+1, mh+1, mh->length);
156   dbg_free(mh+1, file, line);
157   return (void*)(mh2+1);
158 }
159
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.
164  */
165 void
166 dbg_free(void *ptr, const char *file, int line)
167 {
168   struct MemHeader *mh = (struct MemHeader*)ptr - 1;
169   /* XXX but bison gives us NULLs */
170   if (ptr == NULL)
171     return;
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--;
177   GC_free(mh);
178   dbg_check_gcollect();
179 }
180
181 /** Report number of bytes currently allocated.
182  * @return Number of bytes allocated.
183  */
184 size_t
185 fda_get_byte_count(void)
186 {
187   dbg_check_gcollect();
188   return mdbg_bytes_allocated;
189 }
190
191 /** Report number of blocks currently allocated.
192  * @return Number of blocks allocated.
193  */
194 size_t
195 fda_get_block_count(void)
196 {
197   return mdbg_blocks_allocated;
198 }
199
200 #include <stdio.h>
201
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.
205  */
206 void
207 dbg_memory_leaked(void *p, int sz)
208 {
209   struct MemHeader *mh;
210   /* We have to return because the gc "leaks". */
211   mh = p;
212   if (mh->magic != 0xA110CA7E)
213     return;
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);
218   Debug((DEBUG_ERROR,
219          "%s leak at %s:%u(%u bytes for %u seconds)",
220          mh->type, mh->file, mh->line, mh->length,
221          CurrentTime - mh->since));
222 }
223
224 /** Initialize the memory debugging subsystem. */
225 void
226 mem_dbg_initialise(void)
227 {
228   GC_find_leak = 1;
229   GC_set_leak_handler(dbg_memory_leaked);
230 }
231
232 #endif