Home | History | Annotate | Download | only in solaris
      1 /* Tests that Valgrind coredump support works correctly even when
      2    number of segments exceeds 0xffff.
      3    For this to work, large number of pages is mmap()'ed into the
      4    process (virtual) address space. These pages must not be adjacent
      5    to each other otherwise the memory manager will coalesce them
      6    into a single one. So they are one page apart.
      7 
      8    NOTE: Valgrind's internal limit VG_N_SEGMENTS must be at least
      9    140000 otherwise you get a fatal error similar to this one:
     10        "FATAL: VG_N_SEGMENTS is too low."
     11 
     12    Test case passes successfully if the number of segments is
     13    correctly displayed in elfdump output:
     14 
     15    $ elfdump -e vgcore.*
     16 ELF Header
     17   ...
     18   e_phoff: 0x34  e_phentsize: 32  e_phnum: PN_XNUM (see shdr[0].sh_info)
     19                                   ^^^^^^^^^^^^^^^^
     20 Section Header[0]:  (ELF Ehdr extensions)
     21   ...
     22     sh_link: 0 (e_shstrndx)  sh_info: 65554 (e_phnum)
     23                              ^^^^^^^^^^^^^^^^^^^^^^^^
     24 */
     25 
     26 #include <assert.h>
     27 #include <errno.h>
     28 #include <fcntl.h>
     29 #include <inttypes.h>
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #include <unistd.h>
     34 #include <sys/ipc.h>
     35 #include <sys/mman.h>
     36 #include <sys/procfs.h>
     37 #include <sys/stat.h>
     38 
     39 #define SEGMENTS (0xffff + 2)
     40 
     41 #if 0
     42 #define DEBUG(format, ...) printf(format, ## __VA_ARGS__)
     43 #else
     44 #define DEBUG(format, ...)
     45 #endif
     46 
     47 #define PRINT(format, ...) printf(format, ## __VA_ARGS__)
     48 
     49 /* Represents a free range of a virtual address space. */
     50 typedef struct range {
     51    uintptr_t     start;
     52    uintptr_t     end;
     53    size_t        size;
     54    struct range *next;
     55 } range_t;
     56 
     57 /* Processes a single prmap_t entry and builds the free ranges. */
     58 static int process_map(const prmap_t *map, range_t **ranges_head,
     59                        range_t **ranges_tail, size_t page_size)
     60 {
     61    assert(map != NULL);
     62    assert(ranges_head != NULL);
     63    assert(ranges_tail != NULL);
     64 
     65    range_t *head = *ranges_head;
     66    range_t *tail = *ranges_tail;
     67 
     68    DEBUG("processing map with addr=%p and size=%zu\n",
     69          map->pr_vaddr, map->pr_size);
     70 
     71    if (head == NULL) {
     72       head = calloc(1, sizeof(range_t));
     73       if (head == NULL) {
     74          fprintf(stderr, "calloc failed\n");
     75          return -1;
     76       }
     77       head->start = (uintptr_t) page_size; // do not start at address '0'
     78 
     79       tail = head;
     80       *ranges_head = head;
     81       *ranges_tail = tail;
     82    }
     83 
     84    if ((map->pr_vaddr < tail->start) ||
     85        (map->pr_vaddr - tail->start < 3 * page_size)) {
     86       DEBUG("last range at %p is too small, skipping it\n",
     87             tail->start);
     88       tail->start = map->pr_vaddr + map->pr_size + page_size;
     89       return 0;
     90    }
     91 
     92    tail->end = map->pr_vaddr - page_size;
     93    tail->size = tail->end - tail->start;
     94 
     95    range_t *new_one = calloc(1, sizeof(range_t));
     96    if (new_one == NULL) {
     97       fprintf(stderr, "calloc failed\n");
     98       return -1;
     99    }
    100 
    101    new_one->start = map->pr_vaddr + map->pr_size + page_size;
    102    tail->next = new_one;
    103    *ranges_tail = new_one;
    104    return 0;
    105 }
    106 
    107 /* Reads /proc/self/map and builds free ranges. */
    108 static range_t *read_proc_map(size_t page_size)
    109 {
    110    int fd = open("/proc/self/map", O_RDONLY);
    111    if (fd == -1) {
    112       int error = errno;
    113       fprintf(stderr, "open failed: %s (%d)\n", strerror(error), error);
    114       return NULL;
    115    }
    116 
    117    prmap_t map;
    118    range_t *ranges_head = NULL;
    119    range_t *ranges_tail = NULL;
    120 
    121    ssize_t bytes = read(fd, &map, sizeof(map));
    122    while (bytes == sizeof(map)) {
    123       if (map.pr_size != 0) {
    124          if (process_map(&map, &ranges_head, &ranges_tail,
    125                          page_size) != 0) {
    126             return NULL;
    127          }
    128       }
    129       bytes = read(fd, &map, sizeof(map));
    130    }
    131 
    132    if (ranges_tail != NULL) {
    133       ranges_tail->end = (uintptr_t) ~0;
    134       ranges_tail->size = ranges_tail->end - ranges_tail->start;
    135    }
    136 
    137    close(fd);
    138    return ranges_head;
    139 }
    140 
    141 static void print_ranges(const range_t *head)
    142 {
    143    while (head != NULL) {
    144       DEBUG("free range [%8p - %8p] of size %7zuK\n",
    145             head->start, head->end, head->size / 1024);
    146       head = head->next;
    147    }
    148 }
    149 
    150 static size_t sum_ranges(const range_t *head)
    151 {
    152    size_t sum = 0;
    153 
    154    while (head != NULL) {
    155       sum += head->size;
    156       head = head->next;
    157    }
    158 
    159    return sum;
    160 }
    161 
    162 static void *map_segment(void *fixed_addr)
    163 {
    164    int flags = MAP_NORESERVE | MAP_ANON | MAP_PRIVATE | MAP_FIXED;
    165    void *addr = mmap(fixed_addr, 1, PROT_READ | PROT_WRITE,
    166                      flags, -1, 0);
    167    if (addr == MAP_FAILED) {
    168       int error = errno;
    169       fprintf(stderr, "mmap failed: %s (%d)\n", strerror(error), error);
    170       return NULL;
    171    }
    172    assert(addr == fixed_addr);
    173 
    174    *((char *) addr) = 1; // make the mmap'ed page dirty
    175    // DEBUG("mmap(%8p) = %8p\n", fixed_addr, addr);
    176    return addr;
    177 }
    178 
    179 int main(int argc, const char *argv[])
    180 {
    181    long page_size = sysconf(_SC_PAGESIZE);
    182    if (page_size == -1) {
    183       perror("sysconf");
    184       return 1;
    185    }
    186 
    187    PRINT("Page size determined as %ld bytes.\n", page_size);
    188 
    189    range_t *ranges = read_proc_map(page_size);
    190    print_ranges(ranges);
    191 
    192    size_t sum = sum_ranges(ranges);
    193    if (sum < SEGMENTS * page_size) {
    194       fprintf(stderr, "Free (virtual) address space cannot accomodate "
    195               "%u pages.\n", SEGMENTS);
    196       return 1;
    197    }
    198 
    199    PRINT("mmap()'ing %u segments:", SEGMENTS);
    200    fflush(stdout);
    201 
    202    unsigned int segment = 0;
    203    while ((ranges != NULL) && (segment < SEGMENTS)) {
    204       unsigned int page;
    205       for (page = 0; page < ranges->size / (2 * page_size); page++) {
    206          uintptr_t start = ranges->start + 2 * page * page_size;
    207          void *addr = map_segment((void *) start);
    208          if (addr == NULL) {
    209             fprintf(stderr, "Mapping failed for segment %u at address "
    210                     "%" PRIxPTR ".\n", segment, start);
    211             return 1;
    212          }
    213 
    214          segment += 1;
    215          if (segment >= SEGMENTS) {
    216             break;
    217          }
    218 
    219          if (segment % (SEGMENTS / 10) == 0) {
    220             PRINT(" %u0%%", segment / (SEGMENTS / 10));
    221             fflush(stdout);
    222          }
    223       }
    224       ranges = ranges->next;
    225    }
    226    assert(segment == SEGMENTS);
    227 
    228    PRINT(".\nDumping core...\n");
    229    char *nihil = NULL;
    230    *nihil = 0; // SEGV here
    231    fprintf(stderr, "Should not reach here.\n");
    232 
    233    return 0;
    234 }
    235