michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * ``thrashview'' is a program that reads a binary stream of addresses michael@0: * from stdin and displays the pattern graphically in a window. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #define GET_DISPLAY_FD(display_) ConnectionNumber(display_) michael@0: michael@0: static bool opt_working_set = false; michael@0: static bool opt_fixed = false; michael@0: michael@0: static Display *display; michael@0: static Window window; michael@0: static GC gc; michael@0: static XColor colors[256]; michael@0: const unsigned int cellsize = 4; michael@0: static unsigned int width = 64 * cellsize; michael@0: static unsigned int height = 64 * cellsize; michael@0: michael@0: #define PAGESIZE 4096 michael@0: #define MAXPAGES 4096 // should hold 16MB worth of code michael@0: static unsigned int minpage = static_cast(-1); michael@0: static unsigned int maxpage = 0; michael@0: static unsigned char pages[MAXPAGES]; michael@0: michael@0: michael@0: /** michael@0: * Create a simple window and the X objects we'll need to talk with it. michael@0: */ michael@0: static int michael@0: init() michael@0: { michael@0: display = XOpenDisplay(0); michael@0: if (! display) michael@0: return 0; michael@0: michael@0: window = michael@0: XCreateSimpleWindow(display, michael@0: RootWindow(display, 0), michael@0: 1, 1, width, height, michael@0: 0, michael@0: BlackPixel(display, 0), michael@0: BlackPixel(display, 0)); michael@0: michael@0: if (! window) michael@0: return 0; michael@0: michael@0: gc = XCreateGC(display, window, 0, 0); michael@0: if (! gc) michael@0: return 0; michael@0: michael@0: // Set up a grayscale michael@0: const unsigned int ncolors = sizeof colors / sizeof colors[0]; michael@0: const unsigned short step = 65536 / ncolors; michael@0: unsigned short brightness = 0; michael@0: michael@0: XColor *color = colors; michael@0: XColor *limit = colors + ncolors; michael@0: for (; color < limit; ++color, brightness += step) { michael@0: color->red = brightness; michael@0: color->green = brightness; michael@0: color->blue = brightness; michael@0: XAllocColor(display, DefaultColormap(display, 0), color); michael@0: } michael@0: michael@0: // We want exposes and resizes. michael@0: XSelectInput(display, window, ExposureMask | StructureNotifyMask); michael@0: michael@0: XMapWindow(display, window); michael@0: XFlush(display); michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: /** michael@0: * Age pages that haven't been recently touched. michael@0: */ michael@0: static void michael@0: decay() michael@0: { michael@0: int ws_immediate = 0, ws_longterm = 0; michael@0: michael@0: unsigned char *page = pages; michael@0: unsigned char *limit = pages + (maxpage - minpage) + 1; michael@0: for (; page < limit; ++page) { michael@0: if (opt_working_set) { michael@0: if (*page == 255) michael@0: ++ws_immediate; michael@0: if (*page) michael@0: ++ws_longterm; michael@0: } michael@0: michael@0: if (*page) { michael@0: *page /= 8; michael@0: *page *= 7; michael@0: } michael@0: } michael@0: michael@0: if (opt_working_set) { michael@0: dec(cout); michael@0: cout << "immediate: " << ws_immediate << " pages, "; michael@0: cout << "longterm: " << ws_longterm << " pages, "; michael@0: cout << "mapped: " << ((maxpage - minpage) + 1) << " pages"; michael@0: cout << endl; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Blast the state of our pages to the screen. michael@0: */ michael@0: static int michael@0: handle_expose(const XExposeEvent& event) michael@0: { michael@0: //printf("handle_expose(%d, %d, %d, %d)\n", event.x, event.y, event.width, event.height); michael@0: michael@0: int i = event.x / cellsize; michael@0: int imost = i + event.width / cellsize + 1; michael@0: michael@0: int j = event.y / cellsize; michael@0: int jmost = j + event.height / cellsize + 1; michael@0: michael@0: unsigned char *last_cell = pages + maxpage - minpage; michael@0: unsigned char *row = pages + j; michael@0: for (int y = j * cellsize, ymost = jmost * cellsize; michael@0: y < ymost; michael@0: y += cellsize, row += width / cellsize) { michael@0: unsigned char *cell = row + i; michael@0: for (int x = i * cellsize, xmost = imost * cellsize; michael@0: x < xmost; michael@0: x += cellsize, ++cell) { michael@0: unsigned int pixel = (cell <= last_cell) ? colors[*cell].pixel : colors[0].pixel; michael@0: XSetForeground(display, gc, pixel); michael@0: XFillRectangle(display, window, gc, x, y, cellsize - 1, cellsize - 1); michael@0: } michael@0: } michael@0: michael@0: XFlush(display); michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: /** michael@0: * Invalidate the entire window. michael@0: */ michael@0: static void michael@0: invalidate_window() michael@0: { michael@0: XExposeEvent event; michael@0: event.x = event.y = 0; michael@0: event.width = width; michael@0: event.height = height; michael@0: michael@0: handle_expose(event); michael@0: } michael@0: michael@0: /** michael@0: * Handle a configure event. michael@0: */ michael@0: static int michael@0: handle_configure(const XConfigureEvent& event) michael@0: { michael@0: //printf("handle_resize(%d, %d)\n", event.width, event.height); michael@0: width = event.width - event.width % cellsize; michael@0: height = event.height; michael@0: return 1; michael@0: } michael@0: michael@0: /** michael@0: * Filter to select any message. michael@0: */ michael@0: static Bool michael@0: any_event(Display *display, XEvent *event, XPointer arg) michael@0: { michael@0: return 1; michael@0: } michael@0: michael@0: /** michael@0: * An X event occurred. Process it and flush the queue. michael@0: */ michael@0: static int michael@0: handle_xevents() michael@0: { michael@0: int ok; michael@0: michael@0: XEvent event; michael@0: XNextEvent(display, &event); michael@0: do { michael@0: switch (event.type) { michael@0: case Expose: michael@0: ok = handle_expose(reinterpret_cast(event)); michael@0: break; michael@0: michael@0: case ConfigureNotify: michael@0: ok = handle_configure(reinterpret_cast(event)); michael@0: break; michael@0: michael@0: default: michael@0: ok = 1; michael@0: } michael@0: } while (ok && XCheckIfEvent(display, &event, any_event, 0)); michael@0: michael@0: return ok; michael@0: } michael@0: michael@0: /** michael@0: * Read address data from stdin. michael@0: */ michael@0: static int michael@0: read_addrs() michael@0: { michael@0: unsigned int buf[1024]; michael@0: ssize_t count; michael@0: while ((count = read(0, buf, sizeof buf)) > 0) { michael@0: if (count % sizeof(unsigned int)) michael@0: cerr << "truncating unaligned read" << endl; michael@0: michael@0: count /= sizeof buf[0]; michael@0: michael@0: unsigned int *addr = reinterpret_cast(buf); michael@0: unsigned int *limit = addr + count; michael@0: michael@0: for (; addr < limit; ++addr) { michael@0: // map the address to a page michael@0: unsigned int page = *addr / PAGESIZE; michael@0: michael@0: // XXX Don't let stray addresses bring us down. Should michael@0: // really fix this by knowing what the ranges of addresses michael@0: // we ought to expect are (e.g., by reading the symtab) michael@0: if (maxpage && page > maxpage && page - maxpage > MAXPAGES) michael@0: continue; michael@0: michael@0: if (! opt_fixed) { michael@0: // Potentially adjust minpage and maxpage to michael@0: // accomodate an out-of-bounds address. michael@0: if (page < minpage) { michael@0: if (maxpage) { michael@0: // everything needs to shift. michael@0: unsigned int shift = minpage - page; michael@0: memmove(pages + shift, pages, maxpage - minpage); michael@0: memset(pages, 0, shift); michael@0: } michael@0: minpage = page; michael@0: } michael@0: michael@0: if (page > maxpage) michael@0: maxpage = page; michael@0: } michael@0: michael@0: page -= minpage; michael@0: pages[page] = 255; michael@0: } michael@0: } michael@0: michael@0: if (count < 0 && errno != EWOULDBLOCK) { michael@0: perror("read"); michael@0: return 0; michael@0: } michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: /** michael@0: * Run the program michael@0: */ michael@0: static void michael@0: run() michael@0: { michael@0: // We want non-blocking I/O on stdin so we can select on it. michael@0: fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); michael@0: michael@0: // The last time we refreshed the window. michael@0: struct timeval last; michael@0: gettimeofday(&last, 0); michael@0: michael@0: int ok; michael@0: michael@0: do { michael@0: // Select on stdin and the connection to the X server. michael@0: fd_set fds; michael@0: FD_ZERO(&fds); michael@0: FD_SET(STDIN_FILENO, &fds); michael@0: FD_SET(GET_DISPLAY_FD(display), &fds); michael@0: michael@0: struct timeval tv; michael@0: tv.tv_sec = 1; michael@0: tv.tv_usec = 0; michael@0: michael@0: ok = select(GET_DISPLAY_FD(display) + 1, &fds, 0, 0, &tv); michael@0: if (ok < 0) michael@0: break; michael@0: michael@0: if (maxpage) { michael@0: // See if we've waited long enough to refresh the window. michael@0: struct timeval now; michael@0: gettimeofday(&now, 0); michael@0: michael@0: if (now.tv_sec != last.tv_sec) { michael@0: // At least a second has gone by. Decay and refresh. michael@0: last = now; michael@0: decay(); michael@0: invalidate_window(); michael@0: } michael@0: else if (now.tv_usec - last.tv_usec > 100000) { michael@0: // At least 100msec have gone by. Refresh. michael@0: last.tv_usec = now.tv_usec; michael@0: invalidate_window(); michael@0: } michael@0: } michael@0: michael@0: // Now check for X events and input. michael@0: ok = 1; michael@0: michael@0: if (FD_ISSET(GET_DISPLAY_FD(display), &fds)) michael@0: ok = handle_xevents(); michael@0: michael@0: if (FD_ISSET(STDIN_FILENO, &fds)) michael@0: ok = read_addrs(); michael@0: } while (ok); michael@0: } michael@0: michael@0: /** michael@0: * Tear down our window and stuff. michael@0: */ michael@0: static void michael@0: finish() michael@0: { michael@0: if (window) { michael@0: XUnmapWindow(display, window); michael@0: XDestroyWindow(display, window); michael@0: } michael@0: michael@0: if (display) michael@0: XCloseDisplay(display); michael@0: } michael@0: michael@0: static struct option opts[] = { michael@0: { "working-set", no_argument, 0, 'w' }, michael@0: { "min", required_argument, 0, 'm' }, michael@0: { "size", required_argument, 0, 's' }, michael@0: { "max", required_argument, 0, 'x' }, michael@0: { 0, 0, 0, 0 } michael@0: }; michael@0: michael@0: static void michael@0: usage() michael@0: { michael@0: cerr << "thrashview [--working-set] [--min=] [--max=] [--size=]" << endl; michael@0: } michael@0: michael@0: /** michael@0: * Program starts here. michael@0: */ michael@0: int michael@0: main(int argc, char *argv[]) michael@0: { michael@0: int size = 0; michael@0: michael@0: while (1) { michael@0: int option_index = 0; michael@0: int c = getopt_long(argc, argv, "wm:x:s:", opts, &option_index); michael@0: michael@0: if (c < 0) michael@0: break; michael@0: michael@0: switch (c) { michael@0: case 'w': michael@0: opt_working_set = true; michael@0: break; michael@0: michael@0: case 'm': michael@0: minpage = strtol(optarg, 0, 0) / PAGESIZE; michael@0: opt_fixed = true; michael@0: break; michael@0: michael@0: case 's': michael@0: size = strtol(optarg, 0, 0) / PAGESIZE; michael@0: break; michael@0: michael@0: case 'x': michael@0: maxpage = strtol(optarg, 0, 0) / PAGESIZE; michael@0: opt_fixed = true; michael@0: break; michael@0: michael@0: default: michael@0: usage(); michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: if (minpage && !maxpage) { michael@0: if (!size) { michael@0: cerr << argv[0] << ": minpage specified without maxpage or size" << endl; michael@0: return 1; michael@0: } michael@0: michael@0: maxpage = minpage + size; michael@0: } michael@0: michael@0: if (opt_fixed && minpage > maxpage) { michael@0: cerr << argv[0] << ": invalid page range" << endl; michael@0: return 1; michael@0: } michael@0: michael@0: if (init()) michael@0: run(); michael@0: michael@0: finish(); michael@0: michael@0: return 0; michael@0: }