root/opal/mca/hwloc/hwloc201/hwloc/hwloc/topology-xml.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hwloc__xml_verbose
  2. hwloc_nolibxml_import
  3. hwloc_nolibxml_export
  4. hwloc_xml_callbacks_register
  5. hwloc_xml_callbacks_reset
  6. hwloc__xml_import_object_attr
  7. hwloc__xml_import_info
  8. hwloc__xml_import_pagetype
  9. hwloc__xml_v1import_distances
  10. hwloc__xml_import_userdata
  11. hwloc__xml_import_report_outoforder
  12. hwloc__xml_import_object
  13. hwloc__xml_v2import_distances
  14. hwloc__xml_import_diff_one
  15. hwloc__xml_import_diff
  16. hwloc_convert_from_v1dist_floats
  17. hwloc_look_xml
  18. hwloc_topology_diff_load_xml
  19. hwloc_topology_diff_load_xmlbuffer
  20. hwloc__xml_export_check_buffer
  21. hwloc__xml_export_safestrdup
  22. hwloc__xml_export_object_contents
  23. hwloc__xml_v2export_object
  24. hwloc__xml_v1export_object_with_memory
  25. hwloc__xml_v1export_object
  26. hwloc__xml_v2export_distances
  27. hwloc__xml_export_topology
  28. hwloc__xml_export_diff
  29. hwloc_topology_export_xml
  30. hwloc_topology_export_xmlbuffer
  31. hwloc_topology_diff_export_xml
  32. hwloc_topology_diff_export_xmlbuffer
  33. hwloc_free_xmlbuffer
  34. hwloc_topology_set_userdata_export_callback
  35. hwloc__export_obj_userdata
  36. hwloc_export_obj_userdata
  37. hwloc_export_obj_userdata_base64
  38. hwloc_topology_set_userdata_import_callback
  39. hwloc_xml_backend_disable
  40. hwloc_xml_component_instantiate

   1 /*
   2  * Copyright © 2009 CNRS
   3  * Copyright © 2009-2018 Inria.  All rights reserved.
   4  * Copyright © 2009-2011 Université Bordeaux
   5  * Copyright © 2009-2011 Cisco Systems, Inc.  All rights reserved.
   6  * See COPYING in top-level directory.
   7  */
   8 
   9 #include <private/autogen/config.h>
  10 #include <hwloc.h>
  11 #include <private/xml.h>
  12 #include <private/private.h>
  13 #include <private/misc.h>
  14 #include <private/debug.h>
  15 
  16 #include <math.h>
  17 
  18 int
  19 hwloc__xml_verbose(void)
  20 {
  21   static int checked = 0;
  22   static int verbose = 0;
  23   if (!checked) {
  24     const char *env = getenv("HWLOC_XML_VERBOSE");
  25     if (env)
  26       verbose = atoi(env);
  27     checked = 1;
  28   }
  29   return verbose;
  30 }
  31 
  32 static int
  33 hwloc_nolibxml_import(void)
  34 {
  35   static int checked = 0;
  36   static int nolibxml = 0;
  37   if (!checked) {
  38     const char *env = getenv("HWLOC_LIBXML");
  39     if (env) {
  40       nolibxml = !atoi(env);
  41     } else {
  42       env = getenv("HWLOC_LIBXML_IMPORT");
  43       if (env)
  44         nolibxml = !atoi(env);
  45     }
  46     checked = 1;
  47   }
  48   return nolibxml;
  49 }
  50 
  51 static int
  52 hwloc_nolibxml_export(void)
  53 {
  54   static int checked = 0;
  55   static int nolibxml = 0;
  56   if (!checked) {
  57     const char *env = getenv("HWLOC_LIBXML");
  58     if (env) {
  59       nolibxml = !atoi(env);
  60     } else {
  61       env = getenv("HWLOC_LIBXML_EXPORT");
  62       if (env)
  63         nolibxml = !atoi(env);
  64     }
  65     checked = 1;
  66   }
  67   return nolibxml;
  68 }
  69 
  70 #define BASE64_ENCODED_LENGTH(length) (4*(((length)+2)/3))
  71 
  72 /*********************************
  73  ********* XML callbacks *********
  74  *********************************/
  75 
  76 /* set when registering nolibxml and libxml components.
  77  * modifications protected by the components mutex.
  78  * read by the common XML code in topology-xml.c to jump to the right XML backend.
  79  */
  80 static struct hwloc_xml_callbacks *hwloc_nolibxml_callbacks = NULL, *hwloc_libxml_callbacks = NULL;
  81 
  82 void
  83 hwloc_xml_callbacks_register(struct hwloc_xml_component *comp)
  84 {
  85   if (!hwloc_nolibxml_callbacks)
  86     hwloc_nolibxml_callbacks = comp->nolibxml_callbacks;
  87   if (!hwloc_libxml_callbacks)
  88     hwloc_libxml_callbacks = comp->libxml_callbacks;
  89 }
  90 
  91 void
  92 hwloc_xml_callbacks_reset(void)
  93 {
  94   hwloc_nolibxml_callbacks = NULL;
  95   hwloc_libxml_callbacks = NULL;
  96 }
  97 
  98 /************************************************
  99  ********* XML import (common routines) *********
 100  ************************************************/
 101 
 102 #define _HWLOC_OBJ_CACHE_OLD (HWLOC_OBJ_TYPE_MAX+1) /* temporarily used when importing pre-v2.0 attribute-less cache types */
 103 #define _HWLOC_OBJ_FUTURE    (HWLOC_OBJ_TYPE_MAX+2) /* temporarily used when ignoring future types */
 104 
 105 static void
 106 hwloc__xml_import_object_attr(struct hwloc_topology *topology,
 107                               struct hwloc_xml_backend_data_s *data,
 108                               struct hwloc_obj *obj,
 109                               const char *name, const char *value,
 110                               hwloc__xml_import_state_t state)
 111 {
 112   if (!strcmp(name, "type")) {
 113     /* already handled */
 114     return;
 115   }
 116 
 117   else if (!strcmp(name, "os_index"))
 118     obj->os_index = strtoul(value, NULL, 10);
 119   else if (!strcmp(name, "gp_index")) {
 120     obj->gp_index = strtoull(value, NULL, 10);
 121     if (!obj->gp_index && hwloc__xml_verbose())
 122       fprintf(stderr, "%s: unexpected zero gp_index, topology may be invalid\n", state->global->msgprefix);
 123     if (obj->gp_index >= topology->next_gp_index)
 124       topology->next_gp_index = obj->gp_index + 1;
 125   } else if (!strcmp(name, "cpuset")) {
 126     if (!obj->cpuset)
 127       obj->cpuset = hwloc_bitmap_alloc();
 128     hwloc_bitmap_sscanf(obj->cpuset, value);
 129   } else if (!strcmp(name, "complete_cpuset")) {
 130     if (!obj->complete_cpuset)
 131       obj->complete_cpuset = hwloc_bitmap_alloc();
 132     hwloc_bitmap_sscanf(obj->complete_cpuset, value);
 133   } else if (!strcmp(name, "allowed_cpuset")) {
 134     /* ignored except for root */
 135     if (!obj->parent)
 136       hwloc_bitmap_sscanf(topology->allowed_cpuset, value);
 137   } else if (!strcmp(name, "nodeset")) {
 138     if (!obj->nodeset)
 139       obj->nodeset = hwloc_bitmap_alloc();
 140     hwloc_bitmap_sscanf(obj->nodeset, value);
 141   } else if (!strcmp(name, "complete_nodeset")) {
 142     if (!obj->complete_nodeset)
 143       obj->complete_nodeset = hwloc_bitmap_alloc();
 144     hwloc_bitmap_sscanf(obj->complete_nodeset, value);
 145   } else if (!strcmp(name, "allowed_nodeset")) {
 146     /* ignored except for root */
 147     if (!obj->parent)
 148       hwloc_bitmap_sscanf(topology->allowed_nodeset, value);
 149   } else if (!strcmp(name, "name")) {
 150     if (obj->name)
 151       free(obj->name);
 152     obj->name = strdup(value);
 153   } else if (!strcmp(name, "subtype")) {
 154     if (obj->subtype)
 155       free(obj->subtype);
 156     obj->subtype = strdup(value);
 157   }
 158 
 159   else if (!strcmp(name, "cache_size")) {
 160     unsigned long long lvalue = strtoull(value, NULL, 10);
 161     if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD)
 162       obj->attr->cache.size = lvalue;
 163     else if (hwloc__xml_verbose())
 164       fprintf(stderr, "%s: ignoring cache_size attribute for non-cache object type\n",
 165               state->global->msgprefix);
 166   }
 167 
 168   else if (!strcmp(name, "cache_linesize")) {
 169     unsigned long lvalue = strtoul(value, NULL, 10);
 170     if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD)
 171       obj->attr->cache.linesize = lvalue;
 172     else if (hwloc__xml_verbose())
 173       fprintf(stderr, "%s: ignoring cache_linesize attribute for non-cache object type\n",
 174               state->global->msgprefix);
 175   }
 176 
 177   else if (!strcmp(name, "cache_associativity")) {
 178     int lvalue = atoi(value);
 179     if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD)
 180       obj->attr->cache.associativity = lvalue;
 181     else if (hwloc__xml_verbose())
 182       fprintf(stderr, "%s: ignoring cache_associativity attribute for non-cache object type\n",
 183               state->global->msgprefix);
 184   }
 185 
 186   else if (!strcmp(name, "cache_type")) {
 187     unsigned long lvalue = strtoul(value, NULL, 10);
 188     if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) {
 189       if (lvalue == HWLOC_OBJ_CACHE_UNIFIED
 190           || lvalue == HWLOC_OBJ_CACHE_DATA
 191           || lvalue == HWLOC_OBJ_CACHE_INSTRUCTION)
 192         obj->attr->cache.type = (hwloc_obj_cache_type_t) lvalue;
 193       else
 194         fprintf(stderr, "%s: ignoring invalid cache_type attribute %lu\n",
 195                 state->global->msgprefix, lvalue);
 196     } else if (hwloc__xml_verbose())
 197       fprintf(stderr, "%s: ignoring cache_type attribute for non-cache object type\n",
 198               state->global->msgprefix);
 199   }
 200 
 201   else if (!strcmp(name, "local_memory")) {
 202     unsigned long long lvalue = strtoull(value, NULL, 10);
 203     if (obj->type == HWLOC_OBJ_NUMANODE)
 204       obj->attr->numanode.local_memory = lvalue;
 205     else if (!obj->parent)
 206       topology->machine_memory.local_memory = lvalue;
 207     else if (hwloc__xml_verbose())
 208       fprintf(stderr, "%s: ignoring local_memory attribute for non-NUMAnode non-root object\n",
 209               state->global->msgprefix);
 210   }
 211 
 212   else if (!strcmp(name, "depth")) {
 213     unsigned long lvalue = strtoul(value, NULL, 10);
 214      if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) {
 215         obj->attr->cache.depth = lvalue;
 216      } else if (obj->type == HWLOC_OBJ_GROUP || obj->type == HWLOC_OBJ_BRIDGE) {
 217        /* will be overwritten by the core */
 218      } else if (hwloc__xml_verbose())
 219        fprintf(stderr, "%s: ignoring depth attribute for object type without depth\n",
 220                state->global->msgprefix);
 221   }
 222 
 223   else if (!strcmp(name, "kind")) {
 224     unsigned long lvalue = strtoul(value, NULL, 10);
 225     if (obj->type == HWLOC_OBJ_GROUP)
 226       obj->attr->group.kind = lvalue;
 227     else if (hwloc__xml_verbose())
 228       fprintf(stderr, "%s: ignoring kind attribute for non-group object type\n",
 229               state->global->msgprefix);
 230   }
 231 
 232   else if (!strcmp(name, "subkind")) {
 233     unsigned long lvalue = strtoul(value, NULL, 10);
 234     if (obj->type == HWLOC_OBJ_GROUP)
 235       obj->attr->group.subkind = lvalue;
 236     else if (hwloc__xml_verbose())
 237       fprintf(stderr, "%s: ignoring subkind attribute for non-group object type\n",
 238               state->global->msgprefix);
 239   }
 240 
 241   else if (!strcmp(name, "pci_busid")) {
 242     switch (obj->type) {
 243     case HWLOC_OBJ_PCI_DEVICE:
 244     case HWLOC_OBJ_BRIDGE: {
 245       unsigned domain, bus, dev, func;
 246       if (sscanf(value, "%04x:%02x:%02x.%01x",
 247                  &domain, &bus, &dev, &func) != 4) {
 248         if (hwloc__xml_verbose())
 249           fprintf(stderr, "%s: ignoring invalid pci_busid format string %s\n",
 250                   state->global->msgprefix, value);
 251       } else {
 252         obj->attr->pcidev.domain = domain;
 253         obj->attr->pcidev.bus = bus;
 254         obj->attr->pcidev.dev = dev;
 255         obj->attr->pcidev.func = func;
 256       }
 257       break;
 258     }
 259     default:
 260       if (hwloc__xml_verbose())
 261         fprintf(stderr, "%s: ignoring pci_busid attribute for non-PCI object\n",
 262                 state->global->msgprefix);
 263       break;
 264     }
 265   }
 266 
 267   else if (!strcmp(name, "pci_type")) {
 268     switch (obj->type) {
 269     case HWLOC_OBJ_PCI_DEVICE:
 270     case HWLOC_OBJ_BRIDGE: {
 271       unsigned classid, vendor, device, subvendor, subdevice, revision;
 272       if (sscanf(value, "%04x [%04x:%04x] [%04x:%04x] %02x",
 273                  &classid, &vendor, &device, &subvendor, &subdevice, &revision) != 6) {
 274         if (hwloc__xml_verbose())
 275           fprintf(stderr, "%s: ignoring invalid pci_type format string %s\n",
 276                   state->global->msgprefix, value);
 277       } else {
 278         obj->attr->pcidev.class_id = classid;
 279         obj->attr->pcidev.vendor_id = vendor;
 280         obj->attr->pcidev.device_id = device;
 281         obj->attr->pcidev.subvendor_id = subvendor;
 282         obj->attr->pcidev.subdevice_id = subdevice;
 283         obj->attr->pcidev.revision = revision;
 284       }
 285       break;
 286     }
 287     default:
 288       if (hwloc__xml_verbose())
 289         fprintf(stderr, "%s: ignoring pci_type attribute for non-PCI object\n",
 290                 state->global->msgprefix);
 291       break;
 292     }
 293   }
 294 
 295   else if (!strcmp(name, "pci_link_speed")) {
 296     switch (obj->type) {
 297     case HWLOC_OBJ_PCI_DEVICE:
 298     case HWLOC_OBJ_BRIDGE: {
 299       obj->attr->pcidev.linkspeed = (float) atof(value);
 300       break;
 301     }
 302     default:
 303       if (hwloc__xml_verbose())
 304         fprintf(stderr, "%s: ignoring pci_link_speed attribute for non-PCI object\n",
 305                 state->global->msgprefix);
 306       break;
 307     }
 308   }
 309 
 310   else if (!strcmp(name, "bridge_type")) {
 311     switch (obj->type) {
 312     case HWLOC_OBJ_BRIDGE: {
 313       unsigned upstream_type, downstream_type;
 314       if (sscanf(value, "%u-%u", &upstream_type, &downstream_type) != 2) {
 315         if (hwloc__xml_verbose())
 316           fprintf(stderr, "%s: ignoring invalid bridge_type format string %s\n",
 317                   state->global->msgprefix, value);
 318       } else {
 319         obj->attr->bridge.upstream_type = (hwloc_obj_bridge_type_t) upstream_type;
 320         obj->attr->bridge.downstream_type = (hwloc_obj_bridge_type_t) downstream_type;
 321       };
 322       break;
 323     }
 324     default:
 325       if (hwloc__xml_verbose())
 326         fprintf(stderr, "%s: ignoring bridge_type attribute for non-bridge object\n",
 327                 state->global->msgprefix);
 328       break;
 329     }
 330   }
 331 
 332   else if (!strcmp(name, "bridge_pci")) {
 333     switch (obj->type) {
 334     case HWLOC_OBJ_BRIDGE: {
 335       unsigned domain, secbus, subbus;
 336       if (sscanf(value, "%04x:[%02x-%02x]",
 337                  &domain, &secbus, &subbus) != 3) {
 338         if (hwloc__xml_verbose())
 339           fprintf(stderr, "%s: ignoring invalid bridge_pci format string %s\n",
 340                   state->global->msgprefix, value);
 341       } else {
 342         obj->attr->bridge.downstream.pci.domain = domain;
 343         obj->attr->bridge.downstream.pci.secondary_bus = secbus;
 344         obj->attr->bridge.downstream.pci.subordinate_bus = subbus;
 345       }
 346       break;
 347     }
 348     default:
 349       if (hwloc__xml_verbose())
 350         fprintf(stderr, "%s: ignoring bridge_pci attribute for non-bridge object\n",
 351                 state->global->msgprefix);
 352       break;
 353     }
 354   }
 355 
 356   else if (!strcmp(name, "osdev_type")) {
 357     switch (obj->type) {
 358     case HWLOC_OBJ_OS_DEVICE: {
 359       unsigned osdev_type;
 360       if (sscanf(value, "%u", &osdev_type) != 1) {
 361         if (hwloc__xml_verbose())
 362           fprintf(stderr, "%s: ignoring invalid osdev_type format string %s\n",
 363                   state->global->msgprefix, value);
 364       } else
 365         obj->attr->osdev.type = (hwloc_obj_osdev_type_t) osdev_type;
 366       break;
 367     }
 368     default:
 369       if (hwloc__xml_verbose())
 370         fprintf(stderr, "%s: ignoring osdev_type attribute for non-osdev object\n",
 371                 state->global->msgprefix);
 372       break;
 373     }
 374   }
 375 
 376   else if (data->version_major < 2) {
 377     /************************
 378      * deprecated from 1.x
 379      */
 380     if (!strcmp(name, "os_level")
 381         || !strcmp(name, "online_cpuset"))
 382       { /* ignored */ }
 383 
 384     /*************************
 385      * deprecated from 1.0
 386      */
 387     else if (!strcmp(name, "dmi_board_vendor")) {
 388       if (value[0])
 389         hwloc_obj_add_info(obj, "DMIBoardVendor", value);
 390     }
 391     else if (!strcmp(name, "dmi_board_name")) {
 392       if (value[0])
 393         hwloc_obj_add_info(obj, "DMIBoardName", value);
 394     }
 395 
 396     else if (data->version_major < 1) {
 397       /*************************
 398        * deprecated from 0.9
 399        */
 400       if (!strcmp(name, "memory_kB")) {
 401         unsigned long long lvalue = strtoull(value, NULL, 10);
 402         if (obj->type == _HWLOC_OBJ_CACHE_OLD)
 403           obj->attr->cache.size = lvalue << 10;
 404         else if (obj->type == HWLOC_OBJ_NUMANODE)
 405           obj->attr->numanode.local_memory = lvalue << 10;
 406         else if (!obj->parent)
 407           topology->machine_memory.local_memory = lvalue << 10;
 408         else if (hwloc__xml_verbose())
 409           fprintf(stderr, "%s: ignoring memory_kB attribute for non-NUMAnode non-root object\n",
 410                   state->global->msgprefix);
 411       }
 412       else if (!strcmp(name, "huge_page_size_kB")) {
 413         unsigned long lvalue = strtoul(value, NULL, 10);
 414         if (obj->type == HWLOC_OBJ_NUMANODE || !obj->parent) {
 415           struct hwloc_numanode_attr_s *memory = obj->type == HWLOC_OBJ_NUMANODE ? &obj->attr->numanode : &topology->machine_memory;
 416           if (!memory->page_types) {
 417             memory->page_types = malloc(sizeof(*memory->page_types));
 418             memory->page_types_len = 1;
 419           }
 420           memory->page_types[0].size = lvalue << 10;
 421         } else if (hwloc__xml_verbose()) {
 422           fprintf(stderr, "%s: ignoring huge_page_size_kB attribute for non-NUMAnode non-root object\n",
 423                   state->global->msgprefix);
 424         }
 425       }
 426       else if (!strcmp(name, "huge_page_free")) {
 427         unsigned long lvalue = strtoul(value, NULL, 10);
 428         if (obj->type == HWLOC_OBJ_NUMANODE || !obj->parent) {
 429           struct hwloc_numanode_attr_s *memory = obj->type == HWLOC_OBJ_NUMANODE ? &obj->attr->numanode : &topology->machine_memory;
 430           if (!memory->page_types) {
 431             memory->page_types = malloc(sizeof(*memory->page_types));
 432             memory->page_types_len = 1;
 433           }
 434           memory->page_types[0].count = lvalue;
 435         } else if (hwloc__xml_verbose()) {
 436           fprintf(stderr, "%s: ignoring huge_page_free attribute for non-NUMAnode non-root object\n",
 437                   state->global->msgprefix);
 438         }
 439       }
 440       /* end of deprecated from 0.9 */
 441       else goto unknown;
 442     }
 443     /* end of deprecated from 1.0 */
 444     else goto unknown;
 445   }
 446   else {
 447   unknown:
 448     if (hwloc__xml_verbose())
 449       fprintf(stderr, "%s: ignoring unknown object attribute %s\n",
 450               state->global->msgprefix, name);
 451   }
 452 }
 453 
 454 
 455 static int
 456 hwloc__xml_import_info(struct hwloc_xml_backend_data_s *data,
 457                        hwloc_obj_t obj,
 458                        hwloc__xml_import_state_t state)
 459 {
 460   char *infoname = NULL;
 461   char *infovalue = NULL;
 462 
 463   while (1) {
 464     char *attrname, *attrvalue;
 465     if (state->global->next_attr(state, &attrname, &attrvalue) < 0)
 466       break;
 467     if (!strcmp(attrname, "name"))
 468       infoname = attrvalue;
 469     else if (!strcmp(attrname, "value"))
 470       infovalue = attrvalue;
 471     else
 472       return -1;
 473   }
 474 
 475   if (infoname) {
 476     /* empty strings are ignored by libxml */
 477     if (data->version_major < 2 &&
 478         (!strcmp(infoname, "Type") || !strcmp(infoname, "CoProcType"))) {
 479       /* 1.x stored subtype in Type or CoProcType */
 480       if (infovalue) {
 481         if (obj->subtype)
 482           free(obj->subtype);
 483         obj->subtype = strdup(infovalue);
 484       }
 485     } else {
 486       if (infovalue)
 487         hwloc_obj_add_info(obj, infoname, infovalue);
 488     }
 489   }
 490 
 491   return state->global->close_tag(state);
 492 }
 493 
 494 static int
 495 hwloc__xml_import_pagetype(hwloc_topology_t topology __hwloc_attribute_unused, struct hwloc_numanode_attr_s *memory,
 496                            hwloc__xml_import_state_t state)
 497 {
 498   uint64_t size = 0, count = 0;
 499 
 500   while (1) {
 501     char *attrname, *attrvalue;
 502     if (state->global->next_attr(state, &attrname, &attrvalue) < 0)
 503       break;
 504     if (!strcmp(attrname, "size"))
 505       size = strtoull(attrvalue, NULL, 10);
 506     else if (!strcmp(attrname, "count"))
 507       count = strtoull(attrvalue, NULL, 10);
 508     else
 509       return -1;
 510   }
 511 
 512   if (size) {
 513     unsigned idx = memory->page_types_len;
 514     struct hwloc_memory_page_type_s *tmp;
 515     tmp = realloc(memory->page_types, (idx+1)*sizeof(*memory->page_types));
 516     if (tmp) { /* if failed to allocate, ignore this page_type entry */
 517       memory->page_types = tmp;
 518       memory->page_types_len = idx+1;
 519       memory->page_types[idx].size = size;
 520       memory->page_types[idx].count = count;
 521     }
 522   }
 523 
 524   return state->global->close_tag(state);
 525 }
 526 
 527 static int
 528 hwloc__xml_v1import_distances(struct hwloc_xml_backend_data_s *data,
 529                               hwloc_obj_t obj,
 530                               hwloc__xml_import_state_t state)
 531 {
 532   unsigned long reldepth = 0, nbobjs = 0;
 533   float latbase = 0;
 534   char *tag;
 535   int ret;
 536 
 537   while (1) {
 538     char *attrname, *attrvalue;
 539     if (state->global->next_attr(state, &attrname, &attrvalue) < 0)
 540       break;
 541     if (!strcmp(attrname, "nbobjs"))
 542       nbobjs = strtoul(attrvalue, NULL, 10);
 543     else if (!strcmp(attrname, "relative_depth"))
 544       reldepth = strtoul(attrvalue, NULL, 10);
 545     else if (!strcmp(attrname, "latency_base"))
 546       latbase = (float) atof(attrvalue);
 547     else
 548       return -1;
 549   }
 550 
 551   if (nbobjs && reldepth && latbase) {
 552     unsigned i;
 553     float *matrix;
 554     struct hwloc__xml_imported_v1distances_s *v1dist;
 555 
 556     matrix = malloc(nbobjs*nbobjs*sizeof(float));
 557     v1dist = malloc(sizeof(*v1dist));
 558     if (!matrix || !v1dist) {
 559       if (hwloc__xml_verbose())
 560         fprintf(stderr, "%s: failed to allocate v1distance matrix for %lu objects\n",
 561                 state->global->msgprefix, nbobjs);
 562       free(v1dist);
 563       free(matrix);
 564       return -1;
 565     }
 566 
 567     v1dist->kind = HWLOC_DISTANCES_KIND_FROM_OS|HWLOC_DISTANCES_KIND_MEANS_LATENCY;
 568     /* TODO: we can't know for sure if it comes from the OS.
 569      * On Linux/x86, it would be 10 on the diagonal.
 570      * On Solaris/T5, 15 on the diagonal.
 571      * Just check whether all values are integers, and that all values on the diagonal are minimal and identical?
 572      */
 573 
 574     v1dist->nbobjs = nbobjs;
 575     v1dist->floats = matrix;
 576 
 577     for(i=0; i<nbobjs*nbobjs; i++) {
 578       struct hwloc__xml_import_state_s childstate;
 579       char *attrname, *attrvalue;
 580       float val;
 581 
 582       ret = state->global->find_child(state, &childstate, &tag);
 583       if (ret <= 0 || strcmp(tag, "latency")) {
 584         /* a latency child is needed */
 585         free(matrix);
 586         free(v1dist);
 587         return -1;
 588       }
 589 
 590       ret = state->global->next_attr(&childstate, &attrname, &attrvalue);
 591       if (ret < 0 || strcmp(attrname, "value")) {
 592         free(matrix);
 593         free(v1dist);
 594         return -1;
 595       }
 596 
 597       val = (float) atof((char *) attrvalue);
 598       matrix[i] = val * latbase;
 599 
 600       ret = state->global->close_tag(&childstate);
 601       if (ret < 0)
 602         return -1;
 603 
 604       state->global->close_child(&childstate);
 605     }
 606 
 607     if (nbobjs < 2) {
 608       /* distances with a single object are useless, even if the XML isn't invalid */
 609       assert(nbobjs == 1);
 610       if (hwloc__xml_verbose())
 611         fprintf(stderr, "%s: ignoring invalid distance matrix with only 1 object\n",
 612                 state->global->msgprefix);
 613       free(matrix);
 614       free(v1dist);
 615 
 616     } else if (obj->parent) {
 617       /* we currently only import distances attached to root.
 618        * we can't save obj in v1dist because obj could be dropped during insert if ignored.
 619        * we could save its complete_cpu/nodeset instead to find it back later.
 620        * but it doesn't matter much since only NUMA distances attached to root matter.
 621        */
 622       free(matrix);
 623       free(v1dist);
 624 
 625     } else {
 626       /* queue the distance for real */
 627       v1dist->prev = data->last_v1dist;
 628       v1dist->next = NULL;
 629       if (data->last_v1dist)
 630         data->last_v1dist->next = v1dist;
 631       else
 632         data->first_v1dist = v1dist;
 633       data->last_v1dist = v1dist;
 634     }
 635   }
 636 
 637   return state->global->close_tag(state);
 638 }
 639 
 640 static int
 641 hwloc__xml_import_userdata(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj,
 642                            hwloc__xml_import_state_t state)
 643 {
 644   size_t length = 0;
 645   int encoded = 0;
 646   char *name = NULL; /* optional */
 647   int ret;
 648 
 649   while (1) {
 650     char *attrname, *attrvalue;
 651     if (state->global->next_attr(state, &attrname, &attrvalue) < 0)
 652       break;
 653     if (!strcmp(attrname, "length"))
 654       length = strtoul(attrvalue, NULL, 10);
 655     else if (!strcmp(attrname, "encoding"))
 656       encoded = !strcmp(attrvalue, "base64");
 657     else if (!strcmp(attrname, "name"))
 658       name = attrvalue;
 659     else
 660       return -1;
 661   }
 662 
 663   if (!topology->userdata_import_cb) {
 664     char *buffer;
 665     size_t reallength = encoded ? BASE64_ENCODED_LENGTH(length) : length;
 666     ret = state->global->get_content(state, &buffer, reallength);
 667     if (ret < 0)
 668       return -1;
 669 
 670   } else if (topology->userdata_not_decoded) {
 671       char *buffer, *fakename;
 672       size_t reallength = encoded ? BASE64_ENCODED_LENGTH(length) : length;
 673       ret = state->global->get_content(state, &buffer, reallength);
 674       if (ret < 0)
 675         return -1;
 676       fakename = malloc(6 + 1 + (name ? strlen(name) : 4) + 1);
 677       if (!fakename)
 678         return -1;
 679       sprintf(fakename, encoded ? "base64%c%s" : "normal%c%s", name ? ':' : '-', name ? name : "anon");
 680       topology->userdata_import_cb(topology, obj, fakename, buffer, length);
 681       free(fakename);
 682 
 683   } else if (encoded && length) {
 684       char *encoded_buffer;
 685       size_t encoded_length = BASE64_ENCODED_LENGTH(length);
 686       ret = state->global->get_content(state, &encoded_buffer, encoded_length);
 687       if (ret < 0)
 688         return -1;
 689       if (ret) {
 690         char *decoded_buffer = malloc(length+1);
 691         if (!decoded_buffer)
 692           return -1;
 693         assert(encoded_buffer[encoded_length] == 0);
 694         ret = hwloc_decode_from_base64(encoded_buffer, decoded_buffer, length+1);
 695         if (ret != (int) length) {
 696           free(decoded_buffer);
 697           return -1;
 698         }
 699         topology->userdata_import_cb(topology, obj, name, decoded_buffer, length);
 700         free(decoded_buffer);
 701       }
 702 
 703   } else { /* always handle length==0 in the non-encoded case */
 704       char *buffer = (char *) "";
 705       if (length) {
 706         ret = state->global->get_content(state, &buffer, length);
 707         if (ret < 0)
 708           return -1;
 709       }
 710       topology->userdata_import_cb(topology, obj, name, buffer, length);
 711   }
 712 
 713   state->global->close_content(state);
 714   return state->global->close_tag(state);
 715 }
 716 
 717 static void hwloc__xml_import_report_outoforder(hwloc_topology_t topology, hwloc_obj_t new, hwloc_obj_t old)
 718 {
 719   char *progname = hwloc_progname(topology);
 720   const char *origversion = hwloc_obj_get_info_by_name(topology->levels[0][0], "hwlocVersion");
 721   const char *origprogname = hwloc_obj_get_info_by_name(topology->levels[0][0], "ProcessName");
 722   char *c1, *cc1, t1[64];
 723   char *c2 = NULL, *cc2 = NULL, t2[64];
 724 
 725   hwloc_bitmap_asprintf(&c1, new->cpuset);
 726   hwloc_bitmap_asprintf(&cc1, new->complete_cpuset);
 727   hwloc_obj_type_snprintf(t1, sizeof(t1), new, 0);
 728 
 729   if (old->cpuset)
 730     hwloc_bitmap_asprintf(&c2, old->cpuset);
 731   if (old->complete_cpuset)
 732     hwloc_bitmap_asprintf(&cc2, old->complete_cpuset);
 733   hwloc_obj_type_snprintf(t2, sizeof(t2), old, 0);
 734 
 735   fprintf(stderr, "****************************************************************************\n");
 736   fprintf(stderr, "* hwloc has encountered an out-of-order XML topology load.\n");
 737   fprintf(stderr, "* Object %s cpuset %s complete %s\n",
 738           t1, c1, cc1);
 739   fprintf(stderr, "* was inserted after object %s with %s and %s.\n",
 740           t2, c2 ? c2 : "none", cc2 ? cc2 : "none");
 741   fprintf(stderr, "* The error occured in hwloc %s inside process `%s', while\n",
 742           HWLOC_VERSION,
 743           progname ? progname : "<unknown>");
 744   if (origversion || origprogname)
 745     fprintf(stderr, "* the input XML was generated by hwloc %s inside process `%s'.\n",
 746             origversion ? origversion : "(unknown version)",
 747             origprogname ? origprogname : "<unknown>");
 748   else
 749     fprintf(stderr, "* the input XML was generated by an unspecified ancient hwloc release.\n");
 750   fprintf(stderr, "* Please check that your input topology XML file is valid.\n");
 751   fprintf(stderr, "* Set HWLOC_DEBUG_CHECK=1 in the environment to detect further issues.\n");
 752   fprintf(stderr, "****************************************************************************\n");
 753 
 754   free(c1);
 755   free(cc1);
 756   free(c2);
 757   free(cc2);
 758   free(progname);
 759 }
 760 
 761 static int
 762 hwloc__xml_import_object(hwloc_topology_t topology,
 763                          struct hwloc_xml_backend_data_s *data,
 764                          hwloc_obj_t parent, hwloc_obj_t obj, int *gotignored,
 765                          hwloc__xml_import_state_t state)
 766 {
 767   int ignored = 0;
 768   int childrengotignored = 0;
 769   int attribute_less_cache = 0;
 770   int numa_was_root = 0;
 771   char *tag;
 772   struct hwloc__xml_import_state_s childstate;
 773 
 774   /* set parent now since it's used during import below or in subfunctions */
 775   obj->parent = parent;
 776 
 777   /* process attributes */
 778   while (1) {
 779     char *attrname, *attrvalue;
 780     if (state->global->next_attr(state, &attrname, &attrvalue) < 0)
 781       break;
 782     if (!strcmp(attrname, "type")) {
 783       if (hwloc_type_sscanf(attrvalue, &obj->type, NULL, 0) < 0) {
 784         if (!strcasecmp(attrvalue, "Cache")) {
 785           obj->type = _HWLOC_OBJ_CACHE_OLD; /* will be fixed below */
 786           attribute_less_cache = 1;
 787         } else if (!strcasecmp(attrvalue, "System")) {
 788           if (!parent)
 789             obj->type = HWLOC_OBJ_MACHINE;
 790           else {
 791             if (hwloc__xml_verbose())
 792               fprintf(stderr, "%s: obsolete System object only allowed at root\n",
 793                       state->global->msgprefix);
 794             goto error_with_object;
 795           }
 796         } else if (!strcasecmp(attrvalue, "MemCache")) {
 797           /* ignore likely-future types */
 798           obj->type = _HWLOC_OBJ_FUTURE;
 799           ignored = 1;
 800           if (hwloc__xml_verbose())
 801             fprintf(stderr, "%s: %s object not-supported, will be ignored\n",
 802                     state->global->msgprefix, attrvalue);
 803         } else {
 804           if (hwloc__xml_verbose())
 805             fprintf(stderr, "%s: unrecognized object type string %s\n",
 806                     state->global->msgprefix, attrvalue);
 807           goto error_with_object;
 808         }
 809       }
 810     } else {
 811       /* type needed first */
 812       if (obj->type == HWLOC_OBJ_TYPE_NONE) {
 813         if (hwloc__xml_verbose())
 814           fprintf(stderr, "%s: object attribute %s found before type\n",
 815                   state->global->msgprefix,  attrname);
 816         goto error_with_object;
 817       }
 818       hwloc__xml_import_object_attr(topology, data, obj, attrname, attrvalue, state);
 819     }
 820   }
 821 
 822   /* process non-object subnodes to get info attrs (as well as page_types, etc) */
 823   while (1) {
 824     int ret;
 825 
 826     tag = NULL;
 827     ret = state->global->find_child(state, &childstate, &tag);
 828     if (ret < 0)
 829       goto error;
 830     if (!ret)
 831       break;
 832 
 833     if (!strcmp(tag, "object")) {
 834       /* we'll handle children later */
 835       break;
 836 
 837     } else if (!strcmp(tag, "page_type")) {
 838       if (obj->type == HWLOC_OBJ_NUMANODE) {
 839         ret = hwloc__xml_import_pagetype(topology, &obj->attr->numanode, &childstate);
 840       } else if (!parent) {
 841         ret = hwloc__xml_import_pagetype(topology, &topology->machine_memory, &childstate);
 842       } else {
 843         if (hwloc__xml_verbose())
 844           fprintf(stderr, "%s: invalid non-NUMAnode object child %s\n",
 845                   state->global->msgprefix, tag);
 846         ret = -1;
 847       }
 848 
 849     } else if (!strcmp(tag, "info")) {
 850       ret = hwloc__xml_import_info(data, obj, &childstate);
 851     } else if (data->version_major < 2 && !strcmp(tag, "distances")) {
 852       ret = hwloc__xml_v1import_distances(data, obj, &childstate);
 853     } else if (!strcmp(tag, "userdata")) {
 854       ret = hwloc__xml_import_userdata(topology, obj, &childstate);
 855     } else {
 856       if (hwloc__xml_verbose())
 857         fprintf(stderr, "%s: invalid special object child %s\n",
 858                 state->global->msgprefix, tag);
 859       ret = -1;
 860     }
 861 
 862     if (ret < 0)
 863       goto error;
 864 
 865     state->global->close_child(&childstate);
 866   }
 867 
 868   if (parent && obj->type == HWLOC_OBJ_MACHINE) {
 869     /* replace non-root Machine with Groups */
 870     obj->type = HWLOC_OBJ_GROUP;
 871   }
 872 
 873   if (parent && data->version_major >= 2) {
 874     /* check parent/child types for 2.x */
 875     if (hwloc__obj_type_is_normal(obj->type)) {
 876       if (!hwloc__obj_type_is_normal(parent->type)) {
 877         if (hwloc__xml_verbose())
 878           fprintf(stderr, "normal object %s cannot be child of non-normal parent %s\n",
 879                   hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type));
 880         goto error_with_object;
 881       }
 882     } else if (hwloc__obj_type_is_memory(obj->type)) {
 883       if (hwloc__obj_type_is_io(parent->type) || HWLOC_OBJ_MISC == parent->type) {
 884         if (hwloc__xml_verbose())
 885           fprintf(stderr, "Memory object %s cannot be child of non-normal-or-memory parent %s\n",
 886                   hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type));
 887         goto error_with_object;
 888       }
 889     } else if (hwloc__obj_type_is_io(obj->type)) {
 890       if (hwloc__obj_type_is_memory(parent->type) || HWLOC_OBJ_MISC == parent->type) {
 891         if (hwloc__xml_verbose())
 892           fprintf(stderr, "I/O object %s cannot be child of non-normal-or-I/O parent %s\n",
 893                   hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type));
 894         goto error_with_object;
 895       }
 896     }
 897 
 898   } else if (parent && data->version_major < 2) {
 899     /* check parent/child types for pre-v2.0 */
 900     if (hwloc__obj_type_is_normal(obj->type) || HWLOC_OBJ_NUMANODE == obj->type) {
 901       if (hwloc__obj_type_is_special(parent->type)) {
 902         if (hwloc__xml_verbose())
 903           fprintf(stderr, "v1.x normal v1.x object %s cannot be child of special parent %s\n",
 904                   hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type));
 905         goto error_with_object;
 906       }
 907     } else if (hwloc__obj_type_is_io(obj->type)) {
 908       if (HWLOC_OBJ_MISC == parent->type) {
 909         if (hwloc__xml_verbose())
 910           fprintf(stderr, "I/O object %s cannot be child of Misc parent\n",
 911                   hwloc_obj_type_string(obj->type));
 912         goto error_with_object;
 913       }
 914     }
 915   }
 916 
 917   if (data->version_major < 2) {
 918     /***************************
 919      * 1.x specific checks
 920      */
 921 
 922     /* attach pre-v2.0 children of NUMA nodes to normal parent */
 923     if (parent && parent->type == HWLOC_OBJ_NUMANODE) {
 924       parent = parent->parent;
 925       assert(parent);
 926     }
 927 
 928     /* insert a group above pre-v2.0 NUMA nodes if needed */
 929     if (obj->type == HWLOC_OBJ_NUMANODE) {
 930       if (!parent) {
 931         /* crazy case of NUMA node root (only possible when filtering Machine keep_structure in v1.x),
 932          * reinsert a Machine object
 933          */
 934         hwloc_obj_t machine = hwloc_alloc_setup_object(topology, HWLOC_OBJ_MACHINE, HWLOC_UNKNOWN_INDEX);
 935         machine->cpuset = hwloc_bitmap_dup(obj->cpuset);
 936         machine->complete_cpuset = hwloc_bitmap_dup(obj->cpuset);
 937         machine->nodeset = hwloc_bitmap_dup(obj->nodeset);
 938         machine->complete_nodeset = hwloc_bitmap_dup(obj->complete_nodeset);
 939         topology->levels[0][0] = machine;
 940         parent = machine;
 941         numa_was_root = 1;
 942 
 943       } else if (!hwloc_bitmap_isequal(obj->complete_cpuset, parent->complete_cpuset)) {
 944         /* This NUMA node has a different locality from its parent.
 945          * Don't attach it to this parent, or it well get its parent cpusets.
 946          * Add an intermediate Group with the desired locality.
 947          */
 948         int needgroup = 1;
 949         hwloc_obj_t sibling;
 950 
 951         sibling = parent->memory_first_child;
 952         if (sibling && !sibling->subtype
 953             && !sibling->next_sibling
 954             && obj->subtype && !strcmp(obj->subtype, "MCDRAM")
 955             && hwloc_bitmap_iszero(obj->complete_cpuset)) {
 956           /* this is KNL MCDRAM, we want to attach it near its DDR sibling */
 957           needgroup = 0;
 958         }
 959         /* Ideally we would also detect similar cases on future non-KNL platforms with multiple local NUMA nodes.
 960          * That's unlikely to occur with v1.x.
 961          * And we have no way to be sure if this CPU-less node is desired or not.
 962          */
 963 
 964         if (needgroup
 965             && hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP)) {
 966           hwloc_obj_t group = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX);
 967           group->gp_index = 0; /* will be initialized at the end of the discovery once we know the max */
 968           group->cpuset = hwloc_bitmap_dup(obj->cpuset);
 969           group->complete_cpuset = hwloc_bitmap_dup(obj->cpuset);
 970           group->nodeset = hwloc_bitmap_dup(obj->nodeset);
 971           group->complete_nodeset = hwloc_bitmap_dup(obj->complete_nodeset);
 972           group->attr->group.kind = HWLOC_GROUP_KIND_MEMORY;
 973           hwloc_insert_object_by_parent(topology, parent, group);
 974           parent = group;
 975         }
 976       }
 977     }
 978 
 979     /* fixup attribute-less caches imported from pre-v2.0 XMLs */
 980     if (attribute_less_cache) {
 981       assert(obj->type == _HWLOC_OBJ_CACHE_OLD);
 982       obj->type = hwloc_cache_type_by_depth_type(obj->attr->cache.depth, obj->attr->cache.type);
 983     }
 984 
 985     /* fixup Misc objects inserted by cpusets in pre-v2.0 XMLs */
 986     if (obj->type == HWLOC_OBJ_MISC && obj->cpuset)
 987       obj->type = HWLOC_OBJ_GROUP;
 988 
 989     /* check set consistency.
 990      * 1.7.2 and earlier reported I/O Groups with only a cpuset, we don't want to reject those XMLs yet.
 991      * Ignore those Groups since fixing the missing sets is hard (would need to look at children sets which are not available yet).
 992      * Just abort the XML for non-Groups.
 993      */
 994     if (!obj->cpuset != !obj->complete_cpuset) {
 995       /* has some cpuset without others */
 996       if (obj->type == HWLOC_OBJ_GROUP) {
 997         ignored = 1;
 998       } else {
 999         if (hwloc__xml_verbose())
1000           fprintf(stderr, "%s: invalid object %s P#%u with some missing cpusets\n",
1001                   state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index);
1002         goto error_with_object;
1003       }
1004     } else if (!obj->nodeset != !obj->complete_nodeset) {
1005       /* has some nodeset without others */
1006       if (obj->type == HWLOC_OBJ_GROUP) {
1007         ignored = 1;
1008       } else {
1009         if (hwloc__xml_verbose())
1010           fprintf(stderr, "%s: invalid object %s P#%u with some missing nodesets\n",
1011                   state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index);
1012         goto error_with_object;
1013       }
1014     } else if (obj->nodeset && !obj->cpuset) {
1015       /* has nodesets without cpusets (the contrary is allowed in pre-2.0) */
1016       if (obj->type == HWLOC_OBJ_GROUP) {
1017         ignored = 1;
1018       } else {
1019         if (hwloc__xml_verbose())
1020           fprintf(stderr, "%s: invalid object %s P#%u with either cpuset or nodeset missing\n",
1021                   state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index);
1022         goto error_with_object;
1023       }
1024     }
1025     /* end of 1.x specific checks */
1026   }
1027 
1028   /* check that cache attributes are coherent with the actual type */
1029   if (hwloc__obj_type_is_cache(obj->type)
1030       && obj->type != hwloc_cache_type_by_depth_type(obj->attr->cache.depth, obj->attr->cache.type)) {
1031     if (hwloc__xml_verbose())
1032       fprintf(stderr, "%s: invalid cache type %s with attribute depth %u and type %d\n",
1033               state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->attr->cache.depth, (int) obj->attr->cache.type);
1034     goto error_with_object;
1035   }
1036 
1037   /* check special types vs cpuset */
1038   if (!obj->cpuset && !hwloc__obj_type_is_special(obj->type)) {
1039     if (hwloc__xml_verbose())
1040       fprintf(stderr, "%s: invalid normal object %s P#%u without cpuset\n",
1041               state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index);
1042     goto error_with_object;
1043   }
1044   if (obj->cpuset && hwloc__obj_type_is_special(obj->type)) {
1045     if (hwloc__xml_verbose())
1046       fprintf(stderr, "%s: invalid special object %s with cpuset\n",
1047               state->global->msgprefix, hwloc_obj_type_string(obj->type));
1048     goto error_with_object;
1049   }
1050 
1051   /* check parent vs child sets */
1052   if (obj->cpuset && parent && !parent->cpuset) {
1053     if (hwloc__xml_verbose())
1054       fprintf(stderr, "%s: invalid object %s P#%u with cpuset while parent has none\n",
1055               state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index);
1056     goto error_with_object;
1057   }
1058   if (obj->nodeset && parent && !parent->nodeset) {
1059     if (hwloc__xml_verbose())
1060       fprintf(stderr, "%s: invalid object %s P#%u with nodeset while parent has none\n",
1061               state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index);
1062     goto error_with_object;
1063   }
1064 
1065   /* check NUMA nodes */
1066   if (obj->type == HWLOC_OBJ_NUMANODE) {
1067     if (!obj->nodeset) {
1068       if (hwloc__xml_verbose())
1069         fprintf(stderr, "%s: invalid NUMA node object P#%u without nodeset\n",
1070                 state->global->msgprefix, obj->os_index);
1071       goto error_with_object;
1072     }
1073     data->nbnumanodes++;
1074     obj->prev_cousin = data->last_numanode;
1075     obj->next_cousin = NULL;
1076     if (data->last_numanode)
1077       data->last_numanode->next_cousin = obj;
1078     else
1079       data->first_numanode = obj;
1080     data->last_numanode = obj;
1081   }
1082 
1083   if (!hwloc_filter_check_keep_object(topology, obj)) {
1084     /* Ignore this object instead of inserting it.
1085      *
1086      * Well, let the core ignore the root object later
1087      * because we don't know yet if root has more than one child.
1088      */
1089     if (parent)
1090       ignored = 1;
1091   }
1092 
1093   if (parent && !ignored) {
1094     /* root->parent is NULL, and root is already inserted */
1095     hwloc_insert_object_by_parent(topology, parent, obj);
1096     /* insert_object_by_parent() doesn't merge during insert, so obj is still valid */
1097   }
1098 
1099   /* process object subnodes, if we found one win the above loop */
1100   while (tag) {
1101     int ret;
1102 
1103     if (!strcmp(tag, "object")) {
1104       hwloc_obj_t childobj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_TYPE_MAX, HWLOC_UNKNOWN_INDEX);
1105       childobj->parent = ignored ? parent : obj;
1106       ret = hwloc__xml_import_object(topology, data, ignored ? parent : obj, childobj,
1107                                      &childrengotignored,
1108                                      &childstate);
1109     } else {
1110       if (hwloc__xml_verbose())
1111         fprintf(stderr, "%s: invalid special object child %s while looking for objects\n",
1112                 state->global->msgprefix, tag);
1113       ret = -1;
1114     }
1115 
1116     if (ret < 0)
1117       goto error;
1118 
1119     state->global->close_child(&childstate);
1120 
1121     tag = NULL;
1122     ret = state->global->find_child(state, &childstate, &tag);
1123     if (ret < 0)
1124       goto error;
1125     if (!ret)
1126       break;
1127   }
1128 
1129   if (numa_was_root) {
1130     /* duplicate NUMA infos to root, most of them are likely root-specific */
1131     unsigned i;
1132     for(i=0; i<obj->infos_count; i++) {
1133       struct hwloc_info_s *info = &obj->infos[i];
1134       hwloc_obj_add_info(parent, info->name, info->value);
1135     }
1136     /* TODO some infos are root-only (hwlocVersion, ProcessName, etc), remove them from obj? */
1137   }
1138 
1139   if (ignored) {
1140     /* drop that object, and tell the parent that one child got ignored */
1141     hwloc_free_unlinked_object(obj);
1142     *gotignored = 1;
1143 
1144   } else if (obj->first_child) {
1145     /* now that all children are inserted, make sure they are in-order,
1146      * so that the core doesn't have to deal with crappy children list.
1147      */
1148     hwloc_obj_t cur, next;
1149     for(cur = obj->first_child, next = cur->next_sibling;
1150         next;
1151         cur = next, next = next->next_sibling) {
1152       /* If reordering is needed, at least one pair of consecutive children will be out-of-order.
1153        * So just check pairs of consecutive children.
1154        *
1155        * We checked above that complete_cpuset is always set.
1156        */
1157       if (hwloc_bitmap_compare_first(next->complete_cpuset, cur->complete_cpuset) < 0) {
1158         /* next should be before cur */
1159         if (!childrengotignored) {
1160           static int reported = 0;
1161           if (!reported && !hwloc_hide_errors()) {
1162             hwloc__xml_import_report_outoforder(topology, next, cur);
1163             reported = 1;
1164           }
1165         }
1166         hwloc__reorder_children(obj);
1167         break;
1168       }
1169     }
1170     /* no need to reorder memory children as long as there are no intermediate memory objects
1171      * that could cause reordering when filtered-out.
1172      */
1173   }
1174 
1175   return state->global->close_tag(state);
1176 
1177  error_with_object:
1178   if (parent)
1179     /* root->parent is NULL, and root is already inserted. the caller will cleanup that root. */
1180     hwloc_free_unlinked_object(obj);
1181  error:
1182   return -1;
1183 }
1184 
1185 static int
1186 hwloc__xml_v2import_distances(hwloc_topology_t topology,
1187                               hwloc__xml_import_state_t state)
1188 {
1189   hwloc_obj_type_t type = HWLOC_OBJ_TYPE_NONE;
1190   unsigned nbobjs = 0;
1191   int indexing = 0;
1192   int os_indexing = 0;
1193   int gp_indexing = 0;
1194   unsigned long kind = 0;
1195   unsigned nr_indexes, nr_u64values;
1196   uint64_t *indexes;
1197   uint64_t *u64values;
1198   int ret;
1199 
1200   /* process attributes */
1201   while (1) {
1202     char *attrname, *attrvalue;
1203     if (state->global->next_attr(state, &attrname, &attrvalue) < 0)
1204       break;
1205     if (!strcmp(attrname, "nbobjs"))
1206       nbobjs = strtoul(attrvalue, NULL, 10);
1207     else if (!strcmp(attrname, "type")) {
1208       if (hwloc_type_sscanf(attrvalue, &type, NULL, 0) < 0)
1209         goto out;
1210     }
1211     else if (!strcmp(attrname, "indexing")) {
1212       indexing = 1;
1213       if (!strcmp(attrvalue, "os"))
1214         os_indexing = 1;
1215       else if (!strcmp(attrvalue, "gp"))
1216         gp_indexing = 1;
1217     }
1218     else if (!strcmp(attrname, "kind")) {
1219       kind = strtoul(attrvalue, NULL, 10);
1220     }
1221     else {
1222       if (hwloc__xml_verbose())
1223         fprintf(stderr, "%s: ignoring unknown distance attribute %s\n",
1224                 state->global->msgprefix, attrname);
1225     }
1226   }
1227 
1228   /* abort if missing attribute */
1229   if (!nbobjs || type == HWLOC_OBJ_TYPE_NONE || !indexing || !kind) {
1230     if (hwloc__xml_verbose())
1231       fprintf(stderr, "%s: distance2 missing some attributes\n",
1232               state->global->msgprefix);
1233     goto out;
1234   }
1235 
1236   indexes = malloc(nbobjs*sizeof(*indexes));
1237   u64values = malloc(nbobjs*nbobjs*sizeof(*u64values));
1238   if (!indexes || !u64values) {
1239     if (hwloc__xml_verbose())
1240       fprintf(stderr, "%s: failed to allocate distances arrays for %u objects\n",
1241               state->global->msgprefix, nbobjs);
1242     goto out_with_arrays;
1243   }
1244 
1245   /* process children */
1246   nr_indexes = 0;
1247   nr_u64values = 0;
1248   while (1) {
1249     struct hwloc__xml_import_state_s childstate;
1250     char *attrname, *attrvalue, *tag, *buffer;
1251     int length;
1252     int is_index = 0;
1253     int is_u64values = 0;
1254 
1255     ret = state->global->find_child(state, &childstate, &tag);
1256     if (ret <= 0)
1257       break;
1258 
1259     if (!strcmp(tag, "indexes"))
1260       is_index = 1;
1261     else if (!strcmp(tag, "u64values"))
1262       is_u64values = 1;
1263     if (!is_index && !is_u64values) {
1264       if (hwloc__xml_verbose())
1265         fprintf(stderr, "%s: distance2 with unrecognized child %s\n",
1266                 state->global->msgprefix, tag);
1267       goto out_with_arrays;
1268     }
1269 
1270     if (state->global->next_attr(&childstate, &attrname, &attrvalue) < 0
1271         || strcmp(attrname, "length")) {
1272       if (hwloc__xml_verbose())
1273         fprintf(stderr, "%s: distance2 child must have length attribute\n",
1274                 state->global->msgprefix);
1275       goto out_with_arrays;
1276     }
1277     length = atoi(attrvalue);
1278 
1279     ret = state->global->get_content(&childstate, &buffer, length);
1280     if (ret < 0) {
1281       if (hwloc__xml_verbose())
1282         fprintf(stderr, "%s: distance2 child needs content of length %d\n",
1283                 state->global->msgprefix, length);
1284       goto out_with_arrays;
1285     }
1286 
1287     if (is_index) {
1288       /* get indexes */
1289       char *tmp;
1290       if (nr_indexes >= nbobjs) {
1291         if (hwloc__xml_verbose())
1292           fprintf(stderr, "%s: distance2 with more than %u indexes\n",
1293                   state->global->msgprefix, nbobjs);
1294         goto out_with_arrays;
1295       }
1296       tmp = buffer;
1297       while (1) {
1298         char *next;
1299         unsigned long long u = strtoull(tmp, &next, 0);
1300         if (next == tmp)
1301           break;
1302         indexes[nr_indexes++] = u;
1303         if (*next != ' ')
1304           break;
1305         if (nr_indexes == nbobjs)
1306           break;
1307         tmp = next+1;
1308       }
1309 
1310     } else if (is_u64values) {
1311       /* get uint64_t values */
1312       char *tmp;
1313       if (nr_u64values >= nbobjs*nbobjs) {
1314         if (hwloc__xml_verbose())
1315           fprintf(stderr, "%s: distance2 with more than %u u64values\n",
1316                   state->global->msgprefix, nbobjs*nbobjs);
1317         goto out_with_arrays;
1318       }
1319       tmp = buffer;
1320       while (1) {
1321         char *next;
1322         unsigned long long u = strtoull(tmp, &next, 0);
1323         if (next == tmp)
1324           break;
1325         u64values[nr_u64values++] = u;
1326         if (*next != ' ')
1327           break;
1328         if (nr_u64values == nbobjs*nbobjs)
1329           break;
1330         tmp = next+1;
1331       }
1332     }
1333 
1334     state->global->close_content(&childstate);
1335 
1336     ret = state->global->close_tag(&childstate);
1337     if (ret < 0) {
1338       if (hwloc__xml_verbose())
1339         fprintf(stderr, "%s: distance2 with more than %u indexes\n",
1340                 state->global->msgprefix, nbobjs);
1341       goto out_with_arrays;
1342     }
1343 
1344     state->global->close_child(&childstate);
1345   }
1346 
1347   if (nr_indexes != nbobjs) {
1348     if (hwloc__xml_verbose())
1349       fprintf(stderr, "%s: distance2 with less than %u indexes\n",
1350               state->global->msgprefix, nbobjs);
1351     goto out_with_arrays;
1352   }
1353   if (nr_u64values != nbobjs*nbobjs) {
1354     if (hwloc__xml_verbose())
1355       fprintf(stderr, "%s: distance2 with less than %u u64values\n",
1356               state->global->msgprefix, nbobjs*nbobjs);
1357     goto out_with_arrays;
1358   }
1359 
1360   if (nbobjs < 2) {
1361     /* distances with a single object are useless, even if the XML isn't invalid */
1362     if (hwloc__xml_verbose())
1363       fprintf(stderr, "%s: ignoring distances2 with only %u objects\n",
1364               state->global->msgprefix, nbobjs);
1365     goto out_ignore;
1366   }
1367   if (type == HWLOC_OBJ_PU || type == HWLOC_OBJ_NUMANODE) {
1368     if (!os_indexing) {
1369       if (hwloc__xml_verbose())
1370         fprintf(stderr, "%s: ignoring PU or NUMA distances2 without os_indexing\n",
1371                 state->global->msgprefix);
1372       goto out_ignore;
1373     }
1374   } else {
1375     if (!gp_indexing) {
1376       if (hwloc__xml_verbose())
1377         fprintf(stderr, "%s: ignoring !PU or !NUMA distances2 without gp_indexing\n",
1378                 state->global->msgprefix);
1379       goto out_ignore;
1380     }
1381   }
1382 
1383   hwloc_internal_distances_add_by_index(topology, type, nbobjs, indexes, u64values, kind, 0);
1384 
1385   /* prevent freeing below */
1386   indexes = NULL;
1387   u64values = NULL;
1388 
1389  out_ignore:
1390   free(indexes);
1391   free(u64values);
1392   return state->global->close_tag(state);
1393 
1394  out_with_arrays:
1395   free(indexes);
1396   free(u64values);
1397  out:
1398   return -1;
1399 }
1400 
1401 static int
1402 hwloc__xml_import_diff_one(hwloc__xml_import_state_t state,
1403                            hwloc_topology_diff_t *firstdiffp,
1404                            hwloc_topology_diff_t *lastdiffp)
1405 {
1406   char *type_s = NULL;
1407   char *obj_depth_s = NULL;
1408   char *obj_index_s = NULL;
1409   char *obj_attr_type_s = NULL;
1410 /* char *obj_attr_index_s = NULL; unused for now */
1411   char *obj_attr_name_s = NULL;
1412   char *obj_attr_oldvalue_s = NULL;
1413   char *obj_attr_newvalue_s = NULL;
1414 
1415   while (1) {
1416     char *attrname, *attrvalue;
1417     if (state->global->next_attr(state, &attrname, &attrvalue) < 0)
1418       break;
1419     if (!strcmp(attrname, "type"))
1420       type_s = attrvalue;
1421     else if (!strcmp(attrname, "obj_depth"))
1422       obj_depth_s = attrvalue;
1423     else if (!strcmp(attrname, "obj_index"))
1424       obj_index_s = attrvalue;
1425     else if (!strcmp(attrname, "obj_attr_type"))
1426       obj_attr_type_s = attrvalue;
1427     else if (!strcmp(attrname, "obj_attr_index"))
1428       { /* obj_attr_index_s = attrvalue; unused for now */ }
1429     else if (!strcmp(attrname, "obj_attr_name"))
1430       obj_attr_name_s = attrvalue;
1431     else if (!strcmp(attrname, "obj_attr_oldvalue"))
1432       obj_attr_oldvalue_s = attrvalue;
1433     else if (!strcmp(attrname, "obj_attr_newvalue"))
1434       obj_attr_newvalue_s = attrvalue;
1435     else {
1436       if (hwloc__xml_verbose())
1437         fprintf(stderr, "%s: ignoring unknown diff attribute %s\n",
1438                 state->global->msgprefix, attrname);
1439       return -1;
1440     }
1441   }
1442 
1443   if (type_s) {
1444     switch (atoi(type_s)) {
1445     default:
1446       break;
1447     case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR: {
1448       /* object attribute diff */
1449       hwloc_topology_diff_obj_attr_type_t obj_attr_type;
1450       hwloc_topology_diff_t diff;
1451 
1452       /* obj_attr mandatory generic attributes */
1453       if (!obj_depth_s || !obj_index_s || !obj_attr_type_s) {
1454         if (hwloc__xml_verbose())
1455           fprintf(stderr, "%s: missing mandatory obj attr generic attributes\n",
1456                   state->global->msgprefix);
1457         break;
1458       }
1459 
1460       /* obj_attr mandatory attributes common to all subtypes */
1461       if (!obj_attr_oldvalue_s || !obj_attr_newvalue_s) {
1462         if (hwloc__xml_verbose())
1463           fprintf(stderr, "%s: missing mandatory obj attr value attributes\n",
1464                   state->global->msgprefix);
1465         break;
1466       }
1467 
1468       /* mandatory attributes for obj_attr_info subtype */
1469       obj_attr_type = atoi(obj_attr_type_s);
1470       if (obj_attr_type == HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO && !obj_attr_name_s) {
1471         if (hwloc__xml_verbose())
1472           fprintf(stderr, "%s: missing mandatory obj attr info name attribute\n",
1473                   state->global->msgprefix);
1474         break;
1475       }
1476 
1477       /* now we know we have everything we need */
1478       diff = malloc(sizeof(*diff));
1479       if (!diff)
1480         return -1;
1481       diff->obj_attr.type = HWLOC_TOPOLOGY_DIFF_OBJ_ATTR;
1482       diff->obj_attr.obj_depth = atoi(obj_depth_s);
1483       diff->obj_attr.obj_index = atoi(obj_index_s);
1484       memset(&diff->obj_attr.diff, 0, sizeof(diff->obj_attr.diff));
1485       diff->obj_attr.diff.generic.type = obj_attr_type;
1486 
1487       switch (atoi(obj_attr_type_s)) {
1488       case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_SIZE:
1489         diff->obj_attr.diff.uint64.oldvalue = strtoull(obj_attr_oldvalue_s, NULL, 0);
1490         diff->obj_attr.diff.uint64.newvalue = strtoull(obj_attr_newvalue_s, NULL, 0);
1491         break;
1492       case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO:
1493         diff->obj_attr.diff.string.name = strdup(obj_attr_name_s);
1494         /* FALLTHRU */
1495       case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME:
1496         diff->obj_attr.diff.string.oldvalue = strdup(obj_attr_oldvalue_s);
1497         diff->obj_attr.diff.string.newvalue = strdup(obj_attr_newvalue_s);
1498         break;
1499       }
1500 
1501       if (*firstdiffp)
1502         (*lastdiffp)->generic.next = diff;
1503       else
1504         *firstdiffp = diff;
1505       *lastdiffp = diff;
1506       diff->generic.next = NULL;
1507     }
1508     }
1509   }
1510 
1511   return state->global->close_tag(state);
1512 }
1513 
1514 int
1515 hwloc__xml_import_diff(hwloc__xml_import_state_t state,
1516                        hwloc_topology_diff_t *firstdiffp)
1517 {
1518   hwloc_topology_diff_t firstdiff = NULL, lastdiff = NULL;
1519   *firstdiffp = NULL;
1520 
1521   while (1) {
1522     struct hwloc__xml_import_state_s childstate;
1523     char *tag;
1524     int ret;
1525 
1526     ret = state->global->find_child(state, &childstate, &tag);
1527     if (ret < 0)
1528       return -1;
1529     if (!ret)
1530       break;
1531 
1532     if (!strcmp(tag, "diff")) {
1533       ret = hwloc__xml_import_diff_one(&childstate, &firstdiff, &lastdiff);
1534     } else
1535       ret = -1;
1536 
1537     if (ret < 0)
1538       return ret;
1539 
1540     state->global->close_child(&childstate);
1541   }
1542 
1543   *firstdiffp = firstdiff;
1544   return 0;
1545 }
1546 
1547 /***********************************
1548  ********* main XML import *********
1549  ***********************************/
1550 
1551 static void
1552 hwloc_convert_from_v1dist_floats(hwloc_topology_t topology, unsigned nbobjs, float *floats, uint64_t *u64s)
1553 {
1554   unsigned i;
1555   int is_uint;
1556   char *env;
1557   float scale = 1000.f;
1558   char scalestring[20];
1559 
1560   env = getenv("HWLOC_XML_V1DIST_SCALE");
1561   if (env) {
1562     scale = (float) atof(env);
1563     goto scale;
1564   }
1565 
1566   is_uint = 1;
1567   /* find out if all values are integers */
1568   for(i=0; i<nbobjs*nbobjs; i++) {
1569     float f, iptr, fptr;
1570     f = floats[i];
1571     if (f < 0.f) {
1572       is_uint = 0;
1573       break;
1574     }
1575     fptr = modff(f, &iptr);
1576     if (fptr > .001f && fptr < .999f) {
1577       is_uint = 0;
1578       break;
1579     }
1580     u64s[i] = (int)(f+.5f);
1581   }
1582   if (is_uint)
1583     return;
1584 
1585  scale:
1586   /* TODO heuristic to find a good scale */
1587   for(i=0; i<nbobjs*nbobjs; i++)
1588     u64s[i] = (uint64_t)(scale * floats[i]);
1589 
1590   /* save the scale in root info attrs.
1591    * Not perfect since we may have multiple of them,
1592    * and some distances might disappear in case of restrict, etc.
1593    */
1594   sprintf(scalestring, "%f", scale);
1595   hwloc_obj_add_info(hwloc_get_root_obj(topology), "xmlv1DistancesScale", scalestring);
1596 }
1597 
1598 /* this canNOT be the first XML call */
1599 static int
1600 hwloc_look_xml(struct hwloc_backend *backend)
1601 {
1602   struct hwloc_topology *topology = backend->topology;
1603   struct hwloc_xml_backend_data_s *data = backend->private_data;
1604   struct hwloc__xml_import_state_s state, childstate;
1605   struct hwloc_obj *root = topology->levels[0][0];
1606   char *tag;
1607   int gotignored = 0;
1608   hwloc_localeswitch_declare;
1609   int ret;
1610 
1611   state.global = data;
1612 
1613   assert(!root->cpuset);
1614 
1615   hwloc_localeswitch_init();
1616 
1617   data->nbnumanodes = 0;
1618   data->first_numanode = data->last_numanode = NULL;
1619   data->first_v1dist = data->last_v1dist = NULL;
1620 
1621   ret = data->look_init(data, &state);
1622   if (ret < 0)
1623     goto failed;
1624 
1625   if (data->version_major > 2) {
1626     if (hwloc__xml_verbose())
1627       fprintf(stderr, "%s: cannot import XML version %u.%u > 2\n",
1628               data->msgprefix, data->version_major, data->version_minor);
1629     goto err;
1630   }
1631 
1632   /* find root object tag and import it */
1633   ret = state.global->find_child(&state, &childstate, &tag);
1634   if (ret < 0 || !ret || strcmp(tag, "object"))
1635     goto failed;
1636   ret = hwloc__xml_import_object(topology, data, NULL /*  no parent */, root,
1637                                  &gotignored,
1638                                  &childstate);
1639   if (ret < 0)
1640     goto failed;
1641   state.global->close_child(&childstate);
1642   assert(!gotignored);
1643 
1644   /* the root may have changed if we had to reinsert a Machine */
1645   root = topology->levels[0][0];
1646 
1647   if (data->version_major >= 2) {
1648     /* find v2 distances */
1649     while (1) {
1650       ret = state.global->find_child(&state, &childstate, &tag);
1651       if (ret < 0)
1652         goto failed;
1653       if (!ret)
1654         break;
1655       if (strcmp(tag, "distances2")) {
1656         if (hwloc__xml_verbose())
1657           fprintf(stderr, "%s: ignoring unknown tag `%s' after root object, expected `distances2'\n",
1658                   data->msgprefix, tag);
1659         goto done;
1660       }
1661       ret = hwloc__xml_v2import_distances(topology, &childstate);
1662       if (ret < 0)
1663         goto failed;
1664       state.global->close_child(&childstate);
1665     }
1666   }
1667 
1668   /* find end of topology tag */
1669   state.global->close_tag(&state);
1670 
1671 done:
1672   if (!root->cpuset) {
1673     if (hwloc__xml_verbose())
1674       fprintf(stderr, "%s: invalid root object without cpuset\n",
1675               data->msgprefix);
1676     goto err;
1677   }
1678 
1679   /* update pre-v2.0 memory group gp_index */
1680   if (data->version_major < 2 && data->first_numanode) {
1681     hwloc_obj_t node = data->first_numanode;
1682     do {
1683       if (node->parent->type == HWLOC_OBJ_GROUP
1684           && !node->parent->gp_index)
1685         node->parent->gp_index = topology->next_gp_index++;
1686       node = node->next_cousin;
1687     } while (node);
1688   }
1689 
1690   if (data->version_major < 2 && data->first_v1dist) {
1691     /* handle v1 distances */
1692     struct hwloc__xml_imported_v1distances_s *v1dist, *v1next = data->first_v1dist;
1693     while ((v1dist = v1next) != NULL) {
1694       unsigned nbobjs = v1dist->nbobjs;
1695       v1next = v1dist->next;
1696       /* Handle distances as NUMA node distances if nbobjs matches.
1697        * Otherwise drop, only NUMA distances really matter.
1698        *
1699        * We could also attach to a random level with the right nbobjs,
1700        * but it would require to have those objects in the original XML order (like the first_numanode cousin-list).
1701        * because the topology order can be different if some parents are ignored during load.
1702        */
1703       if (nbobjs == data->nbnumanodes) {
1704         hwloc_obj_t *objs = malloc(nbobjs*sizeof(hwloc_obj_t));
1705         uint64_t *values = malloc(nbobjs*nbobjs*sizeof(*values));
1706         if (objs && values) {
1707           hwloc_obj_t node;
1708           unsigned i;
1709           for(i=0, node = data->first_numanode;
1710               i<nbobjs;
1711               i++, node = node->next_cousin)
1712             objs[i] = node;
1713 hwloc_convert_from_v1dist_floats(topology, nbobjs, v1dist->floats, values);
1714           hwloc_internal_distances_add(topology, nbobjs, objs, values, v1dist->kind, 0);
1715         } else {
1716           free(objs);
1717           free(values);
1718         }
1719       }
1720       free(v1dist->floats);
1721       free(v1dist);
1722     }
1723     data->first_v1dist = data->last_v1dist = NULL;
1724   }
1725 
1726   /* FIXME:
1727    * We should check that the existing object sets are consistent:
1728    * no intersection between objects of a same level,
1729    * object sets included in parent sets.
1730    * hwloc never generated such buggy XML, but users could create one.
1731    *
1732    * We want to add these checks to the existing core code that
1733    * adds missing sets and propagates parent/children sets
1734    * (in case another backend ever generates buggy object sets as well).
1735    */
1736 
1737   if (data->version_major >= 2) {
1738     /* v2 must have non-empty nodesets since at least one NUMA node is required */
1739     if (!root->nodeset) {
1740       if (hwloc__xml_verbose())
1741         fprintf(stderr, "%s: invalid root object without nodeset\n",
1742                 data->msgprefix);
1743       goto err;
1744     }
1745     if (hwloc_bitmap_iszero(root->nodeset)) {
1746       if (hwloc__xml_verbose())
1747         fprintf(stderr, "%s: invalid root object with empty nodeset\n",
1748                 data->msgprefix);
1749       goto err;
1750     }
1751   } else {
1752     /* if v1 without nodeset, the core will add a default NUMA node and nodesets */
1753   }
1754 
1755   /* allocate default cpusets and nodesets if missing, the core will restrict them */
1756   hwloc_alloc_root_sets(root);
1757 
1758   /* keep the "Backend" information intact */
1759   /* we could add "BackendSource=XML" to notify that XML was used between the actual backend and here */
1760 
1761   topology->support.discovery->pu = 1;
1762   if (data->nbnumanodes) {
1763     topology->support.discovery->numa = 1;
1764     topology->support.discovery->numa_memory = 1; // FIXME
1765   }
1766 
1767   if (data->look_done)
1768     data->look_done(data, 0);
1769 
1770   hwloc_localeswitch_fini();
1771   return 0;
1772 
1773  failed:
1774   if (data->look_done)
1775     data->look_done(data, -1);
1776   if (hwloc__xml_verbose())
1777     fprintf(stderr, "%s: XML component discovery failed.\n",
1778             data->msgprefix);
1779  err:
1780   hwloc_free_object_siblings_and_children(root->first_child);
1781   root->first_child = NULL;
1782   hwloc_free_object_siblings_and_children(root->memory_first_child);
1783   root->memory_first_child = NULL;
1784   hwloc_free_object_siblings_and_children(root->io_first_child);
1785   root->io_first_child = NULL;
1786   hwloc_free_object_siblings_and_children(root->misc_first_child);
1787   root->misc_first_child = NULL;
1788 
1789   /* make sure the core will abort */
1790   if (root->cpuset)
1791     hwloc_bitmap_zero(root->cpuset);
1792   if (root->nodeset)
1793     hwloc_bitmap_zero(root->nodeset);
1794 
1795   hwloc_localeswitch_fini();
1796   return -1;
1797 }
1798 
1799 /* this can be the first XML call */
1800 int
1801 hwloc_topology_diff_load_xml(const char *xmlpath,
1802                              hwloc_topology_diff_t *firstdiffp, char **refnamep)
1803 {
1804   struct hwloc__xml_import_state_s state;
1805   struct hwloc_xml_backend_data_s fakedata; /* only for storing global info during parsing */
1806   hwloc_localeswitch_declare;
1807   const char *basename;
1808   int force_nolibxml;
1809   int ret;
1810 
1811   state.global = &fakedata;
1812 
1813   basename = strrchr(xmlpath, '/');
1814   if (basename)
1815     basename++;
1816   else
1817     basename = xmlpath;
1818   fakedata.msgprefix = strdup(basename);
1819 
1820   hwloc_components_init();
1821   assert(hwloc_nolibxml_callbacks);
1822 
1823   hwloc_localeswitch_init();
1824 
1825   *firstdiffp = NULL;
1826 
1827   force_nolibxml = hwloc_nolibxml_import();
1828 retry:
1829   if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml))
1830     ret = hwloc_nolibxml_callbacks->import_diff(&state, xmlpath, NULL, 0, firstdiffp, refnamep);
1831   else {
1832     ret = hwloc_libxml_callbacks->import_diff(&state, xmlpath, NULL, 0, firstdiffp, refnamep);
1833     if (ret < 0 && errno == ENOSYS) {
1834       hwloc_libxml_callbacks = NULL;
1835       goto retry;
1836     }
1837   }
1838 
1839   hwloc_localeswitch_fini();
1840   hwloc_components_fini();
1841   free(fakedata.msgprefix);
1842   return ret;
1843 }
1844 
1845 /* this can be the first XML call */
1846 int
1847 hwloc_topology_diff_load_xmlbuffer(const char *xmlbuffer, int buflen,
1848                                    hwloc_topology_diff_t *firstdiffp, char **refnamep)
1849 {
1850   struct hwloc__xml_import_state_s state;
1851   struct hwloc_xml_backend_data_s fakedata; /* only for storing global info during parsing */
1852   hwloc_localeswitch_declare;
1853   int force_nolibxml;
1854   int ret;
1855 
1856   state.global = &fakedata;
1857   fakedata.msgprefix = strdup("xmldiffbuffer");
1858 
1859   hwloc_components_init();
1860   assert(hwloc_nolibxml_callbacks);
1861 
1862   hwloc_localeswitch_init();
1863 
1864   *firstdiffp = NULL;
1865 
1866   force_nolibxml = hwloc_nolibxml_import();
1867  retry:
1868   if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml))
1869     ret = hwloc_nolibxml_callbacks->import_diff(&state, NULL, xmlbuffer, buflen, firstdiffp, refnamep);
1870   else {
1871     ret = hwloc_libxml_callbacks->import_diff(&state, NULL, xmlbuffer, buflen, firstdiffp, refnamep);
1872     if (ret < 0 && errno == ENOSYS) {
1873       hwloc_libxml_callbacks = NULL;
1874       goto retry;
1875     }
1876   }
1877 
1878   hwloc_localeswitch_fini();
1879   hwloc_components_fini();
1880   free(fakedata.msgprefix);
1881   return ret;
1882 }
1883 
1884 /************************************************
1885  ********* XML export (common routines) *********
1886  ************************************************/
1887 
1888 #define HWLOC_XML_CHAR_VALID(c) (((c) >= 32 && (c) <= 126) || (c) == '\t' || (c) == '\n' || (c) == '\r')
1889 
1890 static int
1891 hwloc__xml_export_check_buffer(const char *buf, size_t length)
1892 {
1893   unsigned i;
1894   for(i=0; i<length; i++)
1895     if (!HWLOC_XML_CHAR_VALID(buf[i]))
1896       return -1;
1897   return 0;
1898 }
1899 
1900 /* strdup and remove ugly chars from random string */
1901 static char*
1902 hwloc__xml_export_safestrdup(const char *old)
1903 {
1904   char *new = malloc(strlen(old)+1);
1905   char *dst = new;
1906   const char *src = old;
1907   while (*src) {
1908     if (HWLOC_XML_CHAR_VALID(*src))
1909       *(dst++) = *src;
1910     src++;
1911   }
1912   *dst = '\0';
1913   return new;
1914 }
1915 
1916 static void
1917 hwloc__xml_export_object_contents (hwloc__xml_export_state_t state, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags)
1918 {
1919   char *setstring = NULL, *setstring2 = NULL;
1920   char tmp[255];
1921   int v1export = flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1;
1922   unsigned i,j;
1923 
1924   if (v1export && obj->type == HWLOC_OBJ_PACKAGE)
1925     state->new_prop(state, "type", "Socket");
1926   else if (v1export && hwloc__obj_type_is_cache(obj->type))
1927     state->new_prop(state, "type", "Cache");
1928   else
1929     state->new_prop(state, "type", hwloc_obj_type_string(obj->type));
1930 
1931   if (obj->os_index != HWLOC_UNKNOWN_INDEX) {
1932     sprintf(tmp, "%u", obj->os_index);
1933     state->new_prop(state, "os_index", tmp);
1934   }
1935 
1936   if (obj->cpuset) {
1937     if (v1export && obj->type == HWLOC_OBJ_NUMANODE && obj->sibling_rank > 0) {
1938       /* v1 non-first NUMA nodes have empty cpusets */
1939       state->new_prop(state, "cpuset", "0x0");
1940       state->new_prop(state, "online_cpuset", "0x0");
1941       state->new_prop(state, "complete_cpuset", "0x0");
1942       state->new_prop(state, "allowed_cpuset", "0x0");
1943 
1944     } else {
1945       /* normal case */
1946       hwloc_bitmap_asprintf(&setstring, obj->cpuset);
1947       state->new_prop(state, "cpuset", setstring);
1948 
1949       hwloc_bitmap_asprintf(&setstring2, obj->complete_cpuset);
1950       state->new_prop(state, "complete_cpuset", setstring2);
1951       free(setstring2);
1952 
1953       if (v1export)
1954         state->new_prop(state, "online_cpuset", setstring);
1955       free(setstring);
1956 
1957       if (v1export || !obj->parent) {
1958         hwloc_bitmap_t allowed_cpuset = hwloc_bitmap_dup(obj->cpuset);
1959         hwloc_bitmap_and(allowed_cpuset, allowed_cpuset, topology->allowed_cpuset);
1960         hwloc_bitmap_asprintf(&setstring, allowed_cpuset);
1961         state->new_prop(state, "allowed_cpuset", setstring);
1962         free(setstring);
1963         hwloc_bitmap_free(allowed_cpuset);
1964       }
1965     }
1966 
1967     /* If exporting v1, we should clear second local NUMA bits from nodeset,
1968      * but the importer will clear them anyway.
1969      */
1970     hwloc_bitmap_asprintf(&setstring, obj->nodeset);
1971     state->new_prop(state, "nodeset", setstring);
1972     free(setstring);
1973 
1974     hwloc_bitmap_asprintf(&setstring, obj->complete_nodeset);
1975     state->new_prop(state, "complete_nodeset", setstring);
1976     free(setstring);
1977 
1978     if (v1export || !obj->parent) {
1979       hwloc_bitmap_t allowed_nodeset = hwloc_bitmap_dup(obj->nodeset);
1980       hwloc_bitmap_and(allowed_nodeset, allowed_nodeset, topology->allowed_nodeset);
1981       hwloc_bitmap_asprintf(&setstring, allowed_nodeset);
1982       state->new_prop(state, "allowed_nodeset", setstring);
1983       free(setstring);
1984       hwloc_bitmap_free(allowed_nodeset);
1985     }
1986   }
1987 
1988   if (!v1export) {
1989     sprintf(tmp, "%llu", (unsigned long long) obj->gp_index);
1990     state->new_prop(state, "gp_index", tmp);
1991   }
1992 
1993   if (obj->name) {
1994     char *name = hwloc__xml_export_safestrdup(obj->name);
1995     state->new_prop(state, "name", name);
1996     free(name);
1997   }
1998   if (!v1export && obj->subtype) {
1999     char *subtype = hwloc__xml_export_safestrdup(obj->subtype);
2000     state->new_prop(state, "subtype", subtype);
2001     free(subtype);
2002   }
2003 
2004   switch (obj->type) {
2005   case HWLOC_OBJ_NUMANODE:
2006     if (obj->attr->numanode.local_memory) {
2007       sprintf(tmp, "%llu", (unsigned long long) obj->attr->numanode.local_memory);
2008       state->new_prop(state, "local_memory", tmp);
2009     }
2010     for(i=0; i<obj->attr->numanode.page_types_len; i++) {
2011       struct hwloc__xml_export_state_s childstate;
2012       state->new_child(state, &childstate, "page_type");
2013       sprintf(tmp, "%llu", (unsigned long long) obj->attr->numanode.page_types[i].size);
2014       childstate.new_prop(&childstate, "size", tmp);
2015       sprintf(tmp, "%llu", (unsigned long long) obj->attr->numanode.page_types[i].count);
2016       childstate.new_prop(&childstate, "count", tmp);
2017       childstate.end_object(&childstate, "page_type");
2018     }
2019     break;
2020   case HWLOC_OBJ_L1CACHE:
2021   case HWLOC_OBJ_L2CACHE:
2022   case HWLOC_OBJ_L3CACHE:
2023   case HWLOC_OBJ_L4CACHE:
2024   case HWLOC_OBJ_L5CACHE:
2025   case HWLOC_OBJ_L1ICACHE:
2026   case HWLOC_OBJ_L2ICACHE:
2027   case HWLOC_OBJ_L3ICACHE:
2028     sprintf(tmp, "%llu", (unsigned long long) obj->attr->cache.size);
2029     state->new_prop(state, "cache_size", tmp);
2030     sprintf(tmp, "%u", obj->attr->cache.depth);
2031     state->new_prop(state, "depth", tmp);
2032     sprintf(tmp, "%u", (unsigned) obj->attr->cache.linesize);
2033     state->new_prop(state, "cache_linesize", tmp);
2034     sprintf(tmp, "%d", obj->attr->cache.associativity);
2035     state->new_prop(state, "cache_associativity", tmp);
2036     sprintf(tmp, "%d", (int) obj->attr->cache.type);
2037     state->new_prop(state, "cache_type", tmp);
2038     break;
2039   case HWLOC_OBJ_GROUP:
2040     if (v1export) {
2041       sprintf(tmp, "%u", obj->attr->group.depth);
2042       state->new_prop(state, "depth", tmp);
2043     } else {
2044       sprintf(tmp, "%u", obj->attr->group.kind);
2045       state->new_prop(state, "kind", tmp);
2046       sprintf(tmp, "%u", obj->attr->group.subkind);
2047       state->new_prop(state, "subkind", tmp);
2048     }
2049     break;
2050   case HWLOC_OBJ_BRIDGE:
2051     sprintf(tmp, "%d-%d", (int) obj->attr->bridge.upstream_type, (int) obj->attr->bridge.downstream_type);
2052     state->new_prop(state, "bridge_type", tmp);
2053     sprintf(tmp, "%u", obj->attr->bridge.depth);
2054     state->new_prop(state, "depth", tmp);
2055     if (obj->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI) {
2056       sprintf(tmp, "%04x:[%02x-%02x]",
2057               (unsigned) obj->attr->bridge.downstream.pci.domain,
2058               (unsigned) obj->attr->bridge.downstream.pci.secondary_bus,
2059               (unsigned) obj->attr->bridge.downstream.pci.subordinate_bus);
2060       state->new_prop(state, "bridge_pci", tmp);
2061     }
2062     if (obj->attr->bridge.upstream_type != HWLOC_OBJ_BRIDGE_PCI)
2063       break;
2064     /* FALLTHRU */
2065   case HWLOC_OBJ_PCI_DEVICE:
2066     sprintf(tmp, "%04x:%02x:%02x.%01x",
2067             (unsigned) obj->attr->pcidev.domain,
2068             (unsigned) obj->attr->pcidev.bus,
2069             (unsigned) obj->attr->pcidev.dev,
2070             (unsigned) obj->attr->pcidev.func);
2071     state->new_prop(state, "pci_busid", tmp);
2072     sprintf(tmp, "%04x [%04x:%04x] [%04x:%04x] %02x",
2073             (unsigned) obj->attr->pcidev.class_id,
2074             (unsigned) obj->attr->pcidev.vendor_id, (unsigned) obj->attr->pcidev.device_id,
2075             (unsigned) obj->attr->pcidev.subvendor_id, (unsigned) obj->attr->pcidev.subdevice_id,
2076             (unsigned) obj->attr->pcidev.revision);
2077     state->new_prop(state, "pci_type", tmp);
2078     sprintf(tmp, "%f", obj->attr->pcidev.linkspeed);
2079     state->new_prop(state, "pci_link_speed", tmp);
2080     break;
2081   case HWLOC_OBJ_OS_DEVICE:
2082     sprintf(tmp, "%d", (int) obj->attr->osdev.type);
2083     state->new_prop(state, "osdev_type", tmp);
2084     break;
2085   default:
2086     break;
2087   }
2088 
2089   for(i=0; i<obj->infos_count; i++) {
2090     char *name = hwloc__xml_export_safestrdup(obj->infos[i].name);
2091     char *value = hwloc__xml_export_safestrdup(obj->infos[i].value);
2092     struct hwloc__xml_export_state_s childstate;
2093     state->new_child(state, &childstate, "info");
2094     childstate.new_prop(&childstate, "name", name);
2095     childstate.new_prop(&childstate, "value", value);
2096     childstate.end_object(&childstate, "info");
2097     free(name);
2098     free(value);
2099   }
2100   if (v1export && obj->subtype) {
2101     char *subtype = hwloc__xml_export_safestrdup(obj->subtype);
2102     struct hwloc__xml_export_state_s childstate;
2103     int is_coproctype = (obj->type == HWLOC_OBJ_OS_DEVICE && obj->attr->osdev.type == HWLOC_OBJ_OSDEV_COPROC);
2104     state->new_child(state, &childstate, "info");
2105     childstate.new_prop(&childstate, "name", is_coproctype ? "CoProcType" : "Type");
2106     childstate.new_prop(&childstate, "value", subtype);
2107     childstate.end_object(&childstate, "info");
2108     free(subtype);
2109   }
2110 
2111   if (v1export && !obj->parent) {
2112     /* only latency matrices covering the entire machine can be exported to v1 */
2113     struct hwloc_internal_distances_s *dist;
2114     /* refresh distances since we need objects below */
2115     hwloc_internal_distances_refresh(topology);
2116     for(dist = topology->first_dist; dist; dist = dist->next) {
2117       struct hwloc__xml_export_state_s childstate;
2118       unsigned nbobjs = dist->nbobjs;
2119       int depth;
2120 
2121       if (nbobjs != (unsigned) hwloc_get_nbobjs_by_type(topology, dist->type))
2122         continue;
2123       if (!(dist->kind & HWLOC_DISTANCES_KIND_MEANS_LATENCY))
2124         continue;
2125      {
2126       HWLOC_VLA(unsigned, logical_to_v2array, nbobjs);
2127       for(i=0; i<nbobjs; i++)
2128         logical_to_v2array[dist->objs[i]->logical_index] = i;
2129 
2130       /* compute the relative depth */
2131       if (dist->type == HWLOC_OBJ_NUMANODE) {
2132         /* for NUMA nodes, use the highest normal-parent depth + 1 */
2133         depth = -1;
2134         for(i=0; i<nbobjs; i++) {
2135           hwloc_obj_t parent = dist->objs[i]->parent;
2136           while (hwloc__obj_type_is_memory(parent->type))
2137             parent = parent->parent;
2138           if (parent->depth+1 > depth)
2139             depth = parent->depth+1;
2140         }
2141       } else {
2142         /* for non-NUMA nodes, increase the object depth if any of them has memory above */
2143         int parent_with_memory = 0;
2144         for(i=0; i<nbobjs; i++) {
2145           hwloc_obj_t parent = dist->objs[i]->parent;
2146           while (parent) {
2147             if (parent->memory_first_child) {
2148               parent_with_memory = 1;
2149               goto done;
2150             }
2151             parent = parent->parent;
2152           }
2153         }
2154       done:
2155         depth = hwloc_get_type_depth(topology, dist->type) + parent_with_memory;
2156       }
2157 
2158       state->new_child(state, &childstate, "distances");
2159       sprintf(tmp, "%u", nbobjs);
2160       childstate.new_prop(&childstate, "nbobjs", tmp);
2161       sprintf(tmp, "%d", depth);
2162       childstate.new_prop(&childstate, "relative_depth", tmp);
2163       sprintf(tmp, "%f", 1.f);
2164       childstate.new_prop(&childstate, "latency_base", tmp);
2165       for(i=0; i<nbobjs; i++) {
2166         for(j=0; j<nbobjs; j++) {
2167           /* we should export i*nbobjs+j, we translate using logical_to_v2array[] */
2168           unsigned k = logical_to_v2array[i]*nbobjs+logical_to_v2array[j];
2169           struct hwloc__xml_export_state_s greatchildstate;
2170           childstate.new_child(&childstate, &greatchildstate, "latency");
2171           sprintf(tmp, "%f", (float) dist->values[k]);
2172           greatchildstate.new_prop(&greatchildstate, "value", tmp);
2173           greatchildstate.end_object(&greatchildstate, "latency");
2174         }
2175       }
2176       childstate.end_object(&childstate, "distances");
2177      }
2178     }
2179   }
2180 
2181   if (obj->userdata && topology->userdata_export_cb)
2182     topology->userdata_export_cb((void*) state, topology, obj);
2183 }
2184 
2185 static void
2186 hwloc__xml_v2export_object (hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags)
2187 {
2188   struct hwloc__xml_export_state_s state;
2189   hwloc_obj_t child;
2190 
2191   parentstate->new_child(parentstate, &state, "object");
2192 
2193   hwloc__xml_export_object_contents(&state, topology, obj, flags);
2194 
2195   for_each_memory_child(child, obj)
2196     hwloc__xml_v2export_object (&state, topology, child, flags);
2197   for_each_child(child, obj)
2198     hwloc__xml_v2export_object (&state, topology, child, flags);
2199   for_each_io_child(child, obj)
2200     hwloc__xml_v2export_object (&state, topology, child, flags);
2201   for_each_misc_child(child, obj)
2202     hwloc__xml_v2export_object (&state, topology, child, flags);
2203 
2204   state.end_object(&state, "object");
2205 }
2206 
2207 static void
2208 hwloc__xml_v1export_object (hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags);
2209 
2210 static void
2211 hwloc__xml_v1export_object_with_memory(hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags)
2212 {
2213   struct hwloc__xml_export_state_s gstate, mstate, ostate, *state = parentstate;
2214   hwloc_obj_t child;
2215 
2216   if (obj->parent->arity > 1 && obj->memory_arity > 1 && parentstate->global->v1_memory_group) {
2217     /* child has sibling, we must add a Group around those memory children */
2218     hwloc_obj_t group = parentstate->global->v1_memory_group;
2219     parentstate->new_child(parentstate, &gstate, "object");
2220     group->cpuset = obj->cpuset;
2221     group->complete_cpuset = obj->complete_cpuset;
2222     group->nodeset = obj->nodeset;
2223     group->complete_nodeset = obj->complete_nodeset;
2224     hwloc__xml_export_object_contents (&gstate, topology, group, flags);
2225     group->cpuset = NULL;
2226     group->complete_cpuset = NULL;
2227     group->nodeset = NULL;
2228     group->complete_nodeset = NULL;
2229     state = &gstate;
2230   }
2231 
2232   /* export first memory child */
2233   child = obj->memory_first_child;
2234   assert(child->type == HWLOC_OBJ_NUMANODE);
2235   state->new_child(state, &mstate, "object");
2236   hwloc__xml_export_object_contents (&mstate, topology, child, flags);
2237 
2238   /* then the actual object */
2239   mstate.new_child(&mstate, &ostate, "object");
2240   hwloc__xml_export_object_contents (&ostate, topology, obj, flags);
2241 
2242   /* then its normal/io/misc children */
2243   for_each_child(child, obj)
2244     hwloc__xml_v1export_object (&ostate, topology, child, flags);
2245   for_each_io_child(child, obj)
2246     hwloc__xml_v1export_object (&ostate, topology, child, flags);
2247   for_each_misc_child(child, obj)
2248     hwloc__xml_v1export_object (&ostate, topology, child, flags);
2249 
2250   /* close object and first memory child */
2251   ostate.end_object(&ostate, "object");
2252   mstate.end_object(&mstate, "object");
2253 
2254   /* now other memory children */
2255   for_each_memory_child(child, obj)
2256     if (child->sibling_rank > 0)
2257       hwloc__xml_v1export_object (state, topology, child, flags);
2258 
2259   if (state == &gstate) {
2260     /* close group if any */
2261     gstate.end_object(&gstate, "object");
2262   }
2263 }
2264 
2265 static void
2266 hwloc__xml_v1export_object (hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags)
2267 {
2268   struct hwloc__xml_export_state_s state;
2269   hwloc_obj_t child;
2270 
2271   parentstate->new_child(parentstate, &state, "object");
2272 
2273   hwloc__xml_export_object_contents(&state, topology, obj, flags);
2274 
2275   for_each_child(child, obj) {
2276     if (!child->memory_arity) {
2277       /* no memory child, just export normally */
2278       hwloc__xml_v1export_object (&state, topology, child, flags);
2279     } else {
2280       hwloc__xml_v1export_object_with_memory(&state, topology, child, flags);
2281     }
2282   }
2283 
2284   for_each_io_child(child, obj)
2285     hwloc__xml_v1export_object (&state, topology, child, flags);
2286   for_each_misc_child(child, obj)
2287     hwloc__xml_v1export_object (&state, topology, child, flags);
2288 
2289   state.end_object(&state, "object");
2290 }
2291 
2292 #define EXPORT_ARRAY(state, type, nr, values, tagname, format, maxperline) do { \
2293   unsigned _i = 0; \
2294   while (_i<(nr)) { \
2295     char _tmp[255]; /* enough for (snprintf(format)+space) x maxperline */ \
2296     char _tmp2[16]; \
2297     size_t _len = 0; \
2298     unsigned _j; \
2299     struct hwloc__xml_export_state_s _childstate; \
2300     (state)->new_child(state, &_childstate, tagname); \
2301     for(_j=0; \
2302         _i+_j<(nr) && _j<maxperline; \
2303         _j++) \
2304       _len += sprintf(_tmp+_len, format " ", (type) (values)[_i+_j]); \
2305     _i += _j; \
2306     sprintf(_tmp2, "%lu", (unsigned long) _len); \
2307     _childstate.new_prop(&_childstate, "length", _tmp2); \
2308     _childstate.add_content(&_childstate, _tmp, _len); \
2309     _childstate.end_object(&_childstate, tagname); \
2310   } \
2311 } while (0)
2312 
2313 static void
2314 hwloc__xml_v2export_distances(hwloc__xml_export_state_t parentstate, hwloc_topology_t topology)
2315 {
2316   struct hwloc_internal_distances_s *dist;
2317   for(dist = topology->first_dist; dist; dist = dist->next) {
2318     char tmp[255];
2319     unsigned nbobjs = dist->nbobjs;
2320     struct hwloc__xml_export_state_s state;
2321 
2322     parentstate->new_child(parentstate, &state, "distances2");
2323 
2324     state.new_prop(&state, "type", hwloc_obj_type_string(dist->type));
2325     sprintf(tmp, "%u", nbobjs);
2326     state.new_prop(&state, "nbobjs", tmp);
2327     sprintf(tmp, "%lu", dist->kind);
2328     state.new_prop(&state, "kind", tmp);
2329 
2330     state.new_prop(&state, "indexing",
2331                    (dist->type == HWLOC_OBJ_NUMANODE || dist->type == HWLOC_OBJ_PU) ? "os" : "gp");
2332     /* TODO don't hardwire 10 below. either snprintf the max to guess it, or just append until the end of the buffer */
2333     EXPORT_ARRAY(&state, unsigned long long, nbobjs, dist->indexes, "indexes", "%llu", 10);
2334     EXPORT_ARRAY(&state, unsigned long long, nbobjs*nbobjs, dist->values, "u64values", "%llu", 10);
2335     state.end_object(&state, "distances2");
2336   }
2337 }
2338 
2339 void
2340 hwloc__xml_export_topology(hwloc__xml_export_state_t state, hwloc_topology_t topology, unsigned long flags)
2341 {
2342   if (flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1) {
2343     hwloc__xml_v1export_object (state, topology, hwloc_get_root_obj(topology), flags);
2344   } else {
2345     hwloc__xml_v2export_object (state, topology, hwloc_get_root_obj(topology), flags);
2346     hwloc__xml_v2export_distances (state, topology);
2347   }
2348 }
2349 
2350 void
2351 hwloc__xml_export_diff(hwloc__xml_export_state_t parentstate, hwloc_topology_diff_t diff)
2352 {
2353   while (diff) {
2354     struct hwloc__xml_export_state_s state;
2355     char tmp[255];
2356 
2357     parentstate->new_child(parentstate, &state, "diff");
2358 
2359     sprintf(tmp, "%d", (int) diff->generic.type);
2360     state.new_prop(&state, "type", tmp);
2361 
2362     switch (diff->generic.type) {
2363     case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR:
2364       sprintf(tmp, "%d", diff->obj_attr.obj_depth);
2365       state.new_prop(&state, "obj_depth", tmp);
2366       sprintf(tmp, "%u", diff->obj_attr.obj_index);
2367       state.new_prop(&state, "obj_index", tmp);
2368 
2369       sprintf(tmp, "%d", (int) diff->obj_attr.diff.generic.type);
2370       state.new_prop(&state, "obj_attr_type", tmp);
2371 
2372       switch (diff->obj_attr.diff.generic.type) {
2373       case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_SIZE:
2374         sprintf(tmp, "%llu", (unsigned long long) diff->obj_attr.diff.uint64.index);
2375         state.new_prop(&state, "obj_attr_index", tmp);
2376         sprintf(tmp, "%llu", (unsigned long long) diff->obj_attr.diff.uint64.oldvalue);
2377         state.new_prop(&state, "obj_attr_oldvalue", tmp);
2378         sprintf(tmp, "%llu", (unsigned long long) diff->obj_attr.diff.uint64.newvalue);
2379         state.new_prop(&state, "obj_attr_newvalue", tmp);
2380         break;
2381       case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME:
2382       case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO:
2383         if (diff->obj_attr.diff.string.name)
2384           state.new_prop(&state, "obj_attr_name", diff->obj_attr.diff.string.name);
2385         state.new_prop(&state, "obj_attr_oldvalue", diff->obj_attr.diff.string.oldvalue);
2386         state.new_prop(&state, "obj_attr_newvalue", diff->obj_attr.diff.string.newvalue);
2387         break;
2388       }
2389 
2390       break;
2391     default:
2392       assert(0);
2393     }
2394     state.end_object(&state, "diff");
2395 
2396     diff = diff->generic.next;
2397   }
2398 }
2399 
2400 /**********************************
2401  ********* main XML export ********
2402  **********************************/
2403 
2404 /* this can be the first XML call */
2405 int hwloc_topology_export_xml(hwloc_topology_t topology, const char *filename, unsigned long flags)
2406 {
2407   hwloc_localeswitch_declare;
2408   struct hwloc__xml_export_data_s edata;
2409   int force_nolibxml;
2410   int ret;
2411 
2412   if (!topology->is_loaded) {
2413     errno = EINVAL;
2414     return -1;
2415   }
2416 
2417   assert(hwloc_nolibxml_callbacks); /* the core called components_init() for the topology */
2418 
2419   if (flags & ~HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1) {
2420     errno = EINVAL;
2421     return -1;
2422   }
2423 
2424   hwloc_internal_distances_refresh(topology);
2425 
2426   hwloc_localeswitch_init();
2427 
2428   edata.v1_memory_group = NULL;
2429   if (flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1)
2430     /* temporary group to be used during v1 export of memory children */
2431     edata.v1_memory_group = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX);
2432 
2433   force_nolibxml = hwloc_nolibxml_export();
2434 retry:
2435   if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml))
2436     ret = hwloc_nolibxml_callbacks->export_file(topology, &edata, filename, flags);
2437   else {
2438     ret = hwloc_libxml_callbacks->export_file(topology, &edata, filename, flags);
2439     if (ret < 0 && errno == ENOSYS) {
2440       hwloc_libxml_callbacks = NULL;
2441       goto retry;
2442     }
2443   }
2444 
2445   if (edata.v1_memory_group)
2446     hwloc_free_unlinked_object(edata.v1_memory_group);
2447 
2448   hwloc_localeswitch_fini();
2449   return ret;
2450 }
2451 
2452 /* this can be the first XML call */
2453 int hwloc_topology_export_xmlbuffer(hwloc_topology_t topology, char **xmlbuffer, int *buflen, unsigned long flags)
2454 {
2455   hwloc_localeswitch_declare;
2456   struct hwloc__xml_export_data_s edata;
2457   int force_nolibxml;
2458   int ret;
2459 
2460   if (!topology->is_loaded) {
2461     errno = EINVAL;
2462     return -1;
2463   }
2464 
2465   assert(hwloc_nolibxml_callbacks); /* the core called components_init() for the topology */
2466 
2467   if (flags & ~HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1) {
2468     errno = EINVAL;
2469     return -1;
2470   }
2471 
2472   hwloc_internal_distances_refresh(topology);
2473 
2474   hwloc_localeswitch_init();
2475 
2476   edata.v1_memory_group = NULL;
2477   if (flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1)
2478     /* temporary group to be used during v1 export of memory children */
2479     edata.v1_memory_group = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX);
2480 
2481   force_nolibxml = hwloc_nolibxml_export();
2482 retry:
2483   if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml))
2484     ret = hwloc_nolibxml_callbacks->export_buffer(topology, &edata, xmlbuffer, buflen, flags);
2485   else {
2486     ret = hwloc_libxml_callbacks->export_buffer(topology, &edata, xmlbuffer, buflen, flags);
2487     if (ret < 0 && errno == ENOSYS) {
2488       hwloc_libxml_callbacks = NULL;
2489       goto retry;
2490     }
2491   }
2492 
2493   if (edata.v1_memory_group)
2494     hwloc_free_unlinked_object(edata.v1_memory_group);
2495 
2496   hwloc_localeswitch_fini();
2497   return ret;
2498 }
2499 
2500 /* this can be the first XML call */
2501 int
2502 hwloc_topology_diff_export_xml(hwloc_topology_diff_t diff, const char *refname,
2503                                const char *filename)
2504 {
2505   hwloc_localeswitch_declare;
2506   hwloc_topology_diff_t tmpdiff;
2507   int force_nolibxml;
2508   int ret;
2509 
2510   tmpdiff = diff;
2511   while (tmpdiff) {
2512     if (tmpdiff->generic.type == HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX) {
2513       errno = EINVAL;
2514       return -1;
2515     }
2516     tmpdiff = tmpdiff->generic.next;
2517   }
2518 
2519   hwloc_components_init();
2520   assert(hwloc_nolibxml_callbacks);
2521 
2522   hwloc_localeswitch_init();
2523 
2524   force_nolibxml = hwloc_nolibxml_export();
2525 retry:
2526   if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml))
2527     ret = hwloc_nolibxml_callbacks->export_diff_file(diff, refname, filename);
2528   else {
2529     ret = hwloc_libxml_callbacks->export_diff_file(diff, refname, filename);
2530     if (ret < 0 && errno == ENOSYS) {
2531       hwloc_libxml_callbacks = NULL;
2532       goto retry;
2533     }
2534   }
2535 
2536   hwloc_localeswitch_fini();
2537   hwloc_components_fini();
2538   return ret;
2539 }
2540 
2541 /* this can be the first XML call */
2542 int
2543 hwloc_topology_diff_export_xmlbuffer(hwloc_topology_diff_t diff, const char *refname,
2544                                      char **xmlbuffer, int *buflen)
2545 {
2546   hwloc_localeswitch_declare;
2547   hwloc_topology_diff_t tmpdiff;
2548   int force_nolibxml;
2549   int ret;
2550 
2551   tmpdiff = diff;
2552   while (tmpdiff) {
2553     if (tmpdiff->generic.type == HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX) {
2554       errno = EINVAL;
2555       return -1;
2556     }
2557     tmpdiff = tmpdiff->generic.next;
2558   }
2559 
2560   hwloc_components_init();
2561   assert(hwloc_nolibxml_callbacks);
2562 
2563   hwloc_localeswitch_init();
2564 
2565   force_nolibxml = hwloc_nolibxml_export();
2566 retry:
2567   if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml))
2568     ret = hwloc_nolibxml_callbacks->export_diff_buffer(diff, refname, xmlbuffer, buflen);
2569   else {
2570     ret = hwloc_libxml_callbacks->export_diff_buffer(diff, refname, xmlbuffer, buflen);
2571     if (ret < 0 && errno == ENOSYS) {
2572       hwloc_libxml_callbacks = NULL;
2573       goto retry;
2574     }
2575   }
2576 
2577   hwloc_localeswitch_fini();
2578   hwloc_components_fini();
2579   return ret;
2580 }
2581 
2582 void hwloc_free_xmlbuffer(hwloc_topology_t topology __hwloc_attribute_unused, char *xmlbuffer)
2583 {
2584   int force_nolibxml;
2585 
2586   assert(hwloc_nolibxml_callbacks); /* the core called components_init() for the topology */
2587 
2588   force_nolibxml = hwloc_nolibxml_export();
2589   if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml))
2590     hwloc_nolibxml_callbacks->free_buffer(xmlbuffer);
2591   else
2592     hwloc_libxml_callbacks->free_buffer(xmlbuffer);
2593 }
2594 
2595 void
2596 hwloc_topology_set_userdata_export_callback(hwloc_topology_t topology,
2597                                             void (*export)(void *reserved, struct hwloc_topology *topology, struct hwloc_obj *obj))
2598 {
2599   topology->userdata_export_cb = export;
2600 }
2601 
2602 static void
2603 hwloc__export_obj_userdata(hwloc__xml_export_state_t parentstate, int encoded,
2604                            const char *name, size_t length, const void *buffer, size_t encoded_length)
2605 {
2606   struct hwloc__xml_export_state_s state;
2607   char tmp[255];
2608   parentstate->new_child(parentstate, &state, "userdata");
2609   if (name)
2610     state.new_prop(&state, "name", name);
2611   sprintf(tmp, "%lu", (unsigned long) length);
2612   state.new_prop(&state, "length", tmp);
2613   if (encoded)
2614     state.new_prop(&state, "encoding", "base64");
2615   if (encoded_length)
2616     state.add_content(&state, buffer, encoded ? encoded_length : length);
2617   state.end_object(&state, "userdata");
2618 }
2619 
2620 int
2621 hwloc_export_obj_userdata(void *reserved,
2622                           struct hwloc_topology *topology, struct hwloc_obj *obj __hwloc_attribute_unused,
2623                           const char *name, const void *buffer, size_t length)
2624 {
2625   hwloc__xml_export_state_t state = reserved;
2626 
2627   if (!buffer) {
2628     errno = EINVAL;
2629     return -1;
2630   }
2631 
2632   if ((name && hwloc__xml_export_check_buffer(name, strlen(name)) < 0)
2633       || hwloc__xml_export_check_buffer(buffer, length) < 0) {
2634     errno = EINVAL;
2635     return -1;
2636   }
2637 
2638   if (topology->userdata_not_decoded) {
2639     int encoded;
2640     size_t encoded_length;
2641     const char *realname;
2642     if (!strncmp(name, "base64", 6)) {
2643       encoded = 1;
2644       encoded_length = BASE64_ENCODED_LENGTH(length);
2645     } else {
2646       assert(!strncmp(name, "normal", 6));
2647       encoded = 0;
2648       encoded_length = length;
2649     }
2650     if (name[6] == ':')
2651       realname = name+7;
2652     else {
2653       assert(!strcmp(name+6, "-anon"));
2654       realname = NULL;
2655     }
2656     hwloc__export_obj_userdata(state, encoded, realname, length, buffer, encoded_length);
2657 
2658   } else
2659     hwloc__export_obj_userdata(state, 0, name, length, buffer, length);
2660 
2661   return 0;
2662 }
2663 
2664 int
2665 hwloc_export_obj_userdata_base64(void *reserved,
2666                                  struct hwloc_topology *topology __hwloc_attribute_unused, struct hwloc_obj *obj __hwloc_attribute_unused,
2667                                  const char *name, const void *buffer, size_t length)
2668 {
2669   hwloc__xml_export_state_t state = reserved;
2670   size_t encoded_length;
2671   char *encoded_buffer;
2672   int ret __hwloc_attribute_unused;
2673 
2674   if (!buffer) {
2675     errno = EINVAL;
2676     return -1;
2677   }
2678 
2679   assert(!topology->userdata_not_decoded);
2680 
2681   if (name && hwloc__xml_export_check_buffer(name, strlen(name)) < 0) {
2682     errno = EINVAL;
2683     return -1;
2684   }
2685 
2686   encoded_length = BASE64_ENCODED_LENGTH(length);
2687   encoded_buffer = malloc(encoded_length+1);
2688   if (!encoded_buffer) {
2689     errno = ENOMEM;
2690     return -1;
2691   }
2692 
2693   ret = hwloc_encode_to_base64(buffer, length, encoded_buffer, encoded_length+1);
2694   assert(ret == (int) encoded_length);
2695 
2696   hwloc__export_obj_userdata(state, 1, name, length, encoded_buffer, encoded_length);
2697 
2698   free(encoded_buffer);
2699   return 0;
2700 }
2701 
2702 void
2703 hwloc_topology_set_userdata_import_callback(hwloc_topology_t topology,
2704                                             void (*import)(struct hwloc_topology *topology, struct hwloc_obj *obj, const char *name, const void *buffer, size_t length))
2705 {
2706   topology->userdata_import_cb = import;
2707 }
2708 
2709 /***************************************
2710  ************ XML component ************
2711  ***************************************/
2712 
2713 static void
2714 hwloc_xml_backend_disable(struct hwloc_backend *backend)
2715 {
2716   struct hwloc_xml_backend_data_s *data = backend->private_data;
2717   data->backend_exit(data);
2718   free(data->msgprefix);
2719   free(data);
2720 }
2721 
2722 static struct hwloc_backend *
2723 hwloc_xml_component_instantiate(struct hwloc_disc_component *component,
2724                                 const void *_data1,
2725                                 const void *_data2,
2726                                 const void *_data3)
2727 {
2728   struct hwloc_xml_backend_data_s *data;
2729   struct hwloc_backend *backend;
2730   const char *env;
2731   int force_nolibxml;
2732   const char * xmlpath = (const char *) _data1;
2733   const char * xmlbuffer = (const char *) _data2;
2734   int xmlbuflen = (int)(uintptr_t) _data3;
2735   const char *basename;
2736   int err;
2737 
2738   assert(hwloc_nolibxml_callbacks); /* the core called components_init() for the component's topology */
2739 
2740   if (!xmlpath && !xmlbuffer) {
2741     env = getenv("HWLOC_XMLFILE");
2742     if (env) {
2743       /* 'xml' was given in HWLOC_COMPONENTS without a filename */
2744       xmlpath = env;
2745     } else {
2746       errno = EINVAL;
2747       goto out;
2748     }
2749   }
2750 
2751   backend = hwloc_backend_alloc(component);
2752   if (!backend)
2753     goto out;
2754 
2755   data = malloc(sizeof(*data));
2756   if (!data) {
2757     errno = ENOMEM;
2758     goto out_with_backend;
2759   }
2760 
2761   backend->private_data = data;
2762   backend->discover = hwloc_look_xml;
2763   backend->disable = hwloc_xml_backend_disable;
2764   backend->is_thissystem = 0;
2765 
2766   if (xmlpath) {
2767     basename = strrchr(xmlpath, '/');
2768     if (basename)
2769       basename++;
2770     else
2771       basename = xmlpath;
2772   } else {
2773     basename = "xmlbuffer";
2774   }
2775   data->msgprefix = strdup(basename);
2776 
2777   force_nolibxml = hwloc_nolibxml_import();
2778 retry:
2779   if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml))
2780     err = hwloc_nolibxml_callbacks->backend_init(data, xmlpath, xmlbuffer, xmlbuflen);
2781   else {
2782     err = hwloc_libxml_callbacks->backend_init(data, xmlpath, xmlbuffer, xmlbuflen);
2783     if (err < 0 && errno == ENOSYS) {
2784       hwloc_libxml_callbacks = NULL;
2785       goto retry;
2786     }
2787   }
2788   if (err < 0)
2789     goto out_with_data;
2790 
2791   return backend;
2792 
2793  out_with_data:
2794   free(data->msgprefix);
2795   free(data);
2796  out_with_backend:
2797   free(backend);
2798  out:
2799   return NULL;
2800 }
2801 
2802 static struct hwloc_disc_component hwloc_xml_disc_component = {
2803   HWLOC_DISC_COMPONENT_TYPE_GLOBAL,
2804   "xml",
2805   ~0,
2806   hwloc_xml_component_instantiate,
2807   30,
2808   1,
2809   NULL
2810 };
2811 
2812 const struct hwloc_component hwloc_xml_component = {
2813   HWLOC_COMPONENT_ABI,
2814   NULL, NULL,
2815   HWLOC_COMPONENT_TYPE_DISC,
2816   0,
2817   &hwloc_xml_disc_component
2818 };

/* [<][>][^][v][top][bottom][index][help] */