1 /*
2 * Copyright (C) by Argonne National Laboratory
3 * See COPYRIGHT in top-level directory
4 */
5
6 /* Always enable valgrind macros (if possible) in this file. If these functions
7 * are used, the caller is concerned about correctness, not performance. */
8 #define MPL_VG_ENABLED 1
9
10 /* style: allow:calloc:1 sig:0 */
11 /* style: allow:free:2 sig:0 */
12 /* style: allow:malloc:2 sig:0 */
13 /* style: allow:strdup:1 sig:0 */
14
15 #include "mpl.h"
16 #include <assert.h>
17
18 #ifdef malloc
19 /* Undefine these in case they were set to 'error' */
20 #undef malloc
21 #undef calloc
22 #undef free
23 #undef strdup
24 #undef mmap
25 #undef munmap
26 /* Some GNU implementations use __strdup for strdup */
27 #if defined(__strdup)
28 #define strdup(s) __strdup(s)
29 #endif
30 #endif
31
32 #ifdef MPL_HAVE_SYS_MMAN_H
33 #include <sys/mman.h>
34 #endif
35
36 #define TR_ALIGN_BYTES 8
37 #define TR_ALIGN_MASK 0x7
38 #define TR_FNAME_LEN 48
39
40 #define COOKIE_VALUE 0xf0e0d0c9
41 #define ALREADY_FREED 0x0f0e0d9c
42
43 enum TR_mem_type {
44 TR_MALLOC_TYPE = 0,
45 TR_MMAP_TYPE = 1,
46 };
47
48 typedef struct TRSPACE {
49 enum TR_mem_type type;
50 MPL_memory_class class;
51 size_t size;
52 int id;
53 int lineno;
54 int freed_lineno;
55 char freed_fname[TR_FNAME_LEN];
56 char fname[TR_FNAME_LEN];
57 void *real_head; /* Pointer we got from (libc) malloc */
58 struct TRSPACE *volatile next, *prev;
59 unsigned long cookie; /* Cookie is always the last element
60 * inorder to catch the off-by-one
61 * errors */
62 } TRSPACE;
63 /* This union is used to ensure that the block passed to the user is
64 aligned on a double boundary */
65 typedef union TrSPACE {
66 TRSPACE sp;
67 /* Ensure trSPACE header follows the alignment rules for all predefined types.
68 * Because any internal buffer is allocated as (TrSPACE)header + (void*)buffer.*/
69 MPL_mem_alignment_t alignment;
70 } TrSPACE;
71
72 /*
73 * This package maintains some state about itself. These globals hold
74 * this information.
75 */
76 #define TRHEAD_PRESENTINAL ((TRSPACE *)0xbacdef01)
77 #define TRHEAD_POSTSENTINAL ((TRSPACE *)0x10fedcba)
78 static int world_rank = -1;
79 static volatile size_t allocated = 0;
80 static volatile long frags = 0;
81 static TRSPACE *volatile TRhead[3] = { TRHEAD_PRESENTINAL, 0, TRHEAD_POSTSENTINAL };
82
83 static volatile int TRid = 0;
84 static volatile int TRidSet = 0;
85 static volatile int TRlevel = 0;
86 static unsigned char TRDefaultByte = 0xda;
87 static unsigned char TRFreedByte = 0xfc;
88 static int TRdebugLevel = 0;
89 static int TRSetBytes = 0;
90 #define TR_MALLOC 0x1
91 #define TR_FREE 0x2
92 #define TR_MMAP 0x4
93 #define TR_MUNMAP 0x8
94
95 /* Used to keep track of allocations */
96 static volatile size_t TRMaxMem = 0;
97 static volatile int TRMaxMemId = 0;
98 static volatile size_t TRCurOverhead = 0;
99 static volatile size_t TRMaxOverhead = 314572800;
100 /* Used to limit allocation */
101 static volatile size_t TRMaxMemAllow = 0;
102
103 static int TR_is_threaded = 0;
104
105 static int is_configured = 0;
106 static int classes_initialized = 0;
107
108 static MPL_memory_allocation_t allocation_classes[MPL_MAX_MEMORY_CLASS];
109
110 /* This list should match the enum in mpl_trmem.h */
111 static const char *allocation_class_strings[] = {
112 "MPL_MEM_ADDRESS",
113 "MPL_MEM_OBJECT",
114 "MPL_MEM_COMM",
115 "MPL_MEM_GROUP",
116 "MPL_MEM_STRINGS",
117 "MPL_MEM_RMA",
118 "MPL_MEM_BUFFER",
119 "MPL_MEM_SHM",
120 "MPL_MEM_THREAD",
121 "MPL_MEM_DYNAMIC",
122 "MPL_MEM_IO",
123 "MPL_MEM_GREQ",
124 "MPL_MEM_DATATYPE",
125 "MPL_MEM_MPIT",
126 "MPL_MEM_DEBUG",
127 "MPL_MEM_PM",
128 "MPL_MEM_COLL",
129 "MPL_MEM_USER",
130 "MPL_MEM_OTHER"
131 };
132
133 #if MPL_THREAD_PACKAGE_NAME != MPL_THREAD_PACKAGE_NONE
134
135 static MPL_thread_mutex_t memalloc_mutex;
136
137 #define TR_THREAD_CS_ENTER \
138 do { \
139 if (TR_is_threaded) { \
140 int err_; \
141 MPL_thread_mutex_lock(&memalloc_mutex, &err_, MPL_THREAD_PRIO_HIGH);\
142 if (err_) \
143 MPL_error_printf("Error acquiring memalloc mutex lock\n"); \
144 } \
145 } while (0)
146
147 #define TR_THREAD_CS_EXIT \
148 do { \
149 if (TR_is_threaded) { \
150 int err_; \
151 MPL_thread_mutex_unlock(&memalloc_mutex, &err_); \
152 if (err_) \
153 MPL_error_printf("Error releasing memalloc mutex lock\n"); \
154 } \
155 } while (0)
156
157 #else /* MPL_THREAD_PACKAGE_NAME == MPL_THREAD_PACKAGE_NONE */
158
159 #define TR_THREAD_CS_ENTER
160 #define TR_THREAD_CS_EXIT
161
162 #endif /* MPL_THREAD_PACKAGE_NAME */
163
init_classes()164 static void init_classes()
165 {
166 int i;
167
168 for (i = 0; i < MPL_MAX_MEMORY_CLASS; i++) {
169 allocation_classes[i] = (MPL_memory_allocation_t) {
170 .max_allocated_mem = 0,.curr_allocated_mem = 0,.total_allocated_mem =
171 0,.num_allocations = 0};
172 }
173
174 classes_initialized = 1;
175 }
176
177 /*+C
178 MPL_trinit - Setup the space package. Only needed for
179 error messages and flags.
180 +*/
MPL_trinit()181 void MPL_trinit()
182 {
183 char *s;
184
185 /* FIXME: We should use generalized parameter handling here
186 * to allow use of the command line as well as environment
187 * variables */
188 s = getenv("MPL_TRMEM_INIT");
189 if (s && *s && (strcmp(s, "YES") == 0 || strcmp(s, "yes") == 0)) {
190 TRSetBytes = 1;
191 }
192 s = getenv("MPL_TRMEM_VALIDATE");
193 if (s && *s && (strcmp(s, "YES") == 0 || strcmp(s, "yes") == 0)) {
194 TRdebugLevel = 1;
195 }
196 s = getenv("MPL_TRMEM_INITZERO");
197 if (s && *s && (strcmp(s, "YES") == 0 || strcmp(s, "yes") == 0)) {
198 TRDefaultByte = 0;
199 TRFreedByte = 0;
200 }
201 s = getenv("MPL_TRMEM_TRACELEVEL");
202 if (s && *s) {
203 int l = atoi(s);
204 TRlevel = l;
205 }
206 s = getenv("MPL_TRMEM_MAX_OVERHEAD");
207 if (s && *s) {
208 long l = atol(s);
209 TRMaxOverhead = (size_t) l;
210 }
211 }
212
MPL_trconfig(int rank,int need_thread_safety)213 void MPL_trconfig(int rank, int need_thread_safety)
214 {
215 world_rank = rank;
216
217 if (is_configured)
218 return;
219
220 /* If the upper layer asked for thread safety and there's no
221 * threading package available, we need to return an error. */
222 #if MPL_THREAD_PACKAGE_NAME == MPL_THREAD_PACKAGE_NONE
223 if (need_thread_safety)
224 MPL_error_printf("No thread package to provide thread-safe memory allocation\n");
225 #endif
226
227 #if MPL_THREAD_PACKAGE_NAME != MPL_THREAD_PACKAGE_NONE
228 if (need_thread_safety) {
229 int err;
230
231 MPL_thread_mutex_create(&memalloc_mutex, &err);
232 if (err) {
233 MPL_error_printf("Error creating memalloc mutex\n");
234 }
235
236 TR_is_threaded = 1;
237 }
238 #endif
239
240 is_configured = 1;
241 }
242
243 /*
244 Validate given alignment.
245 Invoked only when memory tracing is enabled.
246 */
is_valid_alignment(size_t a)247 MPL_STATIC_INLINE_PREFIX int is_valid_alignment(size_t a)
248 {
249 /* No alignment constraints - okay */
250 if (a == 0)
251 return 1;
252
253 /* Alignment should be multiple of sizeof(void *), as in posix_memalign(3) */
254 if (a % sizeof(void *) != 0)
255 return 0;
256
257 /* Check if it's power of two */
258 while (a > 1) {
259 if (a % 2 == 1)
260 return 0; /* Don't allow non-power-of-two numbers */
261 a /= 2;
262 }
263
264 return 1;
265 }
266
267 /*+C
268 MPL_trmalloc - Malloc with tracing
269
270 Input Parameters:
271 + a - number of bytes to allocate
272 . lineno - line number where used. Use __LINE__ for this
273 - fname - file name where used. Use __FILE__ for this
274
275 Returns:
276 double aligned pointer to requested storage, or null if not
277 available.
278 +*/
trmalloc(size_t alignment,size_t a,MPL_memory_class class,int lineno,const char fname[])279 static void *trmalloc(size_t alignment, size_t a, MPL_memory_class class, int lineno,
280 const char fname[])
281 {
282 TRSPACE *head;
283 char *new = NULL;
284 unsigned long *nend;
285 size_t nsize, alloc_size, align_shift;
286 int l;
287
288 if (!is_valid_alignment(alignment))
289 goto fn_exit;
290
291 if (TRdebugLevel > 0) {
292 if (MPL_trvalid2("Invalid MALLOC arena detected at line %d in %s\n", lineno, fname))
293 goto fn_exit;
294 }
295
296 nsize = a;
297 if (nsize & TR_ALIGN_MASK)
298 nsize += (TR_ALIGN_BYTES - (nsize & TR_ALIGN_MASK));
299 if ((allocated + nsize > TRMaxMemAllow) && TRMaxMemAllow) {
300 /* Return a null when memory would be exhausted */
301 /* This is only called when additional debugging is enabled,
302 * so the fact that this does not go through the regular error
303 * message system is not a problem. */
304 MPL_error_printf("Exceeded allowed memory!\n");
305 goto fn_exit;
306 }
307
308 /*
309 * Memory layout:
310 * _______________________________________
311 * | pad | TrSPACE | user space | cookie |
312 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
313 * ^ | | |
314 * real_head: pointer we got from underlying malloc (len(pad) == align_shift)
315 * ^ | |
316 * head: our own metadata block for memory tracing
317 * ^ |
318 * Pointer returned to user (aligned if requested)
319 * ^
320 * Cookied at the tail (unsigned long)
321 */
322
323 alloc_size = alignment + sizeof(TrSPACE) + nsize + sizeof(unsigned long);
324
325 new = (char *) malloc(alloc_size);
326 if (!new)
327 goto fn_exit;
328
329 if (TRSetBytes)
330 memset(new, TRDefaultByte, alloc_size);
331
332 if (alignment > 0)
333 align_shift = alignment - ((uintptr_t) new + sizeof(TrSPACE)) % alignment;
334 else
335 align_shift = 0;
336 if (align_shift == alignment)
337 align_shift = 0; /* buffer was already aligned at desired boundary */
338 /* Cast to (void*) to avoid false warnings about alignment issues */
339 head = (TRSPACE *) (void *) (new + align_shift);
340 head->real_head = new; /* Record the pointer we got from malloc */
341 new += sizeof(TrSPACE) + align_shift;
342 assert(!alignment || (uintptr_t) new % alignment == 0);
343
344 if (TRhead[0] != TRHEAD_PRESENTINAL || TRhead[2] != TRHEAD_POSTSENTINAL) {
345 MPL_error_printf("TRhead corrupted - likely memory overwrite.\n");
346 free(head->real_head);
347 new = NULL;
348 goto fn_exit;
349 }
350 if (TRhead[1]) {
351 MPL_VG_MAKE_MEM_DEFINED(&TRhead[1]->prev, sizeof(TRhead[1]->prev));
352 TRhead[1]->prev = head;
353 MPL_VG_MAKE_MEM_NOACCESS(&TRhead[1]->prev, sizeof(TRhead[1]->prev));
354 }
355 head->next = TRhead[1];
356 TRhead[1] = head;
357 head->type = TR_MALLOC_TYPE;
358 head->class = class;
359 head->prev = 0;
360 head->size = nsize;
361 head->id = TRid;
362 head->lineno = lineno;
363 if ((l = (int) strlen(fname)) > TR_FNAME_LEN - 1)
364 fname += (l - (TR_FNAME_LEN - 1));
365 MPL_strncpy(head->fname, fname, TR_FNAME_LEN);
366 head->fname[TR_FNAME_LEN - 1] = 0;
367 head->cookie = COOKIE_VALUE;
368 /* Cast to (void*) to avoid false warning about alignment */
369 nend = (unsigned long *) (void *) (new + nsize);
370 nend[0] = COOKIE_VALUE;
371
372 if (!classes_initialized)
373 init_classes();
374
375 /* Add to the hash counters */
376 allocation_classes[class].curr_allocated_mem += nsize;
377 allocation_classes[class].total_allocated_mem += nsize;
378 allocation_classes[class].num_allocations++;
379 if (allocation_classes[class].curr_allocated_mem > allocation_classes[class].max_allocated_mem)
380 allocation_classes[class].max_allocated_mem = allocation_classes[class].curr_allocated_mem;
381
382 allocated += nsize;
383 if (allocated > TRMaxMem) {
384 TRMaxMem = allocated;
385 TRMaxMemId = TRid;
386 }
387 frags++;
388
389 if (TRlevel & TR_MALLOC) {
390 /* Note that %08p (what we'd like to use) isn't accepted by
391 * all compilers */
392 MPL_error_printf("[%d] Allocating %ld(%ld) bytes at %8p in %s[%d]\n",
393 world_rank, (long) a, (long) nsize, new, fname, lineno);
394 }
395
396 /* Warn the user about tracing overhead if the total memory overhead for
397 * tracing is larger than the threshold, TRMaxOverhead. */
398 TRCurOverhead += sizeof(TrSPACE) + align_shift;
399 if ((TRCurOverhead > TRMaxOverhead) && TRMaxOverhead) {
400 MPL_error_printf("[%d] %.1lf MB was used for memory usage tracing!\n",
401 world_rank, (double) TRCurOverhead / 1024 / 1024);
402 TRMaxOverhead = TRMaxOverhead * 2;
403 }
404
405 /* Without these macros valgrind actually catches far fewer errors when
406 * using --enable-g=mem. Note that it would be nice to use
407 * MPL_VG_MALLOCLIKE_BLOCK and friends, but they don't work when the
408 * underlying source of the memory is malloc/free. */
409 MPL_VG_MAKE_MEM_UNDEFINED(new, nsize);
410 MPL_VG_MAKE_MEM_NOACCESS(head->real_head, sizeof(TrSPACE) + align_shift);
411 MPL_VG_MAKE_MEM_NOACCESS(nend, sizeof(unsigned long));
412 fn_exit:
413 return (void *) new;
414 }
415
MPL_trmalloc(size_t a,MPL_memory_class class,int lineno,const char fname[])416 void *MPL_trmalloc(size_t a, MPL_memory_class class, int lineno, const char fname[])
417 {
418 void *retval;
419
420 TR_THREAD_CS_ENTER;
421 retval = trmalloc(0, a, class, lineno, fname);
422 TR_THREAD_CS_EXIT;
423
424 return retval;
425 }
426
427 #ifdef MPL_DEFINE_ALIGNED_ALLOC
MPL_traligned_alloc(size_t alignment,size_t size,MPL_memory_class class,int lineno,const char fname[])428 void *MPL_traligned_alloc(size_t alignment, size_t size, MPL_memory_class class, int lineno,
429 const char fname[])
430 {
431 void *memptr;
432
433 TR_THREAD_CS_ENTER;
434 memptr = trmalloc(alignment, size, class, lineno, fname);
435 TR_THREAD_CS_EXIT;
436
437 return memptr;
438 }
439 #endif /* #ifdef MPL_DEFINE_ALIGNED_ALLOC */
440
441 /*+C
442 MPL_trfree - Free with tracing
443
444 Input Parameters:
445 + a - pointer to a block allocated with trmalloc
446 . line - line in file where called
447 - file - Name of file where called
448 +*/
trfree(void * a_ptr,int line,const char file[])449 static void trfree(void *a_ptr, int line, const char file[])
450 {
451 TRSPACE *head;
452 unsigned long *nend;
453 size_t nset;
454 int l;
455
456 /* Don't try to handle empty blocks */
457 if (!a_ptr)
458 return;
459
460 if (TRdebugLevel > 0) {
461 if (MPL_trvalid2("Invalid MALLOC arena detected by FREE at line %d in %s\n", line, file))
462 return;
463 }
464
465 /* Alignment guaranteed by the way a_ptr was allocated. Use
466 * (void *) cast to suppress false warning about alignment issues */
467 head = (TRSPACE *) (void *) (((char *) a_ptr) - sizeof(TrSPACE));
468
469 /* We need to mark the memory as defined before performing our own error
470 * checks or valgrind will flag the trfree function as erroneous. The real
471 * free() at the end of this function will mark the whole block as NOACCESS
472 * again. See the corresponding block in the trmalloc function for more
473 * info. */
474 MPL_VG_MAKE_MEM_DEFINED(head, sizeof(TrSPACE));
475
476 if (head->cookie != COOKIE_VALUE) {
477 /* Damaged header */
478 MPL_error_printf("[%d] Block at address %p is corrupted; cannot free;\n"
479 "may be block not allocated with MPL_trmalloc or MALLOC\n"
480 "called in %s at line %d\n", world_rank, a_ptr, file, line);
481 return;
482 }
483 /* Cast to (void*) to avoid false warning about alignment */
484 nend = (unsigned long *) (void *) ((char *) a_ptr + head->size);
485 /* Check that nend is properly aligned */
486 if ((sizeof(long) == 4 && ((long) nend & 0x3) != 0) ||
487 (sizeof(long) == 8 && ((long) nend & 0x7) != 0)) {
488 MPL_error_printf
489 ("[%d] Block at address %p is corrupted (invalid address or header)\n"
490 "called in %p at line %d\n", world_rank, a_ptr, file, line);
491 return;
492 }
493
494 MPL_VG_MAKE_MEM_DEFINED(nend, sizeof(*nend));
495 if (*nend != COOKIE_VALUE) {
496 if (*nend == ALREADY_FREED) {
497 if (TRidSet) {
498 MPL_error_printf
499 ("[%d] Block [id=%d(%lu)] at address %p was already freed\n", world_rank,
500 head->id, (unsigned long) head->size, a_ptr);
501 } else {
502 MPL_error_printf("[%d] Block at address %p was already freed\n", world_rank, a_ptr);
503 }
504 head->fname[TR_FNAME_LEN - 1] = 0; /* Just in case */
505 head->freed_fname[TR_FNAME_LEN - 1] = 0; /* Just in case */
506 MPL_error_printf("[%d] Block freed in %s[%d]\n",
507 world_rank, head->freed_fname, head->freed_lineno);
508 MPL_error_printf("[%d] Block allocated at %s[%d]\n",
509 world_rank, head->fname, head->lineno);
510 return;
511 } else {
512 /* Damaged tail */
513 if (TRidSet) {
514 MPL_error_printf
515 ("[%d] Block [id=%d(%lu)] at address %p is corrupted (probably write past end)\n",
516 world_rank, head->id, (unsigned long) head->size, a_ptr);
517 } else {
518 MPL_error_printf
519 ("[%d] Block at address %p is corrupted (probably write past end)\n",
520 world_rank, a_ptr);
521 }
522 head->fname[TR_FNAME_LEN - 1] = 0; /* Just in case */
523 MPL_error_printf("[%d] Block being freed allocated in %s[%d]\n",
524 world_rank, head->fname, head->lineno);
525 MPL_error_printf("[%d] Block cookie should be %lx but was %lx\n",
526 world_rank, (long) COOKIE_VALUE, *nend);
527 }
528 }
529 /* Mark the location freed */
530 *nend = ALREADY_FREED;
531 head->freed_lineno = line;
532 if ((l = (int) strlen(file)) > TR_FNAME_LEN - 1)
533 file += (l - (TR_FNAME_LEN - 1));
534 MPL_strncpy(head->freed_fname, file, TR_FNAME_LEN);
535
536 allocation_classes[head->class].curr_allocated_mem -= head->size;
537
538 allocated -= head->size;
539 frags--;
540 if (head->prev) {
541 MPL_VG_MAKE_MEM_DEFINED(&head->prev->next, sizeof(head->prev->next));
542 head->prev->next = head->next;
543 MPL_VG_MAKE_MEM_NOACCESS(&head->prev->next, sizeof(head->prev->next));
544 } else {
545 TRhead[1] = head->next;
546 }
547
548 if (head->next) {
549 MPL_VG_MAKE_MEM_DEFINED(&head->next->prev, sizeof(head->next->prev));
550 head->next->prev = head->prev;
551 MPL_VG_MAKE_MEM_NOACCESS(&head->next->prev, sizeof(head->next->prev));
552 }
553
554 if (TRlevel & TR_FREE) {
555 MPL_error_printf("[%d] Freeing %lu bytes at %p in %s[%d]\n",
556 world_rank, (unsigned long) head->size, a_ptr, file, line);
557 }
558
559 TRCurOverhead -= (uintptr_t) a_ptr - (uintptr_t) head->real_head;
560
561 /*
562 * Now, scrub the data (except possibly the first few ints) to
563 * help catch access to already freed data
564 */
565 /* FIXME why do we skip the first few ints? [goodell@] */
566 /* Answer lost in time. Probably because in some case, the
567 * first few bytes provided useful information in tracking down
568 * a problem. */
569 if (head->size > 2 * sizeof(int)) {
570 /* Now that nset is size_t, it might be defined as unsigned,
571 * so we can't compare nset - 2*sizeof(int) against zero */
572 nset = head->size - 2 * sizeof(int);
573 /* If an upper layer (like the handle allocation code) ever used the
574 * MPL_VG_MAKE_MEM_NOACCESS macro on part/all of the data we gave
575 * them then our memset will elicit "invalid write" errors from
576 * valgrind. Mark it as accessible but undefined here to prevent this. */
577 MPL_VG_MAKE_MEM_UNDEFINED((char *) a_ptr + 2 * sizeof(int), nset);
578 if (TRSetBytes)
579 memset((char *) a_ptr + 2 * sizeof(int), TRFreedByte, nset);
580 }
581 free(head->real_head);
582 }
583
MPL_trfree(void * a_ptr,int line,const char fname[])584 void MPL_trfree(void *a_ptr, int line, const char fname[])
585 {
586 TR_THREAD_CS_ENTER;
587 trfree(a_ptr, line, fname);
588 TR_THREAD_CS_EXIT;
589 }
590
591 /*+C
592 MPL_trvalid - test the allocated blocks for validity. This can be used to
593 check for memory overwrites.
594
595 Input Parameters:
596 . str - string to write out only if an error is detected.
597
598 Return value:
599 The number of errors detected.
600
601 Output Effect:
602 Error messages are written to stdout. These have the form of either
603
604 $ Block [id=%d(%d)] at address %lx is corrupted (probably write past end)
605 $ Block allocated in <filename>[<linenumber>]
606
607 if the sentinal at the end of the block has been corrupted, and
608
609 $ Block at address %lx is corrupted
610
611 if the sentinal at the begining of the block has been corrupted.
612
613 The address is the actual address of the block. The id is the
614 value of TRID.
615
616 No output is generated if there are no problems detected.
617 +*/
trvalid(const char str[])618 static int trvalid(const char str[])
619 {
620 return MPL_trvalid2(str, -1, (const char *) 0);
621 }
622
MPL_trvalid2(const char str[],int line,const char file[])623 int MPL_trvalid2(const char str[], int line, const char file[])
624 {
625 TRSPACE *head;
626 TRSPACE *next;
627 char *a;
628 unsigned long *nend;
629 int errs = 0;
630
631 if (TRhead[0] != TRHEAD_PRESENTINAL || TRhead[2] != TRHEAD_POSTSENTINAL) {
632 MPL_error_printf("TRhead corrupted - likely memory overwrite.\n");
633 errs++;
634 goto fn_exit;
635 }
636 head = TRhead[1];
637 while (head) {
638 /* mark defined before accessing head contents */
639 MPL_VG_MAKE_MEM_DEFINED(head, sizeof(*head));
640 if (head->cookie != COOKIE_VALUE) {
641 if (!errs) {
642 if (line > 0)
643 MPL_error_printf(str, line, file);
644 else
645 MPL_error_printf("%s\n", str);
646 }
647 errs++;
648 MPL_error_printf
649 ("[%d] Block at address %p is corrupted (invalid cookie in head)\n",
650 world_rank, head + 1);
651 MPL_VG_MAKE_MEM_NOACCESS(head, sizeof(*head));
652 /* Must stop because if head is invalid, then the data in the
653 * head is probably also invalid, and using could lead to
654 * SEGV or BUS */
655 goto fn_exit;
656 }
657 /* Get the address of the first byte of the memory, which begins
658 * just after the end of the header. We must use the full header
659 * (TrSPACE) rather than the struct with the data (TRSPACE) because
660 * the full header is padded to ensure correct byte alignment with
661 * the data */
662 a = (char *) ((TrSPACE *) head + 1);
663 /* Cast to (void*) to avoid false warning about alignment */
664 nend = (unsigned long *) (void *) (a + head->size);
665
666 /* mark defined before accessing nend contents */
667 MPL_VG_MAKE_MEM_DEFINED(nend, sizeof(*nend));
668
669 if (nend[0] != COOKIE_VALUE) {
670 if (!errs) {
671 if (line > 0)
672 MPL_error_printf(str, line, file);
673 else
674 MPL_error_printf("%s\n", str);
675 }
676 errs++;
677 head->fname[TR_FNAME_LEN - 1] = 0; /* Just in case */
678 if (TRidSet) {
679 MPL_error_printf
680 ("[%d] Block [id=%d(%lu)] at address %p is corrupted (probably write past end)\n",
681 world_rank, head->id, (unsigned long) head->size, a);
682 } else {
683 MPL_error_printf
684 ("[%d] Block at address %p is corrupted (probably write past end)\n",
685 world_rank, a);
686 }
687 MPL_error_printf("[%d] Block allocated in %s[%d]\n",
688 world_rank, head->fname, head->lineno);
689 MPL_error_printf("[%d] Block cookie should be %lx but was %lx\n",
690 world_rank, (long) COOKIE_VALUE, *nend);
691 }
692
693 /* set both regions back to NOACCESS */
694 next = head->next;
695 MPL_VG_MAKE_MEM_NOACCESS(head, sizeof(*head));
696 MPL_VG_MAKE_MEM_NOACCESS(nend, sizeof(*nend));
697 head = next;
698 }
699 fn_exit:
700 return errs;
701 }
702
MPL_trvalid(const char str[])703 int MPL_trvalid(const char str[])
704 {
705 int retval;
706 TR_THREAD_CS_ENTER;
707 retval = trvalid(str);
708 TR_THREAD_CS_EXIT;
709 return retval;
710 }
711
712 /*+C
713 MPL_trdump - Dump the allocated memory blocks to a file
714
715 Input Parameters:
716 + fp - file pointer. If fp is NULL, stderr is assumed.
717 - minid - Only print allocated memory blocks whose id is at least 'minid'
718
719 +*/
trdump(FILE * fp,int minid)720 static void trdump(FILE * fp, int minid)
721 {
722 TRSPACE *head;
723 #ifdef VALGRIND_MAKE_MEM_NOACCESS
724 TRSPACE *old_head;
725 #endif
726
727 if (fp == 0)
728 fp = stderr;
729 if (TRhead[0] != TRHEAD_PRESENTINAL || TRhead[2] != TRHEAD_POSTSENTINAL) {
730 MPL_error_printf("TRhead corrupted - likely memory overwrite.\n");
731 return;
732 }
733 head = TRhead[1];
734 while (head) {
735 /* these "rank and size" strings are supposed to be small: enough to
736 * hold an mpi rank, a size, and a hexadecimal address. */
737 #define ADDRESS_STR_BUFLEN 256
738
739 char address_str[ADDRESS_STR_BUFLEN];
740 MPL_VG_MAKE_MEM_DEFINED(head, sizeof(*head));
741 if (head->id >= minid) {
742 address_str[ADDRESS_STR_BUFLEN - 1] = 0;
743 snprintf(address_str, ADDRESS_STR_BUFLEN - 1, "[%d] %lu at [%p],", world_rank,
744 (unsigned long) head->size, (char *) head + sizeof(TrSPACE));
745 head->fname[TR_FNAME_LEN - 1] = 0; /* Be extra careful */
746 if (TRidSet) {
747 /* For head->id >= 0, we can add code to map the id to
748 * the name of a package, rather than using a raw number */
749 fprintf(fp, "%s id = %d %s[%d]\n", address_str, head->id, head->fname,
750 head->lineno);
751 } else {
752 fprintf(fp, "%s %s[%d]\n", address_str, head->fname, head->lineno);
753 }
754 }
755 #ifdef VALGRIND_MAKE_MEM_NOACCESS
756 old_head = head;
757 #endif
758 head = head->next;
759 MPL_VG_MAKE_MEM_NOACCESS(old_head, sizeof(*old_head));
760 }
761 /*
762 msg_fprintf(fp, "# [%d] The maximum space allocated was %ld bytes [%ld]\n",
763 world_rank, TRMaxMem, TRMaxMemId);
764 */
765 }
766
MPL_trdump(FILE * fp,int minid)767 void MPL_trdump(FILE * fp, int minid)
768 {
769 TR_THREAD_CS_ENTER;
770 trdump(fp, minid);
771 TR_THREAD_CS_EXIT;
772 }
773
774 /*+C
775 MPL_trcalloc - Calloc with tracing
776
777 Input Parameters:
778 . nelem - number of elements to allocate
779 . elsize - size of each element
780 . lineno - line number where used. Use __LINE__ for this
781 . fname - file name where used. Use __FILE__ for this
782
783 Returns:
784 Double aligned pointer to requested storage, or null if not
785 available.
786 +*/
trcalloc(size_t nelem,size_t elsize,MPL_memory_class class,int lineno,const char fname[])787 static void *trcalloc(size_t nelem, size_t elsize, MPL_memory_class class, int lineno,
788 const char fname[])
789 {
790 void *p;
791
792 p = trmalloc(0, nelem * elsize, class, lineno, fname);
793 if (p) {
794 memset(p, 0, nelem * elsize);
795 }
796 return p;
797 }
798
MPL_trcalloc(size_t nelem,size_t elsize,MPL_memory_class class,int lineno,const char fname[])799 void *MPL_trcalloc(size_t nelem, size_t elsize, MPL_memory_class class, int lineno,
800 const char fname[])
801 {
802 void *retval;
803 TR_THREAD_CS_ENTER;
804 retval = trcalloc(nelem, elsize, class, lineno, fname);
805 TR_THREAD_CS_EXIT;
806 return retval;
807 }
808
809 /*+C
810 MPL_trrealloc - Realloc with tracing
811
812 Input Parameters:
813 . p - pointer to old storage
814 . size - number of bytes to allocate
815 . lineno - line number where used. Use __LINE__ for this
816 . fname - file name where used. Use __FILE__ for this
817
818 Returns:
819 Double aligned pointer to requested storage, or null if not
820 available. This implementation ALWAYS allocates new space and copies
821 the contents into the new space.
822 +*/
trrealloc(void * p,size_t size,MPL_memory_class class,int lineno,const char fname[])823 static void *trrealloc(void *p, size_t size, MPL_memory_class class, int lineno, const char fname[])
824 {
825 void *pnew;
826 size_t nsize;
827 TRSPACE *head = 0;
828
829 /* We should really use the size of the old block... */
830 if (p) {
831 head = (TRSPACE *) (void *) ((char *) p - sizeof(TrSPACE));
832 MPL_VG_MAKE_MEM_DEFINED(head, sizeof(*head)); /* mark defined before accessing contents */
833 if (head->cookie != COOKIE_VALUE) {
834 /* Damaged header */
835 MPL_error_printf("[%d] Block at address %p is corrupted; cannot realloc;\n"
836 "may be block not allocated with MPL_trmalloc or MALLOC\n",
837 world_rank, p);
838 return 0;
839 }
840 }
841
842 /* Per the POSIX Standard, realloc() with zero size has two possible
843 * results. In both cases the given pointer (p) is freed, and the function
844 * will either return NULL or a unique value that can safely be passed to
845 * free(). We return NULL here because that is more likely to catch
846 * programming errors at higher levels. */
847 if (!size) {
848 trfree(p, lineno, fname);
849 return NULL;
850 }
851
852 pnew = trmalloc(0, size, class, lineno, fname);
853
854 if (p && pnew) {
855 nsize = size;
856 if (head->size < nsize)
857 nsize = head->size;
858 memcpy(pnew, p, nsize);
859 trfree(p, lineno, fname);
860 }
861
862 /* Re-mark the head as NOACCESS before returning. */
863 /* FIXME: Note head is no longer valid after MPL_trfree above */
864 if (head) {
865 MPL_VG_MAKE_MEM_NOACCESS(head, sizeof(*head));
866 }
867
868 /* If the MPL_trmalloc failed above pnew will be NULL, just like a
869 * regular realloc failure. */
870 return pnew;
871 }
872
MPL_trrealloc(void * p,size_t size,MPL_memory_class class,int lineno,const char fname[])873 void *MPL_trrealloc(void *p, size_t size, MPL_memory_class class, int lineno, const char fname[])
874 {
875 void *retval;
876 TR_THREAD_CS_ENTER;
877 retval = trrealloc(p, size, class, lineno, fname);
878 TR_THREAD_CS_EXIT;
879 return retval;
880 }
881
trmmap(void * addr,size_t length,int prot,int flags,int fd,off_t offset,MPL_memory_class class,int lineno,const char fname[])882 static void *trmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset,
883 MPL_memory_class class, int lineno, const char fname[])
884 {
885 char *new = NULL;
886
887 new = (char *) mmap(addr, length, prot, flags, fd, offset);
888 if (new == MAP_FAILED)
889 goto fn_exit;
890
891 if (TRlevel & TR_MMAP) {
892 MPL_error_printf("[%d] Mmapping %ld(%ld) bytes at %p in %s[%d]\n",
893 world_rank, (long) length, (long) length, new, fname, lineno);
894 }
895
896 if (!classes_initialized)
897 init_classes();
898
899 allocation_classes[class].curr_allocated_mem += length;
900 allocation_classes[class].total_allocated_mem += length;
901 allocation_classes[class].num_allocations++;
902 if (allocation_classes[class].max_allocated_mem < allocation_classes[class].curr_allocated_mem)
903 allocation_classes[class].max_allocated_mem = allocation_classes[class].curr_allocated_mem;
904
905 fn_exit:
906 return (void *) new;
907 }
908
MPL_trmmap(void * addr,size_t length,int prot,int flags,int fd,off_t offset,MPL_memory_class class,int lineno,const char fname[])909 void *MPL_trmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset,
910 MPL_memory_class class, int lineno, const char fname[])
911 {
912 void *retval;
913 TR_THREAD_CS_ENTER;
914 retval = trmmap(addr, length, prot, flags, fd, offset, class, lineno, fname);
915 TR_THREAD_CS_EXIT;
916 return retval;
917 }
918
trmunmap(void * addr,size_t length,MPL_memory_class class,int lineno,const char fname[])919 static void trmunmap(void *addr, size_t length, MPL_memory_class class, int lineno,
920 const char fname[])
921 {
922 allocation_classes[class].curr_allocated_mem -= length;
923
924 munmap(addr, length);
925 }
926
MPL_trmunmap(void * addr,size_t length,MPL_memory_class class,int lineno,const char fname[])927 void MPL_trmunmap(void *addr, size_t length, MPL_memory_class class, int lineno, const char fname[])
928 {
929 TR_THREAD_CS_ENTER;
930 trmunmap(addr, length, class, lineno, fname);
931 TR_THREAD_CS_EXIT;
932 }
933
934 /*+C
935 MPL_trstrdup - Strdup with tracing
936
937 Input Parameters:
938 . str - string to duplicate
939 . lineno - line number where used. Use __LINE__ for this
940 . fname - file name where used. Use __FILE__ for this
941
942 Returns:
943 Pointer to copy of the input string.
944 +*/
trstrdup(const char * str,int lineno,const char fname[])945 static void *trstrdup(const char *str, int lineno, const char fname[])
946 {
947 void *p;
948 size_t len = strlen(str) + 1;
949
950 p = trmalloc(0, len, MPL_MEM_STRINGS, lineno, fname);
951 if (p) {
952 memcpy(p, str, len);
953 }
954 return p;
955 }
956
MPL_trstrdup(const char * str,int lineno,const char fname[])957 void *MPL_trstrdup(const char *str, int lineno, const char fname[])
958 {
959 void *retval;
960 TR_THREAD_CS_ENTER;
961 retval = trstrdup(str, lineno, fname);
962 TR_THREAD_CS_EXIT;
963 return retval;
964 }
965
MPL_trcategorydump(FILE * fp)966 void MPL_trcategorydump(FILE * fp)
967 {
968 int i;
969
970 fprintf(fp, "%16s\t%16s\t%16s\t%16s\t%16s\n",
971 "CLASS", "MAX ALLOCATED", "CURR ALLOCATED", "TOT ALLOCATIED", "NUM ALLOCATIONS");
972 for (i = 0; i < MPL_MAX_MEMORY_CLASS; i++) {
973 fprintf(fp, "%16s\t%16ld\t%16ld\t%16ld\t%16ld\n",
974 allocation_class_strings[i],
975 allocation_classes[i].max_allocated_mem,
976 allocation_classes[i].curr_allocated_mem,
977 allocation_classes[i].total_allocated_mem, allocation_classes[i].num_allocations);
978 }
979 }
980
981
MPL_strdup_no_spaces(const char * str)982 char *MPL_strdup_no_spaces(const char *str)
983 {
984 char *newstr = MPL_malloc(strlen(str) + 1, MPL_MEM_COLL);
985
986 int j = 0;
987 for (int i = 0; i < strlen(str); i++) {
988 if (str[i] != ' ')
989 newstr[j++] = str[i];
990 }
991 newstr[j] = 0;
992
993 return newstr;
994 }
995