Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177479031
D58927.1794524445.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
27 KB
Referenced Files
None
Subscribers
None
D58927.1794524445.diff
View Options
diff --git a/usr.sbin/pmc/Makefile b/usr.sbin/pmc/Makefile
--- a/usr.sbin/pmc/Makefile
+++ b/usr.sbin/pmc/Makefile
@@ -7,7 +7,7 @@
CFLAGS+= -I${SRCTOP}/lib/libpmcstat
CXXSTD= c++20
-LIBADD= pmc m util ncursesw pmcstat elf dwarf
+LIBADD= pmc m util ncursesw tinfow pmcstat elf dwarf
SRCS= pmc.c pmc_util.c cmd_pmc_stat.c \
cmd_pmc_list.c cmd_pmc_filter.cc \
diff --git a/usr.sbin/pmc/display.hh b/usr.sbin/pmc/display.hh
--- a/usr.sbin/pmc/display.hh
+++ b/usr.sbin/pmc/display.hh
@@ -33,7 +33,16 @@
#define COLOR256_RED 196
#define COLOR256_YELLOW 226
-void setup_screen();
+void setup_screen(bool interactive = false);
+
+/*
+ * Select the output mode ("console", "tui", or "html"). An explicit mode is
+ * used if the command supports it, otherwise a warning is printed and the
+ * first supported mode (the default) is used. If no mode is given, the
+ * PMC_FORMAT environment variable is consulted.
+ */
+std::string display_getmode(std::string mode,
+ std::vector<std::string> supported_modes);
/* Formatting */
std::string format_siprefix(uint64_t count);
@@ -41,6 +50,9 @@
std::string format_sample(uint64_t count, uint64_t total);
std::string format_percent(uint64_t count, uint64_t total);
+/* Ncurses */
+void message_box(const std::string &msg, const std::string &title = "");
+
/* Printing */
enum class siunit {
seconds,
@@ -95,11 +107,72 @@
void addrow(std::vector<field> r);
void sort(int col, bool descending = true);
void print();
+ void display(const std::string &title = "Table",
+ std::function<void(std::vector<field>)> cb = [](__unused std::vector<field> r){});
private:
+ void showhelp();
+ void layout();
+ void draw(const std::string &title);
+ void processmouse();
int sortcol;
bool sortdir;
std::vector<std::string> cols;
std::vector<bool> align;
std::vector<std::vector<field>> rows;
+ // ncurses
+ int row;
+ int col;
+ int scroll_row;
+ int scroll_col;
+ std::vector<int> width;
+ std::vector<int> col_offset;
+ std::vector<int> col_end;
};
+/* Graphs */
+class histogram
+{
+public:
+ histogram();
+ ~histogram();
+ void setxlabel(const std::string &lbl);
+ void setylabel(const std::string &lbl);
+ void setxaxis(int min, int max);
+ void setxcolors(const std::vector<int> &colors);
+ void addsample(int s);
+ int min();
+ int max();
+ int average();
+ void print();
+private:
+ int xmin, xmax;
+ std::string xlabel;
+ std::string ylabel;
+ std::vector<int> xcolors;
+ std::unordered_map<int, uint64_t> samples;
+};
+
+class bargraph
+{
+public:
+ bargraph();
+ ~bargraph();
+ void addcolumn(const std::string &c);
+ void addsample(const std::string &c, uint64_t s = 1);
+ void print();
+private:
+ std::vector<std::string> cols;
+ std::unordered_map<std::string, uint64_t> samples;
+};
+
+class bitfield
+{
+public:
+ bitfield();
+ ~bitfield();
+ void setwidth(int width);
+ void fill(uint64_t start, uint64_t length, const std::string &str);
+ void print();
+private:
+ std::vector<std::string> bits;
+};
diff --git a/usr.sbin/pmc/display.cc b/usr.sbin/pmc/display.cc
--- a/usr.sbin/pmc/display.cc
+++ b/usr.sbin/pmc/display.cc
@@ -32,22 +32,28 @@
#include <sys/ioctl.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
+#include <string.h>
#include <term.h>
#include <math.h>
+#define _XOPEN_SOURCE_EXTENDED
#include <curses.h>
+#include <sysexits.h>
+#include <term.h>
#include <unistd.h>
+#include <algorithm>
#include <cxxabi.h>
-#include <cstring>
#include <iomanip>
#include <ios>
#include <iostream>
#include <sstream>
#include <string>
+#include <unordered_map>
#include <vector>
#include "display.hh"
@@ -58,6 +64,7 @@
TERMMODE_XTERM
};
+static bool ncurses;
static termmode tmode;
static int disp_height;
static int disp_width;
@@ -104,6 +111,34 @@
};
static const char *border_basic[] = { "-", "|", "+", "+", "+", "+", "+", "+", "+", "+" };
+static const char *graph_fancy[5][5] = {
+ { " ", "\xe2\xa2\x80", "\xe2\xa2\xa0", "\xe2\xa2\xb0", "\xe2\xa2\xb8" },
+ { "\xe2\xa1\x80", "\xe2\xa3\x80", "\xe2\xa3\xa0", "\xe2\xa2\xb0", "\xe2\xa2\xb8" },
+ { "\xe2\xa1\x84", "\xe2\xa3\x84", "\xe2\xa3\xa4", "\xe2\xa3\xb4", "\xe2\xa3\xbc" },
+ { "\xe2\xa1\x86", "\xe2\xa3\x86", "\xe2\xa3\xa6", "\xe2\xa3\xb6", "\xe2\xa3\xbe" },
+ { "\xe2\xa1\x87", "\xe2\xa3\x87", "\xe2\xa3\xa7", "\xe2\xa3\xb7", "\xe2\xa3\xbf" },
+};
+static const char *graph_vt[5] = {
+ " ", "\xe2\x96\x91", "\xe2\x96\x92", "\xe2\x96\x93", "\xe2\x96\x88"
+};
+static const char *graph_basic[5] = { " ", " ", "*", "*", "*" };
+
+static const char **bar_char;
+
+static const char *bar_fancy[8] = {
+ "\xe2\x96\x8f", "\xe2\x96\x8e", "\xe2\x96\x8d", "\xe2\x96\x8c",
+ "\xe2\x96\x8b", "\xe2\x96\x8a", "\xe2\x96\x89", "\xe2\x96\x88"
+};
+
+static const char *bar_vt[8] = {
+ "\xe2\x96\x91", "\xe2\x96\x91", "\xe2\x96\x92", "\xe2\x96\x92",
+ "\xe2\x96\x93", "\xe2\x96\x93", "\xe2\x96\x88", "\xe2\x96\x88"
+};
+
+static const char *bar_basic[8] = {
+ " ", " ", " ", " ", "*", "*", "*", "*"
+};
+
static char tc_boldbuf[16];
static char tc_sgr0buf[16];
static char tc_afbuf[16];
@@ -129,6 +164,47 @@
tputs(tgoto(TC_SETAF, 0, n), 1, putchar);
}
+void
+setup_pager()
+{
+ int status;
+ int in[2];
+ char *pager;
+
+ pager = getenv("PAGER");
+ if (pager == NULL)
+ return;
+
+ status = pipe(in);
+ if (status != 0) {
+ perror("pipe");
+ return;
+ }
+
+ status = fork();
+ if (status < 0) {
+ close(in[0]);
+ close(in[1]);
+ perror("fork");
+ return;
+ }
+ if (status == 0) {
+ dup2(in[0], STDIN_FILENO);
+
+ while(tcgetpgrp(STDOUT_FILENO) != getpid())
+ sleep(0);
+
+ execl(pager, pager, "-F", NULL);
+ } else {
+ setpgid(status, 0);
+ tcsetpgrp(STDOUT_FILENO, status);
+
+ dup2(in[1], STDOUT_FILENO);
+
+
+ }
+}
+
static void
setup_termcap(const char *term)
{
@@ -185,17 +261,75 @@
}
void
-setup_screen()
+setup_curses()
+{
+ setlocale(LC_ALL, "");
+
+ /* Initialize ncurses */
+ initscr();
+ cbreak();
+ noecho();
+ keypad(stdscr, TRUE);
+ mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
+
+ start_color();
+ use_default_colors();
+ curs_set(0);
+
+ getmaxyx(stdscr, disp_height, disp_width);
+
+ /* Color pairs: 1=normal, 2=selected, 3=header, 4=sort arrow */
+ init_pair(1, COLOR_BLACK, COLOR_WHITE); // Normal
+ init_pair(2, COLOR_WHITE, COLOR_BLUE); // Title
+ init_pair(3, COLOR_WHITE, COLOR_RED); // Header
+ init_pair(4, COLOR_WHITE, COLOR_BLACK); // Selected
+
+ bkgd(COLOR_PAIR(1));
+ clear();
+ refresh();
+}
+
+std::string
+display_getmode(std::string mode, std::vector<std::string> supported_modes)
+{
+ const char *env;
+
+ if (supported_modes.empty())
+ return ("console");
+
+ if (mode.empty()) {
+ /* The environment is a preference, so ignore it if unsupported. */
+ env = getenv("PMC_FORMAT");
+ if (env != NULL && std::find(supported_modes.begin(),
+ supported_modes.end(), env) != supported_modes.end())
+ return (env);
+ return (supported_modes[0]);
+ }
+
+ if (std::find(supported_modes.begin(), supported_modes.end(), mode) !=
+ supported_modes.end())
+ return (mode);
+
+ fprintf(stderr, "WARNING: Output format \"%s\" is not supported by "
+ "this command; using \"%s\"\n", mode.c_str(),
+ supported_modes[0].c_str());
+ return (supported_modes[0]);
+}
+
+void
+setup_screen(bool interactive)
{
int status;
const char *term;
struct winsize wsz;
/* Sane defaults */
+ ncurses = interactive;
tmode = TERMMODE_BASIC;
title_char = title_basic;
arrow_char = arrow_basic;
border_char = border_basic;
+ bar_char = bar_basic;
disp_height = 25;
disp_width = 80;
@@ -204,6 +338,7 @@
title_char = title_vt;
arrow_char = arrow_vt;
border_char = border_vt;
+ bar_char = bar_fancy;
return;
}
@@ -222,6 +357,7 @@
title_char = title_basic;
arrow_char = arrow_basic;
border_char = border_basic;
+ bar_char = bar_basic;
return;
}
@@ -230,14 +366,47 @@
title_char = title_vt;
arrow_char = arrow_vt;
border_char = border_vt;
+ bar_char = bar_fancy;
} else if (strncmp(term, "vt", 2) == 0) {
tmode = TERMMODE_VT;
title_char = title_vt;
arrow_char = arrow_vt;
border_char = border_vt;
+ bar_char = bar_vt;
}
- setup_termcap(term);
+ if (interactive)
+ setup_curses();
+ else
+ setup_termcap(term);
+}
+
+void message_box(const std::string &msg, const std::string &title) {
+ int height = 7;
+ int width = msg.length() > 40 ? msg.length() + 6 : 46;
+ int start_y = (disp_height - height) / 2;
+ int start_x = (disp_width - width) / 2;
+
+ WINDOW *msgwin = newwin(height, width, start_y, start_x);
+ box(msgwin, 0, 0);
+
+ if (title != "") {
+ attron(A_BOLD);
+ mvwprintw(msgwin, 0, 2, "[ %.20s ]", title.c_str());
+ attroff(A_BOLD);
+ }
+
+ mvwprintw(msgwin, 3, 3, "%s", msg.c_str());
+ mvwprintw(msgwin, height - 2, (width - 15) / 2, "Press any key...");
+
+ wrefresh(msgwin);
+ getch();
+
+ // Clean up window
+ werase(msgwin);
+ wrefresh(msgwin);
+ delwin(msgwin);
+ refresh();
}
void
@@ -245,6 +414,9 @@
{
size_t i;
+ if (ncurses)
+ return;
+
if (tmode != TERMMODE_BASIC)
tcemit(TC_BOLD);
printf("%s\n", t.c_str());
@@ -260,6 +432,9 @@
{
size_t i;
+ if (ncurses)
+ return;
+
if (tmode != TERMMODE_BASIC)
tcemit(TC_BOLD);
printf("%s\n", h.c_str());
@@ -302,6 +477,27 @@
}
}
+#define BARWIDTH 40
+
+void
+printbar(float percent)
+{
+ int i;
+
+ printf("|");
+ for (i = 1; i <= BARWIDTH; i++) {
+ float bwidth = 100.0 / BARWIDTH;
+ float rem = percent - bwidth * i;
+ int idx = (rem > bwidth) ? 7 : ((int)(rem / bwidth));
+
+ if (rem <= 0)
+ printf(" ");
+ else
+ printf("%s", bar_char[idx]);
+ }
+ printf("|\n");
+}
+
std::string
format_siprefix(uint64_t count)
{
@@ -315,9 +511,9 @@
}
if (index == 0) {
- snprintf(buf, sizeof(buf), "%" PRIu64, count);
+ snprintf(buf, sizeof(buf), "%lu ", count);
} else {
- snprintf(buf, sizeof(buf), "%" PRIu64 "%c", count, prefix[index]);
+ snprintf(buf, sizeof(buf), "%lu%c", count, prefix[index]);
}
return buf;
@@ -336,9 +532,9 @@
}
if (index == 0) {
- snprintf(buf, sizeof(buf), "%" PRIu64 " B", count);
+ snprintf(buf, sizeof(buf), "%lu B", count);
} else {
- snprintf(buf, sizeof(buf), "%" PRIu64 " %ciB", count, prefix[index]);
+ snprintf(buf, sizeof(buf), "%lu %ciB", count, prefix[index]);
}
return buf;
@@ -476,7 +672,8 @@
/* table class */
-table::table() : sortcol(-1), sortdir(true), cols(), align(), rows()
+table::table() : sortcol(-1), sortdir(true), cols(), align(), rows(),
+ row(0), col(0), scroll_row(0), scroll_col(0), width(), col_offset()
{
}
@@ -498,42 +695,39 @@
}
void
-table::sort(int col, bool descending)
+table::sort(int scol, bool descending)
{
- sortcol = col;
+ sortcol = scol;
sortdir = descending;
if (descending)
- std::sort(rows.begin(), rows.end(), [col](auto &a, auto &b)
- { return (a[col] > b[col]); });
+ std::sort(rows.begin(), rows.end(), [scol](auto &a, auto &b)
+ { return (a[scol] > b[scol]); });
else
- std::sort(rows.begin(), rows.end(), [col](auto &a, auto &b)
- { return (a[col] < b[col]); });
+ std::sort(rows.begin(), rows.end(), [scol](auto &a, auto &b)
+ { return (a[scol] < b[scol]); });
}
void
table::print()
{
- unsigned long c;
- std::vector<unsigned long> width;
-
// Compute column width
for (auto h : cols)
width.push_back(h.length() + 1);
if (sortcol != -1)
width[sortcol] += 1;
for (auto &r : rows) {
- for (c = 0; c < r.size(); c++) {
- if (width[c] < r[c].length())
+ for (size_t c = 0; c < r.size(); c++) {
+ if (width[c] < (int)r[c].length())
width[c] = r[c].length();
}
}
- for (c = 0; c < width.size(); c++) {
+ for (size_t c = 0; c < width.size(); c++) {
if (align[c])
std::cout << std::left;
std::cout << std::setw(width[c]);
tcemit(TC_BOLD);
- if (c == (unsigned long)sortcol)
+ if ((int)c == sortcol)
std::cout << cols[c] + " " + arrow_char[sortdir ? ARROW_DOWN : ARROW_UP];
else
std::cout << cols[c];
@@ -545,8 +739,8 @@
}
std::cout << std::endl;
- for (c = 0; c < width.size(); c++) {
- for (unsigned long i = 0; i < width[c]; i++)
+ for (size_t c = 0; c < width.size(); c++) {
+ for (int i = 0; i < width[c]; i++)
std::cout << border_char[BORDER_HBAR];
if (c != (width.size() - 1))
std::cout << border_char[BORDER_HBAR] <<
@@ -555,7 +749,7 @@
std::cout << std::setfill(' ') << std::endl;
for (auto &r : rows) {
- for (c = 0; c < width.size(); c++) {
+ for (size_t c = 0; c < width.size(); c++) {
if (align[c])
std::cout << std::left;
std::cout << std::setw(width[c]) << r[c].to_string();
@@ -570,3 +764,705 @@
std::cout << std::endl;
}
+void
+table::layout()
+{
+ int ncols = cols.size();
+
+ /* Reserve line 0 for title, line 1 for headers */
+ int data_height = disp_height - 3;
+ if (data_height < 1)
+ data_height = 1;
+
+ /* Calculate column widths */
+ width.clear();
+ for (auto h : cols)
+ width.push_back(h.length() + 1);
+ if (sortcol != -1)
+ width[sortcol] += 1;
+ for (auto &r : rows) {
+ for (int i = 0; i < (int)r.size(); i++) {
+ if ((unsigned long)width[i] < r[i].length())
+ width[i] = r[i].length();
+ }
+ }
+
+ /* Compute column offsets for scrolling */
+ col_offset.clear();
+ col_end.clear();
+ int total_width = 0;
+ for (int i = 0; i < ncols; i++) {
+ col_offset.push_back(total_width);
+ total_width += width[i];
+ col_end.push_back(total_width);
+ if (i < ncols - 1)
+ total_width += 1;
+ }
+
+ /* Adjust row scroll to keep selected row visible */
+ if (row < scroll_row)
+ scroll_row = row;
+ if (row >= scroll_row + data_height)
+ scroll_row = row - data_height + 1;
+ if (scroll_row < 0)
+ scroll_row = 0;
+
+ /* Adjust column scroll to keep selected column visible */
+ if (col < scroll_col)
+ scroll_col = col;
+ while (scroll_col < ncols - 1 &&
+ col_offset[col] - scroll_col + width[col] > disp_width - 1)
+ scroll_col++;
+ if (scroll_col < 0)
+ scroll_col = 0;
+}
+
+void
+table::draw(const std::string &title)
+{
+ int ncols = cols.size(), nrows = rows.size();
+ int data_start = 2;
+ int data_height;
+
+ data_height = disp_height - 3;
+ if (data_height < 1)
+ data_height = 1;
+
+ /* Clear screen */
+ erase();
+
+ /* Draw title on line 0 (bold) */
+ attron(A_BOLD | COLOR_PAIR(2));
+ std::string titleline = (title.length() > (size_t)disp_width) ? title.substr(0, disp_width) : title;
+ titleline += std::string(disp_width - titleline.length(), ' ');
+ mvprintw(0, 0, "%s", titleline.c_str());
+ attroff(A_BOLD);
+ attron(COLOR_PAIR(1));
+
+ /* Draw column headers on line 1 (bold) */
+ attron(A_BOLD | COLOR_PAIR(3));
+ for (int i = 0; i < ncols; i++) {
+ int x = col_offset[i] - scroll_col;
+ if (x < 0 || x >= disp_width) {
+ continue;
+ }
+ int end_x = x + width[i];
+ if (end_x < 0) {
+ continue;
+ }
+ int draw_start = (x < 0) ? 0 : x;
+ // XXX: Fix side-to-side scrolling
+ std::string val = cols[i];
+ if (align[i])
+ val = val + std::string(width[i] - val.length() + ((sortcol == i) ? -1 : 0), ' ');
+ else
+ val = std::string(width[i] - val.length() + ((sortcol == i) ? -1 : 0), ' ') + val;
+ int str_start = (x < 0) ? -x : 0;
+ int str_end = (end_x >= disp_width) ? disp_width - x : width[i];
+ if (str_start < width[i])
+ mvaddnstr(1, draw_start, val.c_str() + str_start,
+ str_end - str_start);
+ /* Draw sort arrow */
+ if (i == sortcol) {
+ int arrow_x = x + width[i] - 1;
+ if (arrow_x >= 0 && arrow_x < disp_width)
+ mvwadd_wch(stdscr, 1, arrow_x, sortdir ? WACS_DARROW : WACS_UARROW);
+ }
+ /* Draw column border */
+ if (i < ncols - 1) {
+ int sep_x = x + width[i];
+ if (sep_x >= 0 && sep_x < disp_width) {
+ attron(COLOR_PAIR(1));
+ mvaddch(1, sep_x, ACS_VLINE);
+ attron(COLOR_PAIR(3));
+ }
+ }
+ }
+ attroff(A_BOLD);
+ attron(COLOR_PAIR(1));
+
+ /* Draw header bottom border (line 2) */
+ for (int i = 0; i < ncols; i++) {
+ int x = col_offset[i] - scroll_col;
+ if (x < 0) {
+ continue;
+ }
+ int end_x = x + width[i];
+ if (end_x < 0) {
+ continue;
+ }
+ int draw_start = (x < 0) ? 0 : x;
+ int draw_end = (end_x >= disp_width) ? disp_width : end_x;
+ for (int j = draw_start; j < draw_end; j++)
+ mvaddch(2, j, ACS_HLINE);
+ if (i < ncols - 1) {
+ int sep_x = x + width[i];
+ if (sep_x >= 0 && sep_x < disp_width) {
+ mvaddch(2, sep_x, ACS_PLUS);
+ if (sep_x + 1 < disp_width)
+ mvaddch(2, sep_x + 1, ACS_HLINE);
+ }
+ }
+ }
+
+ /* Draw data rows */
+ for (int i = scroll_row; i < nrows && (i - scroll_row) < data_height; i++) {
+ int y = data_start + 1 + (i - scroll_row);
+ for (int j = 0; j < ncols; j++) {
+ int x = col_offset[j] - scroll_col;
+ if (x < 0 || x >= disp_width) {
+ continue;
+ }
+ int end_x = x + width[j];
+ if (end_x < 0) {
+ continue;
+ }
+ int draw_start = (x < 0) ? 0 : x;
+
+ /* Highlight selected cell */
+ if (i == row && j == col) {
+ //attron(A_REVERSE);
+ attron(COLOR_PAIR(4));
+ }
+
+ std::string val = rows[i][j].to_string();
+ if (align[j])
+ val = val + std::string(width[j] - val.length(), ' ');
+ else
+ val = std::string(width[j] - val.length(), ' ') + val;
+ int str_start = (x < 0) ? -x : 0;
+ int str_end = (end_x >= disp_width) ? disp_width - x : width[j];
+ if (str_start < width[j])
+ mvaddnstr(y, draw_start, val.c_str() + str_start,
+ str_end - str_start);
+
+ if (i == row && j == col) {
+ //attroff(A_REVERSE);
+ attron(COLOR_PAIR(1));
+ }
+
+ /* Draw column border */
+ if (j < ncols - 1) {
+ int sep_x = x + width[j];
+ if (sep_x >= 0 && sep_x < disp_width)
+ mvaddch(y, sep_x, ACS_VLINE);
+ }
+ }
+ }
+
+ /* Draw bottom border */
+ int y = data_start + 1 + data_height;
+ for (int i = 0; i < ncols; i++) {
+ int x = col_offset[i] - scroll_col;
+ if (x < 0) {
+ continue;
+ }
+ int end_x = x + width[i];
+ if (end_x < 0) {
+ continue;
+ }
+ int draw_start = (x < 0) ? 0 : x;
+ int draw_end = (end_x >= disp_width) ? disp_width : end_x;
+ for (int j = draw_start; j < draw_end; j++)
+ mvaddch(y, j, ACS_HLINE);
+ if (i < ncols - 1) {
+ int sep_x = x + width[i];
+ if (sep_x >= 0 && sep_x < disp_width) {
+ mvaddch(y, sep_x, ACS_PLUS);
+ if (sep_x + 1 < disp_width)
+ mvaddch(y, sep_x + 1, ACS_HLINE);
+ }
+ }
+ }
+}
+
+void
+table::showhelp()
+{
+ int win_height = 15, win_width = 50;
+ int x = (disp_height - win_height) / 2;
+ int y = (disp_width - win_width) / 2;
+
+ WINDOW *win = newwin(win_height, win_width, x, y);
+ box(win, 0, 0);
+
+ wattron(win, A_BOLD);
+ mvwprintw(win, 0, 1, "%s", "Table Help");
+ wattroff(win, A_BOLD);
+
+ mvwprintw(win, 1, 1, "Keys:");
+ mvwprintw(win, 2, 1, " <Up> Move up one row");
+ mvwprintw(win, 3, 1, " <Down> Move down one row");
+ mvwprintw(win, 4, 1, " <Right> Move right one column");
+ mvwprintw(win, 5, 1, " <Left> Move left one column");
+ mvwprintw(win, 6, 1, " <Page-up> Skip up");
+ mvwprintw(win, 7, 1, " <Page-down> Skip down");
+ mvwprintw(win, 9, 1, " <Enter> Show detail view");
+ mvwprintw(win, 10, 1, " <Escape> Return to previous view");
+ mvwprintw(win, 11, 1, " s Sort by current column");
+ mvwprintw(win, 12, 1, " h Show this message");
+ mvwprintw(win, 14, 1, " q Quit");
+
+ mvwprintw(win, 16, 1, "Press any key to continue");
+
+ wrefresh(win);
+
+ getch();
+ delwin(win);
+}
+
+void
+table::processmouse()
+{
+ MEVENT event;
+ int ncols = cols.size(), nrows = rows.size();
+
+ if (getmouse(&event) == OK) {
+ if (event.bstate & BUTTON1_CLICKED) {
+ if (event.y < 3 ||
+ event.y - 3 >= nrows ||
+ event.x > col_end[ncols - 1])
+ return;
+
+ if (row < disp_height - 3)
+ row = event.y - 3;
+ else
+ row = scroll_row + event.y - 3;
+
+ for (size_t i = 0; i < col_end.size(); i++)
+ if (event.x >= col_offset[i] &&
+ event.x <= col_end[i]) {
+ col = i;
+ return;
+ }
+ }
+ if (event.bstate & BUTTON4_PRESSED) {
+ if (row > 0)
+ row--;
+ }
+ if (event.bstate & BUTTON5_PRESSED) {
+ if (row < nrows - 1)
+ row++;
+ }
+ }
+}
+
+void
+table::display(const std::string &title, std::function<void(std::vector<field>)> cb)
+{
+ int ncols = cols.size(), nrows = rows.size();
+ int ch;
+
+ layout();
+ draw(title);
+
+ while ((ch = getch()) && ch != 27) {
+ switch (ch) {
+ case KEY_UP:
+ if (row > 0)
+ row--;
+ break;
+ case KEY_DOWN:
+ if (row < nrows - 1)
+ row++;
+ break;
+ case KEY_PPAGE:
+ if (row > 40)
+ row -= 40;
+ else
+ row = 0;
+ break;
+ case KEY_NPAGE:
+ if ((row + 40) < nrows - 1)
+ row += 40;
+ else
+ row = nrows - 1;
+ break;
+ case KEY_LEFT:
+ if (col > 0)
+ col--;
+ break;
+ case KEY_RIGHT:
+ if (col < ncols - 1)
+ col++;
+ break;
+ case 's':
+ sort(col, (sortcol != col) ? sortdir : !sortdir);
+ break;
+ case 'h':
+ showhelp();
+ break;
+ case 'q':
+ endwin();
+ exit(EX_OK);
+ break;
+ case '\n':
+ case 13:
+ case KEY_ENTER:
+ cb(rows[row]);
+ break;
+ case KEY_RESIZE:
+ getmaxyx(stdscr, disp_height, disp_width);
+ break;
+ case KEY_MOUSE:
+ processmouse();
+ break;
+ }
+
+ layout();
+ draw(title);
+ refresh();
+ }
+
+ endwin();
+}
+
+histogram::histogram() : xmin(0), xmax(0), xlabel(), ylabel(), xcolors(), samples()
+{
+}
+
+histogram::~histogram()
+{
+}
+
+void
+histogram::setxlabel(const std::string &lbl)
+{
+ xlabel = lbl;
+}
+
+void
+histogram::setylabel(const std::string &lbl)
+{
+ ylabel = lbl;
+}
+
+void
+histogram::setxaxis(int min, int max)
+{
+ xmin = min;
+ xmax = max;
+}
+
+/*
+ * Set the x-value cut offs for the start of green, yellow and red.
+ */
+void
+histogram::setxcolors(const std::vector<int> &colors)
+{
+ xcolors = colors;
+}
+
+void
+histogram::addsample(int s)
+{
+ auto e = samples.find(s);
+ if (e == samples.end())
+ samples[s] = 1;
+ else
+ e->second++;
+}
+
+int
+histogram::min()
+{
+ int lowest = INT_MAX;
+ for (auto s : samples) {
+ if (s.first < lowest)
+ lowest = s.first;
+ }
+
+ return lowest;
+}
+
+int
+histogram::max()
+{
+ int highest = INT_MIN;
+ for (auto s : samples) {
+ if (s.first > highest)
+ highest = s.first;
+ }
+
+ return highest;
+}
+
+int
+histogram::average()
+{
+ int64_t sum = 0;
+ int64_t count = 0;
+
+ for (auto s : samples) {
+ sum += ((int64_t)s.first) * ((int64_t)s.second);
+ count += (int64_t)s.second;
+ }
+
+ return sum / count;
+}
+
+void
+histogram::print()
+{
+ int i, j;
+ int min, max;
+ int height, width, bwidth;
+ int xgreen, xyellow, xred;
+ uint64_t ymax;
+ std::unordered_map<int, uint64_t> bins = std::unordered_map<int, uint64_t>();
+
+ height = (disp_height / 2);
+ width = disp_width - 8;
+
+ /*
+ * Ensure that graphs are at least 20 high and have even width to make
+ * the rendering routine below simple.
+ */
+ if (height < 20)
+ height = 20;
+ if (width % 2)
+ width -= 1;
+ bwidth = (tmode == TERMMODE_XTERM) ? (2 * width) : width;
+
+ if (samples.begin() == samples.end()) {
+ printf("Histogram does not contain samples\n\n");
+ return;
+ }
+
+ min = samples.begin()->first;
+ max = samples.begin()->first;
+ for (auto s : samples) {
+ if (s.first < min)
+ min = s.first;
+ if (s.first > max)
+ max = s.first;
+ }
+
+ if (min == max) {
+ printf("Histogram does not contain enough samples\n\n");
+ return;
+ }
+
+ // Automatically select a domain of the graph
+ if (xmin == 0 && xmax == 0) {
+ xmin = min;
+ xmax = max;
+ }
+
+ for (i = 0; i < bwidth; i++)
+ bins[i] = 0;
+
+ for (auto s : samples) {
+ int bin = bwidth * (s.first - xmin) / (xmax - xmin);
+ bins[bin] += s.second;
+ }
+
+ // Determine ymax
+ ymax = 0;
+ for (i = 0; i < bwidth; i++) {
+ if (ymax < bins[i])
+ ymax = bins[i];
+ }
+
+ // Compute log graph
+ for (i = 0; i < bwidth; i++) {
+ float val = 4 * height * log10(bins[i]) / log10(ymax);
+ bins[i] = (val < 0) ? 0 : val;
+ }
+
+ /* Compute the color columns */
+ if (xcolors.size() == 3) {
+ xgreen = width * (xcolors[0] - xmin) / (xmax - xmin);
+ xyellow = width * (xcolors[1] - xmin) / (xmax - xmin);
+ xred = width * (xcolors[2] - xmin) / (xmax - xmin);
+ if (bwidth != width) {
+ xgreen *= 2;
+ xyellow *= 2;
+ xred *= 2;
+ }
+ } else {
+ xgreen = INT_MAX;
+ xyellow = INT_MAX;
+ xred = INT_MAX;
+ }
+
+ int vstart = (height - ylabel.length()) / 2;
+ printf(" %s\n", format_siprefix(ymax).c_str());
+ printf(" %s", border_char[BORDER_TOPRIGHT]);
+ for (i = 0; i < width; i++) {
+ printf("%s", border_char[BORDER_HBAR]);
+ }
+ printf("%s\n", border_char[BORDER_TOPLEFT]);
+ for (i = height; i > 0; i--) {
+ char vc;
+ std::string str = "";
+
+ for (j = 0; j < bwidth; j += 2) {
+ uint64_t row = 4 * (i - 1);
+ uint64_t val[2] = { 0, 0 };
+
+ if (bins[j] > row) {
+ val[0] = bins[j] - row;
+ val[0] = (val[0] > 4) ? 4 : val[0];
+ }
+ if (bins[j + 1] > row) {
+ val[1] = bins[j + 1] - row;
+ val[1] = (val[1] > 4) ? 4 : val[1];
+ }
+
+ if (xgreen == j)
+ str.append(tc_green);
+ if (xyellow == j)
+ str.append(tc_yellow);
+ if (xred == j)
+ str.append(tc_red);
+
+ if (tmode == TERMMODE_XTERM) {
+ str.append(graph_fancy[val[0]][val[1]]);
+ } else if (tmode == TERMMODE_VT) {
+ str.append(graph_vt[val[0]]);
+ str.append(graph_vt[val[1]]);
+ } else {
+ str.append(graph_basic[val[0]]);
+ str.append(graph_basic[val[1]]);
+ }
+ }
+ if (TC_SGR0)
+ str.append(TC_SGR0);
+
+ if ((height - i >= vstart) && (height - i - vstart < (int)ylabel.length()))
+ vc = ylabel[height - i - vstart];
+ else
+ vc = ' ';
+ printf(" %c %s%s%s\n", vc, border_char[BORDER_VBAR],
+ str.c_str(), border_char[BORDER_VBAR]);
+ }
+ printf(" 0 %s", border_char[BORDER_BOTTOMLEFT]);
+ for (i = 0; i < width; i++) {
+ printf("%s", border_char[BORDER_HBAR]);
+ }
+ printf("%s\n", border_char[BORDER_BOTTOMRIGHT]);
+
+ /*
+ * Calculate the spacing on either side of the x-axis label we reserve
+ * 10 characters for the x-min and x-max values.
+ */
+ int rwidth = (width - xlabel.length() - 18) / 2;
+ printf(" %-10d%*c%s%*c%10d\n", xmin, rwidth, ' ', xlabel.c_str(),
+ rwidth, ' ', xmax);
+ printf("\n");
+}
+
+bargraph::bargraph() : cols(), samples()
+{
+}
+
+bargraph::~bargraph()
+{
+}
+
+void
+bargraph::addcolumn(const std::string &c)
+{
+ cols.push_back(c);
+ samples[c] = 0;
+}
+
+void
+bargraph::addsample(const std::string &c, uint64_t s)
+{
+ samples[c] += s;
+}
+
+void
+bargraph::print()
+{
+ uint64_t width;
+ uint64_t total;
+
+ total = 0;
+ width = 0;
+ for (auto &s : samples) {
+ total += s.second;
+ if (width < s.first.length())
+ width = s.first.length();
+ }
+
+ if (total == 0) {
+ printf("No samples\n\n");
+ return;
+ }
+
+ for (auto &s : cols) {
+ float pct = 100.0 * samples[s] / total;
+ printf("% 7.2f%% %*s ", pct, (int)width, s.c_str());
+ printbar(pct);
+ }
+ printf("\n");
+}
+
+bitfield::bitfield() : bits()
+{
+}
+
+bitfield::~bitfield()
+{
+}
+
+void
+bitfield::setwidth(int width)
+{
+ bits.resize(width);
+ for (int i = 0; i < width; i++)
+ bits[i] = " ";
+}
+
+void
+bitfield::fill(uint64_t start, uint64_t length, const std::string &val)
+{
+ for (uint64_t i = start; i < start + length && i < bits.size(); i++)
+ bits[i] = val;
+}
+
+void
+bitfield::print()
+{
+ std::string row;
+
+ row = "";
+ for (uint64_t i = 0; i < bits.size(); i++) {
+ if (i == 0)
+ row += border_char[BORDER_TOPRIGHT];
+ else if (i % 8 == 0)
+ row += border_char[BORDER_TOPSPLIT];
+ row += border_char[BORDER_HBAR];
+ if (i == bits.size() - 1)
+ row += border_char[BORDER_TOPLEFT];
+ }
+ printf(" %s\n", row.c_str());
+
+ row = "";
+ for (uint64_t i = 0; i < bits.size(); i++) {
+ if (i == 0)
+ row += border_char[BORDER_VBAR];
+ else if (i % 8 == 0)
+ row += border_char[BORDER_VBAR];
+ row += bits[i];
+ if (i == bits.size() - 1)
+ row += border_char[BORDER_VBAR];
+ }
+ printf(" %s\n", row.c_str());
+
+ row = "";
+ for (uint64_t i = 0; i < bits.size(); i++) {
+ if (i == 0)
+ row += border_char[BORDER_BOTTOMLEFT];
+ else if (i % 8 == 0)
+ row += border_char[BORDER_BOTTOMSPLIT];
+ row += border_char[BORDER_HBAR];
+ if (i == bits.size() - 1)
+ row += border_char[BORDER_BOTTOMRIGHT];
+ }
+ printf(" %s\n", row.c_str());
+}
+
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Nov 12, 11:00 PM (6 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41685907
Default Alt Text
D58927.1794524445.diff (27 KB)
Attached To
Mode
D58927: pmc: Bargraphs and Bitfields and Histograms, Oh My!
Attached
Detach File
Event Timeline