Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Avoid strict-aliasing related issues when compiling with optimization
[palacios.git] / linux_module / memcheck.c
1 /* 
2    Palacios memory allocation-checking framework
3    (c) Peter Dinda, 2013
4  */
5
6 #include <linux/kernel.h>
7 #include <linux/kthread.h>
8 #include <linux/spinlock.h>
9
10 #include "palacios.h"
11
12 #include "memcheck.h"
13
14 // Whether not all allocations and frees will be printed
15 #define SHOW_ALL_ALLOCS    0
16
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)
25
26 // How far up the stack to track the caller
27 // 0 => palacios_...
28 // 1 => v3_alloc...
29 // 2 => caller of v3_alloc..
30 // ... 
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)
34
35
36 typedef struct {
37   int  inuse;         // 1=inuse
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];
43                       // who allocated this
44 } memcheck_state_t;
45
46
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];
51
52 static void printmem(char *prefix, memcheck_state_t *m);
53
54
55 static memcheck_state_t *get_mem_entry(void)
56 {
57   int i;
58   unsigned long f;
59   memcheck_state_t *m;
60
61   palacios_spinlock_lock_irqsave(&lock,f);
62
63   for (i=0;i<NUM_ALLOCS;i++) { 
64     m=&(state[i]);
65     if (!(m->inuse)) { 
66       m->inuse=1;
67       break;
68     }
69   }
70
71   palacios_spinlock_unlock_irqrestore(&lock,f);
72   
73   if (i<NUM_ALLOCS) { 
74     return m;
75   } else {
76     return 0;
77   }
78 }
79
80
81 static memcheck_state_t *find_mem_entry(void *addr, unsigned long size, palacios_memcheck_memtype_t type)
82 {
83   int i;
84   memcheck_state_t *m;
85
86   for (i=0;i<NUM_ALLOCS;i++) { 
87     m=&(state[i]);
88     if (m->inuse && m->addr == addr && m->type==type) { 
89       if (size) {
90         if (m->size==size) { 
91           return m;
92         } else {
93           return 0;
94         }
95       } else {
96         return m;
97       }
98     }
99   }
100   return 0;
101 }
102
103
104 static void free_mem_entry(memcheck_state_t *m)
105 {
106   m->inuse=0;
107 }
108
109
110 void palacios_memcheck_init()
111 {
112   memset(state,0,sizeof(memcheck_state_t)*NUM_ALLOCS);
113   palacios_spinlock_init(&lock);
114   DEBUG("MEMCHECK: MEMORY CHECKING INITED\n");
115 }
116
117 //
118 // This needs to be defined explictly since the intrinsic does not take a var
119 //
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); 
125
126 //
127 // For printing a backtrace
128 //
129 //
130 #define backtrace_format "%pS << %pS << %pS << %pS"
131 #define backtrace_expand(t) ((t)[0]),((t)[1]),((t)[2]),((t)[3])
132
133
134 #if 0
135 static void clear_trace(void **trace)
136 {
137   int i;
138
139   for (i=0;i<STEP_BACK_DEPTH;i++) { 
140     trace[i]=0;
141   }
142 }
143 #endif
144
145 #define TYPE_TO_STR(type) ((type)==PALACIOS_KMALLOC ? "kmalloc" : \
146                            (type)==PALACIOS_VMALLOC ? "vmalloc" : \
147                            (type)==PALACIOS_PAGE_ALLOC ? "pagealloc" : "UNKNOWN")
148
149
150 static void printmem(char *prefix, memcheck_state_t *m)
151 {
152   if (!m || !(m->addr) ) { 
153     DEBUG("MEMCHECK: %s: memory state 0x%p BOGUS\n",prefix,m);
154     return;
155   }
156   if (m->addr) { 
157     DEBUG("MEMCHECK: %s: %s memory at 0x%p for %lu bytes allocator=" 
158           backtrace_format
159           "\n",
160           prefix,
161           TYPE_TO_STR(m->type),
162           m->addr,
163           m->size,
164           backtrace_expand(m->allocator));
165   }
166 }
167
168
169 void palacios_memcheck_deinit()
170 {
171   int i;
172   memcheck_state_t *m;
173   
174   for (i=0;i<NUM_ALLOCS;i++) { 
175     m=&(state[i]);
176     if (m->inuse) { 
177       printmem("ALLOCATED MEMORY AT DEINIT",m);
178     } 
179   }
180
181   palacios_spinlock_deinit(&lock);
182
183   INFO("MEMCHECK: DEINITED\n");
184
185   // Note that this function could garbage collect at this 
186   // point if we desired
187 }
188
189
190 void threshold(memcheck_state_t *m)
191 {
192 #if SHOW_THRESHOLD_WARNINGS
193   switch (m->type) {
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);
200     }
201     break;
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);
208     }
209     break;
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);
216     }
217     break;
218   default: 
219     break;
220   }
221 #endif
222 }
223
224 void find_overlaps(memcheck_state_t *m)
225 {
226   int i;
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);
236       }
237     }
238   }
239 }
240
241 void palacios_memcheck_alloc(void *addr, unsigned long size, palacios_memcheck_memtype_t type)
242 {
243   memcheck_state_t *m=get_mem_entry();
244
245   if (!m) { 
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);
248     return;
249   }
250   m->type=type;
251   m->addr=addr;
252   m->size=size;
253   backtrace(m->allocator);
254
255 #if SHOW_ALL_ALLOCS
256   printmem("ALLOCATE", m);
257 #endif
258
259   threshold(m);
260   find_overlaps(m);
261 }
262   
263 void palacios_memcheck_free(void *addr,unsigned long size, palacios_memcheck_memtype_t type)
264 {
265   memcheck_state_t *m=find_mem_entry(addr,0,type); // don't care about the size now
266   
267   if (!m){
268     DEBUG("MEMCHECK: FREEING UNTRACKED %s MEMORY AT 0x%p FOR %lu BYTES - stack trace follows\n",TYPE_TO_STR(type),addr,size);
269     dump_stack();
270     return;
271   }
272
273   if (m->type==PALACIOS_PAGE_ALLOC) { 
274     // need to verify sizes are identical
275     if (size!=m->size) {
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);
278     }
279   }
280
281 #if SHOW_ALL_ALLOCS
282   printmem("FREE",m);
283 #endif
284
285   free_mem_entry(m);
286 }
287