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