-
Notifications
You must be signed in to change notification settings - Fork 168
/
lstopo-draw.c
1913 lines (1706 loc) · 69.7 KB
/
lstopo-draw.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright © 2009 CNRS
* Copyright © 2009-2022 Inria. All rights reserved.
* Copyright © 2009-2013, 2015 Université Bordeaux
* Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved.
* See COPYING in top-level directory.
*/
#include "private/autogen/config.h"
#include "hwloc.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include "lstopo.h"
#define LSTOPO_COLOR(r,g,b) (struct lstopo_color) { r, g, b, 0 }
#define LSTOPO_COLOR_GREY(x) (struct lstopo_color) { x, x, x, 0 }
#define LSTOPO_COLOR_WHITE LSTOPO_COLOR_GREY(0xff)
#define LSTOPO_COLOR_BLACK LSTOPO_COLOR_GREY(0)
#define EPOXY_R_COLOR 0xe7
#define EPOXY_G_COLOR 0xff
#define EPOXY_B_COLOR 0xb5
#define LSTOPO_COLOR_EPOXY LSTOPO_COLOR(EPOXY_R_COLOR, EPOXY_G_COLOR, EPOXY_B_COLOR)
#define EPOXY_GREY_COLOR ((EPOXY_R_COLOR+EPOXY_G_COLOR+EPOXY_B_COLOR)/3)
#define LSTOPO_COLOR_GREY_EPOXY LSTOPO_COLOR_GREY(EPOXY_GREY_COLOR)
#define DARK_EPOXY_R_COLOR ((EPOXY_R_COLOR * 100) / 110)
#define DARK_EPOXY_G_COLOR ((EPOXY_G_COLOR * 100) / 110)
#define DARK_EPOXY_B_COLOR ((EPOXY_B_COLOR * 100) / 110)
#define LSTOPO_COLOR_DARK_EPOXY LSTOPO_COLOR(DARK_EPOXY_R_COLOR, DARK_EPOXY_G_COLOR, DARK_EPOXY_B_COLOR)
#define DARK_EPOXY_GREY_COLOR ((EPOXY_GREY_COLOR * 100) / 110)
#define LSTOPO_COLOR_GREY_DARK_EPOXY LSTOPO_COLOR_GREY(DARK_EPOXY_GREY_COLOR)
#define DARKER_EPOXY_R_COLOR ((DARK_EPOXY_R_COLOR * 100) / 110)
#define DARKER_EPOXY_G_COLOR ((DARK_EPOXY_G_COLOR * 100) / 110)
#define DARKER_EPOXY_B_COLOR ((DARK_EPOXY_B_COLOR * 100) / 110)
#define LSTOPO_COLOR_DARKER_EPOXY LSTOPO_COLOR(DARKER_EPOXY_R_COLOR, DARKER_EPOXY_G_COLOR, DARKER_EPOXY_B_COLOR)
#define DARKER_EPOXY_GREY_COLOR ((DARK_EPOXY_GREY_COLOR * 100) / 110)
#define LSTOPO_COLOR_GREY_DARKER_EPOXY LSTOPO_COLOR_GREY(DARKER_EPOXY_GREY_COLOR)
struct lstopo_color_palette lstopo_main_palette, lstopo_grey_palette, lstopo_white_palette;
void
lstopo_palette_init(struct lstopo_output *loutput)
{
#ifdef HWLOC_HAVE_GCC_W_MISSING_FIELD_INITIALIZERS
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
/* each of these colors must be declared in declare_colors() */
lstopo_main_palette.white = LSTOPO_COLOR_WHITE;
lstopo_main_palette.black = LSTOPO_COLOR_BLACK;
lstopo_main_palette.machine = LSTOPO_COLOR_WHITE;
lstopo_main_palette.group = LSTOPO_COLOR_WHITE;
lstopo_main_palette.package = LSTOPO_COLOR_DARK_EPOXY;
lstopo_main_palette.group_in_package = LSTOPO_COLOR_EPOXY;
lstopo_main_palette.die = LSTOPO_COLOR_EPOXY;
lstopo_main_palette.core = LSTOPO_COLOR_GREY(0xbe);
lstopo_main_palette.pu = LSTOPO_COLOR_WHITE;
lstopo_main_palette.numanode = LSTOPO_COLOR(0xef, 0xdf, 0xde);
lstopo_main_palette.memories = LSTOPO_COLOR(0xf2, 0xe8, 0xe8); /* slightly lighter than numanode */
lstopo_main_palette.cache = LSTOPO_COLOR_WHITE;
lstopo_main_palette.pcidev = LSTOPO_COLOR_DARKER_EPOXY;
lstopo_main_palette.osdev = LSTOPO_COLOR_GREY(0xde);
lstopo_main_palette.bridge = LSTOPO_COLOR_WHITE;
lstopo_main_palette.misc = LSTOPO_COLOR_WHITE;
lstopo_main_palette.binding = LSTOPO_COLOR(0, 0xff, 0); /* green */
lstopo_main_palette.disallowed = LSTOPO_COLOR(0xff, 0, 0); /* red */
lstopo_main_palette.process = LSTOPO_COLOR(0xff, 0xff, 0); /* yellow */
memcpy(&lstopo_grey_palette, &lstopo_main_palette, sizeof(lstopo_main_palette));
/* replace non-grey colors by some grey */
lstopo_grey_palette.package = LSTOPO_COLOR_GREY_DARK_EPOXY;
lstopo_grey_palette.group_in_package = LSTOPO_COLOR_GREY_EPOXY;
lstopo_grey_palette.die = LSTOPO_COLOR_GREY_EPOXY;
lstopo_grey_palette.numanode = LSTOPO_COLOR_GREY(0xe4);
lstopo_grey_palette.memories = LSTOPO_COLOR_GREY(0xe8); /* slightly lighter than numanode */
lstopo_grey_palette.pcidev = LSTOPO_COLOR_GREY_DARKER_EPOXY;
lstopo_grey_palette.binding = LSTOPO_COLOR_GREY(0xbb);
lstopo_grey_palette.disallowed = LSTOPO_COLOR_GREY(0x77);
lstopo_grey_palette.process = LSTOPO_COLOR_GREY(0x99);
memcpy(&lstopo_white_palette, &lstopo_main_palette, sizeof(lstopo_main_palette));
/* replace everything but white/black with white */
lstopo_white_palette.machine = LSTOPO_COLOR_WHITE;
lstopo_white_palette.group = LSTOPO_COLOR_WHITE;
lstopo_white_palette.package = LSTOPO_COLOR_WHITE;
lstopo_white_palette.group_in_package = LSTOPO_COLOR_WHITE;
lstopo_white_palette.die = LSTOPO_COLOR_WHITE;
lstopo_white_palette.core = LSTOPO_COLOR_WHITE;
lstopo_white_palette.pu = LSTOPO_COLOR_WHITE;
lstopo_white_palette.numanode = LSTOPO_COLOR_WHITE;
lstopo_white_palette.memories = LSTOPO_COLOR_WHITE;
lstopo_white_palette.cache = LSTOPO_COLOR_WHITE;
lstopo_white_palette.pcidev = LSTOPO_COLOR_WHITE;
lstopo_white_palette.osdev = LSTOPO_COLOR_WHITE;
lstopo_white_palette.bridge = LSTOPO_COLOR_WHITE;
lstopo_white_palette.misc = LSTOPO_COLOR_WHITE;
lstopo_white_palette.binding = LSTOPO_COLOR_WHITE;
lstopo_white_palette.disallowed = LSTOPO_COLOR_WHITE;
lstopo_white_palette.process = LSTOPO_COLOR_WHITE;
#ifdef HWLOC_HAVE_GCC_W_MISSING_FIELD_INITIALIZERS
#pragma GCC diagnostic warning "-Wmissing-field-initializers"
#endif
/* use the color palette by default */
loutput->palette = &lstopo_main_palette;
}
void
lstopo_palette_select(struct lstopo_output *loutput, const char *name)
{
if (!strcmp(name, "grey") || !strcmp(name, "greyscale"))
loutput->palette = &lstopo_grey_palette;
else if (!strcmp(name, "colors") || !strcmp(name, "default"))
loutput->palette = &lstopo_main_palette;
else if (!strcmp(name, "white") || !strcmp(name, "none"))
loutput->palette = &lstopo_white_palette;
else
fprintf(stderr, "Unrecognized palette name `%s', ignoring\n", name);
}
void
lstopo_palette_set_color(struct lstopo_color *color, unsigned rrggbb)
{
color->r = (rrggbb >> 16) & 0xff;
color->g = (rrggbb >> 8) & 0xff;
color->b = (rrggbb >> 0) & 0xff;
}
void
lstopo_palette_set_color_by_name(struct lstopo_output *loutput, const char *name, unsigned rrggbb)
{
if (!strcasecmp(name, "machine"))
lstopo_palette_set_color(&loutput->palette->machine, rrggbb);
else if (!strcasecmp(name, "group"))
lstopo_palette_set_color(&loutput->palette->group, rrggbb);
else if (!strcasecmp(name, "package"))
lstopo_palette_set_color(&loutput->palette->package, rrggbb);
else if (!strcasecmp(name, "group_in_package"))
lstopo_palette_set_color(&loutput->palette->group_in_package, rrggbb);
else if (!strcasecmp(name, "die"))
lstopo_palette_set_color(&loutput->palette->die, rrggbb);
else if (!strcasecmp(name, "core"))
lstopo_palette_set_color(&loutput->palette->core, rrggbb);
else if (!strcasecmp(name, "pu"))
lstopo_palette_set_color(&loutput->palette->pu, rrggbb);
else if (!strcasecmp(name, "numanode"))
lstopo_palette_set_color(&loutput->palette->numanode, rrggbb);
else if (!strcasecmp(name, "memories"))
lstopo_palette_set_color(&loutput->palette->memories, rrggbb);
else if (!strcasecmp(name, "cache"))
lstopo_palette_set_color(&loutput->palette->cache, rrggbb);
else if (!strcasecmp(name, "pcidev"))
lstopo_palette_set_color(&loutput->palette->pcidev, rrggbb);
else if (!strcasecmp(name, "osdev"))
lstopo_palette_set_color(&loutput->palette->osdev, rrggbb);
else if (!strcasecmp(name, "bridge"))
lstopo_palette_set_color(&loutput->palette->bridge, rrggbb);
else if (!strcasecmp(name, "misc"))
lstopo_palette_set_color(&loutput->palette->misc, rrggbb);
else
fprintf(stderr, "Unrecognized palette color name `%s', ignoring\n", name);
/* binding/disallowed/process are handled by --binding/disallowed/top-color */
}
static struct lstopo_color *color_list = NULL;
static struct lstopo_color *
declare_color(struct lstopo_output *loutput, struct lstopo_color *color)
{
memset(&color->private, 0, sizeof(color->private));
/* call the backend callback if any */
if (loutput->methods->declare_color) {
int ret = loutput->methods->declare_color(loutput, color);
if (ret < 0)
return NULL;
}
/* insert */
color->next = color_list;
color_list = color;
return color;
}
void
declare_colors(struct lstopo_output *output)
{
/* don't bother looking for duplicate colors here,
* we want to be able to use those structs so always queue them
*/
declare_color(output, &output->palette->white);
declare_color(output, &output->palette->black);
declare_color(output, &output->palette->machine);
declare_color(output, &output->palette->group);
declare_color(output, &output->palette->package);
declare_color(output, &output->palette->group_in_package);
declare_color(output, &output->palette->die);
declare_color(output, &output->palette->core);
declare_color(output, &output->palette->pu);
declare_color(output, &output->palette->numanode);
declare_color(output, &output->palette->memories);
declare_color(output, &output->palette->cache);
declare_color(output, &output->palette->pcidev);
declare_color(output, &output->palette->osdev);
declare_color(output, &output->palette->bridge);
declare_color(output, &output->palette->misc);
declare_color(output, &output->palette->binding);
declare_color(output, &output->palette->disallowed);
declare_color(output, &output->palette->process);
}
void
destroy_colors(struct lstopo_output *loutput)
{
struct lstopo_color *tmp = color_list;
while (tmp) {
struct lstopo_color *next = tmp->next;
if (loutput->methods->destroy_color)
loutput->methods->destroy_color(loutput, tmp);
if (tmp->free)
free(tmp);
tmp = next;
}
color_list = NULL; /* so that it works after refresh */
}
static struct lstopo_color *
find_or_declare_rgb_color(struct lstopo_output *loutput, int r, int g, int b)
{
struct lstopo_color *color, *tmp;
for(tmp = color_list; tmp; tmp = tmp->next)
if (tmp->r == r && tmp->g == g && tmp->b == b)
return tmp;
color = malloc(sizeof(*color));
if (!color)
return NULL;
color->r = r & 255;
color->g = g & 255;
color->b = b & 255;
color->free = 1;
tmp = declare_color(loutput, color);
if (!tmp)
free(color);
return tmp;
}
static unsigned
get_textwidth(void *output,
const char *text, unsigned length,
unsigned fontsize)
{
struct lstopo_output *loutput = output;
unsigned width;
#ifdef HWLOC_DEBUG
assert(loutput->methods->textsize);
#endif
loutput->methods->textsize(output, text, length, fontsize, &width);
width = (unsigned)(loutput->text_xscale * ((float)width));
return width;
}
/*
* foo_draw functions take a OBJ, computes which size it needs, recurse into
* sublevels with drawing=PREPARE to recursively compute the needed size
* without actually drawing anything, then draw things about OBJ (chip draw,
* cache size information etc) at (X,Y), recurse into sublevels again to
* actually draw things, and return in RETWIDTH and RETHEIGHT the amount of
* space that the drawing took.
*
* For generic detailed comments, see the node_draw function.
*
* border is added around the objects
* separator is added between objects
*/
typedef void (*foo_draw)(struct lstopo_output *loutput, hwloc_obj_t obj, unsigned depth, unsigned x, unsigned y);
static foo_draw get_type_fun(hwloc_obj_type_t type);
/* next child, in all children list, with memory before CPU, ignoring PU if needed.
* similar to hwloc_get_next_child() but returns memory children first.
*/
#define NEXT_CHILD_INIT_STATE -1
static hwloc_obj_t next_child(struct lstopo_output *loutput, hwloc_obj_t parent, unsigned kind, hwloc_obj_t prev, int *statep)
{
int state;
hwloc_obj_t obj;
if (prev) {
obj = prev->next_sibling;
state = *statep;
} else {
obj = NULL;
state = NEXT_CHILD_INIT_STATE;
}
again:
if (!obj && state <= -1 && (kind & LSTOPO_CHILD_KIND_MEMORY)) {
obj = parent->memory_first_child;
state = 0;
}
if (!obj && state <= 0 && (kind & LSTOPO_CHILD_KIND_NORMAL)) {
obj = parent->first_child;
state = 1;
}
if (!obj && state <= 1 && (kind & LSTOPO_CHILD_KIND_IO)) {
obj = parent->io_first_child;
state = 2;
}
if (!obj && state <= 2 && (kind & LSTOPO_CHILD_KIND_MISC)) {
obj = parent->misc_first_child;
state = 3;
}
if (!obj)
return NULL;
if (loutput->factorize_enabled && parent->arity > loutput->factorize_min[obj->type]) {
if (((struct lstopo_obj_userdata *)obj->userdata)->factorized < 0) {
obj = obj->next_sibling;
goto again;
}
}
if (obj->type == HWLOC_OBJ_PU && loutput->ignore_pus) {
obj = obj->next_sibling;
goto again;
}
if (obj->type == HWLOC_OBJ_NUMANODE && loutput->ignore_numanodes) {
obj = obj->next_sibling;
goto again;
}
if (loutput->pci_collapse_enabled && obj->type == HWLOC_OBJ_PCI_DEVICE) {
struct lstopo_obj_userdata *lud = obj->userdata;
if (lud->pci_collapsed == -1) {
obj = obj->next_sibling;
goto again;
}
}
*statep = state;
return obj;
}
static float pci_link_speed(hwloc_obj_t obj)
{
if (obj->type == HWLOC_OBJ_PCI_DEVICE)
return obj->attr->pcidev.linkspeed;
if (obj->type == HWLOC_OBJ_BRIDGE && obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)
return obj->attr->bridge.upstream.pci.linkspeed;
return 0.;
}
/********************************
* Placing children in rectangle
*/
/* preferred width/height compromise */
#define RATIO (4.f/3.f)
/* returns a score <= 1. close to 1 is better */
static __hwloc_inline
float rectangle_score(unsigned width, unsigned height, float ratio)
{
float score = ((float) width)/height/ratio;
if (score > 1)
score = 1/score;
return score;
}
static void find_children_rectangle(struct lstopo_output *loutput, hwloc_obj_t parent,
unsigned kind, unsigned separator,
unsigned *rowsp, unsigned *columnsp,
float ratio)
{
unsigned rows = 0, columns = 0, n;
unsigned numsubobjs = 0, obj_totwidth = 0, obj_totheight = 0;
unsigned obj_avgwidth, obj_avgheight;
unsigned area = 0;
float idealtotheight, under_score, over_score, best_score = 0.f;
hwloc_obj_t child;
int ncstate;
/* Total area for subobjects */
child = NULL;
while ((child=next_child(loutput, parent, kind, child, &ncstate)) != NULL) {
struct lstopo_obj_userdata *clud = child->userdata;
numsubobjs++;
obj_totwidth += clud->width + separator;
obj_totheight += clud->height + separator;
area += (clud->width + separator) * (clud->height + separator);
}
/* Average object size */
obj_avgwidth = obj_totwidth / numsubobjs;
obj_avgheight = obj_totheight / numsubobjs;
/* Try to find a rectangle fitting exactly */
for (n = (unsigned) (float) floor(sqrt(numsubobjs));
rows >= (unsigned) (float) ceil(pow(numsubobjs, 0.33)) && n > 1;
n--) {
float old_best_score = best_score;
unsigned p = numsubobjs / n;
float np_score; /* n rows x p columns */
float pn_score; /* p rows x n columns */
if (p <= 1 || p * n != numsubobjs)
continue;
/* Try both n*p and n*p rectangles */
np_score = rectangle_score(p * obj_avgwidth, obj_avgheight * n, ratio);
pn_score = rectangle_score(n * obj_avgwidth, obj_avgheight * p, ratio);
if (np_score > pn_score) {
if (np_score > best_score) {
rows = n;
columns = p;
best_score = np_score;
}
} else {
if (pn_score > best_score) {
rows = p;
columns = n;
best_score = pn_score;
}
}
if (old_best_score == best_score)
/* Score didn't improve, no need to try further.
* The graph score(n) is an inverted U, with np_score on the left and pn_score on the right.
* Se start somewhere in the middle and walk to both ends simultaneously,
* Only one side can improve towards the maximum, we stop once once it stops improving.
* The other side can just decrease for ever.
*/
goto done;
}
if (best_score)
/* We got a rectangle fitting exactly, it may not be perfect but it's good looking */
goto done;
/* Try to find a rectangle with an incomplete last row */
/* Ideal total height for spreading that area with ratio */
idealtotheight = (float) sqrt(area/ratio);
/* approximation of number of rows */
rows = (unsigned) (idealtotheight / obj_avgheight);
columns = rows ? (numsubobjs + rows - 1) / rows : 1;
/* Score obtained by underestimation */
under_score = rectangle_score(columns * obj_avgwidth, rows * obj_avgheight, ratio);
/* try to overestimate too */
rows++;
columns = (numsubobjs + rows - 1) / rows;
/* Score obtained by overestimation */
over_score = rectangle_score(columns * obj_avgwidth, rows * obj_avgheight, ratio);
/* Revert back to under estimation if it was better */
if (rows > 1 && under_score > over_score) {
rows--;
columns = (numsubobjs + rows - 1) / rows;
}
done:
*rowsp = rows;
*columnsp = columns;
}
/**************************
* Placing children
*/
static void
place_children_horiz(struct lstopo_output *loutput, hwloc_obj_t parent,
unsigned kind, unsigned border, unsigned separator,
unsigned *width, unsigned *height)
{
unsigned curx = 0;
unsigned maxh = 0;
hwloc_obj_t child;
int ncstate;
for(child = next_child(loutput, parent, kind, NULL, &ncstate);
child;
child = next_child(loutput, parent, kind, child, &ncstate)) {
struct lstopo_obj_userdata *clud = child->userdata;
clud->xrel = curx + border;
clud->yrel = border;
if (clud->height > maxh)
maxh = clud->height;
curx += separator + clud->width;
}
*width = curx - separator + 2*border;
*height = maxh + 2*border;
}
/* bridge object height: small empty box */
#define BRIDGE_HEIGHT (gridsize)
static void
place_children_vert(struct lstopo_output *loutput, hwloc_obj_t parent,
unsigned kind, unsigned border, unsigned separator,
unsigned *width, unsigned *height)
{
unsigned cury = 0;
unsigned maxw = 0;
unsigned gridsize = loutput->gridsize;
unsigned fontsize = loutput->fontsize;
int bridge_parent_with_pcilinkspeed = parent->type == HWLOC_OBJ_BRIDGE
&& loutput->show_text_enabled && loutput->show_text[HWLOC_OBJ_BRIDGE];
hwloc_obj_t child;
int ncstate;
for(child = next_child(loutput, parent, kind, NULL, &ncstate);
child;
child = next_child(loutput, parent, kind, child, &ncstate)) {
struct lstopo_obj_userdata *clud = child->userdata;
unsigned child_height = clud->height;
clud->xrel = border;
clud->yrel = cury + border;
if (clud->width > maxw)
maxw = clud->width;
if (bridge_parent_with_pcilinkspeed && pci_link_speed(child) != 0.)
/* make sure padding between children is enough to display pci link speed */
if (child_height <= BRIDGE_HEIGHT + fontsize)
child_height = BRIDGE_HEIGHT + fontsize;
cury += separator + child_height;
}
*width = maxw + 2*border;
*height = cury - separator + 2*border;
}
static void
place_children_rect(struct lstopo_output *loutput, hwloc_obj_t parent,
unsigned kind, unsigned border, unsigned separator,
unsigned *width, unsigned *height)
{
unsigned rows, columns;
unsigned totwidth, totheight; /* total children array size, without borders */
unsigned rowwidth; /* current row width */
unsigned maxheight; /* max height for current row */
hwloc_obj_t child;
int ncstate;
float ratio;
int i;
if (parent->type == HWLOC_OBJ_CORE)
ratio = 1/RATIO;
else
ratio = RATIO;
find_children_rectangle(loutput, parent, kind, separator, &rows, &columns, ratio);
rowwidth = 0;
maxheight = 0;
totwidth = 0;
totheight = 0;
for(i = 0, child = next_child(loutput, parent, kind, NULL, &ncstate);
child;
i++, child = next_child(loutput, parent, kind, child, &ncstate)) {
struct lstopo_obj_userdata *clud = child->userdata;
/* Newline? */
if (i && i%columns == 0) {
/* Update total width using new row */
if (rowwidth > totwidth)
totwidth = rowwidth;
rowwidth = 0;
/* Update total height */
totheight += maxheight + separator;
maxheight = 0;
}
/* Add new child */
clud->xrel = rowwidth + border;
clud->yrel = totheight + border;
rowwidth += clud->width + separator;
if (clud->height > maxheight)
maxheight = clud->height;
}
/* Update total width using last row */
if (rowwidth > totwidth)
totwidth = rowwidth;
/* Remove spurious separator on the right */
totwidth -= separator;
/* Update total height using last row */
totheight += maxheight; /* no separator */
*width = totwidth + 2*border;
*height = totheight + 2*border;
}
static void
place__children(struct lstopo_output *loutput, hwloc_obj_t parent,
unsigned kind,
enum lstopo_orient_e *orientp,
unsigned border, unsigned separator,
unsigned *widthp, unsigned *heightp)
{
if (*orientp == LSTOPO_ORIENT_HORIZ) {
/* force horizontal */
place_children_horiz(loutput, parent, kind, border, separator, widthp, heightp);
} else if (*orientp == LSTOPO_ORIENT_VERT) {
/* force vertical */
place_children_vert(loutput, parent, kind, border, separator, widthp, heightp);
} else {
/* NONE or forced RECT, do a rectangular placement */
place_children_rect(loutput, parent, kind, border, separator, widthp, heightp);
}
}
/* Recurse into children to get their size.
* Place them.
* Save their position and the parent total size for later.
*/
static void
place_children(struct lstopo_output *loutput, hwloc_obj_t parent,
unsigned xrel, unsigned yrel /* position of children within parent */)
{
struct lstopo_obj_userdata *plud = parent->userdata;
enum lstopo_orient_e main_orient, right_orient, below_orient;
unsigned border = loutput->gridsize;
unsigned separator = loutput->gridsize;
unsigned separator_below_cache = loutput->gridsize;
unsigned normal_children_separator = loutput->gridsize;
unsigned totwidth = plud->width, totheight = plud->height;
/* Children placement is divided in 4 zones:
*
* +-------------------------------------------------------------------+
* | Above = Memory Children (by default) |
* +-----------------------------------+-------------------------------+
* | Main = CPU (always), | Right = I/O+Misc (by default) |
* | Memory+I/O+Misc (optional) | |
* +-----------------------------------+-------------------------------+
* | Below = I/O+Misc (optional) |
* +-----------------------------------+
*
* All these children are placed inside the parent box, except:
* - Cache parent are between Above and Main.
* - Bridges have a special drawing for PCI buses and links
*
* I/O and Misc parents only have the Main section because
* their children are either I/O or Misc, no CPU or Memory.
*
* Memory parents may have Main (for Memory children)
* and below/right for Misc.
*/
unsigned children_width = 0, children_height = 0; /* Main children size */
unsigned above_children_width = 0, above_children_height = 0; /* Above children size */
unsigned right_children_width = 0, right_children_height = 0; /* Right children size */
unsigned below_children_width = 0, below_children_height = 0; /* Below children size */
unsigned mrb_children_width = 0, mrb_children_height = 0; /* sum of Main+Right+Below sizes */
unsigned existing_kinds;
int normal_children_are_PUs;
hwloc_obj_t child;
int ncstate;
unsigned i;
/* place main children according to the parent type if specified, or to the main layout */
main_orient = loutput->force_orient[parent->type];
/* place right/below children according to right/below first, or fallback to the parent type */
right_orient = loutput->right_force_orient;
if (right_orient == LSTOPO_ORIENT_NONE)
right_orient = loutput->force_orient[parent->type];
below_orient = loutput->below_force_orient;
if (below_orient == LSTOPO_ORIENT_NONE)
below_orient = loutput->force_orient[parent->type];
/* defaults */
plud->children.box = 0;
plud->above_children.box = 0;
plud->right_children.box = 0;
plud->below_children.box = 0;
/* list the kinds of children that exist in that parent */
existing_kinds = (parent->arity ? LSTOPO_CHILD_KIND_NORMAL : 0)
| (parent->memory_arity ? LSTOPO_CHILD_KIND_MEMORY : 0)
| (parent->io_arity ? LSTOPO_CHILD_KIND_IO : 0)
| (parent->misc_arity ? LSTOPO_CHILD_KIND_MISC : 0);
/* all children together by default */
plud->children.kinds = existing_kinds;
plud->above_children.kinds = 0;
plud->right_children.kinds = 0;
plud->below_children.kinds = 0;
/* if we're not inside a memory object, put memory children above if requested */
if (!hwloc_obj_type_is_memory(parent->type)
&& (loutput->children_order & LSTOPO_ORDER_MEMORY_ABOVE)) {
plud->children.kinds &= ~LSTOPO_CHILD_KIND_MEMORY;
plud->above_children.kinds |= existing_kinds & LSTOPO_CHILD_KIND_MEMORY;
}
/* if we're not inside a I/O, put I/O on the right if requested */
if (!hwloc_obj_type_is_io(parent->type)
&& (loutput->children_order & LSTOPO_ORDER_IO_RIGHT)) {
plud->children.kinds &= ~LSTOPO_CHILD_KIND_IO;
plud->right_children.kinds |= existing_kinds & LSTOPO_CHILD_KIND_IO;
}
/* if we're not inside a I/O, put I/O below if requested */
if (!hwloc_obj_type_is_io(parent->type)
&& (loutput->children_order & LSTOPO_ORDER_IO_BELOW)) {
plud->children.kinds &= ~LSTOPO_CHILD_KIND_IO;
plud->below_children.kinds |= existing_kinds & LSTOPO_CHILD_KIND_IO;
}
/* if we're not inside a Misc, put Misc on the right if requested */
if (parent->type != HWLOC_OBJ_MISC
&& (loutput->children_order & LSTOPO_ORDER_MISC_RIGHT)) {
plud->children.kinds &= ~LSTOPO_CHILD_KIND_MISC;
plud->right_children.kinds |= existing_kinds & LSTOPO_CHILD_KIND_MISC;
}
/* if we're not inside a Misc, put Misc below if requested */
if (parent->type != HWLOC_OBJ_MISC
&& (loutput->children_order & LSTOPO_ORDER_MISC_BELOW)) {
plud->children.kinds &= ~LSTOPO_CHILD_KIND_MISC;
plud->below_children.kinds |= existing_kinds & LSTOPO_CHILD_KIND_MISC;
}
/* bridge children always vertical */
if (parent->type == HWLOC_OBJ_BRIDGE)
main_orient = LSTOPO_ORIENT_VERT;
/* if factorizing children, use horizontal by default */
if (main_orient == LSTOPO_ORIENT_NONE
&& parent->symmetric_subtree
&& parent->first_child
&& loutput->factorize_enabled
&& parent->arity > loutput->factorize_min[parent->first_child->type]) {
main_orient = LSTOPO_ORIENT_HORIZ;
}
/* if there are memory children and using plain children layout, use horizontal by default */
if (main_orient == LSTOPO_ORIENT_NONE
&& parent->memory_arity
&& !(loutput->children_order & LSTOPO_ORDER_MEMORY_ABOVE))
main_orient = LSTOPO_ORIENT_HORIZ;
/* recurse into children to prepare their sizes,
* and check whether all normal children are PUs. */
normal_children_are_PUs = (parent->arity > 0);
for(i = 0, child = next_child(loutput, parent, LSTOPO_CHILD_KIND_ALL, NULL, &ncstate);
child;
i++, child = next_child(loutput, parent, LSTOPO_CHILD_KIND_ALL, child, &ncstate)) {
get_type_fun(child->type)(loutput, child, 0, 0, 0);
if (hwloc_obj_type_is_normal(child->type) && child->type != HWLOC_OBJ_PU)
normal_children_are_PUs = 0;
}
if (!i)
return;
/* no separator between PUs */
if (normal_children_are_PUs)
normal_children_separator = 0;
/* add separator between a cache parent and its children */
if (hwloc_obj_type_is_cache(parent->type) || parent->type == HWLOC_OBJ_MEMCACHE) {
if (normal_children_are_PUs || parent->type == HWLOC_OBJ_MEMCACHE)
/* except between cache parent and PU children */
separator_below_cache = 0;
/* update children placement */
yrel += separator_below_cache;
}
/* compute the size of the main children section */
if (plud->children.kinds)
place__children(loutput, parent, plud->children.kinds, &main_orient, 0, normal_children_separator, &children_width, &children_height);
/* compute the size of the right children section (I/O and Misc), if any */
if (plud->right_children.kinds) {
place__children(loutput, parent, plud->right_children.kinds, &right_orient, 0, separator, &right_children_width, &right_children_height);
}
/* compute the size of the below children section (I/O and Misc), if any */
if (plud->below_children.kinds) {
place__children(loutput, parent, plud->below_children.kinds, &below_orient, 0, separator, &below_children_width, &below_children_height);
}
/* compute the width of the MRB children sections, it may be need for the above children section below */
mrb_children_width = children_width + right_children_width + (children_width && right_children_width ? separator : 0);
if (mrb_children_width < below_children_width)
mrb_children_width = below_children_width;
/* MRB height will be computed later, it's more difficult because of possible overlaps */
/* compute the size of the above children section (Memory), if any */
if (plud->above_children.kinds) {
enum lstopo_orient_e morient = LSTOPO_ORIENT_HORIZ;
int need_box;
assert(plud->above_children.kinds == LSTOPO_CHILD_KIND_MEMORY);
/* we need a memory children box if parent isn't a memory object
* and if there are multiple objects in the box
*/
need_box = !hwloc_obj_type_is_memory(parent->type)
&& (parent->memory_arity + parent->memory_first_child->memory_arity > 1);
place__children(loutput, parent, plud->above_children.kinds, &morient, need_box ? border : 0, separator, &above_children_width, &above_children_height);
if (parent->type == HWLOC_OBJ_MEMCACHE)
above_children_height -= separator;
if (need_box) {
/* if there are multiple memory children, add a box, as large as the parent */
if (above_children_width < children_width) {
above_children_width = mrb_children_width;
}
plud->above_children.boxcolor = &loutput->palette->memories;
plud->above_children.box = 1;
} else {
/* if there's a single memory child without wide memory box, enlarge that child */
struct lstopo_obj_userdata *clud = parent->memory_first_child->userdata;
if (clud->width < children_width) {
clud->width = mrb_children_width;
above_children_width = mrb_children_width;
}
}
}
/* place the main section, assuming there's no above yet */
plud->children.width = children_width;
plud->children.height = children_height;
plud->children.xrel = xrel;
plud->children.yrel = yrel;
/* now place the above section and update main */
if (plud->above_children.kinds) {
plud->above_children.width = above_children_width;
plud->above_children.height = above_children_height;
plud->above_children.xrel = xrel;
plud->above_children.yrel = yrel;
plud->children.yrel += above_children_height + separator;
}
/* place the right section */
if (plud->right_children.kinds) {
plud->right_children.width = right_children_width;
plud->right_children.height = right_children_height;
plud->right_children.xrel = plud->children.xrel + children_width + (children_width ? separator : 0);
plud->right_children.yrel = plud->children.yrel;
}
/* place the below section */
if (plud->below_children.kinds) {
plud->below_children.width = below_children_width;
plud->below_children.height = below_children_height;
plud->below_children.xrel = plud->children.xrel;
if (plud->right_children.kinds
&& below_children_width > children_width
&& right_children_height > children_height) {
/* below section is larger than CPU section, and right section is higher than CPU section.
* right and below would overlap.
* move the below section below right instead of below main
*/
plud->below_children.yrel = plud->children.yrel + right_children_height + separator;
mrb_children_height = right_children_height + below_children_height + separator;
} else {
plud->below_children.yrel = plud->children.yrel + children_height + (children_height ? separator : 0);
mrb_children_height = children_height + below_children_height + (children_height ? separator : 0);
}
} else {
mrb_children_height = children_height > right_children_height ? children_height : right_children_height;
}
/* adjust parent size */
if (hwloc_obj_type_is_cache(parent->type) || parent->type == HWLOC_OBJ_MEMCACHE) {
/* cache children are below */
if (mrb_children_width > totwidth)
totwidth = mrb_children_width;
if (mrb_children_height)
totheight += mrb_children_height + separator_below_cache;
if (plud->above_children.kinds) {
totheight += above_children_height + separator;
if (above_children_width > totwidth)
totwidth = above_children_width;
}
} else if (parent->type == HWLOC_OBJ_BRIDGE) {
/* bridge children are on the right, within any space between bridge and children */
if (children_width)
totwidth += children_width;
if (children_height > totheight)
totheight = children_height;
/* no right or below sections here */
} else {
/* normal objects have children inside their box, with space around them */
if (mrb_children_width + 2*border > totwidth)
totwidth = mrb_children_width + 2*border;
if (mrb_children_height)
totheight += mrb_children_height + border;
if (plud->above_children.kinds) {
totheight += above_children_height + separator;
if (above_children_width + 2*border > totwidth)
totwidth = above_children_width + 2*border;
}
}
/* save config for draw_children() later */
plud->width = totwidth;
plud->height = totheight;
}
/***********************
* Drawing children
*/
static void
draw__children(struct lstopo_output *loutput, hwloc_obj_t parent,
struct lstopo_children_position *children,
unsigned depth,
unsigned x, unsigned y)
{
hwloc_obj_t child;
int ncstate;
if (children->box)
loutput->methods->box(loutput, children->boxcolor, depth, x, children->width, y, children->height, parent, 1);
for(child = next_child(loutput, parent, children->kinds, NULL, &ncstate);
child;
child = next_child(loutput, parent, children->kinds, child, &ncstate)) {
struct lstopo_obj_userdata *clud = child->userdata;
get_type_fun(child->type)(loutput, child, depth-1, x + clud->xrel, y + clud->yrel);
}
}
static void
draw_children(struct lstopo_output *loutput, hwloc_obj_t parent, unsigned depth,
unsigned x, unsigned y)
{
struct lstopo_obj_userdata *plud = parent->userdata;
if (plud->children.kinds)
draw__children(loutput, parent, &plud->children, depth, x + plud->children.xrel, y + plud->children.yrel);
if (plud->above_children.kinds)
draw__children(loutput, parent, &plud->above_children, depth, x + plud->above_children.xrel, y + plud->above_children.yrel);
if (plud->right_children.kinds)
draw__children(loutput, parent, &plud->right_children, depth, x + plud->right_children.xrel, y + plud->right_children.yrel);
if (plud->below_children.kinds)
draw__children(loutput, parent, &plud->below_children, depth, x + plud->below_children.xrel, y + plud->below_children.yrel);
}
/*******
* Misc
*/
static int
lstopo_obj_snprintf(struct lstopo_output *loutput, char *text, size_t textlen, hwloc_obj_t obj)
{
enum lstopo_index_type_e index_type = loutput->index_type;
unsigned idx;
const char *indexprefix;
char typestr[32];
char indexstr[32]= "";
char index2str[32] = "";
char attrstr[256];
char totmemstr[64] = "";
int attrlen;
/* For Misc and Group, name replaces type+index+attrs */
if (obj->name && (obj->type == HWLOC_OBJ_MISC || obj->type == HWLOC_OBJ_GROUP)) {
return snprintf(text, textlen, "%s", obj->name);
}
/* For OSDev, OSDev-type+name replaces type+index+attrs */
if (obj->type == HWLOC_OBJ_OS_DEVICE) {
/* consider the name as an index and remove it if LSTOPO_INDEX_TYPE_NONE */
if (index_type != LSTOPO_INDEX_TYPE_NONE) {
hwloc_obj_type_snprintf(typestr, sizeof(typestr), obj, 0);
return snprintf(text, textlen, "%s %s", typestr, obj->name);
} else {
return hwloc_obj_type_snprintf(text, textlen, obj, 0);
}
}
/* subtype replaces the basic type name */
if (obj->subtype) {
snprintf(typestr, sizeof(typestr), "%s", obj->subtype);
} else {
hwloc_obj_type_snprintf(typestr, sizeof(typestr), obj, 0);
}
if (index_type == LSTOPO_INDEX_TYPE_DEFAULT) {
if (obj->type == HWLOC_OBJ_PU || obj->type == HWLOC_OBJ_NUMANODE) {
/* by default we show logical+physical for PU/NUMA */
idx = obj->logical_index;
indexprefix = loutput->logical_index_prefix;
} else if (obj->type == HWLOC_OBJ_PACKAGE || obj->type == HWLOC_OBJ_DIE || obj->type == HWLOC_OBJ_CORE) {
/* logical only for package+core (so that we see easily how many packages/cores there are */
idx = obj->logical_index;
indexprefix = loutput->logical_index_prefix;
} else {
/* nothing for others */
idx = HWLOC_UNKNOWN_INDEX;