1 krisbash 1.1 #include <nits/base/nits.h>
2 #include "strings.h"
3 #include <assert.h>
4 #include <limits.h>
5 #include <stdio.h>
6
7 #if !defined(_MSC_VER)
8 # include <pthread.h>
9 #endif
10
11 #if defined(USE_ALLOCATOR)
12 # define ALLOC_MAGIC 0x89ABCDEF
13 # define ENABLE_FILLING
14 # define ENABLE_CLEARING
15
16 static AllocStats _stats;
17 static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
18
19 typedef struct _AllocHeader AllocHeader;
20
21 struct _AllocHeader
22 krisbash 1.1 {
23 struct _AllocHeader* prev;
24 struct _AllocHeader* next;
25 const char* file;
26 unsigned int line;
27 unsigned int id;
28 unsigned int magic;
29 unsigned int size;
30 };
31
32 static unsigned int _id;
33
34 static AllocHeader* _list;
35
36 typedef struct _AllocTrailer
37 {
38 unsigned int magic;
39 }
40 AllocTrailer;
41
42 static size_t _GetSize(void* ptr)
43 krisbash 1.1 {
44 AllocHeader* h = (AllocHeader*)ptr - 1;
45 AllocTrailer* t;
46
47 if (h->magic != ALLOC_MAGIC || h->size == 0)
48 return (size_t)-1;
49
50 t = (AllocTrailer*)((char*)ptr + h->size);
51
52 if (t->magic != ALLOC_MAGIC)
53 return (size_t)-1;
54
55 return (size_t)h->size;
56 }
57
58 AllocStats PAL_GetAllocStats()
59 {
60 AllocStats stats;
61 pthread_mutex_lock(&_mutex);
62 stats = _stats;
63 pthread_mutex_unlock(&_mutex);
64 krisbash 1.1 return stats;
65 }
66
67 size_t PAL_GetBlocksAllocated()
68 {
69 AllocStats stats;
70 pthread_mutex_lock(&_mutex);
71 stats = _stats;
72 pthread_mutex_unlock(&_mutex);
73 return (size_t)(stats.numAllocs - stats.numFrees);
74 }
75
76 void PAL_CheckBlocksAllocated(
77 size_t oldBlocksAllocated,
78 const char* file,
79 size_t line,
80 const char* function)
81 {
82 size_t blocksAllocated = PAL_GetBlocksAllocated();
83 size_t nblocks;
84
85 krisbash 1.1 if (blocksAllocated <= oldBlocksAllocated)
86 return;
87
88 nblocks = blocksAllocated - oldBlocksAllocated;
89
90 printf("\nWARNING: %s(%u): %s(): %u blocks were leaked\n\n",
91 file, (int)line, function, (int)nblocks);
92 }
93
94 void PAL_DumpAllocStats()
95 {
96 AllocStats stats = PAL_GetAllocStats();
97
98 printf("=== PAL_DumpAllocStats:\n");
99 printf("usage: %d\n", (int)stats.usage);
100 printf("peakUsage: %d\n", (int)stats.peakUsage);
101 printf("numAllocs: %d\n", (int)stats.numAllocs);
102 printf("numFrees: %d\n", (int)stats.numFrees);
103 printf("\n");
104 }
105
106 krisbash 1.1 void PAL_DumpAllocList()
107 {
108 AllocHeader* p;
109
110 if (!_list)
111 return;
112
113 printf("\nWARNING: one or more blocks still allocated!\n");
114
115 for (p = _list; p; p = p->next)
116 {
117 printf("BLOCK: %s(%u): ptr=%p: magic=%08X id=%u size=%u\n",
118 p->file, (int)p->line, p, p->magic, p->id, p->size);
119 }
120
121 printf("\n");
122 printf("\n");
123 }
124
125 static void* _Alloc(
126 const char* file,
127 krisbash 1.1 size_t line,
128 const char* func,
129 size_t size)
130 {
131 AllocHeader* h;
132 AllocTrailer* t;
133 void* p;
134
135 /* Be sure size will fit into h->size field */
136 assert(size <= UINT_MAX);
137
138 /* Return null if request size is zero */
139 if (size == 0)
140 {
141 assert(0);
142 return NULL;
143 }
144
145 /* Allocate the block of memory with room for header and trailer */
146 if (!(h = SystemMalloc(sizeof(AllocHeader) + size + sizeof(AllocTrailer))))
147 {
148 krisbash 1.1 assert(0);
149 return NULL;
150 }
151
152 /* Initialize the header */
153 h->prev = NULL;
154 h->next = NULL;
155 h->file = file;
156 h->line = line;
157 h->magic = ALLOC_MAGIC;
158 h->size = (unsigned int)size;
159
160 /* Initialize the user block (fill with 0xAA bytes) */
161 p = h + 1;
162 #if defined(ENABLE_FILLING)
163 memset(p, 0xAA, size);
164 #else
165 memset(p, 0x00, size);
166 #endif
167
168 /* Initialize the trailer */
169 krisbash 1.1 t = (AllocTrailer*)((char*)p + size);
170 t->magic = ALLOC_MAGIC;
171
172 /* Check that this function created a valid block */
173 assert(_GetSize(p) == size);
174
175 /* Update statistics and add block to glboal list */
176 pthread_mutex_lock(&_mutex);
177 {
178 _stats.usage += size;
179 _stats.numAllocs++;
180
181 if (_stats.usage > _stats.peakUsage)
182 _stats.peakUsage = _stats.usage;
183
184 /* Prepend block to list */
185 {
186 if (_list)
187 _list->prev = h;
188
189 h->prev = NULL;
190 krisbash 1.1 h->next = _list;
191 _list = h;
192 }
193
194 h->id = _id++;
195 }
196 pthread_mutex_unlock(&_mutex);
197
198 return p;
199 }
200
201 void* __PAL_Malloc(
202 const char* file,
203 size_t line,
204 const char* func,
205 size_t size)
206 {
207 #if defined(ENABLE_ALLOC_FAULT_INJECTION)
208 if (file &&
209 NitsShouldFault(NitsMakeCallSite(-1, func, file, line), NitsAutomatic))
210 return NULL;
211 krisbash 1.1 #endif
212
213 return _Alloc(file, line, func, size);
214 }
215
216 void* __PAL_MallocCallsite(
217 NitsCallSite callsite,
218 size_t size)
219 {
220 #if defined(ENABLE_ALLOC_FAULT_INJECTION)
221 if (NitsShouldFault(callsite, NitsAutomatic))
222 return NULL;
223 #endif
224
225 return _Alloc(callsite.file, callsite.line, callsite.function, size);
226 }
227
228 void* _Calloc(
229 const char* file,
230 size_t line,
231 const char* func,
232 krisbash 1.1 size_t count,
233 size_t size)
234 {
235 void* p;
236 size_t n = count * size;
237
238 if (n == 0)
239 return NULL;
240
241 if (!(p = _Alloc(file, line, func, n)))
242 return NULL;
243
244 return memset(p, 0x00, n);
245 }
246
247 void* __PAL_Calloc(
248 const char* file,
249 size_t line,
250 const char* func,
251 size_t count,
252 size_t size)
253 krisbash 1.1 {
254
255 #if defined(ENABLE_ALLOC_FAULT_INJECTION)
256 if (file &&
257 NitsShouldFault(NitsMakeCallSite(-1, func, file, line), NitsAutomatic))
258 return NULL;
259 #endif
260
261 return _Calloc( file, line, func, count, size);
262 }
263
264 void* __PAL_CallocCallsite(
265 NitsCallSite callsite,
266 size_t count,
267 size_t size)
268 {
269
270 #if defined(ENABLE_ALLOC_FAULT_INJECTION)
271 if (NitsShouldFault(callsite, NitsAutomatic))
272 return NULL;
273 #endif
274 krisbash 1.1
275 return _Calloc(callsite.file, callsite.line, callsite.function, count, size);
276 }
277
278
279 void* _Realloc(
280 const char* file,
281 size_t line,
282 const char* func,
283 void* ptr,
284 size_t size)
285 {
286 size_t n;
287 void* p;
288
289 /* Fail if request size is zero */
290 if (size == 0)
291 {
292 assert(0);
293 return NULL;
294 }
295 krisbash 1.1
296 /* If ptr is null, then just pass to _Alloc() */
297 if (!ptr)
298 return _Alloc(file, line, func, size);
299
300 /* Get the size of the old block (assert if invalid block) */
301 n = _GetSize(ptr);
302 assert(n != (size_t)-1);
303
304 /* Allocate the new block */
305 if (!(p = _Alloc(file, line, func, size)))
306 {
307 assert(0);
308 return NULL;
309 }
310
311 /* Copy over data from the old block */
312 if (n < size)
313 {
314 memcpy(p, ptr, n);
315
316 krisbash 1.1 /* Fill unused part with 0xAA bytes */
317 #if defined(ENABLE_FILLING)
318 memset((char*)p + n, 0xAA, size - n);
319 #else
320 memset((char*)p + n, 0x00, size - n);
321 #endif
322 }
323 else
324 memcpy(p, ptr, size);
325
326 /* Free the old block */
327 __PAL_Free(file, line, func, ptr);
328
329 return p;
330 }
331
332 void* __PAL_Realloc(
333 const char* file,
334 size_t line,
335 const char* func,
336 void* ptr,
337 krisbash 1.1 size_t size)
338 {
339
340 #if defined(ENABLE_ALLOC_FAULT_INJECTION)
341 if (file &&
342 NitsShouldFault(NitsMakeCallSite(-1, func, file, line), NitsAutomatic))
343 return NULL;
344 #endif
345
346 return _Realloc(file, line, func, ptr, size);
347 }
348
349
350 void* __PAL_ReallocCallsite(
351 NitsCallSite callsite,
352 void* ptr,
353 size_t size)
354 {
355 #if defined(ENABLE_ALLOC_FAULT_INJECTION)
356 if (NitsShouldFault(callsite, NitsAutomatic))
357 return NULL;
358 krisbash 1.1 #endif
359
360 return _Realloc(callsite.file, callsite.line, callsite.function, ptr, size);
361 }
362
363 void __PAL_Free(
364 const char* file,
365 size_t line,
366 const char* func,
367 void* ptr)
368 {
369 if (ptr)
370 {
371 size_t n;
372 AllocHeader* h;
373
374 /* Get size of this block */
375 n = _GetSize(ptr);
376 assert(n != (size_t)-1);
377
378 /* Fill the block with 0xDD bytes */
379 krisbash 1.1 #if defined(ENABLE_CLEARING)
380 memset(ptr, 0xDD, n);
381 #endif
382
383 /* Get pointer to header */
384 h = (AllocHeader*)ptr - 1;
385
386 /* Update statistics and remove block from list */
387 pthread_mutex_lock(&_mutex);
388 {
389 _stats.usage -= n;
390 _stats.numFrees++;
391
392 /* Remove block from the list */
393 if (h == _list)
394 {
395 _list = h->next;
396
397 if (_list)
398 _list->prev = NULL;
399 }
400 krisbash 1.1 else
401 {
402 if (h->prev)
403 h->prev->next = h->next;
404 if (h->next)
405 h->next->prev = h->prev;
406 }
407
408 }
409 pthread_mutex_unlock(&_mutex);
410
411 /* Free the block */
412 SystemFree(h);
413 }
414 }
415
416 char* __PAL_Strdup(
417 const char* file,
418 size_t line,
419 const char* func,
420 const char* str)
421 krisbash 1.1 {
422 char* p;
423 size_t n;
424
425 assert(str != NULL);
426
427 n = (strlen(str) + 1) * sizeof(char);
428
429 if (!(p = _Alloc(file, line, func, n)))
430 return NULL;
431
432 return memcpy(p, str, n);
433 }
434
435 wchar_t* __PAL_Wcsdup(
436 const char* file,
437 size_t line,
438 const char* func,
439 const wchar_t* str)
440 {
441 wchar_t* p;
442 krisbash 1.1 size_t n;
443
444 assert(str != NULL);
445
446 n = (wcslen(str) + 1) * sizeof(wchar_t);
447
448 if (!(p = _Alloc(file, line, func, n)))
449 return NULL;
450
451 return memcpy(p, str, n);
452 }
453
454 PAL_Char* __PAL_Tcsdup(
455 const char* file,
456 size_t line,
457 const char* func,
458 const PAL_Char* str)
459 {
460 PAL_Char* p;
461 size_t n;
462
463 krisbash 1.1 assert(str != NULL);
464
465 n = (Tcslen(str) + 1) * sizeof(PAL_Char);
466
467 if (!(p = _Alloc(file, line, func, n)))
468 return NULL;
469
470 return memcpy(p, str, n);
471 }
472
473 #endif /* defined(USE_ALLOCATOR) */
|