Page MenuHomeFreeBSD

D58927.1794524445.diff
No OneTemporary

Authored By
Unknown
Size
27 KB
Referenced Files
None
Subscribers
None

D58927.1794524445.diff

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

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)

Event Timeline