diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/charts.c | 512 |
1 files changed, 396 insertions, 116 deletions
diff --git a/src/charts.c b/src/charts.c index 82516ab..dffa028 100644 --- a/src/charts.c +++ b/src/charts.c @@ -45,6 +45,7 @@ #define CHART_MAX_ARGC 64 /* this is a huge amount */ #define CHART_VMON_PROC_WANTS (VMON_WANT_PROC_STAT | VMON_WANT_PROC_FOLLOW_CHILDREN | VMON_WANT_PROC_FOLLOW_THREADS) #define CHART_VMON_SYS_WANTS (VMON_WANT_SYS_STAT) +#define CHART_MAX_COLUMNS 16 /* the global charts state, supplied to vwm_chart_create() which keeps a reference for future use. */ typedef struct _vwm_charts_t { @@ -72,26 +73,64 @@ typedef struct _vwm_charts_t { finish_fill; } vwm_charts_t; +typedef enum _vwm_column_type_t { + VWM_COLUMN_VWM, + VWM_COLUMN_PROC_USER, + VWM_COLUMN_PROC_SYS, + VWM_COLUMN_PROC_WALL, + VWM_COLUMN_PROC_TREE, + VWM_COLUMN_PROC_ARGV, + VWM_COLUMN_PROC_PID, + VWM_COLUMN_PROC_WCHAN, + VWM_COLUMN_PROC_STATE, + VWM_COLUMN_CNT +} vwm_column_type_t; + +/* which side to pack the column onto */ +typedef enum _vwm_side_t { + VWM_SIDE_LEFT, + VWM_SIDE_RIGHT, + VWM_SIDE_CNT +} vwm_side_t; + +/* how to horizontally justify contents within a given column's area */ +typedef enum _vwm_justify_t { + VWM_JUSTIFY_LEFT, + VWM_JUSTIFY_RIGHT, + VWM_JUSTIFY_CENTER, + VWM_JUSTIFY_CNT +} vwm_justify_t; + +typedef struct _vwm_column_t { + /* TODO: make the columns configurable and add more description/toggled state here */ + unsigned enabled:1; + vwm_column_type_t type; + vwm_side_t side; + int width; +} vwm_column_t; + /* everything needed by the per-window chart's context */ typedef struct _vwm_chart_t { - vmon_proc_t *monitor; /* vmon process monitor handle */ - Pixmap text_pixmap; /* pixmap for charted text (kept around for XDrawText usage) */ - Picture text_picture; /* picture representation of text_pixmap */ - Picture shadow_picture; /* text shadow layer */ - Picture grapha_picture; /* graph A layer */ - Picture graphb_picture; /* graph B layer */ - Picture tmp_picture; /* 1 row worth of temporary picture space */ - Picture picture; /* chart picture derived from the pixmap, for render compositing */ - int width; /* current width of the chart */ - int height; /* current height of the chart */ - int visible_width; /* currently visible width of the chart */ - int visible_height; /* currently visible height of the chart */ - int phase; /* current position within the (horizontally scrolling) graphs */ - int heirarchy_end; /* row where the process heirarchy currently ends */ - int snowflakes_cnt; /* count of snowflaked rows (reset to zero to truncate snowflakes display) */ - int gen_last_composed; /* the last composed vmon generation */ - int redraw_needed; /* if a redraw is required (like when the window is resized...) */ - char *name; /* name if provided, included in chart by the \/\/\ */ + vmon_proc_t *monitor; /* vmon process monitor handle */ + Pixmap text_pixmap; /* pixmap for charted text (kept around for XDrawText usage) */ + Picture text_picture; /* picture representation of text_pixmap */ + Picture shadow_picture; /* text shadow layer */ + Picture grapha_picture; /* graph A layer */ + Picture graphb_picture; /* graph B layer */ + Picture tmp_picture; /* 1 row worth of temporary picture space */ + Picture picture; /* chart picture derived from the pixmap, for render compositing */ + int width; /* current width of the chart */ + int height; /* current height of the chart */ + int visible_width; /* currently visible width of the chart */ + int visible_height; /* currently visible height of the chart */ + int phase; /* current position within the (horizontally scrolling) graphs */ + int heirarchy_end; /* row where the process heirarchy currently ends */ + int snowflakes_cnt; /* count of snowflaked rows (reset to zero to truncate snowflakes display) */ + int gen_last_composed; /* the last composed vmon generation */ + int redraw_needed; /* if a redraw is required (like when the window is resized...) */ + char *name; /* name if provided, included in chart by the \/\/\ */ + vwm_column_t columns[CHART_MAX_COLUMNS]; /* columns in the chart TODO, for now just stowing the real/user/sys width here */ + vwm_column_t snowflake_columns[CHART_MAX_COLUMNS]; /* columns in the snowflaked rows */ } vwm_chart_t; /* space we need for every process being monitored */ @@ -413,7 +452,7 @@ static void shadow_row(vwm_charts_t *charts, vwm_chart_t *chart, int row) /* simple helper to map the vmon per-proc argv array into an XTextItem array, deals with threads vs. processes and the possibility of the comm field not getting read in before the process exited... */ -static void argv2xtext(vmon_proc_t *proc, XTextItem *items, int max_items, int *nr_items) +static void argv2xtext(const vmon_proc_t *proc, XTextItem *items, int max_items, int *nr_items) { int i; int nr = 0; @@ -540,7 +579,7 @@ static void mark_finish(vwm_charts_t *charts, vwm_chart_t *chart, int row) /* helper for drawing a proc's argv @ specified x offset and row on the chart */ -static void print_argv(vwm_charts_t *charts, vwm_chart_t *chart, int x, int row, vmon_proc_t *proc) +static void print_argv(const vwm_charts_t *charts, const vwm_chart_t *chart, int x, int row, const vmon_proc_t *proc, int *res_width) { vwm_xserver_t *xserver = charts->xserver; XTextItem items[CHART_MAX_ARGC]; @@ -550,6 +589,18 @@ static void print_argv(vwm_charts_t *charts, vwm_chart_t *chart, int x, int row, XDrawText(xserver->display, chart->text_pixmap, charts->text_gc, x, (row + 1) * CHART_ROW_HEIGHT - 3, /* dst x, y */ items, nr_items); + + /* if the caller wants to know the width, compute it, it's dumb that XDrawText doesn't + * return the dimensions of what was drawn, fucking xlib. + */ + if (res_width) { + int width = 0; + + for (int i = 0; i < nr_items; i++) + width += XTextWidth(charts->chart_font, items[i].chars, items[i].nchars) + items[i].delta; + + *res_width = width; + } } @@ -570,62 +621,23 @@ static inline int proc_has_subsequent_siblings(vmon_t *vmon, vmon_proc_t *proc) } -/* draws proc in a row of the process heirarchy */ -static void draw_heirarchy_row(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_t *proc, int depth, int row, int heirarchy_changed) +/* convert chart sampling interval back into an integral hertz value, basically + * open-coded ceilf(1.f / charts->sampling_interval) to avoid needing -lm. + */ +static unsigned interval_as_hz(vwm_charts_t *charts) { - vwm_xserver_t *xserver = charts->xserver; - vmon_proc_stat_t *proc_stat = proc->stores[VMON_STORE_PROC_STAT]; - vmon_proc_t *child; - char str[256]; - int str_len, str_width; - -/* process heirarchy text and accompanying per-process details like wchan/pid/state... */ - - /* skip if obviously unnecessary (this can be further improved, but this makes a big difference as-is) */ - if (!chart->redraw_needed && - !heirarchy_changed && - !BITTEST(proc_stat->changed, VMON_PROC_STAT_WCHAN) && - !BITTEST(proc_stat->changed, VMON_PROC_STAT_PID) && - !BITTEST(proc_stat->changed, VMON_PROC_STAT_STATE) && - !BITTEST(proc_stat->changed, VMON_PROC_STAT_ARGV)) - return; - - /* TODO: make the columns interactively configurable @ runtime */ - if (!proc->is_new) - /* XXX for now always clear the row, this should be capable of being optimized in the future (if the datums driving the text haven't changed...) */ - XRenderFillRectangle(xserver->display, PictOpSrc, chart->text_picture, &chart_trans_color, - 0, row * CHART_ROW_HEIGHT, /* dst x, y */ - chart->width, CHART_ROW_HEIGHT); /* dst w, h */ - - /* put the process' wchan, state, and PID columns @ the far right */ - if (proc->is_thread || list_empty(&proc->threads)) { /* only threads or non-threaded processes include the wchan and state */ - str_len = snpf(str, sizeof(str), " %.*s %5i %c ", - proc_stat->wchan.len, - proc_stat->wchan.len == 1 && proc_stat->wchan.array[0] == '0' ? "-" : proc_stat->wchan.array, - proc->pid, - proc_stat->state); - } else { /* we're a process having threads, suppress the wchan and state, as they will be displayed for the thread of same pid */ - str_len = snpf(str, sizeof(str), " %5i ", proc->pid); - } - - str_width = XTextWidth(charts->chart_font, str, str_len); - - /* the process' comm label indented according to depth, followed with their respective argv's */ - print_argv(charts, chart, depth * (CHART_ROW_HEIGHT / 2), row, proc); + return (1.f / charts->sampling_interval + .5f); +} - /* ensure the area for the rest of the stuff is cleared, we don't put much text into thread rows so skip it for those. */ - if (!proc->is_thread) - XRenderFillRectangle(xserver->display, PictOpSrc, chart->text_picture, &chart_trans_color, - chart->visible_width - str_width, row * CHART_ROW_HEIGHT, /* dst x,y */ - chart->width - (chart->visible_width - str_width), CHART_ROW_HEIGHT); /* dst w,h */ - XDrawString(xserver->display, chart->text_pixmap, charts->text_gc, - chart->visible_width - str_width, (row + 1) * CHART_ROW_HEIGHT - 3, /* dst x, y */ - str, str_len); +/* draw a process' row slice of a process tree */ +static void draw_tree_row(vwm_charts_t *charts, vwm_chart_t *chart, int x, int depth, int row, const vmon_proc_t *proc, int *res_width) +{ + vwm_xserver_t *xserver = charts->xserver; /* only if this process isn't the root process @ the window shall we consider all relational drawing conditions */ if (proc != chart->monitor) { - vmon_proc_t *ancestor, *sibling, *last_sibling = NULL; + vmon_proc_t *child, *ancestor, *sibling, *last_sibling = NULL; int needs_tee = 0; int bar_x = 0, bar_y = (row + 1) * CHART_ROW_HEIGHT; int sub; @@ -641,8 +653,8 @@ static void draw_heirarchy_row(vwm_charts_t *charts, vwm_chart_t *chart, vmon_pr /* determine if the ancestor has remaining siblings which are not stale, if so, draw a connecting bar at its depth */ if (proc_has_subsequent_siblings(&charts->vmon, ancestor)) XDrawLine(xserver->display, chart->text_pixmap, charts->text_gc, - bar_x, bar_y - CHART_ROW_HEIGHT, /* dst x1, y1 */ - bar_x, bar_y); /* dst x2, y2 (vertical line) */ + x + bar_x, bar_y - CHART_ROW_HEIGHT, /* dst x1, y1 */ + x + bar_x, bar_y); /* dst x2, y2 (vertical line) */ } /* determine if _any_ of our siblings have children requiring us to draw a tee immediately before our comm string. @@ -689,30 +701,291 @@ static void draw_heirarchy_row(vwm_charts_t *charts, vwm_chart_t *chart, vmon_pr /* if we're the last sibling, corner the tee by shortening the vbar */ if (proc == last_sibling) { XDrawLine(xserver->display, chart->text_pixmap, charts->text_gc, - bar_x, bar_y - CHART_ROW_HEIGHT, /* dst x1, y1 */ - bar_x, bar_y - 4); /* dst x2, y2 (vertical bar) */ + x + bar_x, bar_y - CHART_ROW_HEIGHT, /* dst x1, y1 */ + x + bar_x, bar_y - 4); /* dst x2, y2 (vertical bar) */ } else { XDrawLine(xserver->display, chart->text_pixmap, charts->text_gc, - bar_x, bar_y - CHART_ROW_HEIGHT, /* dst x1, y1 */ - bar_x, bar_y); /* dst x2, y2 (vertical bar) */ + x + bar_x, bar_y - CHART_ROW_HEIGHT, /* dst x1, y1 */ + x + bar_x, bar_y); /* dst x2, y2 (vertical bar) */ } XDrawLine(xserver->display, chart->text_pixmap, charts->text_gc, - bar_x, bar_y - 4, /* dst x1, y1 */ - bar_x + 2, bar_y - 4); /* dst x2, y2 (horizontal bar) */ + x + bar_x, bar_y - 4, /* dst x1, y1 */ + x + bar_x + 2, bar_y - 4); /* dst x2, y2 (horizontal bar) */ /* terminate the outer sibling loop upon drawing the tee... */ break; } } + + /* this currently just returns a max-case width like we drew a tee, even if we didn't draw anything */ + if (res_width) + *res_width = (depth - 1) * (CHART_ROW_HEIGHT / 2) + 6; } +} + + +/* draw a proc row according to the columns configured in columns, + * row==0 is treated specially as the heading row + */ +static void draw_columns(vwm_charts_t *charts, vwm_chart_t *chart, vwm_column_t *columns, int depth, int row, const vmon_proc_t *proc) +{ + vmon_sys_stat_t *sys_stat = charts->vmon.stores[VMON_STORE_SYS_STAT]; + vmon_proc_stat_t *proc_stat = proc->stores[VMON_STORE_PROC_STAT]; + vwm_xserver_t *xserver = charts->xserver; + char str[256]; + + for (int i = 0, left = 0, right = 0; i < CHART_MAX_COLUMNS; i++) { + vwm_column_t *c = &columns[i]; + vwm_justify_t str_justify = VWM_JUSTIFY_CENTER; + int str_len = 0; + if (!c->enabled) + continue; + + switch (c->type) { + case VWM_COLUMN_VWM: + if (!row) /* "\/\/\ # name @ XXHz" is only relevant to the heading */ + str_len = snpf(str, sizeof(str), "\\/\\/\\%s%s @ %2uHz ", + chart->name ? " # " : "", + chart->name ? chart->name : "", + interval_as_hz(charts)); + + str_justify = VWM_JUSTIFY_RIGHT; + break; + + case VWM_COLUMN_PROC_USER: /* User CPU time */ + if (!row) + str_len = snpf(str, sizeof(str), "User"); + else + str_len = snpf(str, sizeof(str), "%.2fs", + (float)proc_stat->utime / (float)charts->vmon.ticks_per_sec); + + str_justify = VWM_JUSTIFY_RIGHT; + break; + + case VWM_COLUMN_PROC_SYS: /* Sys CPU time */ + if (!row) + str_len = snpf(str, sizeof(str), "Sys"); + else + str_len = snpf(str, sizeof(str), "%.2fs", + (float)proc_stat->stime / (float)charts->vmon.ticks_per_sec); + + str_justify = VWM_JUSTIFY_RIGHT; + break; + + case VWM_COLUMN_PROC_WALL: /* User Sys Wall times */ + if (!row) + str_len = snpf(str, sizeof(str), "Wall"); + else + str_len = snpf(str, sizeof(str), "%.2fs", + (float)(sys_stat->boottime - proc_stat->start) / (float)charts->vmon.ticks_per_sec); + + str_justify = VWM_JUSTIFY_RIGHT; + break; + + case VWM_COLUMN_PROC_TREE: { /* print a row of the process heirarchy tree */ + int width = 0; + + if (!row) /* tree markup needs no heading */ + break; + + assert(c->side == VWM_SIDE_LEFT); /* XXX: technically SIDE_RIGHT could work, but doesn't currently */ + + draw_tree_row(charts, chart, left, depth, row, proc, &width); + left += width; + break; + } + + case VWM_COLUMN_PROC_ARGV: { /* print the process' argv */ + if (!row) { + str_len = snpf(str, sizeof(str), "ArgV/~ThreadName"); + str_justify = VWM_JUSTIFY_LEFT; + } else { + int width; + + print_argv(charts, chart, left /* FIXME: consider c->side */, row, proc, &width); + if (width > c->width) { + c->width = width; + chart->redraw_needed++; + } + } + break; + } + + case VWM_COLUMN_PROC_PID: /* print the process' PID */ + if (!row) + str_len = snpf(str, sizeof(str), "PID"); + else + str_len = snpf(str, sizeof(str), "%5i", proc->pid); + + str_justify = VWM_JUSTIFY_RIGHT; + break; + + case VWM_COLUMN_PROC_WCHAN: /* print the process' wchan */ + if (!row) + str_len = snpf(str, sizeof(str), "WChan"); + else { + + /* don't show wchan for processes with threads, since their main thread will show it. */ + if (!proc->is_thread && !list_empty(&proc->threads)) + break; + + str_len = snpf(str, sizeof(str), "%.*s", + proc_stat->wchan.len, + proc_stat->wchan.len == 1 && proc_stat->wchan.array[0] == '0' ? "-" : proc_stat->wchan.array); + } + + str_justify = VWM_JUSTIFY_RIGHT; + break; + + case VWM_COLUMN_PROC_STATE: /* print the process' state */ + if (!row) + str_len = snpf(str, sizeof(str), "State"); + else { + /* don't show process state for processes with threads, since their main thread will show it. */ + if (!proc->is_thread && !list_empty(&proc->threads)) + break; + + str_len = snpf(str, sizeof(str), "%c", proc_stat->state); + } + + str_justify = VWM_JUSTIFY_CENTER; + break; + + default: + assert(0); + } + + /* for plain string draws, str_len is left non-zero and they all get handled equally here */ + if (str_len) { + int str_width, xpos; + + str_width = XTextWidth(charts->chart_font, str, str_len); + if (str_width > c->width) { + c->width = str_width; + chart->redraw_needed++; + } + + /* get xpos to the left edge of the column WRT c->width and c->side */ + switch (c->side) { + case VWM_SIDE_LEFT: + xpos = left; + break; + + case VWM_SIDE_RIGHT: + xpos = chart->visible_width - (right + c->width); + break; + + default: + assert(0); + } + + /* adjust xpos according to str_justify and c->width */ + switch (str_justify) { + case VWM_JUSTIFY_LEFT: + /* xpos already @ left */ + break; + + case VWM_JUSTIFY_RIGHT: + xpos += c->width - str_width; + break; + + case VWM_JUSTIFY_CENTER: + xpos += (c->width - str_width) / 2; + break; + + default: + assert(0); + } + + XDrawString(xserver->display, chart->text_pixmap, charts->text_gc, + xpos, (row + 1) * CHART_ROW_HEIGHT - 3, + str, str_len); + + } + + left += (c->side == VWM_SIDE_LEFT) * (c->width + CHART_ROW_HEIGHT / 2); + right += (c->side == VWM_SIDE_RIGHT) * (c->width + CHART_ROW_HEIGHT / 2); + } +} + + +/* return if any of the enabled columns in columns has changed its contents */ +static int columns_changed(const vwm_charts_t *charts, const vwm_chart_t *chart, vwm_column_t *columns, const vmon_proc_t *proc) +{ + vmon_sys_stat_t *sys_stat = charts->vmon.stores[VMON_STORE_SYS_STAT]; + vmon_proc_stat_t *proc_stat = proc->stores[VMON_STORE_PROC_STAT]; + + for (int i = 0; i < CHART_MAX_COLUMNS; i++) { + const vwm_column_t *c = &columns[i]; + + if (!c->enabled) + continue; + + switch (c->type) { + case VWM_COLUMN_VWM: + /* XXX: meh, maybe we should detect Hz changes here? */ + break; + case VWM_COLUMN_PROC_USER: + if (BITTEST(proc_stat->changed, VMON_PROC_STAT_UTIME)) + return 1; + break; + case VWM_COLUMN_PROC_SYS: + if (BITTEST(proc_stat->changed, VMON_PROC_STAT_STIME)) + return 1; + break; + case VWM_COLUMN_PROC_WALL: + if (BITTEST(proc_stat->changed, VMON_PROC_STAT_START) || + BITTEST(sys_stat->changed, VMON_SYS_STAT_BOOTTIME)) + return 1; + break; + case VWM_COLUMN_PROC_TREE: + break; + case VWM_COLUMN_PROC_ARGV: + if (BITTEST(proc_stat->changed, VMON_PROC_STAT_ARGV)) + return 1; + break; + case VWM_COLUMN_PROC_PID: + if (BITTEST(proc_stat->changed, VMON_PROC_STAT_PID)) + return 1; + break; + case VWM_COLUMN_PROC_WCHAN: + if (BITTEST(proc_stat->changed, VMON_PROC_STAT_WCHAN)) + return 1; + break; + case VWM_COLUMN_PROC_STATE: + if (BITTEST(proc_stat->changed, VMON_PROC_STAT_STATE)) + return 1; + break; + default: + assert(0); + } + } + + return 0; +} + + +/* draws proc in a row of the process heirarchy */ +static void draw_overlay_row(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_t *proc, int depth, int row) +{ + /* skip if obviously unnecessary (this can be further improved, but this makes a big difference as-is) */ + if (!chart->redraw_needed && !columns_changed(charts, chart, chart->columns, proc)) + return; + + if (!proc->is_new) /* XXX for now always clear the row, this should be capable of being optimized in the future (if the datums driving the text haven't changed...) */ + XRenderFillRectangle(charts->xserver->display, PictOpSrc, chart->text_picture, &chart_trans_color, + 0, row * CHART_ROW_HEIGHT, /* dst x, y */ + chart->width, CHART_ROW_HEIGHT); /* dst w, h */ + + draw_columns(charts, chart, chart->columns, depth, row, proc); shadow_row(charts, chart, row); } /* recursive draw function for "rest" of chart: the per-process rows (heirarchy, argv, state, wchan, pid...) */ -static void draw_chart_rest(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_t *proc, int *depth, int *row, int heirarchy_changed) +static void draw_chart_rest(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_t *proc, int *depth, int *row) { vmon_proc_stat_t *proc_stat = proc->stores[VMON_STORE_PROC_STAT]; vwm_perproc_ctxt_t *proc_ctxt = proc->foo; @@ -741,13 +1014,13 @@ static void draw_chart_rest(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_ (*depth)++; list_for_each_entry_prev(child, &proc->children, siblings) { - draw_chart_rest(charts, chart, child, depth, row, heirarchy_changed); + draw_chart_rest(charts, chart, child, depth, row); (*row)--; } if (!proc->is_thread) { list_for_each_entry_prev(child, &proc->threads, threads) { - draw_chart_rest(charts, chart, child, depth, row, heirarchy_changed); + draw_chart_rest(charts, chart, child, depth, row); (*row)--; } } @@ -768,7 +1041,8 @@ static void draw_chart_rest(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_ chart->snowflakes_cnt++; /* stamp the name (and whatever else we include) into chart.text_picture */ - print_argv(charts, chart, 5, chart->heirarchy_end, proc); + // print_argv(charts, chart, 5, chart->heirarchy_end, proc, NULL); + draw_columns(charts, chart, chart->snowflake_columns, 0, chart->heirarchy_end, proc); shadow_row(charts, chart, chart->heirarchy_end); chart->heirarchy_end--; @@ -812,8 +1086,7 @@ static void draw_chart_rest(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_ } draw_bars(charts, chart, *row, stime_delta, charts->total_delta, utime_delta, charts->total_delta); - - draw_heirarchy_row(charts, chart, proc, *depth, *row, heirarchy_changed); + draw_overlay_row(charts, chart, proc, *depth, *row); (*row)++; @@ -821,64 +1094,55 @@ static void draw_chart_rest(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_ (*depth)++; if (!proc->is_thread) { /* XXX: the threads member serves as the list head only when not a thread */ list_for_each_entry(child, &proc->threads, threads) { - draw_chart_rest(charts, chart, child, depth, row, heirarchy_changed); + draw_chart_rest(charts, chart, child, depth, row); } } list_for_each_entry(child, &proc->children, siblings) { - draw_chart_rest(charts, chart, child, depth, row, heirarchy_changed); + draw_chart_rest(charts, chart, child, depth, row); } (*depth)--; } -/* convert chart sampling interval back into an integral hertz value, basically - * open-coded ceilf(1.f / charts->sampling_interval) to avoid needing -lm. - */ -static unsigned interval_as_hz(vwm_charts_t *charts) -{ - return (1.f / charts->sampling_interval + .5f); -} - - /* recursive draw function entrypoint, draws the IOWait/Idle/HZ row, then enters draw_chart_rest() */ static void draw_chart(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_t *proc, int *depth, int *row) { - vwm_xserver_t *xserver = charts->xserver; - int heirarchy_changed = 0; - int str_len, str_width; - char str[256]; + vwm_xserver_t *xserver = charts->xserver; + int prev_redraw_needed; /* CPU utilization graphs */ /* IOWait and Idle % @ row 0 */ - draw_bars(charts, chart, *row, charts->iowait_delta, charts->total_delta, charts->idle_delta, charts->total_delta); + draw_bars(charts, chart, 0, charts->iowait_delta, charts->total_delta, charts->idle_delta, charts->total_delta); /* only draw the \/\/\ and HZ if necessary */ if (chart->redraw_needed || charts->prev_sampling_interval != charts->sampling_interval) { - str_len = snpf(str, sizeof(str), "\\/\\/\\%s%s @ %2uHz ", - chart->name ? " # " : "", - chart->name ? chart->name : "", - interval_as_hz(charts)); XRenderFillRectangle(xserver->display, PictOpSrc, chart->text_picture, &chart_trans_color, 0, 0, /* dst x, y */ chart->visible_width, CHART_ROW_HEIGHT); /* dst w, h */ - str_width = XTextWidth(charts->chart_font, str, str_len); - XDrawString(xserver->display, chart->text_pixmap, charts->text_gc, - chart->visible_width - str_width, CHART_ROW_HEIGHT - 3, /* dst x, y */ - str, str_len); + draw_columns(charts, chart, chart->columns, 0, 0, proc); shadow_row(charts, chart, 0); } (*row)++; if (!chart->redraw_needed) - heirarchy_changed = proc_heirarchy_changed(proc); - - - draw_chart_rest(charts, chart, proc, depth, row, heirarchy_changed); - - chart->redraw_needed = 0; - - return; + chart->redraw_needed = proc_heirarchy_changed(proc); + + prev_redraw_needed = chart->redraw_needed; + draw_chart_rest(charts, chart, proc, depth, row); + if (chart->redraw_needed > prev_redraw_needed) { + /* Drawing bumped redraw_needed (like a layout change from widths changing), + * so don't reset the counter to zero forcing the next redraw. TODO: this does cause + * a small delay between width-affecting values showing and column widths adjusting to them, + * resulting in a sort of eventually-consistent behavior. + * We could trigger a redraw here immediately by basically jumping back to the start of + * this function, but there are problems doing that as-is due to the stateful/incremental + * relationship between the charts and vmon's sample. Rather than attacking that refactor + * now, I'll leave it like this for now. + */ + chart->redraw_needed = 1; + } else + chart->redraw_needed = 0; } @@ -1060,6 +1324,22 @@ vwm_chart_t * vwm_chart_create(vwm_charts_t *charts, int pid, int width, int hei } } + /* TODO: make the columns interactively configurable @ runtime */ + chart->columns[0] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_USER, .side = VWM_SIDE_LEFT }; + chart->columns[1] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_SYS, .side = VWM_SIDE_LEFT }; + chart->columns[2] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_WALL, .side = VWM_SIDE_LEFT }; + chart->columns[3] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_TREE, .side = VWM_SIDE_LEFT }; + chart->columns[4] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_ARGV, .side = VWM_SIDE_LEFT }; + chart->columns[5] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_STATE, .side = VWM_SIDE_RIGHT }; + chart->columns[6] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_PID, .side = VWM_SIDE_RIGHT }; + chart->columns[7] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_WCHAN, .side = VWM_SIDE_RIGHT }; + chart->columns[8] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_VWM, .side = VWM_SIDE_RIGHT }; + + chart->snowflake_columns[0] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_USER, .side = VWM_SIDE_LEFT }; + chart->snowflake_columns[1] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_SYS, .side = VWM_SIDE_LEFT }; + chart->snowflake_columns[2] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_WALL, .side = VWM_SIDE_LEFT }; + chart->snowflake_columns[3] = (vwm_column_t){ .enabled = 1, .type = VWM_COLUMN_PROC_ARGV, .side = VWM_SIDE_LEFT }; + /* add the client process to the monitoring heirarchy */ /* XXX note libvmon here maintains a unique callback for each unique callback+xwin pair, so multi-window processes work */ chart->monitor = vmon_proc_monitor(&charts->vmon, NULL, pid, VMON_WANT_PROC_INHERIT, (void (*)(vmon_t *, void *, vmon_proc_t *, void *))proc_sample_callback, chart); |