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