text: /*
   * Copyright © 2011-2021 Inria.  All rights reserved.
   * See COPYING in top-level directory.
   */
  
  #include "hwloc.h"
  #include "hwloc/shmem.h"
  #include "private/misc.h" /* for hwloc_getpagesize() */
  
  #include <stdlib.h>
  #include <fcntl.h>
  #include <stdio.h>
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  #include <sys/mman.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <sys/wait.h>
  #include <string.h>
  #include <stdint.h>
  #include <assert.h>
  
  #define EXIT_SKIP 77
  
  /* must match the header defined in hwloc/shmem.c */
  struct header {
    uint32_t header_version;
    uint32_t header_length;
    uint64_t mmap_address;
    uint64_t mmap_length;
  };
  
  static int adopt(int fd, unsigned long fileoffset, unsigned long mmap_address, unsigned long mmap_length, int synthetic_with_distances)
  {
    static hwloc_topology_t adopted;
    char *xmlbuf;
    int xmlbuflen;
    char *origxmlbuf;
    struct hwloc_distances_s *distances;
    unsigned nr = 1;
    int err;
    int ret = EXIT_SKIP;
  
    err = lseek(fd, 0, SEEK_SET);
    assert(!err);
  
    printf(" reading XML dump\n");
    origxmlbuf = malloc(fileoffset);
    assert(origxmlbuf);
    err = read(fd, origxmlbuf, fileoffset);
    assert(err > 0);
  
    printf(" adopting from file at offset %lu with address 0x%lx len %lu\n", fileoffset, mmap_address, mmap_length);
  
    err = hwloc_shmem_topology_adopt(&adopted, fd, fileoffset, (void*)(uintptr_t)mmap_address, mmap_length, 0);
    if (err == -1 && errno == EBUSY) {
      /* may fail on 32bits and on some OS (e.g. darwin from time to time), and even on Linux/64bits if unlucky */
      fprintf(stderr, "Failed to shmem adopt, requested mapping is busy\n");
      goto out_with_origxmlbuf;
    }
    assert(!err);
    printf(" adopted OK\n");
  
    err = hwloc_distances_get_by_type(adopted, HWLOC_OBJ_NUMANODE, &nr, &distances, 0, 0);
    assert(!err);
    if (synthetic_with_distances) {
      assert(nr == 1);
      assert(distances->nbobjs == 3);
      assert(distances->kind == (HWLOC_DISTANCES_KIND_MEANS_LATENCY|HWLOC_DISTANCES_KIND_FROM_USER));
      hwloc_distances_release(adopted, distances);
      printf(" distances OK\n");
    }
  
    err = hwloc_topology_export_xmlbuffer(adopted, &xmlbuf, &xmlbuflen, 0);
    assert(!err);
    printf(" XML export %d bytes\n", xmlbuflen);
    assert((unsigned long) xmlbuflen < fileoffset);
    assert(!memcmp(origxmlbuf, xmlbuf, xmlbuflen));
    hwloc_free_xmlbuffer(adopted, xmlbuf);
    printf(" XML export is identical to original\n");
  
    hwloc_topology_destroy(adopted);
    printf(" destroyed\n");
  
    ret = EXIT_SUCCESS;
  
   out_with_origxmlbuf:
    free(origxmlbuf);
    return ret;
  }
  
  static unsigned long
  find_mmap_addr(unsigned long length)
  {
    unsigned long addr;
    void *tmp_mmap;
    int err;
  
    /* try to find a good address starting from something in the middle of the entire/full address space */
  #if SIZEOF_VOID_P == 8
    addr = 0x8000000000000000UL;
  #else
    addr = 0x80000000UL;
  #endif
    printf("testing mmaps to find room for length %lu\n", length);
  
  again:
    tmp_mmap = mmap((void*)(uintptr_t)addr, length, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0);
    if (tmp_mmap != MAP_FAILED) {
      err = munmap((void*)(uintptr_t)tmp_mmap, length);
      assert(!err);
      if (tmp_mmap == (void*)(uintptr_t) addr) {
        /* worked! */
        printf(" test mmap at 0x%lx succeeded, let's use that!\n", addr);
        return addr;
      }
      printf(" test mmap at 0x%lx returned another address\n", addr);
    } else
      printf(" test mmap at 0x%lx failed (errno %d)\n", addr, errno);
    /* couldn't map there, try again with a smaller address */
    addr >>= 3;
    if (addr)
      goto again;
  
    return 0;
  }
  
  static int test(hwloc_topology_t orig, const char *callname) {
    unsigned long forced_addr;
    unsigned long fileoffset;
    size_t shmem_length;
    int synthetic_with_distances = (hwloc_obj_get_info_by_name(hwloc_get_root_obj(orig), "ShmemSyntheticWithDistances") != NULL);
    char tmpname[] = "/tmp/hwloc_test_shmem.XXXXXX";
    char cmd[512];
    struct stat st;
    int fd, err;
    int ret = EXIT_SKIP;
  
    printf("opening temporary file\n");
    fd = mkstemp(tmpname);
    if (fd < 0) {
      perror("mkstemp");
      goto out;
    }
    printf("opened %s\n", tmpname);
  
    printf("exporting XML\n");
    err = hwloc_topology_export_xml(orig, tmpname, 0);
    assert(!err);
    err = stat(tmpname, &st);
    assert(!err);
    printf("exported %lu bytes\n", (unsigned long) st.st_size);
    fileoffset = st.st_size+1; /* skip a couple bytes to make sure the XML is don" */
    fileoffset = (fileoffset + hwloc_getpagesize() - 1) &~(hwloc_getpagesize() - 1);
    printf("will mmap at file offset %lu\n", fileoffset);
  
    err = hwloc_shmem_topology_get_length(orig, &shmem_length, 0);
    assert(!err);
    printf("need mmap length %lu\n", (unsigned long) shmem_length);
  
    forced_addr = find_mmap_addr((unsigned long) shmem_length);
    if (!forced_addr)
      goto out_with_fd;
  
    printf("write to shmem at address 0x%lx in file %s offset %lu\n", forced_addr, tmpname, fileoffset);
    err = hwloc_shmem_topology_write(orig, fd, fileoffset, (void*)(uintptr_t)forced_addr, shmem_length, 0);
    if (err == -1 && errno == EBUSY) {
      fprintf(stderr, "Failed to shmem write, requested mapping is busy\n");
      goto out_with_fd;
    }
    assert(!err);
    printf("wrote length %lu\n", (unsigned long) shmem_length);
  
    printf("adopting locally\n");
    ret = adopt(fd, fileoffset, forced_addr, shmem_length, synthetic_with_distances);
    assert(ret == EXIT_SUCCESS || ret == EXIT_SKIP);
  
    printf("adopting in other child process\n");
    snprintf(cmd, sizeof(cmd), "%s %s %lu 0x%lx %lu %d", callname, tmpname, fileoffset, forced_addr, (unsigned long) shmem_length, synthetic_with_distances);
    printf("running command %s\n", cmd);
    err = system(cmd);
    assert(WIFEXITED(err));
    printf("child process returned %d\n", WEXITSTATUS(err));
    assert(WEXITSTATUS(err) == EXIT_SUCCESS || WEXITSTATUS(err) == EXIT_SKIP);
  
    /* we caught errors above.
     * return SKIP if both returned SKIP. otherwise SUCCESS
     */
    if (WEXITSTATUS(err) == EXIT_SKIP && ret == EXIT_SKIP)
      ret = EXIT_SKIP;
    else
      ret = EXIT_SUCCESS;
  
   out_with_fd:
    close(fd);
    unlink(tmpname);
   out:
    return ret;
  }
  
  int main(int argc, char *argv[])
  {
    static hwloc_topology_t orig;
    hwloc_obj_t nodes[3];
    uint64_t node_distances[9];
    hwloc_distances_add_handle_t handle;
    unsigned i,j;
    const char *top_srcdir;
    int err, ret, ret2;
  
    if (argc > 1) {
      int fd;
      unsigned long forced_addr;
      unsigned long fileoffset;
      size_t shmem_length;
      int synthetic_with_distances;
  
      if (argc < 6) {
        printf("needs 5 arguments\n");
        return EXIT_FAILURE;
      }
  
      printf(" opening %s\n", argv[1]);
      fd = open(argv[1], O_RDONLY);
      if (fd < 0) {
        perror("open");
        return EXIT_FAILURE;
      }
  
      fileoffset = strtoul(argv[2], NULL, 0);
      forced_addr = strtoul(argv[3], NULL, 0);
      shmem_length = strtoul(argv[4], NULL, 0);
      synthetic_with_distances = atoi(argv[5]);
  
      ret = adopt(fd, fileoffset, forced_addr, shmem_length, synthetic_with_distances);
      close(fd);
      exit(ret);
    }
  
    printf("########################\n");
    printf("creating native topology\n");
    err = hwloc_topology_init(&orig);
    assert(!err);
    err = hwloc_topology_set_all_types_filter(orig, HWLOC_TYPE_FILTER_KEEP_ALL);
    assert(!err);
    err = hwloc_topology_load(orig);
    assert(!err);
  
    ret = test(orig, argv[0]);
  
    printf("destroying original\n");
    hwloc_topology_destroy(orig);
  
    printf("###############################################\n");
    printf("creating synthetic topo with distances topology\n");
    err = hwloc_topology_init(&orig);
    assert(!err);
    err = hwloc_topology_set_synthetic(orig, "node:3 core:2 pu:4");
    assert(!err);
    err = hwloc_topology_load(orig);
    assert(!err);
    err = hwloc_obj_add_info(hwloc_get_root_obj(orig), "ShmemSyntheticWithDistances", "1");
    assert(!err);
  
    printf("adding distance matrix\n");
    for(i=0; i<3; i++) {
      nodes[i] = hwloc_get_obj_by_type(orig, HWLOC_OBJ_NUMANODE, i);
      for(j=0; j<3; j++)
        node_distances[i*3+j] = (i == j ? 10 : 20);
    }
    handle = hwloc_distances_add_create(orig, NULL,
                                        HWLOC_DISTANCES_KIND_MEANS_LATENCY|HWLOC_DISTANCES_KIND_FROM_USER,
                                        0);
    assert(handle);
    err = hwloc_distances_add_values(orig, handle, 3, nodes, node_distances, 0);
    assert(!err);
    err = hwloc_distances_add_commit(orig, handle,
                                     HWLOC_DISTANCES_ADD_FLAG_GROUP);
    assert(!err);
  
    ret2 = test(orig, argv[0]);
  
    printf("destroying original\n");
    hwloc_topology_destroy(orig);
  
    top_srcdir = getenv("HWLOC_TOP_SRCDIR");
    if (top_srcdir) {
      const char *xmlnames[4] = {
        "16intel64-manyVFs.xml",
        "8intel64-4n2t-memattrs.xml",
        "fakecpukinds.xml",
        "fakeheterodistances.xml"
      };
      for(i=0; i<4; i++) {
        char xmlpath[PATH_MAX];
        snprintf(xmlpath, sizeof(xmlpath), "%s/tests/hwloc/xml/%s", top_srcdir, xmlnames[i]);
  
        printf("#########################################\n");
        printf("creating from XML %s\n", xmlpath);
        err = hwloc_topology_init(&orig);
        assert(!err);
        err = hwloc_topology_set_xml(orig, xmlpath);
        assert(!err);
        err = hwloc_topology_set_all_types_filter(orig, HWLOC_TYPE_FILTER_KEEP_ALL);
        assert(!err);
        err = hwloc_topology_load(orig);
        assert(!err);
  
        ret = test(orig, argv[0]);
  
        printf("destroying original\n");
        hwloc_topology_destroy(orig);
      }
    } else {
      printf("#########################################\n");
      printf("Skipping XML tests because HWLOC_TOP_SRCDIR isn't defined in the environment\n");
    }
  
    /* we caught errors above.
     * return SKIP if both returned SKIP. otherwise SUCCESS
     */
    if (ret == EXIT_SKIP && ret2 == EXIT_SKIP)
      ret = EXIT_SKIP;
    else
      ret = EXIT_SUCCESS;
  
    return ret;
  }
  