Route ampif77 and ampif90 through ampiCC, factoring out duplicated code
[charm.git] / src / conv-core / memory-charmdebug.c
blob8950cb8bfcea38fb09d848b89d984cbd44c7cc98
1 /*
2 * Filippo's charm debug memory module, gioachin@uiuc.edu, 2005/10
3 * based on Orion's memory-leak.c
5 * This special version of malloc() and company is meant to be used in
6 * conjunction with the parallel debugger CharmDebug.
8 * Functionalities provided:
9 * - detect multiple delete on a pointer
10 * - stacktrace for all memory allocated
11 * - division of allocated memory among different types of allocations
12 * - sweep of the memory searching for leaks
15 #if ! CMK_MEMORY_BUILD_OS
16 /* Use Gnumalloc as meta-meta malloc fallbacks (mm_*) */
17 #include "memory-gnu.c"
18 #endif
19 #include "tracec.h"
20 #include <sys/mman.h>
22 /* Utilities needed by the code */
23 #include "ckhashtable.h"
25 /*#include "pup_c.h" */
27 #include "crc32.h"
29 #if ! CMK_CHARMDEBUG
30 #error "charmdebug is not enabled (e.g. when building with-production)"
31 static void *meta_malloc(size_t size);
32 static void *meta_calloc(size_t nelem, size_t size);
33 static void *meta_realloc(void *oldBuffer, size_t newSize);
34 static void *meta_memalign(size_t align, size_t size);
35 static int meta_posix_memalign(void **outptr, size_t align, size_t size);
36 static void *meta_aligned_alloc(size_t align, size_t size);
37 static void *meta_valloc(size_t size);
38 static void *meta_pvalloc(size_t size);
39 #else
41 void CmiBacktraceRecordHuge(void **retPtrs,int *nLevels);
43 typedef struct _Slot Slot;
44 typedef struct _SlotStack SlotStack;
46 int nextChareID;
47 extern int memory_chare_id;
49 int memory_charmdebug_internal = 0;
51 /**
52 * Struct Slot contains all of the information about a malloc buffer except
53 * for the contents of its memory.
55 struct _Slot {
56 #ifdef CMK_SEPARATE_SLOT
57 char *userData;
58 #else
59 /*Doubly-linked allocated block list*/
60 Slot *next;
61 Slot *prev;
62 #endif
64 /*The number of bytes of user data*/
65 int userSize;
67 #define FLAGS_MASK 0xFF
68 #define BLOCK_PROTECTED 0x80
69 #define MODIFIED 0x40
70 #define NEW_BLOCK 0x20
71 #define LEAK_CLEAN 0x10
72 #define LEAK_FLAG 0x8
73 #define UNKNOWN_TYPE 0x0
74 #define SYSTEM_TYPE 0x1
75 #define USER_TYPE 0x2
76 #define CHARE_TYPE 0x3
77 #define MESSAGE_TYPE 0x4
78 /* A magic number field, to verify this is an actual malloc'd buffer, and what
79 type of allocation it is. The last 4 bits of the magic number are used to
80 define a classification of mallocs. */
81 #define SLOTMAGIC 0x8402a500
82 #define SLOTMAGIC_VALLOC 0x7402a500
83 #define SLOTMAGIC_FREED 0xDEADBEEF
84 int magic;
86 int chareID;
87 /* Controls the number of stack frames to print out. */
88 int stackLen;
89 void **from;
91 /* Pointer to extra stacktrace, when the user requested more trace */
92 SlotStack *extraStack;
94 /* CRC32 checksums */
95 CmiUInt8 slotCRC;
96 CmiUInt8 userCRC;
99 struct _SlotStack {
100 char *protectedMemory;
101 int protectedMemoryLength;
102 /* empty for the moment, to be filled when needed */
105 /********* List of allocated memory *********/
107 /* First memory slot */
108 #ifdef CMK_SEPARATE_SLOT
109 CkHashtable_c block_slots = NULL;
110 #else
111 Slot slot_first_storage = {&slot_first_storage, &slot_first_storage};
112 Slot *slot_first = &slot_first_storage;
113 #endif
115 int memory_allocated_user_total;
116 int get_memory_allocated_user_total() {return memory_allocated_user_total;}
118 void *lastMemoryAllocated = NULL;
119 Slot **allocatedSince = NULL;
120 int allocatedSinceSize = 0;
121 int allocatedSinceMaxSize = 0;
122 int saveAllocationHistory = 0;
124 /* Convert a slot to a user address */
125 static char *SlotToUser(Slot *s) {
126 #ifdef CMK_SEPARATE_SLOT
127 return s->userData;
128 #else
129 return ((char *)s)+sizeof(Slot);
130 #endif
134 /* Convert a user address to a slot. The parameter "user" must be at the
135 * beginning of the allocated block */
136 static Slot *UserToSlot(void *user) {
137 #ifdef CMK_SEPARATE_SLOT
138 return (Slot *)CkHashtableGet(block_slots, &user);
139 #else
140 char *cu=(char *)user;
141 Slot *s=(Slot *)(cu-sizeof(Slot));
142 return s;
143 #endif
146 static int isLeakSlot(Slot *s) {
147 return s->magic & LEAK_FLAG;
150 static int isProtected(Slot *s) {
151 return s->magic & BLOCK_PROTECTED;
154 CMI_EXTERNC
155 int Slot_ChareOwner(void *s) {
156 return ((Slot*)s)->chareID;
159 int Slot_AllocatedSize(void *s) {
160 return ((Slot*)s)->userSize;
163 CMI_EXTERNC
164 int Slot_StackTrace(void *s, void ***stack) {
165 *stack = ((Slot*)s)->from;
166 return ((Slot*)s)->stackLen;
169 static void printSlot(Slot *s) {
170 CmiPrintf("[%d] Leaked block of %d bytes at %p:\n",
171 CmiMyPe(), s->userSize, (void *)SlotToUser(s));
172 CmiBacktracePrint(s->from,s->stackLen);
175 /********* Cpd routines for pupping data to the debugger *********/
177 /** Returns the number of total blocks of memory allocated */
178 CMI_EXTERNC
179 size_t cpd_memory_length(void *lenParam) {
180 size_t n=0;
181 #ifdef CMK_SEPARATE_SLOT
182 n = CkHashtableSize(block_slots) - 1;
183 #else
184 Slot *cur = slot_first->next;
185 while (cur != slot_first) {
186 n++;
187 cur = cur->next;
189 #endif
190 return n;
193 /** PUP a single slot of memory for the debugger. This includes the information
194 * about the slot (like size and location), but not the allocated data itself. */
195 #ifdef CMK_SEPARATE_SLOT
196 void cpd_memory_single_pup(CkHashtable_c h, pup_er p) {
197 CkHashtableIterator_c hashiter;
198 void *key;
199 Slot *cur;
200 //int counter=0;
202 memory_charmdebug_internal = 1;
203 hashiter = CkHashtableGetIterator(h);
204 while ((cur = (Slot *)CkHashtableIteratorNext(hashiter, &key)) != NULL) {
205 if (pup_isPacking(p) && cur->userData == lastMemoryAllocated) continue;
206 //counter++;
207 #else
208 void cpd_memory_single_pup(Slot* list, pup_er p) {
209 Slot *cur = list->next;
210 /* Stupid hack to avoid sending the memory we just allocated for this packing,
211 otherwise the lengths will mismatch */
212 if (pup_isPacking(p)) cur = cur->next;
213 for ( ; cur != list; cur = cur->next) {
214 #endif
216 int i;
217 int flags;
218 void *loc = SlotToUser(cur);
219 CpdListBeginItem(p, 0);
220 pup_comment(p, "loc");
221 pup_pointer(p, &loc);
222 pup_comment(p, "size");
223 pup_int(p, &cur->userSize);
224 pup_comment(p, "flags");
225 flags = cur->magic & FLAGS_MASK;
226 pup_int(p, &flags);
227 pup_comment(p, "chare");
228 pup_int(p, &cur->chareID);
229 pup_comment(p, "stack");
230 //for (i=0; i<STACK_LEN; ++i) {
231 // if (cur->from[i] <= 0) break;
232 // if (cur->from[i] > 0) pup_uint(p, (unsigned int*)&cur->from[i]);
233 // else break;
235 if (cur->from != NULL)
236 pup_pointers(p, cur->from, cur->stackLen);
237 else {
238 void *myNULL = NULL;
239 printf("Block %p has no stack!\n",(void *)cur);
240 pup_pointer(p, &myNULL);
244 /*CmiPrintf("counter=%d\n",counter);*/
245 memory_charmdebug_internal = 0;
248 /** PUP the entire information about the allocated memory to the debugger */
249 CMI_EXTERNC
250 void cpd_memory_pup(void *itemParam, pup_er p, CpdListItemsRequest *req) {
251 static char s_memory[] = "memory";
252 CpdListBeginItem(p, 0);
253 pup_comment(p, "name");
254 pup_chars(p, s_memory, sizeof(s_memory)-1);
255 pup_comment(p, "slots");
256 pup_syncComment(p, pup_sync_begin_array, 0);
257 #ifdef CMK_SEPARATE_SLOT
258 cpd_memory_single_pup(block_slots, p);
259 #else
260 cpd_memory_single_pup(slot_first, p);
261 #endif
262 pup_syncComment(p, pup_sync_end_array, 0);
266 CMI_EXTERNC
267 void check_memory_leaks(CpdListItemsRequest *);
268 CMI_EXTERNC
269 void cpd_memory_leak(void *iterParam, pup_er p, CpdListItemsRequest *req) {
270 if (pup_isSizing(p)) {
271 // let's perform the memory leak checking. This is the first step in the
272 // packing, where we size, in the second step we pack and we avoid doing
273 // this check again.
274 check_memory_leaks(req);
276 cpd_memory_pup(iterParam, p, req);
280 CMI_EXTERNC
281 size_t cpd_memory_getLength(void *lenParam) { return 1; }
282 /** Returns the content of a block of memory (i.e the user data).
283 This request must always be at the beginning of an allocated block
284 (not for example an object part of an array) */
285 CMI_EXTERNC
286 void cpd_memory_get(void *iterParam, pup_er p, CpdListItemsRequest *req) {
287 void *userData = (void*)(uintptr_t)(((uint64_t)req->lo) + (((uint64_t)req->hi)<<32));
288 Slot *sl = UserToSlot(userData);
289 CpdListBeginItem(p, 0);
290 pup_comment(p, "size");
291 //printf("value: %x %x %x %d\n",sl->magic, sl->magic&~FLAGS_MASK, SLOTMAGIC, ((sl->magic&~FLAGS_MASK) != SLOTMAGIC));
292 if ((sl->magic&~FLAGS_MASK) != SLOTMAGIC) {
293 int zero = 0;
294 pup_int(p, &zero);
295 } else {
296 pup_int(p, &sl->userSize);
297 pup_comment(p, "value");
298 pup_bytes(p, userData, sl->userSize);
302 /********* Heap Checking ***********/
304 int charmEnvelopeSize = 0;
306 #include "pcqueue.h"
308 #ifdef CMK_SEPARATE_SLOT
309 #define SLOTSPACE 0
310 #define SLOT_ITERATE_START(scanner) \
312 CkHashtableIterator_c hashiter = CkHashtableGetIterator(block_slots); \
313 void *key; \
314 while ((scanner = (Slot *)CkHashtableIteratorNext(hashiter, &key)) != NULL) {
315 #define SLOT_ITERATE_END \
317 CkHashtableDestroyIterator(hashiter); \
319 #else
320 #define SLOTSPACE sizeof(Slot)
321 #define SLOT_ITERATE_START(scanner) \
322 for (scanner=slot_first->next; scanner!=slot_first; scanner=scanner->next) {
323 #define SLOT_ITERATE_END }
324 #endif
326 /** Perform a scan of all the memory to find all the memory that is reacheable
327 * from either the stack or the global variables. */
328 // FIXME: this function assumes that all memory is allocated in slot_unknown!
329 CMI_EXTERNC
330 void check_memory_leaks(LeakSearchInfo *info) {
331 //FILE* fd=fopen("check_memory_leaks", "w");
332 // Step 1)
333 // index all memory into a CkHashtable, with a scan of 4 bytes.
334 CkHashtable_c table;
335 PCQueue inProgress;
336 Slot *sl, **fnd, *found;
337 char *scanner;
338 char *begin_stack, *end_stack;
339 //char *begin_data, *end_data;
340 //char *begin_bss, *end_bss;
341 int growing_dimension = 0;
343 // copy all the memory from "slot_first" to "leaking"
345 Slot *slold1=0, *slold2=0, *slold3=0;
347 memory_charmdebug_internal = 1;
349 inProgress = PCQueueCreate();
350 table = CkCreateHashtable_pointer(sizeof(char *), 10000);
351 SLOT_ITERATE_START(sl)
352 // index the i-th memory slot
353 //printf("hashing slot %p\n",sl);
354 char *ptr;
355 sl->magic |= LEAK_FLAG;
356 if (info->quick > 0) {
357 char **object;
358 //CmiPrintf("checking memory fast\n");
359 // means index only specific offsets of the memory region
360 ptr = SlotToUser(sl);
361 object = (char**)CkHashtablePut(table, &ptr);
362 *object = (char*)sl;
363 ptr += 4;
364 object = (char**)CkHashtablePut(table, &ptr);
365 *object = (char*)sl;
366 // beginning of converse header
367 ptr += sizeof(CmiChunkHeader) - 4;
368 if (ptr < SlotToUser(sl)+sizeof(Slot)+sl->userSize) {
369 object = (char**)CkHashtablePut(table, &ptr);
370 *object = (char*)sl;
372 // beginning of charm header
373 ptr += CmiReservedHeaderSize;
374 if (ptr < SlotToUser(sl)+sizeof(Slot)+sl->userSize) {
375 object = (char**)CkHashtablePut(table, &ptr);
376 *object = (char*)sl;
378 // beginning of ampi header
379 ptr += charmEnvelopeSize - CmiReservedHeaderSize;
380 if (ptr < SlotToUser(sl)+sizeof(Slot)+sl->userSize) {
381 object = (char**)CkHashtablePut(table, &ptr);
382 *object = (char*)sl;
384 } else {
385 //CmiPrintf("checking memory extensively\n");
386 // means index every fourth byte of the memory region
387 for (ptr = SlotToUser(sl); ptr <= SlotToUser(sl)+sl->userSize; ptr+=sizeof(char*)) {
388 //printf("memory %p\n",ptr);
389 //growing_dimension++;
390 //if ((growing_dimension&0xFF) == 0) printf("inserted %d objects\n",growing_dimension);
391 char **object = (char**)CkHashtablePut(table, &ptr);
392 *object = (char*)sl;
395 slold3 = slold2;
396 slold2 = slold1;
397 slold1 = sl;
398 SLOT_ITERATE_END
400 // Step 2)
401 // start the check with the stack and the global data. The stack is found
402 // through the current pointer, going up until 16 bits filling (considering
403 // the stack grows toward decreasing addresses). The pointers to the global
404 // data (segments .data and .bss) are passed in with "req" as the "extra"
405 // field, with the structure "begin .data", "end .data", "begin .bss", "end .bss".
406 begin_stack = (char*)&table;
407 end_stack = (char*)memory_stack_top;
408 /*if (req->extraLen != 4*4 / *sizeof(char*) FIXME: assumes 64 bit addresses of .data and .bss are small enough * /) {
409 CmiPrintf("requested for a memory leak check with wrong information! %d bytes\n",req->extraLen);
411 /*if (sizeof(char*) == 4) {
412 / * 32 bit addresses; for 64 bit machines it assumes the following addresses were small enough * /
413 begin_data = (char*)ntohl(((int*)(req->extra))[0]);
414 end_data = (char*)ntohl(((int*)(req->extra))[1]) - sizeof(char*) + 1;
415 begin_bss = (char*)ntohl(((int*)(req->extra))[2]);
416 end_bss = (char*)ntohl(((int*)(req->extra))[3]) - sizeof(char*) + 1;
417 / *} else {
418 CmiAbort("not ready yet");
419 begin_data = ntohl(((char**)(req->extra))[0]);
420 end_data = ntohl(((char**)(req->extra))[1]) - sizeof(char*) + 1;
421 begin_bss = ntohl(((char**)(req->extra))[2]);
422 end_bss = ntohl(((char**)(req->extra))[3]) - sizeof(char*) + 1;
424 printf("scanning stack from %p to %p\n", (void *)begin_stack, (void *)end_stack);
425 for (scanner = begin_stack; scanner < end_stack; scanner+=sizeof(char*)) {
426 fnd = (Slot**)CkHashtableGet(table, scanner);
427 //if (fnd != NULL) printf("scanning stack %p, %d\n",*fnd,isLeakSlot(*fnd));
428 if (fnd != NULL && isLeakSlot(*fnd)) {
429 found = *fnd;
430 /* mark slot as not leak */
431 //printf("stack pointing to %p\n",found+1);
432 found->magic &= ~LEAK_FLAG;
433 /* move the slot into inProgress */
434 PCQueuePush(inProgress, (char*)found);
437 printf("scanning data from %p to %p\n", (void *)info->begin_data, (void *)info->end_data);
438 for (scanner = info->begin_data; scanner < info->end_data; scanner+=sizeof(char*)) {
439 //fprintf(fd, "scanner = %p\n",scanner);
440 //fflush(fd);
441 fnd = (Slot**)CkHashtableGet(table, scanner);
442 //if (fnd != NULL) printf("scanning data %p, %d\n",*fnd,isLeakSlot(*fnd));
443 if (fnd != NULL && isLeakSlot(*fnd)) {
444 found = *fnd;
445 /* mark slot as not leak */
446 //printf("data pointing to %p\n",found+1);
447 found->magic &= ~LEAK_FLAG;
448 /* move the slot into inProgress */
449 PCQueuePush(inProgress, (char*)found);
452 printf("scanning bss from %p to %p\n", (void *)info->begin_bss, (void *)info->end_bss);
453 for (scanner = info->begin_bss; scanner < info->end_bss; scanner+=sizeof(char*)) {
454 //printf("bss: %p %p\n",scanner,*(char**)scanner);
455 fnd = (Slot**)CkHashtableGet(table, scanner);
456 //if (fnd != NULL) printf("scanning bss %p, %d\n",*fnd,isLeakSlot(*fnd));
457 if (fnd != NULL && isLeakSlot(*fnd)) {
458 found = *fnd;
459 /* mark slot as not leak */
460 //printf("bss pointing to %p\n",found+1);
461 found->magic &= ~LEAK_FLAG;
462 /* move the slot into inProgress */
463 PCQueuePush(inProgress, (char*)found);
467 // Step 3)
468 // continue iteratively to check the memory by sweeping it with the
469 // "inProcess" list
470 while ((sl = (Slot *)PCQueuePop(inProgress)) != NULL) {
471 //printf("scanning memory %p of size %d\n",sl,sl->userSize);
472 /* scan through this memory and pick all the slots which are still leaking
473 and add them to the inProgress list */
474 if (sl->extraStack != NULL && sl->extraStack->protectedMemory != NULL) mprotect(sl->extraStack->protectedMemory, sl->extraStack->protectedMemoryLength, PROT_READ);
475 for (scanner = SlotToUser(sl); scanner < SlotToUser(sl)+sl->userSize-sizeof(char*)+1; scanner+=sizeof(char*)) {
476 fnd = (Slot**)CkHashtableGet(table, scanner);
477 //if (fnd != NULL) printf("scanning heap %p, %d\n",*fnd,isLeakSlot(*fnd));
478 if (fnd != NULL && isLeakSlot(*fnd)) {
479 found = *fnd;
480 /* mark slot as not leak */
481 //printf("heap pointing to %p\n",found+1);
482 found->magic &= ~LEAK_FLAG;
483 /* move the slot into inProgress */
484 PCQueuePush(inProgress, (char*)found);
487 if (sl->extraStack != NULL && sl->extraStack->protectedMemory != NULL) mprotect(sl->extraStack->protectedMemory, sl->extraStack->protectedMemoryLength, PROT_NONE);
490 // Step 4)
491 // move back all the entries in leaking to slot_first
492 /*if (leaking.next != &leaking) {
493 leaking.next->prev = slot_first;
494 leaking.prev->next = slot_first->next;
495 slot_first->next->prev = leaking.prev;
496 slot_first->next = leaking.next;
500 // mark all the entries in the leaking list as leak, and put them back
501 // into the main list
502 /*sl = leaking.next;
503 while (sl != &leaking) {
504 sl->magic | LEAK_FLAG;
506 if (leaking.next != &leaking) {
507 slot_first->next->prev = leaking.prev;
508 leaking.prev->next = slot_first->next;
509 leaking.next->prev = slot_first;
510 slot_first->next = leaking.next;
514 PCQueueDestroy(inProgress);
515 CkDeleteHashtable(table);
517 memory_charmdebug_internal = 0;
520 CMI_EXTERNC
521 void CpdMemoryMarkClean(char *msg) {
522 Slot *sl;
523 /* The first byte of the data packet indicates if we want o mark or unmark */
524 if ((msg+CmiMsgHeaderSizeBytes)[0]) {
525 SLOT_ITERATE_START(sl)
526 sl->magic |= LEAK_CLEAN;
527 SLOT_ITERATE_END
528 } else {
529 SLOT_ITERATE_START(sl)
530 sl->magic &= ~LEAK_CLEAN;
531 SLOT_ITERATE_END
533 CmiFree(msg);
536 /****************** memory allocation tree ******************/
538 /* This allows the representation and creation of a tree where each node
539 * represents a line in the code part of a stack trace of a malloc. The node
540 * contains how much data has been allocated starting from that line of code,
541 * down the stack.
544 typedef struct _AllocationPoint AllocationPoint;
546 struct _AllocationPoint {
547 /* The stack pointer this allocation refers to */
548 void * key;
549 /* Pointer to the parent AllocationPoint of this AllocationPoint in the tree */
550 AllocationPoint * parent;
551 /* Pointer to the first child AllocationPoint in the tree */
552 AllocationPoint * firstChild;
553 /* Pointer to the sibling of this AllocationPoint (i.e the next child of the parent) */
554 AllocationPoint * sibling;
555 /* Pointer to the next AllocationPoint with the same key.
556 * There can be more than one AllocationPoint with the same key because the
557 * parent can be different. Used only in the hashtable. */
558 AllocationPoint * next;
559 /* Size of the memory allocate */
560 int size;
561 /* How many blocks have been allocated from this point */
562 int count;
563 /* Flags pertaining to the allocation point, currently only LEAK_FLAG */
564 char flags;
567 // pup a single AllocationPoint. The data structure must be already allocated
568 void pupAllocationPointSingle(pup_er p, AllocationPoint *node, int *numChildren) {
569 AllocationPoint *child;
570 pup_pointer(p, &node->key);
571 pup_int(p, &node->size);
572 pup_int(p, &node->count);
573 pup_char(p, &node->flags);
574 if (pup_isUnpacking(p)) {
575 node->parent = NULL;
576 node->firstChild = NULL;
577 node->sibling = NULL;
578 node->next = NULL;
580 *numChildren = 0;
581 for (child = node->firstChild; child != NULL; child = child->sibling) (*numChildren) ++;
582 pup_int(p, numChildren);
586 // TODO: the following pup does not work for unpacking!
587 void pupAllocationPoint(pup_er p, void *data) {
588 AllocationPoint *node = (AllocationPoint*)data;
589 int numChildren;
590 AllocationPoint *child;
591 pupAllocationPointSingle(p, node, &numChildren);
592 for (child = node->firstChild; child != NULL; child = child->sibling) {
593 pupAllocationPoint(p, child);
597 void deleteAllocationPoint(void *ptr) {
598 AllocationPoint *node = (AllocationPoint*)ptr;
599 AllocationPoint *child;
600 for (child = node->firstChild; child != NULL; child = child->sibling) deleteAllocationPoint(child);
601 BEFORE_MALLOC_CALL;
602 mm_free(node);
603 AFTER_MALLOC_CALL;
606 void printAllocationTree(AllocationPoint *node, FILE *fd, int depth) {
607 int i;
608 int numChildren = 0;
609 AllocationPoint *child;
611 if (node==NULL) return;
612 for (child = node->firstChild; child != NULL; child = child->sibling) numChildren ++;
613 for (i=0; i<depth; ++i) fprintf(fd, " ");
614 fprintf(fd, "node %p: bytes=%d, count=%d, child=%d\n",(void *)node->key,node->size,node->count,numChildren);
615 printAllocationTree(node->sibling, fd, depth);
616 printAllocationTree(node->firstChild, fd, depth+2);
619 AllocationPoint * CreateAllocationTree(int *nodesCount) {
620 Slot *scanner;
621 CkHashtable_c table;
622 int i, isnew;
623 AllocationPoint *parent, **start, *cur;
624 AllocationPoint *root = NULL;
625 int numNodes = 0;
626 char filename[100];
627 FILE *fd;
628 CkHashtableIterator_c it;
629 AllocationPoint **startscan, *scan;
631 table = CkCreateHashtable_pointer(sizeof(char *), 10000);
633 BEFORE_MALLOC_CALL;
634 root = (AllocationPoint*) mm_malloc(sizeof(AllocationPoint));
635 AFTER_MALLOC_CALL;
636 *(AllocationPoint**)CkHashtablePut(table, &numNodes) = root;
637 numNodes ++;
638 root->key = 0;
639 root->parent = NULL;
640 root->size = 0;
641 root->count = 0;
642 root->flags = 0;
643 root->firstChild = NULL;
644 root->sibling = NULL;
645 root->next = root;
647 SLOT_ITERATE_START(scanner)
648 parent = root;
649 for (i=scanner->stackLen-1; i>=0; --i) {
650 isnew = 0;
651 start = (AllocationPoint**)CkHashtableGet(table, &scanner->from[i]);
652 if (start == NULL) {
653 BEFORE_MALLOC_CALL;
654 cur = (AllocationPoint*) mm_malloc(sizeof(AllocationPoint));
655 AFTER_MALLOC_CALL;
656 numNodes ++;
657 isnew = 1;
658 cur->next = cur;
659 *(AllocationPoint**)CkHashtablePut(table, &scanner->from[i]) = cur;
660 } else {
661 for (cur = (*start)->next; cur != *start && cur->parent != parent; cur = cur->next);
662 if (cur->parent != parent) {
663 BEFORE_MALLOC_CALL;
664 cur = (AllocationPoint*) mm_malloc(sizeof(AllocationPoint));
665 AFTER_MALLOC_CALL;
666 numNodes ++;
667 isnew = 1;
668 cur->next = (*start)->next;
669 (*start)->next = cur;
672 // here "cur" points to the correct AllocationPoint for this stack frame
673 if (isnew) {
674 cur->key = scanner->from[i];
675 cur->parent = parent;
676 cur->size = 0;
677 cur->count = 0;
678 cur->flags = 0;
679 cur->firstChild = NULL;
680 //if (parent == NULL) {
681 // cur->sibling = NULL;
682 // CmiAssert(root == NULL);
683 // root = cur;
684 //} else {
685 cur->sibling = parent->firstChild;
686 parent->firstChild = cur;
689 cur->size += scanner->userSize;
690 cur->count ++;
691 cur->flags |= isLeakSlot(scanner);
692 parent = cur;
694 SLOT_ITERATE_END
696 sprintf(filename, "allocationTree_%d", CmiMyPe());
697 fd = fopen(filename, "w");
698 fprintf(fd, "digraph %s {\n", filename);
699 it = CkHashtableGetIterator(table);
700 while ((startscan=(AllocationPoint**)CkHashtableIteratorNext(it,NULL))!=NULL) {
701 fprintf(fd, "\t\"n%p\" [label=\"%p\\nsize=%d\\ncount=%d\"];\n",(void *)*startscan,(void *)(*startscan)->key,
702 (*startscan)->size,(*startscan)->count);
703 for (scan = (*startscan)->next; scan != *startscan; scan = scan->next) {
704 fprintf(fd, "\t\"n%p\" [label=\"%p\\nsize=%d\\ncount=%d\"];\n",(void *)scan,(void *)scan->key,scan->size,scan->count);
707 CkHashtableIteratorSeekStart(it);
708 while ((startscan=(AllocationPoint**)CkHashtableIteratorNext(it,NULL))!=NULL) {
709 fprintf(fd, "\t\"n%p\" -> \"n%p\";\n",(void *)(*startscan)->parent,(void *)(*startscan));
710 for (scan = (*startscan)->next; scan != *startscan; scan = scan->next) {
711 fprintf(fd, "\t\"n%p\" -> \"n%p\";\n",(void *)scan->parent,(void *)scan);
714 fprintf(fd, "}\n");
715 fclose(fd);
717 sprintf(filename, "allocationTree_%d.tree", CmiMyPe());
718 fd = fopen(filename, "w");
719 printAllocationTree(root, fd, 0);
720 fclose(fd);
722 CkDeleteHashtable(table);
723 if (nodesCount != NULL) *nodesCount = numNodes;
724 return root;
727 void MergeAllocationTreeSingle(AllocationPoint *node, AllocationPoint *remote, int numChildren, pup_er p) {
728 AllocationPoint child;
729 int numChildChildren;
730 int i;
731 //pupAllocationPointSingle(p, &remote, &numChildren);
732 /* Update the node with the information coming from remote */
733 node->size += remote->size;
734 node->count += remote->count;
735 node->flags |= remote->flags;
736 /* Recursively merge the children */
737 for (i=0; i<numChildren; ++i) {
738 AllocationPoint *localChild;
739 pupAllocationPointSingle(p, &child, &numChildChildren);
740 /* Find the child in the local tree */
741 for (localChild = node->firstChild; localChild != NULL; localChild = localChild->sibling) {
742 if (localChild->key == child.key) {
743 break;
746 if (localChild == NULL) {
747 /* This child did not exist locally, allocate it */
748 BEFORE_MALLOC_CALL;
749 localChild = (AllocationPoint*) mm_malloc(sizeof(AllocationPoint));
750 AFTER_MALLOC_CALL;
751 localChild->key = child.key;
752 localChild->flags = 0;
753 localChild->count = 0;
754 localChild->size = 0;
755 localChild->firstChild = NULL;
756 localChild->next = NULL;
757 localChild->parent = node;
758 localChild->sibling = node->firstChild;
759 node->firstChild = localChild;
761 MergeAllocationTreeSingle(localChild, &child, numChildChildren, p);
765 void * MergeAllocationTree(int *size, void *data, void **remoteData, int numRemote) {
766 int i;
767 for (i=0; i<numRemote; ++i) {
768 pup_er p = pup_new_fromMem(remoteData[i]);
769 AllocationPoint root;
770 int numChildren;
771 pupAllocationPointSingle(p, &root, &numChildren);
772 MergeAllocationTreeSingle((AllocationPoint*)data, &root, numChildren, p);
773 pup_destroy(p);
775 return data;
778 /********************** Memory statistics ***********************/
780 /* Collect the statistics relative to the amount of memory allocated.
781 * Starts from the statistics of a single processor and combines them to contain
782 * all the processors in the application.
785 typedef struct MemStatSingle {
786 // [0] is total, [1] is the leaking part
787 int pe;
788 int sizes[2][5];
789 int counts[2][5];
790 } MemStatSingle;
792 typedef struct MemStat {
793 int count;
794 MemStatSingle array[1];
795 } MemStat;
797 void pupMemStat(pup_er p, void *st) {
798 int i;
799 MemStat *comb = (MemStat*)st;
800 pup_fmt_sync_begin_object(p);
801 pup_comment(p, "count");
802 pup_int(p, &comb->count);
803 pup_comment(p, "list");
804 pup_fmt_sync_begin_array(p);
805 for (i=0; i<comb->count; ++i) {
806 MemStatSingle *stat = &comb->array[i];
807 pup_fmt_sync_item(p);
808 pup_comment(p, "pe");
809 pup_int(p, &stat->pe);
810 pup_comment(p, "totalsize");
811 pup_ints(p, stat->sizes[0], 5);
812 pup_comment(p, "totalcount");
813 pup_ints(p, stat->counts[0], 5);
814 pup_comment(p, "leaksize");
815 pup_ints(p, stat->sizes[1], 5);
816 pup_comment(p, "leakcount");
817 pup_ints(p, stat->counts[1], 5);
819 pup_fmt_sync_end_array(p);
820 pup_fmt_sync_end_object(p);
823 void deleteMemStat(void *ptr) {
824 BEFORE_MALLOC_CALL;
825 mm_free(ptr);
826 AFTER_MALLOC_CALL;
829 static int memStatReturnOnlyOne = 1;
830 void * mergeMemStat(int *size, void *data, void **remoteData, int numRemote) {
831 int i,j,k;
832 if (memStatReturnOnlyOne) {
833 MemStatSingle *l = &((MemStat*) data)->array[0];
834 MemStat r;
835 MemStatSingle *m = &r.array[0];
836 l->pe = -1;
837 for (i=0; i<numRemote; ++i) {
838 pup_er p = pup_new_fromMem(remoteData[i]);
839 pupMemStat(p, &r);
840 for (j=0; j<2; ++j) {
841 for (k=0; k<5; ++k) {
842 l->sizes[j][k] += m->sizes[j][k];
843 l->counts[j][k] += m->counts[j][k];
846 pup_destroy(p);
848 return data;
849 } else {
850 MemStat *l = (MemStat*)data, *res;
851 MemStat r;
852 int count = l->count;
853 for (i=0; i<numRemote; ++i) count += ((MemStat*)remoteData[i])->count;
854 BEFORE_MALLOC_CALL;
855 res = (MemStat*)mm_malloc(sizeof(MemStat) + (count-1)*sizeof(MemStatSingle));
856 AFTER_MALLOC_CALL;
857 memset(res, 0, sizeof(MemStat)+(count-1)*sizeof(MemStatSingle));
858 res->count = count;
859 memcpy(res->array, l->array, l->count*sizeof(MemStatSingle));
860 count = l->count;
861 for (i=0; i<numRemote; ++i) {
862 pup_er p = pup_new_fromMem(remoteData[i]);
863 pupMemStat(p, &r);
864 memcpy(&res->array[count], r.array, r.count*sizeof(MemStatSingle));
865 count += r.count;
866 pup_destroy(p);
868 deleteMemStat(data);
869 return res;
873 MemStat * CreateMemStat(void) {
874 Slot *cur;
875 MemStat *st;
876 MemStatSingle *stat;
877 BEFORE_MALLOC_CALL;
878 st = (MemStat*)mm_calloc(1, sizeof(MemStat));
879 AFTER_MALLOC_CALL;
880 st->count = 1;
881 stat = &st->array[0];
882 SLOT_ITERATE_START(cur)
883 stat->sizes[0][(cur->magic&0x7)] += cur->userSize;
884 stat->counts[0][(cur->magic&0x7)] ++;
885 if (cur->magic & 0x8) {
886 stat->sizes[1][(cur->magic&0x7)] += cur->userSize;
887 stat->counts[1][(cur->magic&0x7)] ++;
889 SLOT_ITERATE_END
890 stat->pe=CmiMyPe();
891 return st;
895 /*********************** Cross-chare corruption detection *******************/
896 static int reportMEM = 0;
898 /* This first method uses two fields (userCRC and slotCRC) of the Slot structure
899 * to store the CRC32 checksum of the user data and the slot itself. It compares
900 * the stored values against a new value recomputed after the entry method
901 * returned to detect cross-chare corruption.
904 static int CpdCRC32 = 0;
906 #define SLOT_CRC_LENGTH (sizeof(Slot) - 2*sizeof(CmiUInt8))
908 static int checkSlotCRC(void *userPtr) {
909 Slot *sl = UserToSlot(userPtr);
910 if (sl!=NULL) {
911 unsigned int crc = crc32_initial((unsigned char*)sl, SLOT_CRC_LENGTH);
912 crc = crc32_update((unsigned char*)sl->from, sl->stackLen*sizeof(void*), crc);
913 return sl->slotCRC == crc;
914 } else return 0;
917 static int checkUserCRC(void *userPtr) {
918 Slot *sl = UserToSlot(userPtr);
919 if (sl!=NULL) return sl->userCRC == crc32_initial((unsigned char*)userPtr, sl->userSize);
920 else return 0;
923 static void resetUserCRC(void *userPtr) {
924 Slot *sl = UserToSlot(userPtr);
925 if (sl!=NULL) sl->userCRC = crc32_initial((unsigned char*)userPtr, sl->userSize);
928 static void resetSlotCRC(void *userPtr) {
929 Slot *sl = UserToSlot(userPtr);
930 if (sl!=NULL) {
931 unsigned int crc = crc32_initial((unsigned char*)sl, SLOT_CRC_LENGTH);
932 crc = crc32_update((unsigned char*)sl->from, sl->stackLen*sizeof(void*), crc);
933 sl->slotCRC = crc;
937 static void ResetAllCRC(void) {
938 Slot *cur;
939 unsigned int crc1, crc2;
941 SLOT_ITERATE_START(cur)
942 crc1 = crc32_initial((unsigned char*)cur, SLOT_CRC_LENGTH);
943 crc1 = crc32_update((unsigned char*)cur->from, cur->stackLen*sizeof(void*), crc1);
944 crc2 = crc32_initial((unsigned char*)SlotToUser(cur), cur->userSize);
945 cur->slotCRC = crc1;
946 cur->userCRC = crc2;
947 SLOT_ITERATE_END
950 static void CheckAllCRC(void) {
951 Slot *cur;
952 unsigned int crc1, crc2;
954 SLOT_ITERATE_START(cur)
955 crc1 = crc32_initial((unsigned char*)cur, SLOT_CRC_LENGTH);
956 crc1 = crc32_update((unsigned char*)cur->from, cur->stackLen*sizeof(void*), crc1);
957 crc2 = crc32_initial((unsigned char*)SlotToUser(cur), cur->userSize);
958 /* Here we can check if a modification has occured */
959 if (reportMEM && cur->slotCRC != crc1) CmiPrintf("CRC: Object %d modified slot for %p\n",memory_chare_id,(void *)SlotToUser(cur));
960 cur->slotCRC = crc1;
961 if (reportMEM && cur->userCRC != crc2 && memory_chare_id != cur->chareID)
962 CmiPrintf("CRC: Object %d modified memory of object %d for %p\n",memory_chare_id,cur->chareID,(void *)SlotToUser(cur));
963 cur->userCRC = crc2;
964 SLOT_ITERATE_END
967 /* This second method requires all the memory in the processor to be copied
968 * into a safe region, and then compare it with the working copy after the
969 * entry method returned.
972 static int CpdMemBackup = 0;
974 static void backupMemory(void) {
975 Slot *cur;
976 char * ptr;
977 int totalMemory = SLOTSPACE;
978 if (*memoryBackup != NULL)
979 CmiAbort("memoryBackup != NULL\n");
982 SLOT_ITERATE_START(cur)
983 totalMemory += sizeof(Slot) + cur->userSize + cur->stackLen*sizeof(void*);
984 SLOT_ITERATE_END
986 if (reportMEM) CmiPrintf("CPD: total memory in use (%d): %d\n",CmiMyPe(),totalMemory);
987 BEFORE_MALLOC_CALL;
988 *memoryBackup = (char *)mm_malloc(totalMemory);
989 AFTER_MALLOC_CALL;
991 ptr = *memoryBackup;
992 #ifndef CMK_SEPARATE_SLOT
993 memcpy(*memoryBackup, slot_first, sizeof(Slot));
994 ptr += sizeof(Slot);
995 #endif
996 SLOT_ITERATE_START(cur)
997 int tocopy = SLOTSPACE + cur->userSize + cur->stackLen*sizeof(void*);
998 char *data = (char *)cur;
999 #ifdef CMK_SEPARATE_SLOT
1000 memcpy(ptr, cur, sizeof(Slot));
1001 ptr += sizeof(Slot);
1002 data = SlotToUser(cur);
1003 #endif
1004 memcpy(ptr, data, tocopy);
1005 cur->magic &= ~ (NEW_BLOCK | MODIFIED);
1006 ptr += tocopy;
1007 SLOT_ITERATE_END
1008 allocatedSinceSize = 0;
1011 static void checkBackup(void) {
1012 #ifndef CMK_SEPARATE_SLOT
1013 Slot *cur = slot_first->next;
1014 char *ptr = *memoryBackup + sizeof(Slot);
1016 // skip over the new allocated blocks
1017 //while (cur != ((Slot*)*memoryBackup)->next) cur = cur->next;
1018 int idx = allocatedSinceSize-1;
1019 while (idx >= 0) {
1020 while (idx >= 0 && allocatedSince[idx] != cur) idx--;
1021 if (idx >= 0) {
1022 cur = cur->next;
1023 idx --;
1027 while (cur != slot_first) {
1028 char *last;
1029 // ptr is the old copy of cur
1030 if (memory_chare_id != cur->chareID) {
1031 int res = memcmp(ptr+sizeof(Slot), ((char*)cur)+sizeof(Slot), cur->userSize + cur->stackLen*sizeof(void*));
1032 if (res != 0) {
1033 cur->magic |= MODIFIED;
1034 if (reportMEM) CmiPrintf("CPD: Object %d modified memory of object %d for %p on pe %d\n",memory_chare_id,cur->chareID,(void *)(cur+1),CmiMyPe());
1038 // advance to next, skipping deleted memory blocks
1039 cur = cur->next;
1040 do {
1041 last = ptr;
1042 ptr += sizeof(Slot) + ((Slot*)ptr)->userSize + ((Slot*)ptr)->stackLen*sizeof(void*);
1043 } while (((Slot*)last)->next != cur);
1045 #endif
1047 BEFORE_MALLOC_CALL;
1048 mm_free(*memoryBackup);
1049 AFTER_MALLOC_CALL;
1050 *memoryBackup = NULL;
1053 /* Third method to detect cross-chare corruption. Use mprotect to change the
1054 * protection bits of each page, and a following segmentation fault to detect
1055 * the corruption. It is more accurate as it can provide the stack trace of the
1056 * first instruction that modified the memory.
1059 #include <signal.h>
1061 static int CpdMprotect = 0;
1063 static void** unProtectedPages = NULL;
1064 static int unProtectedPagesSize = 0;
1065 static int unProtectedPagesMaxSize = 0;
1067 static void* lastAddressSegv;
1068 static void CpdMMAPhandler(int sig, siginfo_t *si, void *unused){
1069 void *pageToUnprotect;
1070 if (lastAddressSegv == si->si_addr) {
1071 CmiPrintf("Second SIGSEGV at address 0x%lx\n", (long) si->si_addr);
1072 CpdFreeze();
1074 lastAddressSegv = si->si_addr;
1075 pageToUnprotect = (void*)(uintptr_t)((CmiUInt8)(uintptr_t)si->si_addr & ~(CmiGetPageSize()-1));
1076 mprotect(pageToUnprotect, 4, PROT_READ|PROT_WRITE);
1077 if (unProtectedPagesSize >= unProtectedPagesMaxSize) {
1078 void **newUnProtectedPages;
1079 unProtectedPagesMaxSize += 10;
1080 BEFORE_MALLOC_CALL;
1081 newUnProtectedPages = (void**)mm_malloc((unProtectedPagesMaxSize)*sizeof(void*));
1082 memcpy(newUnProtectedPages, unProtectedPages, unProtectedPagesSize*sizeof(void*));
1083 mm_free(unProtectedPages);
1084 AFTER_MALLOC_CALL;
1085 unProtectedPages = newUnProtectedPages;
1087 unProtectedPages[unProtectedPagesSize++] = pageToUnprotect;
1088 if (reportMEM) CpdNotify(CPD_CROSSCORRUPTION, si->si_addr, memory_chare_id);
1089 //CmiPrintf("Got SIGSEGV at address: 0x%lx\n", (long) si->si_addr);
1090 //CmiPrintStackTrace(0);
1093 static void protectMemory(void) {
1094 #ifdef CPD_USE_MMAP
1095 Slot *cur;
1096 /*printf("protecting memory (chareid=%d)",memory_chare_id);*/
1097 SLOT_ITERATE_START(cur)
1098 if (cur->chareID != memory_chare_id && cur->chareID > 0) {
1099 /*printf(" %p",cur->userData);*/
1100 #ifdef CMK_SEPARATE_SLOT
1101 char * data = cur->userData;
1102 #else
1103 char * data = (char *)cur;
1104 #endif
1105 cur->magic |= BLOCK_PROTECTED;
1106 mprotect(data, cur->userSize+SLOTSPACE+cur->stackLen*sizeof(void*), PROT_READ);
1107 } /*else printf(" (%p)",cur->userData);*/
1108 SLOT_ITERATE_END
1109 /*printf("\n");*/
1110 #endif
1113 static void unProtectMemory(void) {
1114 #ifdef CPD_USE_MMAP
1115 Slot *cur;
1116 SLOT_ITERATE_START(cur)
1117 #ifdef CMK_SEPARATE_SLOT
1118 char * data = cur->userData;
1119 #else
1120 char * data = (char *)cur;
1121 #endif
1122 mprotect(data, cur->userSize+SLOTSPACE+cur->stackLen*sizeof(void*), PROT_READ|PROT_WRITE);
1123 cur->magic &= ~BLOCK_PROTECTED;
1124 SLOT_ITERATE_END
1125 /*printf("unprotecting memory\n");*/
1126 #endif
1129 /** Called before the entry method: resets all current memory for the chare
1130 * receiving the message.
1132 void CpdResetMemory(void) {
1133 if (CpdMemBackup) backupMemory();
1134 if (CpdCRC32) ResetAllCRC();
1135 if (CpdMprotect) protectMemory();
1138 /** Called after the entry method to check if the chare that just received the
1139 * message has corrupted the memory of some other chare, or some system memory.
1141 void CpdCheckMemory(void) {
1142 Slot *cur;
1143 if (CpdMprotect) unProtectMemory();
1144 if (CpdCRC32) CheckAllCRC();
1145 if (CpdMemBackup) checkBackup();
1146 SLOT_ITERATE_START(cur)
1147 if (cur->magic == SLOTMAGIC_FREED) CmiAbort("SLOT deallocate in list");
1148 if (cur->from == NULL) printf("SLOT %p has no stack\n",(void *)cur);
1149 #ifndef CMK_SEPARATE_SLOT
1150 if (cur->next == NULL) printf("SLOT %p has null next!\n",(void *)cur);
1151 #endif
1152 SLOT_ITERATE_END
1155 void CpdSystemEnter(void) {
1156 #ifdef CPD_USE_MMAP
1157 Slot *cur;
1158 if (++cpdInSystem == 1) {
1159 if (CpdMprotect) {
1160 int count=0;
1161 SLOT_ITERATE_START(cur)
1162 if (cur->chareID == 0) {
1163 #ifdef CMK_SEPARATE_SLOT
1164 char * data = cur->userData;
1165 #else
1166 char * data = (char *)cur;
1167 #endif
1168 mprotect(data, cur->userSize+SLOTSPACE+cur->stackLen*sizeof(void*), PROT_READ|PROT_WRITE);
1169 cur->magic &= ~BLOCK_PROTECTED;
1170 count++;
1172 SLOT_ITERATE_END
1173 //printf("CpdSystemEnter: unprotected %d elements\n",count);
1176 #endif
1179 void CpdSystemExit(void) {
1180 #ifdef CPD_USE_MMAP
1181 Slot *cur;
1182 int i;
1183 if (--cpdInSystem == 0) {
1184 if (CpdMprotect) {
1185 int count=0;
1186 SLOT_ITERATE_START(cur)
1187 if (cur->chareID == 0) {
1188 #ifdef CMK_SEPARATE_SLOT
1189 char * data = cur->userData;
1190 #else
1191 char * data = (char *)cur;
1192 #endif
1193 cur->magic |= BLOCK_PROTECTED;
1194 mprotect(data, cur->userSize+SLOTSPACE+cur->stackLen*sizeof(void*), PROT_READ);
1195 count++;
1197 SLOT_ITERATE_END
1198 //printf("CpdSystemExit: protected %d elements\n",count);
1199 /* unprotect the pages that have been unprotected by a signal SEGV */
1200 for (i=0; i<unProtectedPagesSize; ++i) {
1201 mprotect(unProtectedPages[i], 4, PROT_READ|PROT_WRITE);
1205 #endif
1211 / *Head of the current circular allocated block list* /
1212 Slot slot_first_storage={&slot_first_storage,&slot_first_storage};
1213 Slot *slot_first=&slot_first_storage;
1215 #define CMI_MEMORY_ROUTINES 1
1217 / * Mark all allocated memory as being OK * /
1218 void CmiMemoryMark(void) {
1219 CmiMemLock();
1220 / * Just make a new linked list of slots * /
1221 slot_first=(Slot *)mm_malloc(sizeof(Slot));
1222 slot_first->next=slot_first->prev=slot_first;
1223 CmiMemUnlock();
1226 / * Mark this allocated block as being OK * /
1227 void CmiMemoryMarkBlock(void *blk) {
1228 Slot *s=Slot_fmUser(blk);
1229 CmiMemLock();
1230 if (s->magic!=SLOTMAGIC) CmiAbort("CmiMemoryMarkBlock called on non-malloc'd block!\n");
1231 / * Splice us out of the current linked list * /
1232 s->next->prev=s->prev;
1233 s->prev->next=s->next;
1234 s->prev=s->next=s; / * put us in our own list * /
1235 CmiMemUnlock();
1238 / * Print out all allocated memory * /
1239 void CmiMemorySweep(const char *where) {
1240 Slot *cur;
1241 int nBlocks=0,nBytes=0;
1242 CmiMemLock();
1243 cur=slot_first->next;
1244 CmiPrintf("[%d] ------- LEAK CHECK: %s -----\n",CmiMyPe(), where);
1245 while (cur!=slot_first) {
1246 printSlot(cur);
1247 nBlocks++; nBytes+=cur->userSize;
1248 cur=cur->next;
1250 if (nBlocks) {
1251 CmiPrintf("[%d] Total leaked memory: %d blocks, %d bytes\n",
1252 CmiMyPe(),nBlocks,nBytes);
1253 / * CmiAbort("Memory leaks detected!\n"); * /
1255 CmiMemUnlock();
1256 CmiMemoryMark();
1258 void CmiMemoryCheck(void) {}
1262 /********** Allocation/Free ***********/
1264 static int stackTraceDisabled = 0;
1265 #define MAX_STACK_FRAMES 2048
1266 static int numStackFrames; // the number of frames presetn in stackFrames - 4 (this number is trimmed at 0
1267 static void *stackFrames[MAX_STACK_FRAMES];
1269 static void dumpStackFrames(void) {
1270 numStackFrames=MAX_STACK_FRAMES;
1271 if (stackTraceDisabled==0) {
1272 stackTraceDisabled = 1;
1273 CmiBacktraceRecordHuge(stackFrames,&numStackFrames);
1274 stackTraceDisabled = 0;
1275 numStackFrames-=4;
1276 if (numStackFrames < 0) numStackFrames = 0;
1277 } else {
1278 numStackFrames=0;
1279 stackFrames[0] = (void*)0;
1283 /* Write a valid slot to this field */
1284 static void *setSlot(Slot **sl,int userSize) {
1285 #ifdef CMK_SEPARATE_SLOT
1286 Slot *s;
1287 char *user;
1289 static int mallocFirstTime = 1;
1290 if (mallocFirstTime) {
1291 mallocFirstTime = 0;
1292 memory_charmdebug_internal = 1;
1293 block_slots = CkCreateHashtable_pointer(sizeof(Slot), 10000);
1294 memory_charmdebug_internal = 0;
1297 user = (char *)*sl;
1298 memory_charmdebug_internal = 1;
1299 s = (Slot *)CkHashtablePut(block_slots, sl);
1300 memory_charmdebug_internal = 0;
1301 *sl = s;
1303 s->userData = user;
1304 #else
1305 Slot *s = *sl;
1306 char *user=(char*)(s+1);
1308 /* Splice into the slot list just past the head (part 1) */
1309 s->next=slot_first->next;
1310 s->prev=slot_first;
1311 /* Handle correctly memory protection while changing neighbor blocks */
1312 if (CpdMprotect) {
1313 mprotect(s->next, 4, PROT_READ | PROT_WRITE);
1314 mprotect(s->prev, 4, PROT_READ | PROT_WRITE);
1316 /* Splice into the slot list just past the head (part 2) */
1317 s->next->prev=s;
1318 s->prev->next=s;
1320 if (CpdCRC32) {
1321 /* fix crc for previous and next block */
1322 resetSlotCRC(s->next + 1);
1323 resetSlotCRC(s->prev + 1);
1325 if (CpdMprotect) {
1326 if (isProtected(s->next)) mprotect(s->next, 4, PROT_READ);
1327 if (isProtected(s->prev)) mprotect(s->prev, 4, PROT_READ);
1329 #endif
1331 /* Set the last 4 bits of magic to classify the memory together with the magic */
1332 s->magic=SLOTMAGIC + NEW_BLOCK + (memory_status_info>0? USER_TYPE : SYSTEM_TYPE);
1333 //if (memory_status_info>0) printf("user allocation\n");
1334 s->chareID = memory_chare_id;
1335 s->userSize=userSize;
1336 s->extraStack=(SlotStack *)0;
1338 /* Set the stack frames */
1339 s->stackLen=numStackFrames;
1340 s->from=(void**)(user+userSize);
1341 memcpy(s->from, &stackFrames[4], numStackFrames*sizeof(void*));
1343 if (CpdCRC32) {
1344 unsigned int crc = crc32_initial((unsigned char*)s, SLOT_CRC_LENGTH);
1345 s->slotCRC = crc32_update((unsigned char*)s->from, numStackFrames*sizeof(void*), crc);
1346 s->userCRC = crc32_initial((unsigned char*)user, userSize);
1348 if (saveAllocationHistory) {
1349 if (allocatedSinceSize >= allocatedSinceMaxSize) {
1350 Slot **newAllocatedSince;
1351 allocatedSinceMaxSize += 10;
1352 BEFORE_MALLOC_CALL;
1353 newAllocatedSince = (Slot**)mm_malloc((allocatedSinceMaxSize)*sizeof(Slot*));
1354 memcpy(newAllocatedSince, allocatedSince, allocatedSinceSize*sizeof(Slot*));
1355 mm_free(allocatedSince);
1356 AFTER_MALLOC_CALL;
1357 allocatedSince = newAllocatedSince;
1359 allocatedSince[allocatedSinceSize++] = s;
1361 lastMemoryAllocated = user;
1363 return (void *)user;
1366 /* Delete this slot structure */
1367 static void freeSlot(Slot *s) {
1368 #ifdef CMK_SEPARATE_SLOT
1369 /* Don't delete it from the hash table, simply mark it as freed */
1370 int removed = CkHashtableRemove(block_slots, &s->userData);
1371 CmiAssert(removed);
1372 /* WARNING! After the slot has been removed from the hashtable,
1373 * the pointer "s" becomes invalid.
1375 #else
1376 /* Handle correctly memory protection while changing neighbor blocks */
1377 if (CpdMprotect) {
1378 mprotect(s->next, 4, PROT_READ | PROT_WRITE);
1379 mprotect(s->prev, 4, PROT_READ | PROT_WRITE);
1381 /* Splice out of the slot list */
1382 s->next->prev=s->prev;
1383 s->prev->next=s->next;
1384 if (CpdCRC32) {
1385 /* fix crc for previous and next block */
1386 resetSlotCRC(s->next + 1);
1387 resetSlotCRC(s->prev + 1);
1389 if (CpdMprotect) {
1390 if (isProtected(s->next)) mprotect(s->next, 4, PROT_READ);
1391 if (isProtected(s->prev)) mprotect(s->prev, 4, PROT_READ);
1393 s->prev=s->next=(Slot *)0;//0x0F00; why was it not 0?
1395 s->magic=SLOTMAGIC_FREED;
1396 s->userSize=-1;
1397 #endif
1400 /********** meta_ routines ***********/
1402 /* Only display startup status messages from processor 0 */
1403 static void status(const char *msg) {
1404 if (CmiMyPe()==0 && !CmiArgGivingUsage()) {
1405 CmiPrintf("%s",msg);
1409 extern int getCharmEnvelopeSize(void);
1411 static int disableVerbosity = 1;
1412 int cpdInitializeMemory;
1413 void CpdSetInitializeMemory(int v) { cpdInitializeMemory = v; }
1415 static void meta_init(char **argv) {
1416 if (CmiMyRank()==0) {
1417 char buf[100];
1418 status("Converse -memory mode: charmdebug\n");
1419 sprintf(buf, "slot size %d\n", (int)sizeof(Slot));
1420 status(buf);
1421 CmiMemoryIs_flag|=CMI_MEMORY_IS_CHARMDEBUG;
1422 cpdInitializeMemory = 0;
1423 charmEnvelopeSize = getCharmEnvelopeSize();
1424 CpdDebugGetAllocationTree = (void* (*)(int*))CreateAllocationTree;
1425 CpdDebug_pupAllocationPoint = pupAllocationPoint;
1426 CpdDebug_deleteAllocationPoint = deleteAllocationPoint;
1427 CpdDebug_MergeAllocationTree = MergeAllocationTree;
1428 CpdDebugGetMemStat = (void* (*)(void))CreateMemStat;
1429 CpdDebug_pupMemStat = pupMemStat;
1430 CpdDebug_deleteMemStat = deleteMemStat;
1431 CpdDebug_mergeMemStat = mergeMemStat;
1432 memory_allocated_user_total = 0;
1433 nextChareID = 1;
1434 #ifndef CMK_SEPARATE_SLOT
1435 slot_first->userSize = 0;
1436 slot_first->stackLen = 0;
1437 #endif
1439 if (CmiGetArgFlagDesc(argv,"+memory_report", "Print all cross-object violations")) {
1440 if (CmiMyRank()==0) reportMEM = 1;
1442 if (CmiGetArgFlagDesc(argv,"+memory_backup", "Backup all memory at every entry method")) {
1443 if (CmiMyRank()==0) {
1444 CpdMemBackup = 1;
1445 saveAllocationHistory = 1;
1448 if (CmiGetArgFlagDesc(argv,"+memory_crc", "Use CRC32 to detect memory changes")) {
1449 if (CmiMyRank()==0) CpdCRC32 = 1;
1451 #ifdef CPD_USE_MMAP
1452 if (CmiGetArgFlagDesc(argv,"+memory_mprotect", "Use mprotect to protect memory of other chares")) {
1453 if (CmiMyRank()==0) {
1454 struct sigaction sa;
1455 CpdMprotect = 1;
1456 sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_RESTART;
1457 sigemptyset(&sa.sa_mask);
1458 sa.sa_sigaction = CpdMMAPhandler;
1459 if (sigaction(SIGSEGV, &sa, NULL) == -1) CmiPrintf("failed to install signal handler\n");
1462 #endif
1463 if (CmiGetArgFlagDesc(argv,"+memory_verbose", "Print all memory-related operations")) {
1464 if (CmiMyRank()==0) disableVerbosity = 0;
1466 if (CmiGetArgFlagDesc(argv,"+memory_nostack", "Do not collect stack traces for memory allocations")) {
1467 if (CmiMyRank()==0) stackTraceDisabled = 1;
1471 static void *meta_malloc(size_t size) {
1472 void *user = NULL;
1473 if (memory_charmdebug_internal==0) {
1474 Slot *s;
1475 dumpStackFrames();
1476 BEFORE_MALLOC_CALL;
1477 #if CPD_USE_MMAP
1478 s=(Slot *)mmap(NULL, SLOTSPACE+size+numStackFrames*sizeof(void*), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1479 #else
1480 s=(Slot *)mm_malloc(SLOTSPACE+size+numStackFrames*sizeof(void*));
1481 #endif
1482 AFTER_MALLOC_CALL;
1483 if (s!=NULL) {
1484 user = (char*)setSlot(&s,size);
1485 memory_allocated_user_total += size;
1486 #if ! CMK_BIGSIM_CHARM
1487 traceMalloc_c(user, size, s->from, s->stackLen);
1488 #endif
1490 if (disableVerbosity == 0) {
1491 disableVerbosity = 1;
1492 CmiPrintf("allocating %p: %d bytes\n",(void *)s,size);
1493 disableVerbosity = 0;
1495 } else {
1496 BEFORE_MALLOC_CALL;
1497 user = mm_malloc(size);
1498 AFTER_MALLOC_CALL;
1500 if (cpdInitializeMemory) {
1501 memset(user, 0, size); // for Record-Replay must initialize all memory otherwise paddings may differ (screwing up the CRC)
1503 return user;
1506 static void meta_free(void *mem) {
1507 if (memory_charmdebug_internal==0) {
1508 int memSize;
1509 Slot *s;
1510 if (mem==NULL) return; /*Legal, but misleading*/
1511 s=UserToSlot(mem);
1512 #if CMK_MEMORY_BUILD_OS
1513 /* In this situation, we can have frees that were not allocated by our malloc...
1514 * for now simply skip over them. */
1515 if (s == NULL || ((s->magic&~FLAGS_MASK) != SLOTMAGIC_VALLOC &&
1516 (s->magic&~FLAGS_MASK) != SLOTMAGIC)) {
1517 BEFORE_MALLOC_CALL;
1518 mm_free(mem);
1519 AFTER_MALLOC_CALL;
1520 return;
1522 #endif
1524 /* Check that the memory is really allocated, and we can use its slot */
1525 /* TODO */
1527 if (s == NULL ||
1528 ((s->magic&~FLAGS_MASK) != SLOTMAGIC &&
1529 (s->magic&~FLAGS_MASK) != SLOTMAGIC_FREED &&
1530 (s->magic&~FLAGS_MASK) != SLOTMAGIC_VALLOC)) {
1531 CmiAbort("Free'd non-malloc'd block");
1533 #ifdef CMK_SEPARATE_SLOT
1534 CmiAssert(s->userData == mem);
1535 #endif
1537 memSize = 0;
1538 if (mem!=NULL) memSize = s->userSize;
1539 memory_allocated_user_total -= memSize;
1540 #if ! CMK_BIGSIM_CHARM
1541 traceFree_c(mem, memSize);
1542 #endif
1544 if (disableVerbosity == 0) {
1545 disableVerbosity = 1;
1546 CmiPrintf("freeing %p\n",(void *)mem);
1547 disableVerbosity = 0;
1550 /*Overwrite stack trace with the one of the free*/
1551 dumpStackFrames();
1552 if (s->stackLen > numStackFrames) s->stackLen=numStackFrames;
1553 memcpy(s->from, &stackFrames[4], s->stackLen*sizeof(void*));
1555 if ((s->magic&~FLAGS_MASK)==SLOTMAGIC_VALLOC)
1556 { /*Allocated with special alignment*/
1557 void *ptr = s->extraStack;
1558 freeSlot(s);
1559 BEFORE_MALLOC_CALL;
1560 mm_free(ptr);
1561 /*mm_free(((char *)mem)-CmiGetPageSize());*/
1562 AFTER_MALLOC_CALL;
1564 else if ((s->magic&~FLAGS_MASK)==SLOTMAGIC)
1565 { /*Ordinary allocated block */
1566 int freeSize=SLOTSPACE+s->userSize+s->stackLen*sizeof(void*);
1567 void *ptr;
1568 freeSlot(s);
1569 #ifdef CMK_SEPARATE_SLOT
1570 ptr = mem;
1571 #else
1572 ptr = s;
1573 #endif
1574 BEFORE_MALLOC_CALL;
1575 #if CPD_USE_MMAP
1576 munmap(ptr, freeSize);
1577 #else
1578 mm_free(ptr);
1579 #endif
1580 AFTER_MALLOC_CALL;
1582 else if (s->magic==SLOTMAGIC_FREED)
1583 CmiAbort("Free'd block twice");
1584 else /*Unknown magic number*/
1585 CmiAbort("Free'd non-malloc'd block");
1586 } else {
1587 BEFORE_MALLOC_CALL;
1588 mm_free(mem);
1589 AFTER_MALLOC_CALL;
1593 static void *meta_calloc(size_t nelem, size_t size) {
1594 void *area=meta_malloc(nelem*size);
1595 if (area != NULL) memset(area,0,nelem*size);
1596 return area;
1599 static void meta_cfree(void *mem) {
1600 meta_free(mem);
1603 static void *meta_realloc(void *oldBuffer, size_t newSize) {
1604 void *newBuffer = meta_malloc(newSize);
1605 if ( newBuffer && oldBuffer ) {
1606 /*Preserve old buffer contents*/
1607 Slot *o=UserToSlot(oldBuffer);
1608 size_t size=o->userSize;
1609 if (size>newSize) size=newSize;
1610 if (size > 0)
1611 memcpy(newBuffer, oldBuffer, size);
1613 meta_free(oldBuffer);
1615 return newBuffer;
1618 static void *meta_memalign(size_t align, size_t size) {
1619 int overhead = align;
1620 char *alloc;
1621 Slot *s;
1622 void *user;
1624 while (overhead < SLOTSPACE+sizeof(SlotStack)) overhead += align;
1625 /* Allocate the required size + the overhead needed to keep the user alignment */
1626 dumpStackFrames();
1628 BEFORE_MALLOC_CALL;
1629 alloc=(char *)mm_memalign(align,overhead+size+numStackFrames*sizeof(void*));
1630 AFTER_MALLOC_CALL;
1631 s=(Slot*)(alloc+overhead-SLOTSPACE);
1632 user=setSlot(&s,size);
1633 s->magic = SLOTMAGIC_VALLOC + (s->magic&0xF);
1634 s->extraStack = (SlotStack *)alloc; /* use the extra space as stack */
1635 s->extraStack->protectedMemory = NULL;
1636 s->extraStack->protectedMemoryLength = 0;
1637 memory_allocated_user_total += size;
1638 #if ! CMK_BIGSIM_CHARM
1639 traceMalloc_c(user, size, s->from, s->stackLen);
1640 #endif
1641 return user;
1644 static int meta_posix_memalign(void **outptr, size_t align, size_t size) {
1645 int overhead = align;
1646 int ret;
1647 char *alloc;
1648 Slot *s;
1649 void *user;
1651 while (overhead < SLOTSPACE+sizeof(SlotStack)) overhead += align;
1652 /* Allocate the required size + the overhead needed to keep the user alignment */
1653 dumpStackFrames();
1655 BEFORE_MALLOC_CALL;
1656 ret = mm_posix_memalign(outptr,align,overhead+size+numStackFrames*sizeof(void*));
1657 alloc=(char *)*outptr;
1658 AFTER_MALLOC_CALL;
1659 if (ret != 0)
1660 return ret;
1661 s=(Slot*)(alloc+overhead-SLOTSPACE);
1662 user=setSlot(&s,size);
1663 s->magic = SLOTMAGIC_VALLOC + (s->magic&0xF);
1664 s->extraStack = (SlotStack *)alloc; /* use the extra space as stack */
1665 s->extraStack->protectedMemory = NULL;
1666 s->extraStack->protectedMemoryLength = 0;
1667 memory_allocated_user_total += size;
1668 #if ! CMK_BIGSIM_CHARM
1669 traceMalloc_c(user, size, s->from, s->stackLen);
1670 #endif
1671 *outptr = user;
1672 return 0;
1675 static void *meta_aligned_alloc(size_t align, size_t size) {
1676 int overhead = align;
1677 char *alloc;
1678 Slot *s;
1679 void *user;
1681 while (overhead < SLOTSPACE+sizeof(SlotStack)) overhead += align;
1682 /* Allocate the required size + the overhead needed to keep the user alignment */
1683 dumpStackFrames();
1685 BEFORE_MALLOC_CALL;
1686 alloc=(char *)mm_aligned_alloc(align,overhead+size+numStackFrames*sizeof(void*));
1687 AFTER_MALLOC_CALL;
1688 s=(Slot*)(alloc+overhead-SLOTSPACE);
1689 user=setSlot(&s,size);
1690 s->magic = SLOTMAGIC_VALLOC + (s->magic&0xF);
1691 s->extraStack = (SlotStack *)alloc; /* use the extra space as stack */
1692 s->extraStack->protectedMemory = NULL;
1693 s->extraStack->protectedMemoryLength = 0;
1694 memory_allocated_user_total += size;
1695 #if ! CMK_BIGSIM_CHARM
1696 traceMalloc_c(user, size, s->from, s->stackLen);
1697 #endif
1698 return user;
1701 static void *meta_valloc(size_t size) {
1702 return meta_memalign(CmiGetPageSize(),size);
1705 static void *meta_pvalloc(size_t size) {
1706 const size_t pagesize = CmiGetPageSize();
1707 return meta_memalign(pagesize, (size + pagesize - 1) & ~(pagesize - 1));
1710 void setProtection(char* mem, char *ptr, int len, int flag) {
1711 Slot *sl = UserToSlot(mem);
1712 if (sl->extraStack == NULL) CmiAbort("Tried to protect memory not memaligned\n");
1713 if (flag != 0) {
1714 sl->extraStack->protectedMemory = ptr;
1715 sl->extraStack->protectedMemoryLength = len;
1716 } else {
1717 sl->extraStack->protectedMemory = NULL;
1718 sl->extraStack->protectedMemoryLength = 0;
1722 void **chareObjectMemory = NULL;
1723 int chareObjectMemorySize = 0;
1725 void setMemoryTypeChare(void *ptr) {
1726 Slot *sl = UserToSlot(ptr);
1727 sl->magic = (sl->magic & ~FLAGS_MASK) | CHARE_TYPE;
1728 sl->chareID = nextChareID;
1729 if (nextChareID >= chareObjectMemorySize) {
1730 void **newChare;
1731 BEFORE_MALLOC_CALL;
1732 newChare = (void**)mm_malloc((nextChareID+100) * sizeof(void*));
1733 AFTER_MALLOC_CALL;
1734 memcpy(newChare, chareObjectMemory, chareObjectMemorySize*sizeof(void*));
1735 chareObjectMemorySize = nextChareID+100;
1736 BEFORE_MALLOC_CALL;
1737 mm_free(chareObjectMemory);
1738 AFTER_MALLOC_CALL;
1739 chareObjectMemory = newChare;
1741 chareObjectMemory[nextChareID] = ptr;
1742 nextChareID ++;
1745 /* The input parameter is the pointer to the envelope, after the CmiChunkHeader */
1746 void setMemoryTypeMessage(void *ptr) {
1747 void *realptr = (char*)ptr - sizeof(CmiChunkHeader);
1748 Slot *sl = UserToSlot(realptr);
1749 if ((sl->magic&~FLAGS_MASK) == SLOTMAGIC || (sl->magic&~FLAGS_MASK) == SLOTMAGIC_VALLOC) {
1750 sl->magic = (sl->magic & ~FLAGS_MASK) | MESSAGE_TYPE;
1754 int setMemoryChareIDFromPtr(void *ptr) {
1755 int tmp = memory_chare_id;
1756 if (ptr == NULL || ptr == 0) memory_chare_id = 0;
1757 else memory_chare_id = UserToSlot(ptr)->chareID;
1758 return tmp;
1761 void setMemoryChareID(int chareID) {
1762 memory_chare_id = chareID;
1765 void setMemoryOwnedBy(void *ptr, int chareID) {
1766 Slot *sl = UserToSlot(ptr);
1767 sl->chareID = chareID;
1770 CMI_EXTERNC
1771 void * MemoryToSlot(void *ptr) {
1772 Slot *sl;
1773 int i;
1774 #if defined(CPD_USE_MMAP) && defined(CMK_SEPARATE_SLOT)
1775 for (i=0; i<1000; ++i) {
1776 sl = UserToSlot((void*)(intptr_t)(((CmiUInt8)(intptr_t)ptr)-i*CmiGetPageSize() & ~(CmiGetPageSize()-1)));
1777 if (sl != NULL) return sl;
1779 #endif
1780 return NULL;
1783 /*void PrintDebugStackTrace(void *ptr) {
1784 int i;
1785 Slot *sl = UserToSlot((void*)(((CmiUInt8)ptr) & ~(CmiGetPageSize()-1)));
1786 if (sl != NULL) {
1787 CmiPrintf("%d %d ",sl->chareID,sl->stackLen);
1788 for (i=0; i<sl->stackLen; ++i) CmiPrintf("%p ",sl->from[i]);
1789 } else {
1790 CmiPrintf("%d 0 ",sl->chareID);
1795 #endif