xref: /freebsd/usr.sbin/pmc/display.cc (revision 503f16182e08d58ce17843cf5a71c7dbc27af6b5)
1a53d2e4fSAli Mashtizadeh /*-
2a53d2e4fSAli Mashtizadeh  * SPDX-License-Identifier: BSD-2-Clause
3a53d2e4fSAli Mashtizadeh  *
4a53d2e4fSAli Mashtizadeh  * Copyright (c) 2026, Netflix, Inc.
5a53d2e4fSAli Mashtizadeh  *
6a53d2e4fSAli Mashtizadeh  * This software was developed by Ali Mashtizadeh under the sponsorship from
7a53d2e4fSAli Mashtizadeh  * Netflix, Inc.
8a53d2e4fSAli Mashtizadeh  *
9a53d2e4fSAli Mashtizadeh  * Redistribution and use in source and binary forms, with or without
10a53d2e4fSAli Mashtizadeh  * modification, are permitted provided that the following conditions
11a53d2e4fSAli Mashtizadeh  * are met:
12a53d2e4fSAli Mashtizadeh  * 1. Redistributions of source code must retain the above copyright
13a53d2e4fSAli Mashtizadeh  *    notice, this list of conditions and the following disclaimer.
14a53d2e4fSAli Mashtizadeh  * 2. Redistributions in binary form must reproduce the above copyright
15a53d2e4fSAli Mashtizadeh  *    notice, this list of conditions and the following disclaimer in the
16a53d2e4fSAli Mashtizadeh  *    documentation and/or other materials provided with the distribution.
17a53d2e4fSAli Mashtizadeh  *
18a53d2e4fSAli Mashtizadeh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19a53d2e4fSAli Mashtizadeh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20a53d2e4fSAli Mashtizadeh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21a53d2e4fSAli Mashtizadeh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22a53d2e4fSAli Mashtizadeh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23a53d2e4fSAli Mashtizadeh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24a53d2e4fSAli Mashtizadeh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25a53d2e4fSAli Mashtizadeh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26a53d2e4fSAli Mashtizadeh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27a53d2e4fSAli Mashtizadeh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28a53d2e4fSAli Mashtizadeh  * SUCH DAMAGE.
29a53d2e4fSAli Mashtizadeh  *
30a53d2e4fSAli Mashtizadeh  */
31a53d2e4fSAli Mashtizadeh 
32a53d2e4fSAli Mashtizadeh #include <sys/ioctl.h>
33a53d2e4fSAli Mashtizadeh 
34a53d2e4fSAli Mashtizadeh #include <inttypes.h>
35a53d2e4fSAli Mashtizadeh #include <stdbool.h>
36a53d2e4fSAli Mashtizadeh #include <stdint.h>
37a53d2e4fSAli Mashtizadeh #include <stdio.h>
38a53d2e4fSAli Mashtizadeh #include <term.h>
39a53d2e4fSAli Mashtizadeh #include <math.h>
40a53d2e4fSAli Mashtizadeh 
41a53d2e4fSAli Mashtizadeh #include <curses.h>
42a53d2e4fSAli Mashtizadeh #include <unistd.h>
43a53d2e4fSAli Mashtizadeh 
44a53d2e4fSAli Mashtizadeh #include <cxxabi.h>
45*503f1618SEnji Cooper #include <cstring>
46a53d2e4fSAli Mashtizadeh #include <iomanip>
47a53d2e4fSAli Mashtizadeh #include <ios>
48a53d2e4fSAli Mashtizadeh #include <iostream>
49a53d2e4fSAli Mashtizadeh #include <sstream>
50a53d2e4fSAli Mashtizadeh #include <string>
51a53d2e4fSAli Mashtizadeh #include <vector>
52a53d2e4fSAli Mashtizadeh 
53a53d2e4fSAli Mashtizadeh #include "display.hh"
54a53d2e4fSAli Mashtizadeh 
55a53d2e4fSAli Mashtizadeh enum termmode {
56a53d2e4fSAli Mashtizadeh     TERMMODE_BASIC,
57a53d2e4fSAli Mashtizadeh     TERMMODE_VT,
58a53d2e4fSAli Mashtizadeh     TERMMODE_XTERM
59a53d2e4fSAli Mashtizadeh };
60a53d2e4fSAli Mashtizadeh 
61a53d2e4fSAli Mashtizadeh static termmode tmode;
62a53d2e4fSAli Mashtizadeh static int disp_height;
63a53d2e4fSAli Mashtizadeh static int disp_width;
64a53d2e4fSAli Mashtizadeh 
65a53d2e4fSAli Mashtizadeh #define TITLE_TITLE		0
66a53d2e4fSAli Mashtizadeh #define TITLE_SUBTITLE		1
67a53d2e4fSAli Mashtizadeh 
68a53d2e4fSAli Mashtizadeh static const char **title_char;
69a53d2e4fSAli Mashtizadeh 
70a53d2e4fSAli Mashtizadeh static const char *title_vt[] = {
71a53d2e4fSAli Mashtizadeh     "\xe2\x95\x90", "\xe2\x94\x80"
72a53d2e4fSAli Mashtizadeh };
73a53d2e4fSAli Mashtizadeh static const char *title_basic[] = { "=", "-" };
74a53d2e4fSAli Mashtizadeh 
75a53d2e4fSAli Mashtizadeh #define ARROW_DOWN		0
76a53d2e4fSAli Mashtizadeh #define ARROW_UP		1
77a53d2e4fSAli Mashtizadeh 
78a53d2e4fSAli Mashtizadeh static const char **arrow_char;
79a53d2e4fSAli Mashtizadeh 
80a53d2e4fSAli Mashtizadeh static const char *arrow_vt[] = {
81a53d2e4fSAli Mashtizadeh     "\xe2\x96\xbc", "\xe2\x96\xb2"
82a53d2e4fSAli Mashtizadeh };
83a53d2e4fSAli Mashtizadeh static const char *arrow_basic[] = { "D", "U" };
84a53d2e4fSAli Mashtizadeh 
85a53d2e4fSAli Mashtizadeh #define BORDER_HBAR		0
86a53d2e4fSAli Mashtizadeh #define BORDER_VBAR		1
87a53d2e4fSAli Mashtizadeh #define BORDER_TOPRIGHT		2
88a53d2e4fSAli Mashtizadeh #define BORDER_TOPLEFT		3
89a53d2e4fSAli Mashtizadeh #define BORDER_BOTTOMLEFT	4
90a53d2e4fSAli Mashtizadeh #define BORDER_BOTTOMRIGHT	5
91a53d2e4fSAli Mashtizadeh #define BORDER_TOPSPLIT		6
92a53d2e4fSAli Mashtizadeh #define BORDER_BOTTOMSPLIT	7
93a53d2e4fSAli Mashtizadeh #define BORDER_RIGHTSPLIT	8
94a53d2e4fSAli Mashtizadeh #define BORDER_LEFTSPLIT	9
95a53d2e4fSAli Mashtizadeh #define BORDER_CROSS		10
96a53d2e4fSAli Mashtizadeh 
97a53d2e4fSAli Mashtizadeh static const char **border_char;
98a53d2e4fSAli Mashtizadeh 
99a53d2e4fSAli Mashtizadeh static const char *border_vt[] = {
100a53d2e4fSAli Mashtizadeh     "\xe2\x94\x80", "\xe2\x94\x82",
101a53d2e4fSAli Mashtizadeh     "\xe2\x94\x8c", "\xe2\x94\x90", "\xe2\x94\x94", "\xe2\x94\x98",
102a53d2e4fSAli Mashtizadeh     "\xe2\x94\xac", "\xe2\x94\xb4", "\xe2\x94\xa4", "\xe2\x94\x9c",
103a53d2e4fSAli Mashtizadeh     "\xe2\x94\xbc"
104a53d2e4fSAli Mashtizadeh };
105a53d2e4fSAli Mashtizadeh static const char *border_basic[] = { "-", "|", "+", "+", "+", "+", "+", "+", "+", "+" };
106a53d2e4fSAli Mashtizadeh 
107a53d2e4fSAli Mashtizadeh static char tc_boldbuf[16];
108a53d2e4fSAli Mashtizadeh static char tc_sgr0buf[16];
109a53d2e4fSAli Mashtizadeh static char tc_afbuf[16];
110a53d2e4fSAli Mashtizadeh static char tc_green[16];
111a53d2e4fSAli Mashtizadeh static char tc_yellow[16];
112a53d2e4fSAli Mashtizadeh static char tc_red[16];
113a53d2e4fSAli Mashtizadeh 
114a53d2e4fSAli Mashtizadeh static const char *TC_BOLD;
115a53d2e4fSAli Mashtizadeh static const char *TC_SGR0;
116a53d2e4fSAli Mashtizadeh static const char *TC_SETAF;
117a53d2e4fSAli Mashtizadeh 
118a53d2e4fSAli Mashtizadeh void
tcemit(const char * c)119a53d2e4fSAli Mashtizadeh tcemit(const char *c)
120a53d2e4fSAli Mashtizadeh {
121a53d2e4fSAli Mashtizadeh 	if (c)
122a53d2e4fSAli Mashtizadeh 		tputs(c, 1, putchar);
123a53d2e4fSAli Mashtizadeh }
124a53d2e4fSAli Mashtizadeh 
125a53d2e4fSAli Mashtizadeh void
tcfg(int n)126a53d2e4fSAli Mashtizadeh tcfg(int n)
127a53d2e4fSAli Mashtizadeh {
128a53d2e4fSAli Mashtizadeh 	if (TC_SETAF)
129a53d2e4fSAli Mashtizadeh 		tputs(tgoto(TC_SETAF, 0, n), 1, putchar);
130a53d2e4fSAli Mashtizadeh }
131a53d2e4fSAli Mashtizadeh 
132a53d2e4fSAli Mashtizadeh static void
setup_termcap(const char * term)133a53d2e4fSAli Mashtizadeh setup_termcap(const char *term)
134a53d2e4fSAli Mashtizadeh {
135a53d2e4fSAli Mashtizadeh 	bool xcolor;
136a53d2e4fSAli Mashtizadeh 	char *buf;
137a53d2e4fSAli Mashtizadeh 	char *clicolor;
138a53d2e4fSAli Mashtizadeh 	char termcapbuf[4096];
139a53d2e4fSAli Mashtizadeh 
140a53d2e4fSAli Mashtizadeh 	if (tgetent(termcapbuf, "xterm-256color") != 1) {
141a53d2e4fSAli Mashtizadeh 		TC_BOLD = NULL;
142a53d2e4fSAli Mashtizadeh 		TC_SGR0 = NULL;
143a53d2e4fSAli Mashtizadeh 		TC_SETAF = NULL;
144a53d2e4fSAli Mashtizadeh 		return;
145a53d2e4fSAli Mashtizadeh 	}
146a53d2e4fSAli Mashtizadeh 
147a53d2e4fSAli Mashtizadeh 	buf = tc_boldbuf;
148a53d2e4fSAli Mashtizadeh 	TC_BOLD = tgetstr("md", &buf);
149a53d2e4fSAli Mashtizadeh 	buf = tc_sgr0buf;
150a53d2e4fSAli Mashtizadeh 	TC_SGR0 = tgetstr("me", &buf);
151a53d2e4fSAli Mashtizadeh 	buf = tc_afbuf;
152a53d2e4fSAli Mashtizadeh 	TC_SETAF = tgetstr("AF", &buf);
153a53d2e4fSAli Mashtizadeh 
154a53d2e4fSAli Mashtizadeh 	clicolor = getenv("CLICOLOR");
155a53d2e4fSAli Mashtizadeh 	if (clicolor == NULL || strcmp(clicolor, "1") != 0) {
156a53d2e4fSAli Mashtizadeh 		return;
157a53d2e4fSAli Mashtizadeh 	}
158a53d2e4fSAli Mashtizadeh 
159a53d2e4fSAli Mashtizadeh 	if (strcmp(term, "xterm-256color") == 0) {
160a53d2e4fSAli Mashtizadeh 		xcolor = true;
161a53d2e4fSAli Mashtizadeh 	} else {
162a53d2e4fSAli Mashtizadeh 		xcolor = false;
163a53d2e4fSAli Mashtizadeh 	}
164a53d2e4fSAli Mashtizadeh 
165a53d2e4fSAli Mashtizadeh 	buf = tgoto(TC_SETAF, 1, xcolor ? COLOR256_GREEN : COLOR_GREEN);
166a53d2e4fSAli Mashtizadeh 	if (buf) {
167a53d2e4fSAli Mashtizadeh 		strlcpy(tc_green, buf, sizeof(tc_green));
168a53d2e4fSAli Mashtizadeh 	} else {
169a53d2e4fSAli Mashtizadeh 		tc_green[0] = '\0';
170a53d2e4fSAli Mashtizadeh 	}
171a53d2e4fSAli Mashtizadeh 
172a53d2e4fSAli Mashtizadeh 	buf = tgoto(TC_SETAF, 1, xcolor ? COLOR256_YELLOW : COLOR_YELLOW);
173a53d2e4fSAli Mashtizadeh 	if (buf) {
174a53d2e4fSAli Mashtizadeh 		strlcpy(tc_yellow, buf, sizeof(tc_yellow));
175a53d2e4fSAli Mashtizadeh 	} else {
176a53d2e4fSAli Mashtizadeh 		tc_yellow[0] = '\0';
177a53d2e4fSAli Mashtizadeh 	}
178a53d2e4fSAli Mashtizadeh 
179a53d2e4fSAli Mashtizadeh 	buf = tgoto(TC_SETAF, 1, xcolor ? COLOR256_RED : COLOR_RED);
180a53d2e4fSAli Mashtizadeh 	if (buf) {
181a53d2e4fSAli Mashtizadeh 		strlcpy(tc_red, buf, sizeof(tc_red));
182a53d2e4fSAli Mashtizadeh 	} else {
183a53d2e4fSAli Mashtizadeh 		tc_red[0] = '\0';
184a53d2e4fSAli Mashtizadeh 	}
185a53d2e4fSAli Mashtizadeh }
186a53d2e4fSAli Mashtizadeh 
187a53d2e4fSAli Mashtizadeh void
setup_screen()188a53d2e4fSAli Mashtizadeh setup_screen()
189a53d2e4fSAli Mashtizadeh {
190a53d2e4fSAli Mashtizadeh 	int status;
191a53d2e4fSAli Mashtizadeh 	const char *term;
192a53d2e4fSAli Mashtizadeh 	struct winsize wsz;
193a53d2e4fSAli Mashtizadeh 
194a53d2e4fSAli Mashtizadeh 	/* Sane defaults */
195a53d2e4fSAli Mashtizadeh 	tmode = TERMMODE_BASIC;
196a53d2e4fSAli Mashtizadeh 	title_char = title_basic;
197a53d2e4fSAli Mashtizadeh 	arrow_char = arrow_basic;
198a53d2e4fSAli Mashtizadeh 	border_char = border_basic;
199a53d2e4fSAli Mashtizadeh 	disp_height = 25;
200a53d2e4fSAli Mashtizadeh 	disp_width = 80;
201a53d2e4fSAli Mashtizadeh 
202a53d2e4fSAli Mashtizadeh 	if (isatty(STDOUT_FILENO) == 0) {
203a53d2e4fSAli Mashtizadeh 		tmode = TERMMODE_XTERM;
204a53d2e4fSAli Mashtizadeh 		title_char = title_vt;
205a53d2e4fSAli Mashtizadeh 		arrow_char = arrow_vt;
206a53d2e4fSAli Mashtizadeh 		border_char = border_vt;
207a53d2e4fSAli Mashtizadeh 		return;
208a53d2e4fSAli Mashtizadeh 	}
209a53d2e4fSAli Mashtizadeh 
210a53d2e4fSAli Mashtizadeh 	status = ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz);
211a53d2e4fSAli Mashtizadeh 	if (status < 0) {
212a53d2e4fSAli Mashtizadeh 		perror("ioctl(TIOCGWINSZ)");
213a53d2e4fSAli Mashtizadeh 		return;
214a53d2e4fSAli Mashtizadeh 	}
215a53d2e4fSAli Mashtizadeh 
216a53d2e4fSAli Mashtizadeh 	disp_height = wsz.ws_row;
217a53d2e4fSAli Mashtizadeh 	disp_width = wsz.ws_col;
218a53d2e4fSAli Mashtizadeh 
219a53d2e4fSAli Mashtizadeh 	term = getenv("TERM");
220a53d2e4fSAli Mashtizadeh 	if (term == NULL) {
221a53d2e4fSAli Mashtizadeh 		tmode = TERMMODE_BASIC;
222a53d2e4fSAli Mashtizadeh 		title_char = title_basic;
223a53d2e4fSAli Mashtizadeh 		arrow_char = arrow_basic;
224a53d2e4fSAli Mashtizadeh 		border_char = border_basic;
225a53d2e4fSAli Mashtizadeh 		return;
226a53d2e4fSAli Mashtizadeh 	}
227a53d2e4fSAli Mashtizadeh 
228a53d2e4fSAli Mashtizadeh 	if (strncmp(term, "xterm", 5) == 0) {
229a53d2e4fSAli Mashtizadeh 		tmode = TERMMODE_XTERM;
230a53d2e4fSAli Mashtizadeh 		title_char = title_vt;
231a53d2e4fSAli Mashtizadeh 		arrow_char = arrow_vt;
232a53d2e4fSAli Mashtizadeh 		border_char = border_vt;
233a53d2e4fSAli Mashtizadeh 	} else if (strncmp(term, "vt", 2) == 0) {
234a53d2e4fSAli Mashtizadeh 		tmode = TERMMODE_VT;
235a53d2e4fSAli Mashtizadeh 		title_char = title_vt;
236a53d2e4fSAli Mashtizadeh 		arrow_char = arrow_vt;
237a53d2e4fSAli Mashtizadeh 		border_char = border_vt;
238a53d2e4fSAli Mashtizadeh 	}
239a53d2e4fSAli Mashtizadeh 
240a53d2e4fSAli Mashtizadeh 	setup_termcap(term);
241a53d2e4fSAli Mashtizadeh }
242a53d2e4fSAli Mashtizadeh 
243a53d2e4fSAli Mashtizadeh void
title(const std::string & t)244a53d2e4fSAli Mashtizadeh title(const std::string &t)
245a53d2e4fSAli Mashtizadeh {
246a53d2e4fSAli Mashtizadeh 	size_t i;
247a53d2e4fSAli Mashtizadeh 
248a53d2e4fSAli Mashtizadeh 	if (tmode != TERMMODE_BASIC)
249a53d2e4fSAli Mashtizadeh 		tcemit(TC_BOLD);
250a53d2e4fSAli Mashtizadeh 	printf("%s\n", t.c_str());
251a53d2e4fSAli Mashtizadeh 	if (tmode != TERMMODE_BASIC)
252a53d2e4fSAli Mashtizadeh 		tcemit(TC_SGR0);
253a53d2e4fSAli Mashtizadeh 	for (i = 0; i < t.size(); i++)
254a53d2e4fSAli Mashtizadeh 		printf("%s", title_char[TITLE_TITLE]);
255a53d2e4fSAli Mashtizadeh 	printf("\n\n");
256a53d2e4fSAli Mashtizadeh }
257a53d2e4fSAli Mashtizadeh 
258a53d2e4fSAli Mashtizadeh void
header(const std::string & h)259a53d2e4fSAli Mashtizadeh header(const std::string &h)
260a53d2e4fSAli Mashtizadeh {
261a53d2e4fSAli Mashtizadeh 	size_t i;
262a53d2e4fSAli Mashtizadeh 
263a53d2e4fSAli Mashtizadeh 	if (tmode != TERMMODE_BASIC)
264a53d2e4fSAli Mashtizadeh 		tcemit(TC_BOLD);
265a53d2e4fSAli Mashtizadeh 	printf("%s\n", h.c_str());
266a53d2e4fSAli Mashtizadeh 	if (tmode != TERMMODE_BASIC)
267a53d2e4fSAli Mashtizadeh 		tcemit(TC_SGR0);
268a53d2e4fSAli Mashtizadeh 	for (i = 0; i < h.size(); i++)
269a53d2e4fSAli Mashtizadeh 		printf("%s", title_char[TITLE_SUBTITLE]);
270a53d2e4fSAli Mashtizadeh 	printf("\n");
271a53d2e4fSAli Mashtizadeh }
272a53d2e4fSAli Mashtizadeh 
273a53d2e4fSAli Mashtizadeh void
printval(const std::string & msg,uint64_t val,siunit ui)274a53d2e4fSAli Mashtizadeh printval(const std::string &msg, uint64_t val, siunit ui)
275a53d2e4fSAli Mashtizadeh {
276a53d2e4fSAli Mashtizadeh 	switch (ui) {
277a53d2e4fSAli Mashtizadeh 	case siunit::seconds:
278a53d2e4fSAli Mashtizadeh 		printf("%s: %" PRIu64 " s\n", msg.c_str(), val);
279a53d2e4fSAli Mashtizadeh 		break;
280a53d2e4fSAli Mashtizadeh 	case siunit::percent:
281a53d2e4fSAli Mashtizadeh 		printf("%s: %" PRIu64 "%%\n", msg.c_str(), val);
282a53d2e4fSAli Mashtizadeh 		break;
283a53d2e4fSAli Mashtizadeh 	case siunit::cycles:
284a53d2e4fSAli Mashtizadeh 		printf("%s: %" PRIu64 " cycles\n", msg.c_str(), val);
285a53d2e4fSAli Mashtizadeh 		break;
286a53d2e4fSAli Mashtizadeh 	}
287a53d2e4fSAli Mashtizadeh }
288a53d2e4fSAli Mashtizadeh 
289a53d2e4fSAli Mashtizadeh void
printval(const std::string & msg,float val,siunit ui)290a53d2e4fSAli Mashtizadeh printval(const std::string &msg, float val, siunit ui)
291a53d2e4fSAli Mashtizadeh {
292a53d2e4fSAli Mashtizadeh 	switch (ui) {
293a53d2e4fSAli Mashtizadeh 	case siunit::seconds:
294a53d2e4fSAli Mashtizadeh 		printf("%s: %.2f s\n", msg.c_str(), val);
295a53d2e4fSAli Mashtizadeh 		break;
296a53d2e4fSAli Mashtizadeh 	case siunit::percent:
297a53d2e4fSAli Mashtizadeh 		printf("%s: %6.2f%%\n", msg.c_str(), val);
298a53d2e4fSAli Mashtizadeh 		break;
299a53d2e4fSAli Mashtizadeh 	case siunit::cycles:
300a53d2e4fSAli Mashtizadeh 		printf("%s: %.2f cycles\n", msg.c_str(), val);
301a53d2e4fSAli Mashtizadeh 		break;
302a53d2e4fSAli Mashtizadeh 	}
303a53d2e4fSAli Mashtizadeh }
304a53d2e4fSAli Mashtizadeh 
305a53d2e4fSAli Mashtizadeh std::string
format_siprefix(uint64_t count)306a53d2e4fSAli Mashtizadeh format_siprefix(uint64_t count)
307a53d2e4fSAli Mashtizadeh {
308a53d2e4fSAli Mashtizadeh 	unsigned long index = 0;
309a53d2e4fSAli Mashtizadeh 	char prefix[] = " kMGTP";
310a53d2e4fSAli Mashtizadeh 	char buf[10];
311a53d2e4fSAli Mashtizadeh 
312a53d2e4fSAli Mashtizadeh 	while (count > 10000 && index < sizeof(prefix)) {
313a53d2e4fSAli Mashtizadeh 		index++;
314a53d2e4fSAli Mashtizadeh 		count /= 1000;
315a53d2e4fSAli Mashtizadeh 	}
316a53d2e4fSAli Mashtizadeh 
317a53d2e4fSAli Mashtizadeh 	if (index == 0) {
318a53d2e4fSAli Mashtizadeh 		snprintf(buf, sizeof(buf), "%" PRIu64, count);
319a53d2e4fSAli Mashtizadeh 	} else {
320a53d2e4fSAli Mashtizadeh 		snprintf(buf, sizeof(buf), "%" PRIu64 "%c", count, prefix[index]);
321a53d2e4fSAli Mashtizadeh 	}
322a53d2e4fSAli Mashtizadeh 
323a53d2e4fSAli Mashtizadeh 	return buf;
324a53d2e4fSAli Mashtizadeh }
325a53d2e4fSAli Mashtizadeh 
326a53d2e4fSAli Mashtizadeh std::string
format_binprefix(uint64_t count)327a53d2e4fSAli Mashtizadeh format_binprefix(uint64_t count)
328a53d2e4fSAli Mashtizadeh {
329a53d2e4fSAli Mashtizadeh 	unsigned long index = 0;
330a53d2e4fSAli Mashtizadeh 	char prefix[] = " KMGTP";
331a53d2e4fSAli Mashtizadeh 	char buf[10];
332a53d2e4fSAli Mashtizadeh 
333a53d2e4fSAli Mashtizadeh 	while (count > 10000 && index < sizeof(prefix)) {
334a53d2e4fSAli Mashtizadeh 		index++;
335a53d2e4fSAli Mashtizadeh 		count /= 1000;
336a53d2e4fSAli Mashtizadeh 	}
337a53d2e4fSAli Mashtizadeh 
338a53d2e4fSAli Mashtizadeh 	if (index == 0) {
339a53d2e4fSAli Mashtizadeh 		snprintf(buf, sizeof(buf), "%" PRIu64 " B", count);
340a53d2e4fSAli Mashtizadeh 	} else {
341a53d2e4fSAli Mashtizadeh 		snprintf(buf, sizeof(buf), "%" PRIu64 " %ciB", count, prefix[index]);
342a53d2e4fSAli Mashtizadeh 	}
343a53d2e4fSAli Mashtizadeh 
344a53d2e4fSAli Mashtizadeh 	return buf;
345a53d2e4fSAli Mashtizadeh }
346a53d2e4fSAli Mashtizadeh 
347a53d2e4fSAli Mashtizadeh std::string
format_sample(uint64_t count,uint64_t total)348a53d2e4fSAli Mashtizadeh format_sample(uint64_t count, uint64_t total)
349a53d2e4fSAli Mashtizadeh {
350a53d2e4fSAli Mashtizadeh 	char buf[20];
351a53d2e4fSAli Mashtizadeh 	std::string fmt = format_siprefix(count);
352a53d2e4fSAli Mashtizadeh 
353a53d2e4fSAli Mashtizadeh 	snprintf(buf, sizeof(buf), "%5s (%4.1f%%)", fmt.c_str(),
354a53d2e4fSAli Mashtizadeh 	    100.0 * (float)count / (float)total);
355a53d2e4fSAli Mashtizadeh 
356a53d2e4fSAli Mashtizadeh 	return buf;
357a53d2e4fSAli Mashtizadeh }
358a53d2e4fSAli Mashtizadeh 
359a53d2e4fSAli Mashtizadeh std::string
format_percent(uint64_t count,uint64_t total)360a53d2e4fSAli Mashtizadeh format_percent(uint64_t count, uint64_t total)
361a53d2e4fSAli Mashtizadeh {
362a53d2e4fSAli Mashtizadeh 	char buf[20];
363a53d2e4fSAli Mashtizadeh 
364a53d2e4fSAli Mashtizadeh 	if (total == 0) {
365a53d2e4fSAli Mashtizadeh 		snprintf(buf, sizeof(buf), "%6s", "");
366a53d2e4fSAli Mashtizadeh 	} else {
367a53d2e4fSAli Mashtizadeh 		snprintf(buf, sizeof(buf), "%5.1f%%",
368a53d2e4fSAli Mashtizadeh 		    100.0 * (float)count / (float)total);
369a53d2e4fSAli Mashtizadeh 	}
370a53d2e4fSAli Mashtizadeh 
371a53d2e4fSAli Mashtizadeh 	return buf;
372a53d2e4fSAli Mashtizadeh }
373a53d2e4fSAli Mashtizadeh 
374a53d2e4fSAli Mashtizadeh /* fields */
375a53d2e4fSAli Mashtizadeh 
field(int64_t v)376a53d2e4fSAli Mashtizadeh field::field(int64_t v)
377a53d2e4fSAli Mashtizadeh {
378a53d2e4fSAli Mashtizadeh 	type = FIELD::INTEGER;
379a53d2e4fSAli Mashtizadeh 	value[0] = v;
380a53d2e4fSAli Mashtizadeh }
381a53d2e4fSAli Mashtizadeh 
field(float v)382a53d2e4fSAli Mashtizadeh field::field(float v)
383a53d2e4fSAli Mashtizadeh {
384a53d2e4fSAli Mashtizadeh 	type = FIELD::FLOAT;
385a53d2e4fSAli Mashtizadeh 	fvalue = v;
386a53d2e4fSAli Mashtizadeh }
387a53d2e4fSAli Mashtizadeh 
field(const std::string & s)388a53d2e4fSAli Mashtizadeh field::field(const std::string &s)
389a53d2e4fSAli Mashtizadeh {
390a53d2e4fSAli Mashtizadeh 	type = FIELD::STRING;
391a53d2e4fSAli Mashtizadeh 	svalue = s;
392a53d2e4fSAli Mashtizadeh }
393a53d2e4fSAli Mashtizadeh 
field(int64_t c,int64_t t,bool percent)394a53d2e4fSAli Mashtizadeh field::field(int64_t c, int64_t t, bool percent)
395a53d2e4fSAli Mashtizadeh {
396a53d2e4fSAli Mashtizadeh 	if (percent)
397a53d2e4fSAli Mashtizadeh 		type = FIELD::PERCENT;
398a53d2e4fSAli Mashtizadeh 	else
399a53d2e4fSAli Mashtizadeh 		type = FIELD::SAMPLE;
400a53d2e4fSAli Mashtizadeh 	value[0] = c;
401a53d2e4fSAli Mashtizadeh 	value[1] = t;
402a53d2e4fSAli Mashtizadeh }
403a53d2e4fSAli Mashtizadeh 
field(int64_t uc,int64_t ut,int64_t ku,int64_t kt)404a53d2e4fSAli Mashtizadeh field::field(int64_t uc, int64_t ut, int64_t ku, int64_t kt)
405a53d2e4fSAli Mashtizadeh {
406a53d2e4fSAli Mashtizadeh 	type = FIELD::DSAMPLE;
407a53d2e4fSAli Mashtizadeh 	value[0] = uc;
408a53d2e4fSAli Mashtizadeh 	value[1] = ut;
409a53d2e4fSAli Mashtizadeh 	value[2] = ku;
410a53d2e4fSAli Mashtizadeh 	value[3] = kt;
411a53d2e4fSAli Mashtizadeh }
412a53d2e4fSAli Mashtizadeh 
413a53d2e4fSAli Mashtizadeh bool
operator >(const field & b) const414a53d2e4fSAli Mashtizadeh field::operator>(const field &b) const
415a53d2e4fSAli Mashtizadeh {
416a53d2e4fSAli Mashtizadeh 	switch (type) {
417a53d2e4fSAli Mashtizadeh 	case FIELD::STRING:
418a53d2e4fSAli Mashtizadeh 		return (svalue > b.svalue);
419a53d2e4fSAli Mashtizadeh 	case FIELD::FLOAT:
420a53d2e4fSAli Mashtizadeh 		return (fvalue > b.fvalue);
421a53d2e4fSAli Mashtizadeh 	case FIELD::INTEGER: [[fallthrough]];
422a53d2e4fSAli Mashtizadeh 	case FIELD::SAMPLE:
423a53d2e4fSAli Mashtizadeh 		return (value[0] > b.value[0]);
424a53d2e4fSAli Mashtizadeh 	case FIELD::DSAMPLE:
425a53d2e4fSAli Mashtizadeh 		return ((value[0] + value[2]) > (b.value[0] + b.value[2]));
426a53d2e4fSAli Mashtizadeh 	case FIELD::PERCENT:
427a53d2e4fSAli Mashtizadeh 		return (((float)value[0] / (float)value[1]) >
428a53d2e4fSAli Mashtizadeh 		    ((float)b.value[0] / (float)b.value[1]));
429a53d2e4fSAli Mashtizadeh 	}
430a53d2e4fSAli Mashtizadeh }
431a53d2e4fSAli Mashtizadeh 
432a53d2e4fSAli Mashtizadeh bool
operator <(const field & b) const433a53d2e4fSAli Mashtizadeh field::operator<(const field &b) const
434a53d2e4fSAli Mashtizadeh {
435a53d2e4fSAli Mashtizadeh 	switch (type) {
436a53d2e4fSAli Mashtizadeh 	case FIELD::STRING:
437a53d2e4fSAli Mashtizadeh 		return (svalue < b.svalue);
438a53d2e4fSAli Mashtizadeh 	case FIELD::FLOAT:
439a53d2e4fSAli Mashtizadeh 		return (fvalue < b.fvalue);
440a53d2e4fSAli Mashtizadeh 	case FIELD::INTEGER: [[fallthrough]];
441a53d2e4fSAli Mashtizadeh 	case FIELD::SAMPLE:
442a53d2e4fSAli Mashtizadeh 		return (value[0] < b.value[0]);
443a53d2e4fSAli Mashtizadeh 	case FIELD::DSAMPLE:
444a53d2e4fSAli Mashtizadeh 		return ((value[0] + value[2]) < (b.value[0] + b.value[2]));
445a53d2e4fSAli Mashtizadeh 	case FIELD::PERCENT:
446a53d2e4fSAli Mashtizadeh 		return (((float)value[0] / (float)value[1]) <
447a53d2e4fSAli Mashtizadeh 		    ((float)b.value[0] / (float)b.value[1]));
448a53d2e4fSAli Mashtizadeh 	}
449a53d2e4fSAli Mashtizadeh }
450a53d2e4fSAli Mashtizadeh 
451a53d2e4fSAli Mashtizadeh size_t
length()452a53d2e4fSAli Mashtizadeh field::length()
453a53d2e4fSAli Mashtizadeh {
454a53d2e4fSAli Mashtizadeh 	return (to_string().length());
455a53d2e4fSAli Mashtizadeh }
456a53d2e4fSAli Mashtizadeh 
457a53d2e4fSAli Mashtizadeh std::string
to_string()458a53d2e4fSAli Mashtizadeh field::to_string()
459a53d2e4fSAli Mashtizadeh {
460a53d2e4fSAli Mashtizadeh 	switch (type) {
461a53d2e4fSAli Mashtizadeh 	case FIELD::STRING:
462a53d2e4fSAli Mashtizadeh 		return (svalue);
463a53d2e4fSAli Mashtizadeh 	case FIELD::FLOAT:
464a53d2e4fSAli Mashtizadeh 		return (std::to_string(fvalue));
465a53d2e4fSAli Mashtizadeh 	case FIELD::INTEGER:
466a53d2e4fSAli Mashtizadeh 		return (std::to_string(value[0]));
467a53d2e4fSAli Mashtizadeh 	case FIELD::SAMPLE:
468a53d2e4fSAli Mashtizadeh 		return (format_sample(value[0], value[1]));
469a53d2e4fSAli Mashtizadeh 	case FIELD::DSAMPLE:
470a53d2e4fSAli Mashtizadeh 		return (format_sample(value[0], value[1]) + " / " +
471a53d2e4fSAli Mashtizadeh 		    format_sample(value[2], value[3]));
472a53d2e4fSAli Mashtizadeh 	case FIELD::PERCENT:
473a53d2e4fSAli Mashtizadeh 		return (format_percent(value[0], value[1]));
474a53d2e4fSAli Mashtizadeh 	}
475a53d2e4fSAli Mashtizadeh }
476a53d2e4fSAli Mashtizadeh 
477a53d2e4fSAli Mashtizadeh /* table class */
478a53d2e4fSAli Mashtizadeh 
table()479a53d2e4fSAli Mashtizadeh table::table() : sortcol(-1), sortdir(true), cols(), align(), rows()
480a53d2e4fSAli Mashtizadeh {
481a53d2e4fSAli Mashtizadeh }
482a53d2e4fSAli Mashtizadeh 
~table()483a53d2e4fSAli Mashtizadeh table::~table()
484a53d2e4fSAli Mashtizadeh {
485a53d2e4fSAli Mashtizadeh }
486a53d2e4fSAli Mashtizadeh 
487a53d2e4fSAli Mashtizadeh void
addcolumn(const std::string & c,bool alignleft)488a53d2e4fSAli Mashtizadeh table::addcolumn(const std::string &c, bool alignleft)
489a53d2e4fSAli Mashtizadeh {
490a53d2e4fSAli Mashtizadeh 	cols.push_back(c);
491a53d2e4fSAli Mashtizadeh 	align.push_back(alignleft);
492a53d2e4fSAli Mashtizadeh }
493a53d2e4fSAli Mashtizadeh 
494a53d2e4fSAli Mashtizadeh void
addrow(std::vector<field> r)495a53d2e4fSAli Mashtizadeh table::addrow(std::vector<field> r)
496a53d2e4fSAli Mashtizadeh {
497a53d2e4fSAli Mashtizadeh 	rows.push_back(std::move(r));
498a53d2e4fSAli Mashtizadeh }
499a53d2e4fSAli Mashtizadeh 
500a53d2e4fSAli Mashtizadeh void
sort(int col,bool descending)501a53d2e4fSAli Mashtizadeh table::sort(int col, bool descending)
502a53d2e4fSAli Mashtizadeh {
503a53d2e4fSAli Mashtizadeh 	sortcol = col;
504a53d2e4fSAli Mashtizadeh 	sortdir = descending;
505a53d2e4fSAli Mashtizadeh 	if (descending)
506a53d2e4fSAli Mashtizadeh 		std::sort(rows.begin(), rows.end(), [col](auto &a, auto &b)
507a53d2e4fSAli Mashtizadeh 		    { return (a[col] > b[col]); });
508a53d2e4fSAli Mashtizadeh 	else
509a53d2e4fSAli Mashtizadeh 		std::sort(rows.begin(), rows.end(), [col](auto &a, auto &b)
510a53d2e4fSAli Mashtizadeh 		    { return (a[col] < b[col]); });
511a53d2e4fSAli Mashtizadeh }
512a53d2e4fSAli Mashtizadeh 
513a53d2e4fSAli Mashtizadeh void
print()514a53d2e4fSAli Mashtizadeh table::print()
515a53d2e4fSAli Mashtizadeh {
516a53d2e4fSAli Mashtizadeh 	unsigned long c;
517a53d2e4fSAli Mashtizadeh 	std::vector<unsigned long> width;
518a53d2e4fSAli Mashtizadeh 
519a53d2e4fSAli Mashtizadeh 	// Compute column width
520a53d2e4fSAli Mashtizadeh 	for (auto h : cols)
521a53d2e4fSAli Mashtizadeh 		width.push_back(h.length() + 1);
522a53d2e4fSAli Mashtizadeh 	if (sortcol != -1)
523a53d2e4fSAli Mashtizadeh 		width[sortcol] += 1;
524a53d2e4fSAli Mashtizadeh 	for (auto &r : rows) {
525a53d2e4fSAli Mashtizadeh 		for (c = 0; c < r.size(); c++) {
526a53d2e4fSAli Mashtizadeh 			if (width[c] < r[c].length())
527a53d2e4fSAli Mashtizadeh 				width[c] = r[c].length();
528a53d2e4fSAli Mashtizadeh 		}
529a53d2e4fSAli Mashtizadeh 	}
530a53d2e4fSAli Mashtizadeh 
531a53d2e4fSAli Mashtizadeh 	for (c = 0; c < width.size(); c++) {
532a53d2e4fSAli Mashtizadeh 		if (align[c])
533a53d2e4fSAli Mashtizadeh 			std::cout << std::left;
534a53d2e4fSAli Mashtizadeh 		std::cout << std::setw(width[c]);
535a53d2e4fSAli Mashtizadeh 		tcemit(TC_BOLD);
536a53d2e4fSAli Mashtizadeh 		if (c == (unsigned long)sortcol)
537a53d2e4fSAli Mashtizadeh 		    std::cout << cols[c] + " " + arrow_char[sortdir ? ARROW_DOWN : ARROW_UP];
538a53d2e4fSAli Mashtizadeh 		else
539a53d2e4fSAli Mashtizadeh 		    std::cout << cols[c];
540a53d2e4fSAli Mashtizadeh 		tcemit(TC_SGR0);
541a53d2e4fSAli Mashtizadeh 		if (c != (width.size() - 1))
542a53d2e4fSAli Mashtizadeh 			std::cout << " " << border_char[BORDER_VBAR] << " ";
543a53d2e4fSAli Mashtizadeh 		if (align[c])
544a53d2e4fSAli Mashtizadeh 			std::cout << std::right;
545a53d2e4fSAli Mashtizadeh 	}
546a53d2e4fSAli Mashtizadeh 	std::cout << std::endl;
547a53d2e4fSAli Mashtizadeh 
548a53d2e4fSAli Mashtizadeh 	for (c = 0; c < width.size(); c++) {
549a53d2e4fSAli Mashtizadeh 		for (unsigned long i = 0; i < width[c]; i++)
550a53d2e4fSAli Mashtizadeh 		    std::cout << border_char[BORDER_HBAR];
551a53d2e4fSAli Mashtizadeh 		if (c != (width.size() - 1))
552a53d2e4fSAli Mashtizadeh 			std::cout << border_char[BORDER_HBAR] <<
553a53d2e4fSAli Mashtizadeh 			    border_char[BORDER_CROSS] << border_char[BORDER_HBAR];
554a53d2e4fSAli Mashtizadeh 	}
555a53d2e4fSAli Mashtizadeh 	std::cout << std::setfill(' ') << std::endl;
556a53d2e4fSAli Mashtizadeh 
557a53d2e4fSAli Mashtizadeh 	for (auto &r : rows) {
558a53d2e4fSAli Mashtizadeh 		for (c = 0; c < width.size(); c++) {
559a53d2e4fSAli Mashtizadeh 			if (align[c])
560a53d2e4fSAli Mashtizadeh 				std::cout << std::left;
561a53d2e4fSAli Mashtizadeh 			std::cout << std::setw(width[c]) << r[c].to_string();
562a53d2e4fSAli Mashtizadeh 			if (c != (width.size() - 1))
563a53d2e4fSAli Mashtizadeh 				std::cout << " " << border_char[BORDER_VBAR] << " ";
564a53d2e4fSAli Mashtizadeh 			if (align[c])
565a53d2e4fSAli Mashtizadeh 				std::cout << std::right;
566a53d2e4fSAli Mashtizadeh 		}
567a53d2e4fSAli Mashtizadeh 		std::cout << std::endl;
568a53d2e4fSAli Mashtizadeh 	}
569a53d2e4fSAli Mashtizadeh 
570a53d2e4fSAli Mashtizadeh 	std::cout << std::endl;
571a53d2e4fSAli Mashtizadeh }
572a53d2e4fSAli Mashtizadeh 
573