diff options
Diffstat (limited to 'src/vmon.c')
-rw-r--r-- | src/vmon.c | 293 |
1 files changed, 117 insertions, 176 deletions
@@ -16,11 +16,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <X11/Xlib.h> #include <assert.h> #include <limits.h> -#include <png.h> -#include <poll.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -33,20 +30,17 @@ #include <unistd.h> #include "charts.h" +#include "vcr.h" #include "util.h" -#include "xserver.h" /* vmon exposes the monitoring charts to the shell in an strace-like cli */ typedef struct vmon_t { - vwm_xserver_t *xserver; + vcr_backend_t *vcr_backend; + vcr_dest_t *vcr_dest; vwm_charts_t *charts; vwm_chart_t *chart; - Atom wm_protocols_atom; - Atom wm_delete_atom; - Window window; int width, height; - Picture picture; int pid; int done; int linger; @@ -54,6 +48,8 @@ typedef struct vmon_t { int snapshots_interval; int snapshot; int now_names; + int headless; + int hertz; char *output_dir; char *name; unsigned n_snapshots; @@ -163,20 +159,18 @@ static int parse_flag_str(const char * const *flag, const char * const *end, con /* set vmon->{width,height} to fullscreen dimensions */ static int set_fullscreen(vmon_t *vmon) { - XWindowAttributes wattr; - +#ifdef USE_XLIB assert(vmon); - assert(vmon->xserver); - if (!XGetWindowAttributes(vmon->xserver->display, XSERVER_XROOT(vmon->xserver), &wattr)) { - VWM_ERROR("unable to get root window attributes"); + if (vcr_backend_get_dimensions(vmon->vcr_backend, &vmon->width, &vmon->height) < 0) { + VWM_ERROR("unable to get vcr_backend dimensions"); return 0; } - vmon->width = wattr.width; - vmon->height = wattr.height; - return 1; +#else /* USE_XLIB */ + return -ENOTSUP; +#endif } @@ -188,9 +182,10 @@ static void print_help(void) " Flag Description\n" "-------------------------------------------------------------------------------\n" " -- Sentinel, subsequent arguments form command to execute\n" - " -f --fullscreen Fullscreen window\n" + " -f --fullscreen Fullscreen window (X only; no effect with --headless) \n" + " -d --headless Headless mode; no X, only snapshots (default on no-X builds)\n" " -h --help Show this help\n" - " -H --height Window height\n" + " -H --height Chart height\n" " -l --linger Don't exit when top-level process exits\n" " -n --name Name of chart, shows in window title and output filenames\n" " -N --now-names Use current time in filenames instead of start time\n" @@ -199,7 +194,7 @@ static void print_help(void) " -i --snapshots Save a PNG snapshot every N seconds (SIGUSR1 also snapshots)\n" " -s --snapshot Save a PNG snapshot upon receiving SIGCHLD\n" " -v --version Print version\n" - " -W --width Window width\n" + " -W --width Chart width\n" " -z --hertz Sample rate in hertz\n" "-------------------------------------------------------------------------------" ); @@ -316,10 +311,12 @@ static char * arg_interpolate(const vmon_t *vmon, const char *arg) } switch (c) { +#ifdef USE_XLIB case 'W': /* vmon's X window id in hex */ - fprintf(memfp, "%#x", (unsigned)vmon->window); + assert(!vmon->headless); + fprintf(memfp, "%#x", vcr_dest_xwindow_get_id(vmon->vcr_dest)); break; - +#endif case 'n': /* --name verbatim */ if (!vmon->name) { VWM_ERROR("%%n requires --name"); @@ -423,6 +420,9 @@ static int vmon_handle_argv(vmon_t *vmon, int argc, const char * const *argv) } else if (is_flag(*argv, "-N", "--now-names")) { vmon->now_names = 1; last = argv; + } else if (is_flag(*argv, "-d", "--headless")) { + vmon->headless = 1; + last = argv; } else if (is_flag(*argv, "-i", "--snapshots")) { if (!parse_flag_int(argv, end, argv + 1, 1, INT_MAX, &vmon->snapshots_interval)) return 0; @@ -446,13 +446,9 @@ static int vmon_handle_argv(vmon_t *vmon, int argc, const char * const *argv) last = argv; break; } else if (is_flag(*argv, "-z", "--hertz")) { - int hertz; - - if (!parse_flag_int(argv, end, argv + 1, 1, 1000, &hertz)) + if (!parse_flag_int(argv, end, argv + 1, 1, 1000, &vmon->hertz)) return 0; - vwm_charts_rate_set(vmon->charts, hertz); - last = ++argv; } else if (is_flag(*argv, "-h", "--help")) { print_help(); @@ -548,12 +544,11 @@ int vmon_execv(vmon_t *vmon) } -/* parse argv, connect to X, create window, attach libvmon to monitored process */ +/* parse argv, init charts/vcr_backend/vcr_dest, attach libvmon to monitored process via vwm_chart_create() */ static vmon_t * vmon_startup(int argc, const char * const *argv) { - vmon_t *vmon; - XRenderPictureAttributes pattr = {}; - XWindowAttributes wattr; + vcr_backend_type_t backend_type; + vmon_t *vmon; assert(argv); @@ -574,34 +569,44 @@ static vmon_t * vmon_startup(int argc, const char * const *argv) goto _err_free; } - vmon->xserver = vwm_xserver_open(); - if (!vmon->xserver) { - VWM_ERROR("unable to open xserver"); + if (!vmon_handle_argv(vmon, argc, argv)) { + VWM_ERROR("unable to handle arguments"); + goto _err_vcr; + } + +#ifdef USE_XLIB + if (!vmon->headless) + backend_type = VCR_BACKEND_TYPE_XLIB; +#else + /* force headless without X support */ + vmon->headless = 1; +#endif + if (vmon->headless) + backend_type = VCR_BACKEND_TYPE_MEM; + + vmon->vcr_backend = vcr_backend_new(backend_type, NULL); + if (!vmon->vcr_backend) { + VWM_ERROR("unable to create vcr backend"); goto _err_free; } - vmon->wm_delete_atom = XInternAtom(vmon->xserver->display, "WM_DELETE_WINDOW", False); - vmon->wm_protocols_atom = XInternAtom(vmon->xserver->display, "WM_PROTOCOLS", False); - - vmon->charts = vwm_charts_create(vmon->xserver); + vmon->charts = vwm_charts_create(vmon->vcr_backend); if (!vmon->charts) { VWM_ERROR("unable to create charts instance"); - goto _err_xserver; + goto _err_vcr; } - if (!vmon_handle_argv(vmon, argc, argv)) { - VWM_ERROR("unable to handle arguments"); - goto _err_xserver; - } + if (vmon->hertz) + vwm_charts_rate_set(vmon->charts, vmon->hertz); if (signal(SIGUSR1, handle_sigusr1) == SIG_ERR) { VWM_PERROR("unable to set SIGUSR1 handler"); - goto _err_xserver; + goto _err_vcr; } if (signal(SIGALRM, handle_sigusr1) == SIG_ERR) { VWM_PERROR("unable to set SIGALRM handler"); - goto _err_xserver; + goto _err_vcr; } if (vmon->snapshots_interval) { @@ -614,18 +619,19 @@ static vmon_t * vmon_startup(int argc, const char * const *argv) }, NULL); if (r < 0) { VWM_PERROR("unable to set interval timer"); - goto _err_xserver; + goto _err_vcr; } } - vmon->window = XCreateSimpleWindow(vmon->xserver->display, XSERVER_XROOT(vmon->xserver), 0, 0, vmon->width, vmon->height, 1, 0, 0); - if (vmon->name) - XStoreName(vmon->xserver->display, vmon->window, vmon->name); - XGetWindowAttributes(vmon->xserver->display, vmon->window, &wattr); - vmon->picture = XRenderCreatePicture(vmon->xserver->display, vmon->window, XRenderFindVisualFormat(vmon->xserver->display, wattr.visual), 0, &pattr); - XMapWindow(vmon->xserver->display, vmon->window); - XSelectInput(vmon->xserver->display, vmon->window, StructureNotifyMask|ExposureMask); - XSync(vmon->xserver->display, False); +#ifdef USE_XLIB + if (!vmon->headless) { + vmon->vcr_dest = vcr_dest_xwindow_new(vmon->vcr_backend, vmon->name, vmon->width, vmon->height); + if (!vmon->vcr_dest) { + VWM_ERROR("unable to create destination XWindow"); + goto _err_vcr; + } + } +#endif if (vmon->execv) { if (vmon->pid) { @@ -646,10 +652,9 @@ static vmon_t * vmon_startup(int argc, const char * const *argv) return vmon; _err_win: - XDestroyWindow(vmon->xserver->display, vmon->window); - XRenderFreePicture(vmon->xserver->display, vmon->picture); -_err_xserver: - vwm_xserver_close(vmon->xserver); + (void) vcr_dest_free(vmon->vcr_dest); +_err_vcr: + (void) vcr_backend_free(vmon->vcr_backend); _err_free: free(vmon); _err: @@ -661,12 +666,11 @@ _err: static void vmon_shutdown(vmon_t *vmon) { assert(vmon); - assert(vmon->xserver); - XDestroyWindow(vmon->xserver->display, vmon->window); vwm_chart_destroy(vmon->charts, vmon->chart); vwm_charts_destroy(vmon->charts); - vwm_xserver_close(vmon->xserver); + (void) vcr_dest_free(vmon->vcr_dest); + (void) vcr_backend_free(vmon->vcr_backend); free(vmon); } @@ -682,84 +686,9 @@ static void vmon_resize(vmon_t *vmon, int width, int height) } -static int vmon_snapshot_as_png(vmon_t *vmon, FILE *output) -{ - XImage *chart_as_ximage; - png_bytepp row_pointers; - png_infop info_ctx; - png_structp png_ctx; - - assert(vmon); - assert(output); - - vwm_chart_render_as_ximage(vmon->charts, vmon->chart, NULL, &chart_as_ximage); - - row_pointers = malloc(sizeof(void *) * chart_as_ximage->height); - if (!row_pointers) { - XDestroyImage(chart_as_ximage); - - return -ENOMEM; - } - - for (unsigned i = 0; i < chart_as_ximage->height; i++) - row_pointers[i] = &((png_byte *)chart_as_ximage->data)[i * chart_as_ximage->bytes_per_line]; - - png_ctx = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ctx) { - free(row_pointers); - XDestroyImage(chart_as_ximage); - - return -ENOMEM; - } - - info_ctx = png_create_info_struct(png_ctx); - if (!info_ctx) { - png_destroy_write_struct(&png_ctx, NULL); - XDestroyImage(chart_as_ximage); - free(row_pointers); - - return -ENOMEM; - } - - png_init_io(png_ctx, output); - - if (setjmp(png_jmpbuf(png_ctx)) != 0) { - png_destroy_write_struct(&png_ctx, &info_ctx); - XDestroyImage(chart_as_ximage); - free(row_pointers); - - return -ENOMEM; - } - - /* XXX: I'm sure this is making flawed assumptions about the color format - * and type etc, but this makes it work for me and that's Good Enough for now. - * One can easily turn runtime mapping of X color formats, endianness, and packing - * details to whatever a file format like PNG can express into a tar-filled rabbithole - * of fruitless wankery. - */ - png_set_bgr(png_ctx); - png_set_IHDR(png_ctx, info_ctx, - chart_as_ximage->width, - chart_as_ximage->height, - 8, - PNG_COLOR_TYPE_RGBA, - PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, - PNG_FILTER_TYPE_BASE); - - png_write_info(png_ctx, info_ctx); - png_write_image(png_ctx, row_pointers); - png_write_end(png_ctx, NULL); - - XDestroyImage(chart_as_ximage); - free(row_pointers); - - return 0; -} - - static int vmon_snapshot(vmon_t *vmon) { +#ifdef USE_PNG time_t now, *t_ptr = &now; struct tm *t; char t_str[32]; @@ -806,11 +735,19 @@ static int vmon_snapshot(vmon_t *vmon) if (!output) return -errno; - r = vmon_snapshot_as_png(vmon, output); - if (r < 0) { - (void) unlink(tmp_path); - (void) fclose(output); - return r; + { + vcr_dest_t *png_dest; + + png_dest = vcr_dest_png_new(vmon->vcr_backend, output); + if (!png_dest) { + (void) unlink(tmp_path); + (void) fclose(output); + return -ENOMEM; + } + + /* FIXME: render/libpng errors need to propagate and be handled */ + vwm_chart_render(vmon->charts, vmon->chart, VCR_PRESENT_OP_SRC, png_dest, -1, -1, -1, -1); + png_dest = vcr_dest_free(png_dest); } fflush(output); @@ -821,50 +758,48 @@ static int vmon_snapshot(vmon_t *vmon) return -errno; return 0; +#else + return -ENOTSUP; +#endif } - -/* handle the next X event, may block */ +/* handle the next backend event, may block */ static void vmon_process_event(vmon_t *vmon) { - XEvent ev; + int width, height; + vcr_backend_event_t ev; assert(vmon); - XNextEvent(vmon->xserver->display, &ev); - - switch (ev.type) { - case ConfigureNotify: - vmon_resize(vmon, ev.xconfigure.width, ev.xconfigure.height); - vwm_chart_compose(vmon->charts, vmon->chart, NULL); - vwm_chart_render(vmon->charts, vmon->chart, PictOpSrc, vmon->picture, 0, 0, vmon->width, vmon->height); + ev = vcr_backend_next_event(vmon->vcr_backend, &width, &height); + switch (ev) { + case VCR_BACKEND_EVENT_RESIZE: + vmon_resize(vmon, width, height); + vwm_chart_compose(vmon->charts, vmon->chart); + vwm_chart_render(vmon->charts, vmon->chart, VCR_PRESENT_OP_SRC, vmon->vcr_dest, -1, -1, -1, -1); break; - case Expose: - vwm_chart_render(vmon->charts, vmon->chart, PictOpSrc, vmon->picture, 0, 0, vmon->width, vmon->height); + case VCR_BACKEND_EVENT_REDRAW: + vwm_chart_render(vmon->charts, vmon->chart, VCR_PRESENT_OP_SRC, vmon->vcr_dest, -1, -1, -1, -1); break; - case ClientMessage: - if (ev.xclient.message_type != vmon->wm_protocols_atom) - break; - - if (ev.xclient.data.l[0] != vmon->wm_delete_atom) - break; - + case VCR_BACKEND_EVENT_QUIT: vmon->done = 1; break; + case VCR_BACKEND_EVENT_NOOP: + break; + default: - VWM_TRACE("unhandled event: %x\n", ev.type); + VWM_TRACE("unhandled event: %x\n", ev); } } int main(int argc, const char * const *argv) { - vmon_t *vmon; - struct pollfd pfd = { .events = POLLIN }; - int ret = EXIT_SUCCESS; + vmon_t *vmon; + int ret = EXIT_SUCCESS; vmon = vmon_startup(argc, argv); if (!vmon) { @@ -872,17 +807,21 @@ int main(int argc, const char * const *argv) return EXIT_FAILURE; } - pfd.fd = ConnectionNumber(vmon->xserver->display); - while (!vmon->done) { int delay; + /* update only actually updates when enough time has passed, and always returns how much time + * to sleep before calling update again (-1 for infinity (paused)). + * + * if 0 is returned, no update was performed/no changes occured. + */ if (vwm_charts_update(vmon->charts, &delay)) { - vwm_chart_compose(vmon->charts, vmon->chart, NULL); - vwm_chart_render(vmon->charts, vmon->chart, PictOpSrc, vmon->picture, 0, 0, vmon->width, vmon->height); + vwm_chart_compose(vmon->charts, vmon->chart); + if (!vmon->headless) + vwm_chart_render(vmon->charts, vmon->chart, VCR_PRESENT_OP_SRC, vmon->vcr_dest, -1, -1, -1, -1); } - if (XPending(vmon->xserver->display) || poll(&pfd, 1, delay) > 0) + if (vcr_backend_poll(vmon->vcr_backend, delay) > 0) vmon_process_event(vmon); if (got_sigint > 2 || got_sigquit > 2) { @@ -898,10 +837,10 @@ int main(int argc, const char * const *argv) } if (got_sigchld) { - int status; + int status, r; - if (vmon->snapshot && vmon_snapshot(vmon) < 0) - VWM_ERROR("error saving snapshot"); + if (vmon->snapshot && (r = vmon_snapshot(vmon)) < 0) + VWM_ERROR("error saving snapshot: %s", strerror(-r)); got_sigchld = 0; wait(&status); @@ -914,9 +853,11 @@ int main(int argc, const char * const *argv) } } - if (got_sigusr1) { - if (vmon_snapshot(vmon) < 0) - VWM_ERROR("error saving snapshot"); + if (got_sigusr1 || (vmon->snapshots_interval && !vmon->n_snapshots)) { + int r; + + if ((r = vmon_snapshot(vmon)) < 0) + VWM_ERROR("error saving snapshot: %s", strerror(-r)); got_sigusr1 = 0; } |