2 Palacios memory allocation-checking framework
6 #include <linux/kernel.h>
7 #include <linux/kthread.h>
8 #include <linux/spinlock.h>
14 // Whether not all allocations and frees will be printed
15 #define SHOW_ALL_ALLOCS 0
17 // Whether or now allocations outside of a size range will be printed
18 #define SHOW_THRESHOLD_WARNINGS 1
19 #define SMALL_KMALLOC_THRESHOLD 9
20 #define BIG_KMALLOC_THRESHOLD ((1024*16)-1)
21 #define SMALL_VMALLOC_THRESHOLD 9
22 #define BIG_VMALLOC_THRESHOLD ((1024*1024)-1)
23 #define SMALL_PAGE_ALLOC_THRESHOLD (4096-1)
24 #define BIG_PAGE_ALLOC_THRESHOLD (4096*8-1)
26 // How far up the stack to track the caller
29 // 2 => caller of v3_alloc..
31 #define STEP_BACK_DEPTH_FIRST 1
32 #define STEP_BACK_DEPTH_LAST 4
33 #define STEP_BACK_DEPTH (STEP_BACK_DEPTH_LAST-STEP_BACK_DEPTH_FIRST+1)
38 palacios_memcheck_memtype_t type;
39 // PALACIOS_KMALLOC,VMALLOC,PAGE_ALLOC
40 void *addr; // the allocated block's address
41 unsigned long size; // the allocated block's size
42 void *allocator[STEP_BACK_DEPTH];
47 // This lock is currently used only to control
48 // allocation of entries in the global state
49 static spinlock_t lock;
50 static memcheck_state_t state[NUM_ALLOCS];
52 static void printmem(char *prefix, memcheck_state_t *m);
55 static memcheck_state_t *get_mem_entry(void)
61 palacios_spinlock_lock_irqsave(&lock,f);
63 for (i=0;i<NUM_ALLOCS;i++) {
71 palacios_spinlock_unlock_irqrestore(&lock,f);
81 static memcheck_state_t *find_mem_entry(void *addr, unsigned long size, palacios_memcheck_memtype_t type)
86 for (i=0;i<NUM_ALLOCS;i++) {
88 if (m->inuse && m->addr == addr && m->type==type) {
104 static void free_mem_entry(memcheck_state_t *m)
110 void palacios_memcheck_init()
112 memset(state,0,sizeof(memcheck_state_t)*NUM_ALLOCS);
113 palacios_spinlock_init(&lock);
114 DEBUG("MEMCHECK: MEMORY CHECKING INITED\n");
118 // This needs to be defined explictly since the intrinsic does not take a var
120 #define backtrace(t) \
121 t[0]=__builtin_return_address(STEP_BACK_DEPTH_FIRST); \
122 t[1]=__builtin_return_address(STEP_BACK_DEPTH_FIRST+1); \
123 t[2]=__builtin_return_address(STEP_BACK_DEPTH_FIRST+2); \
124 t[3]=__builtin_return_address(STEP_BACK_DEPTH_FIRST+3);
127 // For printing a backtrace
130 #define backtrace_format "%pS << %pS << %pS << %pS"
131 #define backtrace_expand(t) ((t)[0]),((t)[1]),((t)[2]),((t)[3])
135 static void clear_trace(void **trace)
139 for (i=0;i<STEP_BACK_DEPTH;i++) {
145 #define TYPE_TO_STR(type) ((type)==PALACIOS_KMALLOC ? "kmalloc" : \
146 (type)==PALACIOS_VMALLOC ? "vmalloc" : \
147 (type)==PALACIOS_PAGE_ALLOC ? "pagealloc" : "UNKNOWN")
150 static void printmem(char *prefix, memcheck_state_t *m)
152 if (!m || !(m->addr) ) {
153 DEBUG("MEMCHECK: %s: memory state 0x%p BOGUS\n",prefix,m);
157 DEBUG("MEMCHECK: %s: %s memory at 0x%p for %lu bytes allocator="
161 TYPE_TO_STR(m->type),
164 backtrace_expand(m->allocator));
169 void palacios_memcheck_deinit()
174 for (i=0;i<NUM_ALLOCS;i++) {
177 printmem("ALLOCATED MEMORY AT DEINIT",m);
181 palacios_spinlock_deinit(&lock);
183 INFO("MEMCHECK: DEINITED\n");
185 // Note that this function could garbage collect at this
186 // point if we desired
190 void threshold(memcheck_state_t *m)
192 #if SHOW_THRESHOLD_WARNINGS
194 case PALACIOS_KMALLOC:
195 if (m->size < SMALL_KMALLOC_THRESHOLD ||
196 m->size > BIG_KMALLOC_THRESHOLD) {
197 DEBUG("MEMCHECK: ALLOCATION EXCEEDS THRESHOLDS of %u and %u\n",
198 SMALL_KMALLOC_THRESHOLD, BIG_KMALLOC_THRESHOLD);
199 printmem("ALLOCATION EXCEEDS",m);
202 case PALACIOS_VMALLOC:
203 if (m->size < SMALL_VMALLOC_THRESHOLD ||
204 m->size > BIG_VMALLOC_THRESHOLD) {
205 DEBUG("MEMCHECK: ALLOCATION EXCEEDS THRESHOLDS of %u and %u\n",
206 SMALL_VMALLOC_THRESHOLD, BIG_VMALLOC_THRESHOLD);
207 printmem("ALLOCATION EXCEEDS",m);
210 case PALACIOS_PAGE_ALLOC:
211 if (m->size < SMALL_PAGE_ALLOC_THRESHOLD ||
212 m->size > BIG_PAGE_ALLOC_THRESHOLD) {
213 DEBUG("MEMCHECK: ALLOCATION EXCEEDS THRESHOLDS of %u and %u\n",
214 SMALL_PAGE_ALLOC_THRESHOLD, BIG_PAGE_ALLOC_THRESHOLD);
215 printmem("ALLOCATION EXCEEDS",m);
224 void find_overlaps(memcheck_state_t *m)
227 for (i=0;i<NUM_ALLOCS;i++) {
228 memcheck_state_t *s = &(state[i]);
229 if (s->inuse && s!=m && s->type==m->type) {
230 if (((m->addr >= s->addr) && (m->addr < (s->addr+s->size))) ||
231 (((m->addr+m->size-1) >= s->addr) && ((m->addr+m->size-1) < (s->addr+s->size))) ||
232 ((m->addr < s->addr) && (m->addr+m->size-1)>=(s->addr+s->size))) {
233 DEBUG("MEMCHECK: OVERLAP DETECTED\n");
234 printmem("OVERLAP (0)",s);
235 printmem("OVERLAP (1)",s);
241 void palacios_memcheck_alloc(void *addr, unsigned long size, palacios_memcheck_memtype_t type)
243 memcheck_state_t *m=get_mem_entry();
246 DEBUG("MEMCHECK: UNABLE TO ALLOCATE TRACKING DATA FOR %s ALLOC AT 0x%p FOR %lu BYTES\n",
247 TYPE_TO_STR(type),addr,size);
253 backtrace(m->allocator);
256 printmem("ALLOCATE", m);
263 void palacios_memcheck_free(void *addr,unsigned long size, palacios_memcheck_memtype_t type)
265 memcheck_state_t *m=find_mem_entry(addr,0,type); // don't care about the size now
268 DEBUG("MEMCHECK: FREEING UNTRACKED %s MEMORY AT 0x%p FOR %lu BYTES - stack trace follows\n",TYPE_TO_STR(type),addr,size);
273 if (m->type==PALACIOS_PAGE_ALLOC) {
274 // need to verify sizes are identical
276 DEBUG("MEMCHECK: FREEING %s MEMORY AT 0x%p FOR %lu bytes, BUT MATCHING ENTRY HAS %lu BYTES\n",TYPE_TO_STR(type),addr,size,m->size);
277 printmem("MATCHING ENTRY",m);