1 /*
2 * Copyright © 2009 CNRS
3 * Copyright © 2009-2021 Inria. All rights reserved.
4 * Copyright © 2009-2012 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
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <errno.h>
16
17 #ifdef HWLOC_WIN_SYS
18 #include <hwloc/windows.h>
19 #endif
20
21 #include "lstopo.h"
22 #include "misc.h"
23
24 #define indent(output, i) \
25 fprintf (output, "%*s", (int) i, "");
26
27 /*
28 * Console fashion text output
29 */
30
31 static void
output_console_obj(struct lstopo_output * loutput,hwloc_obj_t l,int collapse)32 output_console_obj (struct lstopo_output *loutput, hwloc_obj_t l, int collapse)
33 {
34 FILE *output = loutput->file;
35 enum lstopo_index_type_e index_type = loutput->index_type;
36 int verbose_mode = loutput->verbose_mode;
37 char pidxstr[16];
38 char lidxstr[32];
39 char busidstr[32];
40
41 if (collapse > 1 && l->type == HWLOC_OBJ_PCI_DEVICE) {
42 strcpy(pidxstr, "P#[collapsed]"); /* shouldn't be used, os_index should be -1 except if importing old XMLs */
43 snprintf(lidxstr, sizeof(lidxstr), "L#%u-%u", l->logical_index, l->logical_index+collapse-1);
44 } else {
45 snprintf(pidxstr, sizeof(pidxstr), "P#%u", l->os_index);
46 snprintf(lidxstr, sizeof(lidxstr), "L#%u", l->logical_index);
47 }
48 if (l->type == HWLOC_OBJ_PCI_DEVICE)
49 lstopo_busid_snprintf(loutput, busidstr, sizeof(busidstr), l, collapse, loutput->need_pci_domain);
50
51 if (loutput->show_cpuset < 2) {
52 char type[64], *attr, phys[32] = "";
53 int len;
54 hwloc_obj_type_snprintf (type, sizeof(type), l, verbose_mode-1);
55 if (l->subtype)
56 fprintf(output, "%s(%s)", type, l->subtype);
57 else
58 fprintf(output, "%s", type);
59 if (l->depth != 0 && (verbose_mode >= 2 || (hwloc_obj_type_is_normal(l->type) || hwloc_obj_type_is_memory(l->type)))) {
60 if (index_type != LSTOPO_INDEX_TYPE_PHYSICAL)
61 /* print logical index in logical and default case */
62 fprintf(output, " %s", lidxstr);
63 else if (index_type == LSTOPO_INDEX_TYPE_PHYSICAL && l->os_index != HWLOC_UNKNOWN_INDEX)
64 /* print physical index in physical case */
65 fprintf(output, " %s", pidxstr);
66 }
67 if (l->name && (l->type == HWLOC_OBJ_MISC || l->type == HWLOC_OBJ_GROUP))
68 fprintf(output, " %s", l->name);
69 if (index_type == LSTOPO_INDEX_TYPE_DEFAULT
70 && l->os_index != HWLOC_UNKNOWN_INDEX
71 && (verbose_mode >= 2 || l->type == HWLOC_OBJ_PU || l->type == HWLOC_OBJ_NUMANODE))
72 /* print physical index too if default index */
73 snprintf(phys, sizeof(phys), "%s", pidxstr);
74 if (l->type == HWLOC_OBJ_PCI_DEVICE && verbose_mode <= 1)
75 fprintf(output, " %s (%s)",
76 busidstr, hwloc_pci_class_string(l->attr->pcidev.class_id));
77 /* display attributes */
78 len = hwloc_obj_attr_snprintf (NULL, 0, l, " ", verbose_mode-1);
79 attr = malloc(len+1);
80 *attr = '\0';
81 hwloc_obj_attr_snprintf (attr, len+1, l, " ", verbose_mode-1);
82 if (*phys || *attr) {
83 fprintf(output, " (");
84 if (*phys)
85 fprintf(output, "%s", phys);
86 if (*phys && *attr)
87 fprintf(output, " ");
88 if (*attr) {
89 if (collapse > 1 && l->type == HWLOC_OBJ_PCI_DEVICE) {
90 assert(!strncmp(attr, "busid=", 6));
91 assert(!strncmp(attr+18, " id=", 4));
92 fprintf(output, "busid=%s%s", busidstr, attr+18);
93 } else
94 fprintf(output, "%s", attr);
95 }
96 fprintf(output, ")");
97 }
98 free(attr);
99 /* display the root total_memory if not verbose (already shown)
100 * (cannot be local_memory since root cannot be a NUMA node) */
101 if (verbose_mode == 1 && !l->parent && l->total_memory)
102 fprintf(output, " (%lu%s total)",
103 (unsigned long) hwloc_memory_size_printf_value(l->total_memory, 0),
104 hwloc_memory_size_printf_unit(l->total_memory, 0));
105 /* append the name */
106 if (l->name && (l->type == HWLOC_OBJ_OS_DEVICE || verbose_mode >= 2)
107 && l->type != HWLOC_OBJ_MISC && l->type != HWLOC_OBJ_GROUP)
108 fprintf(output, " \"%s\"", l->name);
109 }
110 if (!l->cpuset)
111 return;
112 if (loutput->show_cpuset == 1)
113 fprintf(output, " cpuset=");
114 if (loutput->show_cpuset) {
115 char *cpusetstr;
116 if (loutput->show_taskset)
117 hwloc_bitmap_taskset_asprintf(&cpusetstr, l->cpuset);
118 else
119 hwloc_bitmap_asprintf(&cpusetstr, l->cpuset);
120 fprintf(output, "%s", cpusetstr);
121 free(cpusetstr);
122 }
123
124 /* annotate if the PU/NUMA is disallowed/binding */
125 if (verbose_mode >= 2) {
126 if (l->type == HWLOC_OBJ_PU) {
127 if (lstopo_pu_disallowed(loutput, l))
128 fprintf(output, " (disallowed)");
129 else if (lstopo_pu_binding(loutput, l))
130 fprintf(output, " (binding)");
131 } else if (l->type == HWLOC_OBJ_NUMANODE) {
132 if (lstopo_numa_disallowed(loutput, l))
133 fprintf(output, " (disallowed)");
134 else if (lstopo_numa_binding(loutput, l))
135 fprintf(output, " (binding)");
136 }
137 }
138 }
139
140 /* Recursively output topology in a console fashion */
141 static void
output_topology(struct lstopo_output * loutput,hwloc_obj_t l,hwloc_obj_t parent,int i)142 output_topology (struct lstopo_output *loutput, hwloc_obj_t l, hwloc_obj_t parent, int i)
143 {
144 FILE *output = loutput->file;
145 int verbose_mode = loutput->verbose_mode;
146 hwloc_obj_t child;
147 int group_identical = (verbose_mode <= 1) && !loutput->show_cpuset;
148 int collapse = loutput->pci_collapse_enabled ? ((struct lstopo_obj_userdata *) l->userdata)->pci_collapsed : 0;
149
150 if (l->type == HWLOC_OBJ_PCI_DEVICE && collapse == -1)
151 return;
152
153 if (group_identical
154 && parent && parent->arity == 1
155 && !parent->memory_arity && !parent->io_arity && !parent->misc_arity
156 && l->cpuset && parent->cpuset && hwloc_bitmap_isequal(l->cpuset, parent->cpuset)) {
157 /* in non-verbose mode, merge objects with their parent is they are exactly identical */
158 fprintf(output, " + ");
159 } else {
160 if (parent)
161 fprintf(output, "\n");
162 indent (output, 2*i);
163 i++;
164 }
165
166 if (collapse > 1)
167 fprintf(output, "%d x { ", collapse);
168 output_console_obj(loutput, l, collapse);
169 if (collapse > 1)
170 fprintf(output, " }");
171
172 for_each_memory_child(child, l)
173 if (child->type != HWLOC_OBJ_PU || !loutput->ignore_numanodes)
174 output_topology (loutput, child, l, i);
175 for_each_child(child, l)
176 if (child->type != HWLOC_OBJ_PU || !loutput->ignore_pus)
177 output_topology (loutput, child, l, i);
178 for_each_io_child(child, l)
179 output_topology (loutput, child, l, i);
180 for_each_misc_child(child, l)
181 output_topology (loutput, child, l, i);
182 }
183
184 /* Recursive so that multiple depth types are properly shown */
185 static void
output_only(struct lstopo_output * loutput,hwloc_obj_t l)186 output_only (struct lstopo_output *loutput, hwloc_obj_t l)
187 {
188 FILE *output = loutput->file;
189 hwloc_obj_t child;
190 if (loutput->show_only == l->type) {
191 output_console_obj (loutput, l, 0);
192 fprintf (output, "\n");
193 }
194 /* there can be anything below normal children */
195 for_each_child(child, l)
196 output_only (loutput, child);
197 /* there can be only memory or Misc below memory children */
198 if (hwloc_obj_type_is_memory(loutput->show_only) || loutput->show_only == HWLOC_OBJ_MISC) {
199 for(child = l->memory_first_child; child; child = child->next_sibling)
200 output_only (loutput, child);
201 }
202 /* there can be only I/O or Misc below I/O children */
203 if (hwloc_obj_type_is_io(loutput->show_only) || loutput->show_only == HWLOC_OBJ_MISC) {
204 for_each_io_child(child, l)
205 output_only (loutput, child);
206 }
207 /* there can be only Misc below Misc children */
208 if (loutput->show_only == HWLOC_OBJ_MISC) {
209 /* Misc can only contain other Misc, no need to recurse otherwise */
210 for_each_misc_child(child, l)
211 output_only (loutput, child);
212 }
213 }
214
output_distances(struct lstopo_output * loutput)215 static void output_distances(struct lstopo_output *loutput)
216 {
217 hwloc_topology_t topology = loutput->topology;
218 enum lstopo_index_type_e index_type = loutput->index_type;
219 FILE *output = loutput->file;
220 struct hwloc_distances_s **dist;
221 unsigned nr = 0, j;
222 int err = hwloc_distances_get(topology, &nr, NULL, 0, 0);
223 if (err < 0 || !nr)
224 return;
225 dist = malloc(nr * sizeof(*dist));
226 if (!dist)
227 return;
228 err = hwloc_distances_get(topology, &nr, dist, 0, 0);
229 if (!err) {
230 for(j=0; j<nr; j++) {
231 const char *kindmeans = (dist[j]->kind & HWLOC_DISTANCES_KIND_MEANS_LATENCY) ? "latency" : (dist[j]->kind & HWLOC_DISTANCES_KIND_MEANS_BANDWIDTH) ? "bandwidth" : "distance";
232 const char *name = hwloc_distances_get_name(topology, dist[j]);
233 if (!name)
234 name = "(null)";
235 if (loutput->transform_distances != -1)
236 hwloc_distances_transform(topology, dist[j], loutput->transform_distances, NULL, 0);
237 if (dist[j]->kind & HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES) {
238 fprintf(output, "Relative %s matrix (name %s kind %lu) between %u heterogeneous objects by %s indexes:\n",
239 kindmeans, name, dist[j]->kind,
240 dist[j]->nbobjs,
241 index_type != LSTOPO_INDEX_TYPE_PHYSICAL ? "logical" : "physical");
242 } else {
243 fprintf(output, "Relative %s matrix (name %s kind %lu) between %u %ss (depth %d) by %s indexes:\n",
244 kindmeans, name, dist[j]->kind,
245 dist[j]->nbobjs,
246 hwloc_obj_type_string(dist[j]->objs[0]->type),
247 dist[j]->objs[0]->depth,
248 index_type != LSTOPO_INDEX_TYPE_PHYSICAL ? "logical" : "physical");
249 }
250 hwloc_utils_print_distance_matrix(output, dist[j]->nbobjs, dist[j]->objs, dist[j]->values, index_type != LSTOPO_INDEX_TYPE_PHYSICAL, dist[j]->kind & HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES);
251 hwloc_distances_release(topology, dist[j]);
252 }
253 }
254 free(dist);
255 }
256
output_memattr_obj(struct lstopo_output * loutput,hwloc_obj_t obj)257 static void output_memattr_obj(struct lstopo_output *loutput,
258 hwloc_obj_t obj)
259 {
260 enum lstopo_index_type_e index_type = loutput->index_type;
261 unsigned idx = (index_type == LSTOPO_INDEX_TYPE_PHYSICAL ? obj->os_index : obj->logical_index);
262 char objtype[16];
263
264 hwloc_obj_type_snprintf(objtype, sizeof(objtype), obj, 0);
265 if (idx == (unsigned) -1)
266 printf("%s %c#-1", objtype,
267 index_type == LSTOPO_INDEX_TYPE_PHYSICAL ? 'P' : 'L');
268 else
269 printf("%s %c#%u", objtype,
270 index_type == LSTOPO_INDEX_TYPE_PHYSICAL ? 'P' : 'L',
271 idx);
272
273 if (obj->name)
274 printf(" \"%s\"", obj->name);
275 }
276
output_memattr_initiator(struct lstopo_output * loutput,struct hwloc_location * initiator)277 static void output_memattr_initiator(struct lstopo_output *loutput,
278 struct hwloc_location *initiator)
279 {
280 hwloc_topology_t topology = loutput->topology;
281
282 if (initiator->type == HWLOC_LOCATION_TYPE_CPUSET) {
283 hwloc_obj_t obj;
284 char *c;
285
286 assert(initiator->location.cpuset);
287 hwloc_bitmap_asprintf(&c, initiator->location.cpuset);
288 printf(" from cpuset %s", c);
289 free(c);
290
291 obj = hwloc_get_obj_covering_cpuset(topology, initiator->location.cpuset);
292 if (obj && !hwloc_bitmap_isequal(obj->cpuset, initiator->location.cpuset))
293 obj = NULL;
294 if (obj) {
295 while (obj->parent && hwloc_bitmap_isequal(obj->cpuset, obj->parent->cpuset))
296 obj = obj->parent;
297 printf(" (");
298 output_memattr_obj(loutput, obj);
299 printf(")");
300 }
301
302 } else if (initiator->type == HWLOC_LOCATION_TYPE_OBJECT) {
303 printf(" from ");
304 output_memattr_obj(loutput, initiator->location.object);
305
306 } else {
307 printf(" from initiator with unexpected type %d",
308 (int) initiator->type);
309 }
310
311 }
312
output_memattrs(struct lstopo_output * loutput)313 static void output_memattrs(struct lstopo_output *loutput)
314 {
315 hwloc_topology_t topology = loutput->topology;
316 int verbose_mode = loutput->verbose_mode;
317 int show_all = (loutput->show_memattrs_only || (verbose_mode >= 3));
318 unsigned id;
319
320 for(id=0; ; id++) {
321 const char *name;
322 unsigned long flags;
323 unsigned nr_targets;
324 hwloc_obj_t *targets;
325 unsigned i;
326 int err;
327
328 if (!show_all
329 && (id == HWLOC_MEMATTR_ID_CAPACITY || id == HWLOC_MEMATTR_ID_LOCALITY))
330 continue;
331
332 err = hwloc_memattr_get_name(topology, id, &name);
333 if (err < 0)
334 break;
335 err = hwloc_memattr_get_flags(topology, id, &flags);
336 assert(!err);
337
338 nr_targets = 0;
339 err = hwloc_memattr_get_targets(topology, id, NULL, 0, &nr_targets, NULL, NULL);
340 assert(!err);
341
342 if (!show_all && !nr_targets)
343 continue;
344
345 printf("Memory attribute #%u name `%s' flags %lu\n", id, name, flags);
346
347 targets = malloc(nr_targets * sizeof(*targets));
348 if (!targets)
349 continue;
350
351 err = hwloc_memattr_get_targets(topology, id, NULL, 0, &nr_targets, targets, NULL);
352 assert(!err);
353
354 for(i=0; i<nr_targets; i++) {
355
356 if (!(flags & HWLOC_MEMATTR_FLAG_NEED_INITIATOR)) {
357 hwloc_uint64_t value;
358 err = hwloc_memattr_get_value(topology, id, targets[i], NULL, 0, &value);
359 if (!err) {
360 printf(" ");
361 output_memattr_obj(loutput, targets[i]);
362 printf(" = %llu\n", (unsigned long long) value);
363 }
364
365 } else {
366 unsigned nr_initiators = 0;
367 err = hwloc_memattr_get_initiators(topology, id, targets[i], 0, &nr_initiators, NULL, NULL);
368 if (!err) {
369 struct hwloc_location *initiators = malloc(nr_initiators * sizeof(*initiators));
370 hwloc_uint64_t *values = malloc(nr_initiators * sizeof(*values));
371 if (initiators && values) {
372 err = hwloc_memattr_get_initiators(topology, id, targets[i], 0, &nr_initiators, initiators, values);
373 if (!err) {
374 unsigned j;
375 for(j=0; j<nr_initiators; j++) {
376 printf(" ");
377 output_memattr_obj(loutput, targets[i]);
378 printf(" = %llu",
379 (unsigned long long) values[j]);
380 output_memattr_initiator(loutput, &initiators[j]);
381 printf("\n");
382 }
383 }
384 }
385 free(initiators);
386 free(values);
387 }
388 }
389 }
390 free(targets);
391 }
392 }
393
394
output_windows_processor_groups(struct lstopo_output * loutput __hwloc_attribute_unused,int force __hwloc_attribute_unused)395 static void output_windows_processor_groups(struct lstopo_output *loutput __hwloc_attribute_unused,
396 int force __hwloc_attribute_unused)
397 {
398 #ifdef HWLOC_WIN_SYS
399 hwloc_topology_t topology = loutput->topology;
400 int err = hwloc_windows_get_nr_processor_groups(topology, 0);
401 if (err > 0) {
402 unsigned nr = (unsigned) err;
403 if (nr > 1 || force) {
404 hwloc_bitmap_t set = hwloc_bitmap_alloc();
405 if (set) {
406 unsigned i;
407 for(i=0; i<nr; i++) {
408 err = hwloc_windows_get_processor_group_cpuset(topology, i, set, 0);
409 if (!err) {
410 char *s;
411 hwloc_bitmap_asprintf(&s, set);
412 printf("Processor Group #%u = %s\n", i, s);
413 free(s);
414 }
415 }
416 hwloc_bitmap_free(set);
417 }
418 }
419 }
420 #endif
421 }
422
output_cpukinds(struct lstopo_output * loutput)423 static void output_cpukinds(struct lstopo_output *loutput)
424 {
425 hwloc_topology_t topology = loutput->topology;
426 unsigned i, j, nr;
427 hwloc_bitmap_t cpuset = hwloc_bitmap_alloc();
428
429 nr = hwloc_cpukinds_get_nr(topology, 0);
430
431 for(i=0; i<nr; i++) {
432 int efficiency;
433 struct hwloc_info_s *infos;
434 unsigned nr_infos;
435 int err;
436
437 err = hwloc_cpukinds_get_info(topology, i, cpuset, &efficiency, &nr_infos, &infos, 0);
438 if (!err) {
439 char *cpusets;
440 hwloc_bitmap_asprintf(&cpusets, cpuset);
441 printf("CPU kind #%u efficiency %d cpuset %s\n", i, efficiency, cpusets);
442 free(cpusets);
443 for(j=0; j<nr_infos; j++)
444 printf(" %s = %s\n", infos[j].name, infos[j].value);
445 }
446 }
447
448 hwloc_bitmap_free(cpuset);
449 }
450
451 int
output_console(struct lstopo_output * loutput,const char * filename)452 output_console(struct lstopo_output *loutput, const char *filename)
453 {
454 hwloc_topology_t topology = loutput->topology;
455 int verbose_mode = loutput->verbose_mode;
456 FILE *output;
457
458 output = open_output(filename, loutput->overwrite);
459 if (!output) {
460 fprintf(stderr, "Failed to open %s for writing (%s)\n", filename, strerror(errno));
461 return -1;
462 }
463 loutput->file = output;
464
465 if (loutput->show_distances_only) {
466 output_distances(loutput);
467 return 0;
468 }
469 if (loutput->show_memattrs_only) {
470 output_memattrs(loutput);
471 return 0;
472 }
473 if (loutput->show_cpukinds_only) {
474 output_cpukinds(loutput);
475 return 0;
476 }
477 if (loutput->show_windows_processor_groups_only) {
478 output_windows_processor_groups(loutput, 1);
479 return 0;
480 }
481
482 /*
483 * if verbose_mode == 0, only print the summary.
484 * if verbose_mode == 1, only print the topology tree.
485 * if verbose_mode > 1, print both.
486 */
487
488 if (loutput->show_only != HWLOC_OBJ_TYPE_NONE) {
489 if (verbose_mode > 1)
490 fprintf(output, "Only showing %s objects\n", hwloc_obj_type_string(loutput->show_only));
491 output_only (loutput, hwloc_get_root_obj(topology));
492 } else if (verbose_mode >= 1) {
493 output_topology (loutput, hwloc_get_root_obj(topology), NULL, 0);
494 fprintf(output, "\n");
495 }
496
497 if ((verbose_mode > 1 || !verbose_mode) && loutput->show_only == HWLOC_OBJ_TYPE_NONE) {
498 hwloc_lstopo_show_summary(output, topology);
499 }
500
501 if (verbose_mode > 1 && loutput->show_only == HWLOC_OBJ_TYPE_NONE) {
502 output_distances(loutput);
503 output_memattrs(loutput);
504 output_cpukinds(loutput);
505 output_windows_processor_groups(loutput, verbose_mode > 2);
506 }
507
508 if (verbose_mode > 1 && loutput->show_only == HWLOC_OBJ_TYPE_NONE) {
509 hwloc_const_bitmap_t complete = hwloc_topology_get_complete_cpuset(topology);
510 hwloc_const_bitmap_t topo = hwloc_topology_get_topology_cpuset(topology);
511 hwloc_const_bitmap_t allowed = hwloc_topology_get_allowed_cpuset(topology);
512
513 if (!hwloc_bitmap_isequal(topo, complete)) {
514 hwloc_bitmap_t unknown = hwloc_bitmap_alloc();
515 char *unknownstr;
516 hwloc_bitmap_copy(unknown, complete);
517 hwloc_bitmap_andnot(unknown, unknown, topo);
518 hwloc_bitmap_asprintf(&unknownstr, unknown);
519 fprintf (output, "%d processors not represented in topology: %s\n", hwloc_bitmap_weight(unknown), unknownstr);
520 free(unknownstr);
521 hwloc_bitmap_free(unknown);
522 }
523 if (!hwloc_bitmap_isequal(topo, allowed)) {
524 hwloc_bitmap_t disallowed = hwloc_bitmap_alloc();
525 char *disallowedstr;
526 hwloc_bitmap_copy(disallowed, topo);
527 hwloc_bitmap_andnot(disallowed, disallowed, allowed);
528 hwloc_bitmap_asprintf(&disallowedstr, disallowed);
529 fprintf(output, "%d processors represented but not allowed: %s\n", hwloc_bitmap_weight(disallowed), disallowedstr);
530 free(disallowedstr);
531 hwloc_bitmap_free(disallowed);
532 }
533 if (!hwloc_topology_is_thissystem(topology))
534 fprintf (output, "Topology not from this system\n");
535 }
536
537 if (output != stdout)
538 fclose(output);
539
540 return 0;
541 }
542
543 int
output_synthetic(struct lstopo_output * loutput,const char * filename)544 output_synthetic(struct lstopo_output *loutput, const char *filename)
545 {
546 hwloc_topology_t topology = loutput->topology;
547 FILE *output;
548 int length;
549 char sbuffer[1024];
550 char * dbuffer = NULL;
551 unsigned nb1, nb2, nb3;
552
553 if (!hwloc_get_root_obj(topology)->symmetric_subtree) {
554 fprintf(stderr, "Cannot output assymetric topology in synthetic format.\n");
555 goto out;
556 }
557
558 nb1 = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_MISC);
559 if (nb1) {
560 fprintf(stderr, "# Ignoring %u Misc objects.\n", nb1);
561 fprintf(stderr, "# (pass --filter Misc:none to hide this message).\n");
562 }
563 nb1 = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_BRIDGE);
564 nb2 = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_PCI_DEVICE);
565 nb3 = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_OS_DEVICE);
566 if (nb1 || nb2 || nb3) {
567 fprintf(stderr, "# Ignoring %u Bridge, %u PCI device and %u OS device objects\n", nb1, nb2, nb3);
568 fprintf(stderr, "# (pass --no-io to hide this message).\n");
569 }
570
571 length = hwloc_topology_export_synthetic(topology, sbuffer, sizeof(sbuffer), loutput->export_synthetic_flags);
572 if (length < 0) {
573 fprintf(stderr, "Failed to export a synthetic description (%s)\n", strerror(errno));
574 goto out;
575 }
576
577 if (length >= (int) sizeof(sbuffer)) {
578 dbuffer = malloc(length+1 /* \0 */);
579 if (!dbuffer)
580 goto out;
581
582 length = hwloc_topology_export_synthetic(topology, dbuffer, length+1, loutput->export_synthetic_flags);
583 if (length < 0)
584 goto out_with_dbuffer;
585 }
586
587 output = open_output(filename, loutput->overwrite);
588 if (!output) {
589 fprintf(stderr, "Failed to open %s for writing (%s)\n", filename, strerror(errno));
590 goto out_with_dbuffer;
591 }
592
593 fprintf(output, "%s\n", dbuffer ? dbuffer : sbuffer);
594
595 if (output != stdout)
596 fclose(output);
597
598 free(dbuffer);
599 return 0;
600
601 out_with_dbuffer:
602 free(dbuffer);
603 out:
604 return -1;
605 }
606