/*
* Copyright © 2012-2018 Inria. All rights reserved.
* See COPYING in top-level directory.
*/
#include <private/autogen/config.h>
#include <hwloc-calc.h>
#include <hwloc.h>
#include "misc.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void usage(const char *callname __hwloc_attribute_unused, FILE *where)
{
fprintf(where, "Usage: hwloc-annotate [options] <input.xml> <output.xml> <location> <annotation>\n");
fprintf(where, " <location> may be:\n");
fprintf(where, " all, root, <type>:<logicalindex>, <type>:all\n");
fprintf(where, " <annotation> may be:\n");
fprintf(where, " info <name> <value>\n");
fprintf(where, " misc <name>\n");
fprintf(where, " distances <filename> [<flags>]\n");
fprintf(where, " none\n");
fprintf(where, "Options:\n");
fprintf(where, " --ci\tClear existing infos\n");
fprintf(where, " --ri\tReplace or remove existing infos with same name (annotation must be info)\n");
fprintf(where, " --cu\tClear existing userdata\n");
fprintf(where, " --cd\tClear existing distances\n");
}
static char *infoname = NULL, *infovalue = NULL;
static char *miscname = NULL;
static char *distancesfilename = NULL;
static unsigned long distancesflags = 0;
static int clearinfos = 0;
static int replaceinfos = 0;
static int clearuserdata = 0;
static int cleardistances = 0;
static void apply(hwloc_topology_t topology, hwloc_obj_t obj)
{
unsigned i,j;
if (clearinfos) {
/* this may be considered dangerous, applications should not modify objects directly */
for(i=0; i<obj->infos_count; i++) {
struct hwloc_info_s *info = &obj->infos[i];
free(info->name);
free(info->value);
}
free(obj->infos);
obj->infos = NULL;
obj->infos_count = 0;
}
if (clearuserdata) {
hwloc_utils_userdata_free(obj);
}
if (infoname) {
if (replaceinfos) {
/* this may be considered dangerous, applications should not modify objects directly */
for(i=0, j=0; i<obj->infos_count; i++) {
struct hwloc_info_s *info = &obj->infos[i];
if (!strcmp(infoname, info->name)) {
/* remove info */
free(info->name);
info->name = NULL;
free(info->value);
} else {
if (i != j) {
/* shift info to where it belongs */
obj->infos[j].name = info->name;
obj->infos[j].value = info->value;
}
j++;
}
}
obj->infos_count = j;
if (!j) {
free(obj->infos);
obj->infos = NULL;
}
}
if (infovalue)
hwloc_obj_add_info(obj, infoname, infovalue);
}
if (miscname)
hwloc_topology_insert_misc_object(topology, obj, miscname);
}
static void apply_recursive(hwloc_topology_t topology, hwloc_obj_t obj)
{
hwloc_obj_t child = NULL;
while ((child = hwloc_get_next_child(topology, obj, child)) != NULL)
apply_recursive(topology, child);
apply(topology, obj);
}
static void
hwloc_calc_process_location_annotate_cb(struct hwloc_calc_location_context_s *lcontext,
void *_data __hwloc_attribute_unused,
hwloc_obj_t obj)
{
apply(lcontext->topology, obj);
}
static void
hwloc_calc_get_obj_cb(struct hwloc_calc_location_context_s *lcontext __hwloc_attribute_unused,
void *_data,
hwloc_obj_t obj)
{
*(hwloc_obj_t*)_data = obj;
}
static void
add_distances(hwloc_topology_t topology, int topodepth)
{
unsigned long kind = 0;
unsigned nbobjs = 0;
hwloc_obj_t *objs = NULL;
hwloc_uint64_t *values = NULL;
FILE *file;
char line[64];
unsigned i, x, y, z;
int err;
file = fopen(distancesfilename, "r");
if (!file) {
fprintf(stderr, "Failed to open distances file %s\n", distancesfilename);
return;
}
if (!fgets(line, sizeof(line), file)) {
fprintf(stderr, "Failed to read kind line\n");
goto out;
}
kind = strtoul(line, NULL, 0);
if (!fgets(line, sizeof(line), file)) {
fprintf(stderr, "Failed to read nbobjs line\n");
goto out;
}
nbobjs = strtoul(line, NULL, 0);
if (nbobjs < 2) {
fprintf(stderr, "Invalid distances with nbobjs == %u\n", nbobjs);
goto out;
}
objs = malloc(nbobjs * sizeof(*objs));
values = malloc(nbobjs*nbobjs * sizeof(*values));
if (!objs || !values)
goto out;
for(i=0; i<nbobjs; i++) {
size_t typelen;
hwloc_obj_t obj = NULL;
if (!fgets(line, sizeof(line), file)) {
fprintf(stderr, "Failed to read object #%u line\n", i);
goto out;
}
typelen = strspn(line, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
if (typelen && line[typelen] == ':') {
struct hwloc_calc_location_context_s lcontext;
size_t length;
lcontext.topology = topology;
lcontext.topodepth = topodepth;
lcontext.only_hbm = -1;
lcontext.logical = 1;
lcontext.verbose = 0;
length = strspn(line+typelen+1, "0123456789");
line[typelen+1+length] = '\0';
err = hwloc_calc_process_location(&lcontext, line, typelen,
hwloc_calc_get_obj_cb, &obj);
if (err < 0)
goto out;
} else {
fprintf(stderr, "Cannot parse object #%u line\n", i);
goto out;
}
if (!obj)
goto out;
objs[i] = obj;
}
/* scan the first value line to see if we have all of them or just a combination */
if (!fgets(line, sizeof(line), file)) {
fprintf(stderr, "Failed to read object #%u line\n", i);
goto out;
}
z = 1; /* default if sscanf finds only 2 values below */
if (sscanf(line, "%u*%u*%u", &x, &y, &z) >= 2) {
/* combination: generate the matrix to create x groups of y elements */
unsigned j;
if (x*y*z != nbobjs) {
fprintf(stderr, "Invalid distances combination (%u*%u*%u=%u instead of %u)\n",
x, y, z, x*y*z, nbobjs);
goto out;
}
for(i=0; i<nbobjs; i++)
for(j=0; j<nbobjs; j++)
if (i==j)
values[i*nbobjs+j] = 10;
else if (i/z == j/z)
values[i*nbobjs+j] = 20;
else if (i/z/y == j/z/y)
values[i*nbobjs+j] = 40;
else
values[i*nbobjs+j] = 80;
} else {
/* read all other values */
values[0] = strtoull(line, NULL, 10);
for(i=1; i<nbobjs*nbobjs; i++) {
if (!fgets(line, sizeof(line), file)) {
fprintf(stderr, "Failed to read object #%u line\n", i);
goto out;
}
values[i] = strtoull(line, NULL, 10);
}
}
err = hwloc_distances_add(topology, nbobjs, objs, values, kind, distancesflags);
if (err < 0) {
fprintf(stderr, "Failed to add distances\n");
goto out;
}
out:
free(objs);
free(values);
fclose(file);
return;
}
int main(int argc, char *argv[])
{
hwloc_topology_t topology;
char *callname, *input, *output, *location;
int topodepth;
int err;
callname = argv[0];
/* skip argv[0], handle options */
argc--;
argv++;
hwloc_utils_check_api_version(callname);
putenv((char *) "HWLOC_XML_VERBOSE=1");
while (argc && *argv[0] == '-') {
if (!strcmp(argv[0], "--ci"))
clearinfos = 1;
else if (!strcmp(argv[0], "--ri"))
replaceinfos = 1;
else if (!strcmp(argv[0], "--cu"))
clearuserdata = 1;
else if (!strcmp(argv[0], "--cd"))
cleardistances = 1;
else {
fprintf(stderr, "Unrecognized options: %s\n", argv[0]);
usage(callname, stderr);
exit(EXIT_FAILURE);
}
argc--;
argv++;
}
if (argc < 3) {
usage(callname, stderr);
exit(EXIT_FAILURE);
}
input = argv[0];
output = argv[1];
location = argv[2];
argc -= 3;
argv += 3;
if (argc < 1) {
usage(callname, stderr);
exit(EXIT_FAILURE);
}
if (!strcmp(argv[0], "info")) {
if (argc < 2 || (!replaceinfos && argc < 3)) {
usage(callname, stderr);
exit(EXIT_FAILURE);
}
infoname = argv[1];
infovalue = argc >= 3 ? argv[2] : NULL;
} else if (!strcmp(argv[0], "misc")) {
if (argc < 2) {
usage(callname, stderr);
exit(EXIT_FAILURE);
}
miscname = argv[1];
} else if (!strcmp(argv[0], "distances")) {
if (argc < 2) {
usage(callname, stderr);
exit(EXIT_FAILURE);
}
distancesfilename = argv[1];
if (argc >= 3)
distancesflags = strtoul(argv[2], NULL, 0);
} else if (!strcmp(argv[0], "none")) {
/* do nothing (maybe clear) */
} else {
fprintf(stderr, "Unrecognized annotation type: %s\n", argv[0]);
usage(callname, stderr);
exit(EXIT_FAILURE);
}
if (replaceinfos && !infoname) {
fprintf(stderr, "--ri missing a info name\n");
usage(callname, stderr);
exit(EXIT_FAILURE);
}
hwloc_topology_init(&topology);
hwloc_topology_set_all_types_filter(topology, HWLOC_TYPE_FILTER_KEEP_ALL);
hwloc_topology_set_flags(topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM);
err = hwloc_topology_set_xml(topology, input);
if (err < 0)
goto out;
putenv((char *) "HWLOC_XML_USERDATA_NOT_DECODED=1");
hwloc_topology_set_userdata_import_callback(topology, hwloc_utils_userdata_import_cb);
hwloc_topology_set_userdata_export_callback(topology, hwloc_utils_userdata_export_cb);
hwloc_topology_load(topology);
topodepth = hwloc_topology_get_depth(topology);
if (cleardistances) {
hwloc_distances_remove(topology);
}
if (distancesfilename) {
add_distances(topology, topodepth);
} else if (!strcmp(location, "all")) {
apply_recursive(topology, hwloc_get_root_obj(topology));
} else if (!strcmp(location, "root")) {
apply(topology, hwloc_get_root_obj(topology));
} else {
size_t typelen;
typelen = strspn(location, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
if (typelen && (location[typelen] == ':' || location[typelen] == '=' || location[typelen] == '[')) {
struct hwloc_calc_location_context_s lcontext;
lcontext.topology = topology;
lcontext.topodepth = topodepth;
lcontext.only_hbm = -1;
lcontext.logical = 1;
lcontext.verbose = 0;
err = hwloc_calc_process_location(&lcontext, location, typelen,
hwloc_calc_process_location_annotate_cb, topology);
}
}
err = hwloc_topology_export_xml(topology, output, 0);
if (err < 0)
goto out;
hwloc_utils_userdata_free_recursive(hwloc_get_root_obj(topology));
hwloc_topology_destroy(topology);
exit(EXIT_SUCCESS);
out:
hwloc_utils_userdata_free_recursive(hwloc_get_root_obj(topology));
hwloc_topology_destroy(topology);
exit(EXIT_FAILURE);
}