diff options
author | Vito Caputo <vcaputo@pengaru.com> | 2025-06-13 19:32:36 -0700 |
---|---|---|
committer | Vito Caputo <vcaputo@pengaru.com> | 2025-06-13 19:32:36 -0700 |
commit | a7ad112deef0a4dc6e0ab912300e64569e635352 (patch) | |
tree | 431065b66111bc5ed3321b63af28d001f124548b | |
parent | eeaa389328bd8e7d4b3a1954e1d18978162fed42 (diff) |
WIP DO NOT MERGE
just need to put this somewhere
-rw-r--r-- | src/charts.c | 58 | ||||
-rw-r--r-- | src/libvmon/vmon.c | 45 | ||||
-rw-r--r-- | src/vcr.c | 115 | ||||
-rw-r--r-- | src/vcr.h | 19 |
4 files changed, 183 insertions, 54 deletions
diff --git a/src/charts.c b/src/charts.c index 1040d45..aeacb18 100644 --- a/src/charts.c +++ b/src/charts.c @@ -67,7 +67,7 @@ typedef struct _vwm_charts_t { float prev_sampling_interval_secs, sampling_interval_secs; int sampling_paused, contiguous_drops, primed; unsigned marker_distance; - float inv_ticks_per_sec, inv_total_delta; + double inv_ticks_per_sec, inv_total_delta; unsigned defer_maintenance:1; unsigned no_threads:1; } vwm_charts_t; @@ -197,7 +197,7 @@ static void sample_callback(vmon_t *vmon, void *arg) sys_stat->softirq + sys_stat->steal + sys_stat->guest; charts->total_delta = charts->this_total - charts->last_total; - charts->inv_total_delta = 1.f / (float)charts->total_delta; + charts->inv_total_delta = 1.0 / (double)charts->total_delta; charts->idle_delta = sys_stat->idle - charts->last_idle; charts->iowait_delta = sys_stat->iowait - charts->last_iowait; charts->irq_delta = sys_stat->irq - charts->last_irq; @@ -287,6 +287,7 @@ static void snowflake_row(vwm_charts_t *charts, vwm_chart_t *chart, int row) /* stash the graph rows */ vcr_stash_row(chart->vcr, VCR_LAYER_GRAPHA, row); vcr_stash_row(chart->vcr, VCR_LAYER_GRAPHB, row); + vcr_stash_row(chart->vcr, VCR_LAYER_META, row); /* shift _all_ the layers up by 1 row */ vcr_shift_below_row_up_one(chart->vcr, row); @@ -294,6 +295,7 @@ static void snowflake_row(vwm_charts_t *charts, vwm_chart_t *chart, int row) /* unstash the graph rows @ hierarchy end so we have them in the snowflakes */ vcr_unstash_row(chart->vcr, VCR_LAYER_GRAPHA, chart->hierarchy_end); vcr_unstash_row(chart->vcr, VCR_LAYER_GRAPHB, chart->hierarchy_end); + vcr_unstash_row(chart->vcr, VCR_LAYER_META, chart->hierarchy_end); /* clear the others @ hierarchy end, new argv will get stamped over it */ vcr_clear_row(chart->vcr, VCR_LAYER_TEXT, chart->hierarchy_end, -1, -1); @@ -319,6 +321,7 @@ static void allocate_row(vwm_charts_t *charts, vwm_chart_t *chart, int row) vcr_clear_row(chart->vcr, VCR_LAYER_GRAPHB, row, -1, -1); vcr_clear_row(chart->vcr, VCR_LAYER_TEXT, row, -1, -1); vcr_clear_row(chart->vcr, VCR_LAYER_SHADOW, row, -1, -1); + vcr_clear_row(chart->vcr, VCR_LAYER_META, row, -1, -1); } @@ -411,18 +414,35 @@ static int proc_hierarchy_changed(vmon_proc_t *proc) /* helper for drawing the vertical bars in the graph layers */ -static void draw_bars(vwm_charts_t *charts, vwm_chart_t *chart, int row, float mult, float a_fraction, float inv_a_total, vcr_layer_t a_layer, float b_fraction, float inv_b_total, vcr_layer_t b_layer) +static void draw_bars(vwm_charts_t *charts, vwm_chart_t *chart, int row, float mult, float a_fraction, double inv_a_total, vcr_layer_t a_layer, float b_fraction, double inv_b_total, vcr_layer_t b_layer) { + int a_height, b_height; float a_t, b_t; +//#error "the *ncpus scaling done in floating point seems to often result in enough rounding error to produce 1-pixel overlap of utime and stime bars where they really shouldn't exist" /* compute the bar %ages for this sample */ a_t = a_fraction * inv_a_total * mult; + if (a_t > 1.f) + a_t = 1.f; + b_t = b_fraction * inv_b_total * mult; + if (b_t > 1.f) + b_t = 1.f; + + a_height = a_t * (float)(VCR_ROW_HEIGHT - 1); + if (a_height == 0 && a_fraction != 0.f) + a_height = 1; + b_height = b_t * (float)(VCR_ROW_HEIGHT - 1); + if (b_height == 0 && b_fraction != 0.f) + b_height = 1; + + //fprintf(stderr, "a_t=%f a_h=%i b_t=%f b_h=%i\n", + // a_t, a_height, b_t, b_height); /* ensure at least 1 pixel when the scaled result is a fraction less than 1, * I want to at least see 1 pixel blips for the slightest cpu utilization */ - vcr_draw_bar(chart->vcr, a_layer, row, a_t, a_fraction != 0 ? 1 : 0 /* min_height */); - vcr_draw_bar(chart->vcr, b_layer, row, b_t, b_fraction != 0 ? 1 : 0 /* min_height */); + vcr_draw_bar(chart->vcr, a_layer, VCR_BAR_BASE_TOP, row, a_height); + vcr_draw_bar(chart->vcr, b_layer, VCR_BAR_BASE_BOTTOM, row, b_height); } @@ -1116,6 +1136,7 @@ static void draw_chart_rest(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_ if (!proc->is_thread) { /* per-proc RSS row */ allocate_row(charts, chart, (*row) + 1); + vcr_set_row_palette(chart->vcr, (*row) + 1, VCR_ROW_PALETTE_1); chart->hierarchy_end++; } @@ -1129,7 +1150,7 @@ static void draw_chart_rest(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_ draw_bars(charts, chart, *row, (proc->is_thread || !proc->is_threaded) ? charts->vmon.num_cpus : 1.f /* mult */, - -stime_delta, + stime_delta, charts->inv_total_delta, VCR_LAYER_GRAPHA, utime_delta, @@ -1137,15 +1158,24 @@ static void draw_chart_rest(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_ VCR_LAYER_GRAPHB); if (!proc->is_thread) { - if (proc_ctxt->rss != proc_ctxt->prev_rss) { + if (proc->is_new) { + draw_bars(charts, chart, *row + 1, + 1.f /* mult */, + 1.f, + 1.f, + VCR_LAYER_GRAPHB, + 1.f, + 1.f, + VCR_LAYER_GRAPHA); + } else if (proc_ctxt->rss != proc_ctxt->prev_rss) { // fprintf(stderr, "row=%i rss_delta=%i rss=%llu\n", (*row) + 1, proc_ctxt->rss_log2_delta, proc_ctxt->rss); draw_bars(charts, chart, *row + 1, 1.f /* mult */, - proc_ctxt->rss_log2_delta < 0.f ? proc_ctxt->rss_log2_delta : 0.f, - 1.f / (VCR_ROW_HEIGHT - 1), + proc_ctxt->rss_log2_delta < 0 ? -proc_ctxt->rss_log2_delta : 0.f, + 1.f / VCR_ROW_HEIGHT, VCR_LAYER_GRAPHB, - proc_ctxt->rss_log2_delta > 0.f ? proc_ctxt->rss_log2_delta : 0.f, - 1.f / (VCR_ROW_HEIGHT - 1), + proc_ctxt->rss_log2_delta > 0 ? proc_ctxt->rss_log2_delta : 0.f, + 1.f / VCR_ROW_HEIGHT, VCR_LAYER_GRAPHA); } } @@ -1187,7 +1217,7 @@ static void draw_chart(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_t *pr /* IOWait and Idle % @ row 0 */ draw_bars(charts, chart, row, 1.f /* mult */, - -charts->iowait_delta, + charts->iowait_delta, charts->inv_total_delta, VCR_LAYER_GRAPHA, charts->idle_delta, @@ -1197,7 +1227,7 @@ static void draw_chart(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_t *pr /* IRQ and SoftIRQ % @ row 1 */ draw_bars(charts, chart, row + 1, 1.f /* mult */, - -charts->irq_delta, + charts->irq_delta, charts->inv_total_delta, VCR_LAYER_GRAPHA, charts->softirq_delta, @@ -1207,7 +1237,7 @@ static void draw_chart(vwm_charts_t *charts, vwm_chart_t *chart, vmon_proc_t *pr /* "Adherence" @ row 2 */ draw_bars(charts, chart, row + 2, 1.f /* mult */, - charts->this_sample_adherence > 0.f ? -charts->this_sample_adherence : 0.f /* a_fraction */, + charts->this_sample_adherence > 0.f ? charts->this_sample_adherence : 0.f /* a_fraction */, 1.f /* inv_a_total */, VCR_LAYER_GRAPHA, charts->this_sample_adherence < 0.f ? -charts->this_sample_adherence : 0.f /* b_fraction */, diff --git a/src/libvmon/vmon.c b/src/libvmon/vmon.c index b4c008c..b8b9dcf 100644 --- a/src/libvmon/vmon.c +++ b/src/libvmon/vmon.c @@ -1637,10 +1637,27 @@ static int sample_siblings_pass2(vmon_t *vmon, list_head_t *siblings) } +static void sample_sys_wants(vmon_t *vmon) +{ + assert(vmon); + + /* first the sys-wide samplers */ + vmon->activity = 0; + for (int i = 0, cur = 1, wants = vmon->sys_wants; wants; cur <<= 1, i++) { + if (wants & cur) { + if (vmon->sys_funcs[i](vmon, &vmon->stores[i]) == SAMPLE_CHANGED) + vmon->activity |= cur; + + wants &= ~cur; + } + } +} + + /* collect information for all monitored processes, this is the interesting part, call it periodically at a regular interval */ int vmon_sample(vmon_t *vmon) { - int i, wants, cur, ret = 1; + int ret = 1; assert(vmon); @@ -1704,21 +1721,6 @@ int vmon_sample(vmon_t *vmon) /* now for actual sampling */ - /* first the sys-wide samplers */ - wants = vmon->sys_wants; /* the caller-requested sys-wide wants */ - vmon->activity = 0; - for (i = 0, cur = 1; wants; cur <<= 1, i++) { - if (wants & cur) { - if (vmon->sys_funcs[i](vmon, &vmon->stores[i]) == SAMPLE_CHANGED) - vmon->activity |= cur; - - wants &= ~cur; - } - } - - if (vmon->sample_cb) - vmon->sample_cb(vmon, vmon->sample_cb_arg); - /* then the per-process samplers */ if ((vmon->flags & VMON_FLAG_PROC_ARRAY)) { int j; @@ -1730,6 +1732,9 @@ int vmon_sample(vmon_t *vmon) * mode, only FOLLOW_CHILDREN mode, and it's likely PROC_ARRAY will generally be used together with PROC_ALL, so no hierarchy * is available to traverse even if we wanted to. */ + sample_sys_wants(vmon); + if (vmon->sample_cb) + vmon->sample_cb(vmon, vmon->sample_cb_arg); /* flat process-array ordered sampling, in this mode threads and processes are all placed flatly in the array, * so this does the sampling for all monitored in no particular order */ @@ -1757,11 +1762,19 @@ int vmon_sample(vmon_t *vmon) * XXX this is the path vwm utilizes, everything else is for other uses, like implementing top-like programs. */ ret = sample_siblings_pass1(vmon, &vmon->processes); /* XXX TODO: errors */ + + sample_sys_wants(vmon); + if (vmon->sample_cb) + vmon->sample_cb(vmon, vmon->sample_cb_arg); + ret = sample_siblings_pass2(vmon, &vmon->processes); } else { /* recursive hierarchical depth-first processes tree sampling, at each node threads come before children, done in a single pass: * Pass 1. samplers; callbacks (for every node) */ + sample_sys_wants(vmon); + if (vmon->sample_cb) + vmon->sample_cb(vmon, vmon->sample_cb_arg); ret = sample_siblings_unipass(vmon, &vmon->processes); } @@ -76,6 +76,10 @@ #define VCR_USECS_PER_SECOND 1000000 +typedef struct vcr_row_meta_t { + vcr_row_palette_t palette; +} vcr_row_meta_t; + /* backend is the root vcr object everything else here derives from, * for an X backend it encompasses the xserver/display connection. */ @@ -135,6 +139,9 @@ typedef struct vcr_t { int *snowflakes_cnt_ptr; /* pointer to count of snowflaked rows (reset to zero to truncate snowflakes display) */ const unsigned *marker_distance_ptr; /* pointer to marker distance to use (0 disables markers, this is kind of silly but I don't want to add setters everywhere so sharing the instance in vwm_charts_t) */ + vcr_row_meta_t *meta; /* height / VCR_ROW_HEIGHT elements for per-row metadata */ + vcr_row_meta_t tmp_meta; /* temp storage for a row's meta */ + union { #ifdef USE_XLIB struct { @@ -149,9 +156,9 @@ typedef struct vcr_t { } xlib; #endif /* USE_XLIB */ struct { - uint8_t *bits; /* .pitch * height bytes are used to represent the coverage status of up to 4 layers (was 8 until nibbles happened) */ - uint8_t *tmp; /* .pitch * VCR_ROW_HEIGHT bytes for a row's worth of temporary storage */ - int pitch; /* "pitch" of mem surface in bytes, which is half the width rounded up to an even number divisible by two. */ + uint8_t *bits; /* .pitch * height bytes are used to represent the coverage status of up to 4 layers (was 8 until nibbles happened) */ + uint8_t *tmp; /* .pitch * VCR_ROW_HEIGHT bytes for a row's worth of temporary storage */ + int pitch; /* "pitch" of mem surface in bytes, which is half the width rounded up to an even number divisible by two. */ } mem; }; } vcr_t; @@ -816,6 +823,7 @@ vcr_t * vcr_free(vcr_t *vcr) assert(0); } + free(vcr->meta); free(vcr); } @@ -854,6 +862,19 @@ int vcr_resize_visible(vcr_t *vcr, int width, int height) return 1; /* redraw needed */ } + { /* the meta layer isn't backend-specific */ + vcr_row_meta_t *old = vcr->meta; + + vcr->meta = calloc(height / VCR_ROW_HEIGHT, sizeof(vcr_row_meta_t)); + if (!vcr->meta) + return -ENOMEM; + + if (old) { + memcpy(vcr->meta, old, (vcr->height / VCR_ROW_HEIGHT) * sizeof(vcr_row_meta_t)); + free(old); + } + } + /* we're going outside the current allocation dimensions, so we need to involve the backend * in _really_ resizing. */ @@ -1195,35 +1216,32 @@ void vcr_mark_finish_line(vcr_t *vcr, vcr_layer_t layer, int row) } -/* draw a bar at the current phase into the specified layer of t % with a minimum of min_height pixels. +/* draw a bar at the current phase into the specified layer of height pixels * * the only layers supported right now are grapha/graphb */ -void vcr_draw_bar(vcr_t *vcr, vcr_layer_t layer, int row, float t, int min_height) +void vcr_draw_bar(vcr_t *vcr, vcr_layer_t layer, vcr_bar_base_t base, int row, int height) { - int height, y = row * VCR_ROW_HEIGHT; + int y = row * VCR_ROW_HEIGHT; assert(vcr); assert(vcr->backend); assert(row >= 0); assert(layer == VCR_LAYER_GRAPHA || layer == VCR_LAYER_GRAPHB); - assert(min_height >= 0 && min_height < (VCR_ROW_HEIGHT - 1)); + assert(height >= 0 && height < VCR_ROW_HEIGHT); if ((row + 1) * VCR_ROW_HEIGHT >= vcr->height) return; - height = fabsf(t) * (float)(VCR_ROW_HEIGHT - 1); - - if (height < min_height) - height = min_height; - - /* clamp the height to not potentially overflow */ - if (height > (VCR_ROW_HEIGHT - 1)) - height = (VCR_ROW_HEIGHT - 1); - - /* negative values project down from the top, positive up from bottom */ - if (t > 0.f) + switch (base) { + case VCR_BAR_BASE_BOTTOM: y += VCR_ROW_HEIGHT - height - 1; + break; + case VCR_BAR_BASE_TOP: + break; + default: + assert(0); + } switch (vcr->backend->type) { #ifdef USE_XLIB @@ -1296,6 +1314,11 @@ void vcr_clear_row(vcr_t *vcr, vcr_layer_t layer, int row, int x, int width) if ((row + 1) * VCR_ROW_HEIGHT >= vcr->height) return; + if (layer == VCR_LAYER_META) { + vcr->meta[row] = (vcr_row_meta_t){}; + return; + } + switch (vcr->backend->type) { #ifdef USE_XLIB case VCR_BACKEND_TYPE_XLIB: { @@ -1351,6 +1374,20 @@ void vcr_clear_row(vcr_t *vcr, vcr_layer_t layer, int row, int x, int width) } +void vcr_set_row_palette(vcr_t *vcr, int row, vcr_row_palette_t palette) +{ + assert(vcr); + assert(vcr->backend); + assert(palette < VCR_ROW_PALETTE_CNT); + assert(row >= 0); + + if ((row + 1) * VCR_ROW_HEIGHT >= vcr->height) + return; + + vcr->meta[row].palette = palette; +} + + /* copy what's below a given row up by one row across all the layers */ void vcr_shift_below_row_up_one(vcr_t *vcr, int row) { @@ -1363,6 +1400,16 @@ void vcr_shift_below_row_up_one(vcr_t *vcr, int row) assert(*(vcr->hierarchy_end_ptr) >= row); + { + vcr_row_meta_t *dest = &vcr->meta[row]; + vcr_row_meta_t *src = &vcr->meta[(1 + row)]; + size_t len = ((1 + *(vcr->hierarchy_end_ptr)) - (1 + row)) * sizeof(vcr_row_meta_t); + + assert(*(vcr->hierarchy_end_ptr) >= row); + + memmove(dest, src, len); + } + switch (vcr->backend->type) { #ifdef USE_XLIB case VCR_BACKEND_TYPE_XLIB: { @@ -1432,6 +1479,14 @@ void vcr_shift_below_row_down_one(vcr_t *vcr, int row) if (dest_y >= vcr->height) return; + { + vcr_row_meta_t *dest = &vcr->meta[row + 1]; + vcr_row_meta_t *src = &vcr->meta[row]; + size_t len = ((vcr->height / VCR_ROW_HEIGHT) - (row + 1)) * sizeof(vcr_row_meta_t); + + memmove(dest, src, len); + } + switch (vcr->backend->type) { #ifdef USE_XLIB case VCR_BACKEND_TYPE_XLIB: { @@ -1599,12 +1654,17 @@ void vcr_stash_row(vcr_t *vcr, vcr_layer_t layer, int row) { assert(vcr); assert(vcr->backend); - assert(layer == VCR_LAYER_GRAPHA || layer == VCR_LAYER_GRAPHB); + assert(layer == VCR_LAYER_GRAPHA || layer == VCR_LAYER_GRAPHB || layer == VCR_LAYER_META); /* for now we only support stashing graphs */ if ((row + 1) * VCR_ROW_HEIGHT >= vcr->height) return; + if (layer == VCR_LAYER_META) { + vcr->tmp_meta = vcr->meta[row]; + return; + } + switch (vcr->backend->type) { #ifdef USE_XLIB case VCR_BACKEND_TYPE_XLIB: { @@ -1658,11 +1718,16 @@ void vcr_unstash_row(vcr_t *vcr, vcr_layer_t layer, int row) { assert(vcr); assert(vcr->backend); - assert(layer == VCR_LAYER_GRAPHA || layer == VCR_LAYER_GRAPHB); + assert(layer == VCR_LAYER_GRAPHA || layer == VCR_LAYER_GRAPHB || layer == VCR_LAYER_META); if ((row + 1) * VCR_ROW_HEIGHT >= vcr->height) return; + if (layer == VCR_LAYER_META) { + vcr->meta[row] = vcr->tmp_meta; + return; + } + switch (vcr->backend->type) { #ifdef USE_XLIB case VCR_BACKEND_TYPE_XLIB: { @@ -2253,9 +2318,13 @@ static int vcr_present_mem_to_png(vcr_t *vcr, vcr_dest_t *dest) marker_distance = *(vcr->marker_distance_ptr); for (int i = 0; i < n_rows; i++) { - uint8_t *d = row_pixels; - uint8_t mask = (0x1 << VCR_LAYER_GRAPHA) | (0x1 << VCR_LAYER_GRAPHB); - uint8_t odd = ((VCR_ODD << 4 | VCR_ODD) * (i & 0x1)); + uint8_t *d = row_pixels; + uint8_t mask = (0x1 << VCR_LAYER_GRAPHA) | (0x1 << VCR_LAYER_GRAPHB); + uint8_t odd = ((VCR_ODD << 4 | VCR_ODD) * (i & 0x1)); + vcr_row_palette_t row_pal = vcr->meta[i].palette; + + lut[VCR_GRAPHA] = lut[VCR_GRAPHA_ODD] = VCR_LUT_ROWSUB0_A + (row_pal << 1); + lut[VCR_GRAPHB] = lut[VCR_GRAPHB_ODD] = VCR_LUT_ROWSUB0_B + (row_pal << 1); /* The graph layers need to be moved to vcr->phase, since the per-sample updates just draw * individual graph bars without bothering to move the whole graph layer every sample. @@ -35,9 +35,25 @@ typedef enum vcr_layer_t { VCR_LAYER_SHADOW, /* the shadow layer below the text (XXX: this must be kept after text) */ VCR_LAYER_GRAPHA, /* the graph A layer below the shadow layer */ VCR_LAYER_GRAPHB, /* the graph B layer below the shadow layer */ + VCR_LAYER_META, /* the meta layer carries metadata about the row (today only the palette exists here), you can't draw to it */ VCR_LAYER_CNT, } vcr_layer_t; +typedef enum vcr_bar_base_t { + VCR_BAR_BASE_BOTTOM, + VCR_BAR_BASE_TOP, + VCR_BAR_BASE_CNT +} vcr_bar_base_t; + +typedef enum vcr_row_palette_t { + VCR_ROW_PALETTE_0, + VCR_ROW_PALETTE_1, + VCR_ROW_PALETTE_2, + VCR_ROW_PALETTE_3, + VCR_ROW_PALETTE_4, + VCR_ROW_PALETTE_CNT, +} vcr_row_palette_t; + typedef struct vcr_backend_t vcr_backend_t; typedef struct vcr_dest_t vcr_dest_t; typedef struct vcr_t vcr_t; @@ -69,8 +85,9 @@ int vcr_resize_visible(vcr_t *vcr, int width, int height); void vcr_draw_text(vcr_t *vcr, vcr_layer_t layer, int x, int row, const vcr_str_t *strs, int n_strs, int *res_width); void vcr_draw_ortho_line(vcr_t *vcr, vcr_layer_t layer, int x1, int y1, int x2, int y2); void vcr_mark_finish_line(vcr_t *vcr, vcr_layer_t layer, int row); -void vcr_draw_bar(vcr_t *vcr, vcr_layer_t layer, int row, float t, int min_height); +void vcr_draw_bar(vcr_t *vcr, vcr_layer_t layer, vcr_bar_base_t base, int row, int height); void vcr_clear_row(vcr_t *vcr, vcr_layer_t layer, int row, int x, int width); +void vcr_set_row_palette(vcr_t *vcr, int row, vcr_row_palette_t palette); void vcr_shift_below_row_up_one(vcr_t *vcr, int row); void vcr_shift_below_row_down_one(vcr_t *vcr, int row); void vcr_shadow_row(vcr_t *vcr, vcr_layer_t layer, int row); |