Fixes to improve portability (especially to OS X, Solaris, OpenBSD).
[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 struct MemHeader
30 {
31   uint32_t magic;
32   char type[32];
33   char file[32];
34   int line;
35   size_t length;
36   time_t since;
37 };
38
39 void
40 memfrob(void *p, size_t len)
41 {
42   /* deadbeef */
43   int i = 0;
44   const char *pat = "\xde\xad\xbe\xef";
45   char *s, *se;
46
47   for (s = (char*)p, se = s + (len & ~3) - 4;
48        s <= se;
49        s += 4)
50     *(uint32_t*)s = *(uint32_t*)pat;
51   for (se = s; se < s; s++)
52     *s = pat[i++];
53 }
54
55 static size_t mdbg_bytes_allocated = 0, mdbg_blocks_allocated = 0;
56 static time_t last_gcollect = 0;
57
58 #define GC_FREQ 5
59
60 void
61 dbg_check_gcollect(void)
62 {
63   if (CurrentTime - last_gcollect < GC_FREQ)
64     return;
65   GC_gcollect();
66   last_gcollect = CurrentTime;
67 }
68
69 void*
70 dbg_malloc(size_t size, const char *type, const char *file, int line)
71 {
72   struct MemHeader *mh = GC_malloc(size + sizeof(*mh));
73   if (mh == NULL)
74     return mh;
75   memfrob((void*)(mh + 1), size);
76   mh->magic = 0xA110CA7E;
77   ircd_strncpy(mh->type, type, sizeof(mh->type) - 1)[sizeof(mh->type) - 1] = 0;
78   ircd_strncpy(mh->file, file, sizeof(mh->file) - 1)[sizeof(mh->file) - 1] = 0;
79   mh->line = line;
80   mh->length = size;
81   mh->since = CurrentTime;
82   mdbg_bytes_allocated += size;
83   mdbg_blocks_allocated++;
84   dbg_check_gcollect();
85   return (void*)(mh + 1);
86 }
87
88 void*
89 dbg_malloc_zero(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   memset((void*)(mh + 1), 0, 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   mdbg_bytes_allocated += size;
101   mdbg_blocks_allocated++;
102   dbg_check_gcollect();
103   return (void*)(mh + 1);
104 }
105
106 void*
107 dbg_realloc(void *ptr, size_t size, const char *file, int line)
108 {
109   struct MemHeader *mh, *mh2;
110   if (ptr == NULL)
111     return dbg_malloc(size, "realloc", file, line);
112   mh = (struct MemHeader*)ptr - 1;
113   assert(mh->magic == 0xA110CA7E);
114   if (mh->length >= size)
115     return mh;
116   mh2 = dbg_malloc(size, "realloc", file, line);
117   if (mh2 == NULL)
118   {
119     dbg_free(mh+1, file, line);
120     return NULL;
121   }
122   memcpy(mh2+1, mh+1, mh->length);
123   dbg_free(mh+1, file, line);
124   return (void*)(mh2+1);
125 }
126
127 void
128 dbg_free(void *ptr, const char *file, int line)
129 {
130   struct MemHeader *mh = (struct MemHeader*)ptr - 1;
131   /* XXX but bison gives us NULLs */
132   if (ptr == NULL)
133     return;
134   assert(mh->magic == 0xA110CA7E);
135   /* XXX can we get boehmgc to check for references to it? */
136   memfrob(mh, mh->length + sizeof(*mh));
137   mdbg_bytes_allocated -= mh->length;
138   mdbg_blocks_allocated--;
139   GC_free(mh);
140   dbg_check_gcollect();
141 }
142
143 size_t
144 fda_get_byte_count(void)
145 {
146   dbg_check_gcollect();
147   return mdbg_bytes_allocated;
148 }
149
150 size_t
151 fda_get_block_count(void)
152 {
153   return mdbg_blocks_allocated;
154 }
155
156 #include <stdio.h>
157
158 void
159 dbg_memory_leaked(void *p, int sz)
160 {
161   struct MemHeader *mh;
162   /* We have to return because the gc "leaks". */
163   mh = p;
164   if (mh->magic != 0xA110CA7E)
165     return;
166   sendto_opmask_butone(NULL, SNO_OLDSNO,
167                        "%s leak at %s:%u(%u bytes for %u seconds)",
168                        mh->type, mh->file, mh->line, mh->length,
169                        CurrentTime - mh->since);
170   Debug((DEBUG_ERROR,
171          "%s leak at %s:%u(%u bytes for %u seconds)",
172          mh->type, mh->file, mh->line, mh->length,
173          CurrentTime - mh->since));
174 }
175
176 void
177 mem_dbg_initialise(void)
178 {
179   GC_find_leak = 1;
180   GC_set_leak_handler(dbg_memory_leaked);
181 }
182
183 #endif