1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/footprint/thrashview.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,424 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * ``thrashview'' is a program that reads a binary stream of addresses 1.10 + * from stdin and displays the pattern graphically in a window. 1.11 + */ 1.12 + 1.13 +#include <errno.h> 1.14 +#include <fcntl.h> 1.15 +#include <stdio.h> 1.16 +#include <stdlib.h> 1.17 +#include <string.h> 1.18 +#include <sys/time.h> 1.19 +#include <unistd.h> 1.20 +#include <X11/Xlib.h> 1.21 +#include <fstream> 1.22 +#include <getopt.h> 1.23 + 1.24 +#define GET_DISPLAY_FD(display_) ConnectionNumber(display_) 1.25 + 1.26 +static bool opt_working_set = false; 1.27 +static bool opt_fixed = false; 1.28 + 1.29 +static Display *display; 1.30 +static Window window; 1.31 +static GC gc; 1.32 +static XColor colors[256]; 1.33 +const unsigned int cellsize = 4; 1.34 +static unsigned int width = 64 * cellsize; 1.35 +static unsigned int height = 64 * cellsize; 1.36 + 1.37 +#define PAGESIZE 4096 1.38 +#define MAXPAGES 4096 // should hold 16MB worth of code 1.39 +static unsigned int minpage = static_cast<unsigned int>(-1); 1.40 +static unsigned int maxpage = 0; 1.41 +static unsigned char pages[MAXPAGES]; 1.42 + 1.43 + 1.44 +/** 1.45 + * Create a simple window and the X objects we'll need to talk with it. 1.46 + */ 1.47 +static int 1.48 +init() 1.49 +{ 1.50 + display = XOpenDisplay(0); 1.51 + if (! display) 1.52 + return 0; 1.53 + 1.54 + window = 1.55 + XCreateSimpleWindow(display, 1.56 + RootWindow(display, 0), 1.57 + 1, 1, width, height, 1.58 + 0, 1.59 + BlackPixel(display, 0), 1.60 + BlackPixel(display, 0)); 1.61 + 1.62 + if (! window) 1.63 + return 0; 1.64 + 1.65 + gc = XCreateGC(display, window, 0, 0); 1.66 + if (! gc) 1.67 + return 0; 1.68 + 1.69 + // Set up a grayscale 1.70 + const unsigned int ncolors = sizeof colors / sizeof colors[0]; 1.71 + const unsigned short step = 65536 / ncolors; 1.72 + unsigned short brightness = 0; 1.73 + 1.74 + XColor *color = colors; 1.75 + XColor *limit = colors + ncolors; 1.76 + for (; color < limit; ++color, brightness += step) { 1.77 + color->red = brightness; 1.78 + color->green = brightness; 1.79 + color->blue = brightness; 1.80 + XAllocColor(display, DefaultColormap(display, 0), color); 1.81 + } 1.82 + 1.83 + // We want exposes and resizes. 1.84 + XSelectInput(display, window, ExposureMask | StructureNotifyMask); 1.85 + 1.86 + XMapWindow(display, window); 1.87 + XFlush(display); 1.88 + 1.89 + return 1; 1.90 +} 1.91 + 1.92 +/** 1.93 + * Age pages that haven't been recently touched. 1.94 + */ 1.95 +static void 1.96 +decay() 1.97 +{ 1.98 + int ws_immediate = 0, ws_longterm = 0; 1.99 + 1.100 + unsigned char *page = pages; 1.101 + unsigned char *limit = pages + (maxpage - minpage) + 1; 1.102 + for (; page < limit; ++page) { 1.103 + if (opt_working_set) { 1.104 + if (*page == 255) 1.105 + ++ws_immediate; 1.106 + if (*page) 1.107 + ++ws_longterm; 1.108 + } 1.109 + 1.110 + if (*page) { 1.111 + *page /= 8; 1.112 + *page *= 7; 1.113 + } 1.114 + } 1.115 + 1.116 + if (opt_working_set) { 1.117 + dec(cout); 1.118 + cout << "immediate: " << ws_immediate << " pages, "; 1.119 + cout << "longterm: " << ws_longterm << " pages, "; 1.120 + cout << "mapped: " << ((maxpage - minpage) + 1) << " pages"; 1.121 + cout << endl; 1.122 + } 1.123 +} 1.124 + 1.125 +/** 1.126 + * Blast the state of our pages to the screen. 1.127 + */ 1.128 +static int 1.129 +handle_expose(const XExposeEvent& event) 1.130 +{ 1.131 + //printf("handle_expose(%d, %d, %d, %d)\n", event.x, event.y, event.width, event.height); 1.132 + 1.133 + int i = event.x / cellsize; 1.134 + int imost = i + event.width / cellsize + 1; 1.135 + 1.136 + int j = event.y / cellsize; 1.137 + int jmost = j + event.height / cellsize + 1; 1.138 + 1.139 + unsigned char *last_cell = pages + maxpage - minpage; 1.140 + unsigned char *row = pages + j; 1.141 + for (int y = j * cellsize, ymost = jmost * cellsize; 1.142 + y < ymost; 1.143 + y += cellsize, row += width / cellsize) { 1.144 + unsigned char *cell = row + i; 1.145 + for (int x = i * cellsize, xmost = imost * cellsize; 1.146 + x < xmost; 1.147 + x += cellsize, ++cell) { 1.148 + unsigned int pixel = (cell <= last_cell) ? colors[*cell].pixel : colors[0].pixel; 1.149 + XSetForeground(display, gc, pixel); 1.150 + XFillRectangle(display, window, gc, x, y, cellsize - 1, cellsize - 1); 1.151 + } 1.152 + } 1.153 + 1.154 + XFlush(display); 1.155 + 1.156 + return 1; 1.157 +} 1.158 + 1.159 +/** 1.160 + * Invalidate the entire window. 1.161 + */ 1.162 +static void 1.163 +invalidate_window() 1.164 +{ 1.165 + XExposeEvent event; 1.166 + event.x = event.y = 0; 1.167 + event.width = width; 1.168 + event.height = height; 1.169 + 1.170 + handle_expose(event); 1.171 +} 1.172 + 1.173 +/** 1.174 + * Handle a configure event. 1.175 + */ 1.176 +static int 1.177 +handle_configure(const XConfigureEvent& event) 1.178 +{ 1.179 + //printf("handle_resize(%d, %d)\n", event.width, event.height); 1.180 + width = event.width - event.width % cellsize; 1.181 + height = event.height; 1.182 + return 1; 1.183 +} 1.184 + 1.185 +/** 1.186 + * Filter to select any message. 1.187 + */ 1.188 +static Bool 1.189 +any_event(Display *display, XEvent *event, XPointer arg) 1.190 +{ 1.191 + return 1; 1.192 +} 1.193 + 1.194 +/** 1.195 + * An X event occurred. Process it and flush the queue. 1.196 + */ 1.197 +static int 1.198 +handle_xevents() 1.199 +{ 1.200 + int ok; 1.201 + 1.202 + XEvent event; 1.203 + XNextEvent(display, &event); 1.204 + do { 1.205 + switch (event.type) { 1.206 + case Expose: 1.207 + ok = handle_expose(reinterpret_cast<const XExposeEvent&>(event)); 1.208 + break; 1.209 + 1.210 + case ConfigureNotify: 1.211 + ok = handle_configure(reinterpret_cast<const XConfigureEvent&>(event)); 1.212 + break; 1.213 + 1.214 + default: 1.215 + ok = 1; 1.216 + } 1.217 + } while (ok && XCheckIfEvent(display, &event, any_event, 0)); 1.218 + 1.219 + return ok; 1.220 +} 1.221 + 1.222 +/** 1.223 + * Read address data from stdin. 1.224 + */ 1.225 +static int 1.226 +read_addrs() 1.227 +{ 1.228 + unsigned int buf[1024]; 1.229 + ssize_t count; 1.230 + while ((count = read(0, buf, sizeof buf)) > 0) { 1.231 + if (count % sizeof(unsigned int)) 1.232 + cerr << "truncating unaligned read" << endl; 1.233 + 1.234 + count /= sizeof buf[0]; 1.235 + 1.236 + unsigned int *addr = reinterpret_cast<unsigned int *>(buf); 1.237 + unsigned int *limit = addr + count; 1.238 + 1.239 + for (; addr < limit; ++addr) { 1.240 + // map the address to a page 1.241 + unsigned int page = *addr / PAGESIZE; 1.242 + 1.243 + // XXX Don't let stray addresses bring us down. Should 1.244 + // really fix this by knowing what the ranges of addresses 1.245 + // we ought to expect are (e.g., by reading the symtab) 1.246 + if (maxpage && page > maxpage && page - maxpage > MAXPAGES) 1.247 + continue; 1.248 + 1.249 + if (! opt_fixed) { 1.250 + // Potentially adjust minpage and maxpage to 1.251 + // accomodate an out-of-bounds address. 1.252 + if (page < minpage) { 1.253 + if (maxpage) { 1.254 + // everything needs to shift. 1.255 + unsigned int shift = minpage - page; 1.256 + memmove(pages + shift, pages, maxpage - minpage); 1.257 + memset(pages, 0, shift); 1.258 + } 1.259 + minpage = page; 1.260 + } 1.261 + 1.262 + if (page > maxpage) 1.263 + maxpage = page; 1.264 + } 1.265 + 1.266 + page -= minpage; 1.267 + pages[page] = 255; 1.268 + } 1.269 + } 1.270 + 1.271 + if (count < 0 && errno != EWOULDBLOCK) { 1.272 + perror("read"); 1.273 + return 0; 1.274 + } 1.275 + 1.276 + return 1; 1.277 +} 1.278 + 1.279 +/** 1.280 + * Run the program 1.281 + */ 1.282 +static void 1.283 +run() 1.284 +{ 1.285 + // We want non-blocking I/O on stdin so we can select on it. 1.286 + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); 1.287 + 1.288 + // The last time we refreshed the window. 1.289 + struct timeval last; 1.290 + gettimeofday(&last, 0); 1.291 + 1.292 + int ok; 1.293 + 1.294 + do { 1.295 + // Select on stdin and the connection to the X server. 1.296 + fd_set fds; 1.297 + FD_ZERO(&fds); 1.298 + FD_SET(STDIN_FILENO, &fds); 1.299 + FD_SET(GET_DISPLAY_FD(display), &fds); 1.300 + 1.301 + struct timeval tv; 1.302 + tv.tv_sec = 1; 1.303 + tv.tv_usec = 0; 1.304 + 1.305 + ok = select(GET_DISPLAY_FD(display) + 1, &fds, 0, 0, &tv); 1.306 + if (ok < 0) 1.307 + break; 1.308 + 1.309 + if (maxpage) { 1.310 + // See if we've waited long enough to refresh the window. 1.311 + struct timeval now; 1.312 + gettimeofday(&now, 0); 1.313 + 1.314 + if (now.tv_sec != last.tv_sec) { 1.315 + // At least a second has gone by. Decay and refresh. 1.316 + last = now; 1.317 + decay(); 1.318 + invalidate_window(); 1.319 + } 1.320 + else if (now.tv_usec - last.tv_usec > 100000) { 1.321 + // At least 100msec have gone by. Refresh. 1.322 + last.tv_usec = now.tv_usec; 1.323 + invalidate_window(); 1.324 + } 1.325 + } 1.326 + 1.327 + // Now check for X events and input. 1.328 + ok = 1; 1.329 + 1.330 + if (FD_ISSET(GET_DISPLAY_FD(display), &fds)) 1.331 + ok = handle_xevents(); 1.332 + 1.333 + if (FD_ISSET(STDIN_FILENO, &fds)) 1.334 + ok = read_addrs(); 1.335 + } while (ok); 1.336 +} 1.337 + 1.338 +/** 1.339 + * Tear down our window and stuff. 1.340 + */ 1.341 +static void 1.342 +finish() 1.343 +{ 1.344 + if (window) { 1.345 + XUnmapWindow(display, window); 1.346 + XDestroyWindow(display, window); 1.347 + } 1.348 + 1.349 + if (display) 1.350 + XCloseDisplay(display); 1.351 +} 1.352 + 1.353 +static struct option opts[] = { 1.354 + { "working-set", no_argument, 0, 'w' }, 1.355 + { "min", required_argument, 0, 'm' }, 1.356 + { "size", required_argument, 0, 's' }, 1.357 + { "max", required_argument, 0, 'x' }, 1.358 + { 0, 0, 0, 0 } 1.359 +}; 1.360 + 1.361 +static void 1.362 +usage() 1.363 +{ 1.364 + cerr << "thrashview [--working-set] [--min=<min>] [--max=<max>] [--size=<size>]" << endl; 1.365 +} 1.366 + 1.367 +/** 1.368 + * Program starts here. 1.369 + */ 1.370 +int 1.371 +main(int argc, char *argv[]) 1.372 +{ 1.373 + int size = 0; 1.374 + 1.375 + while (1) { 1.376 + int option_index = 0; 1.377 + int c = getopt_long(argc, argv, "wm:x:s:", opts, &option_index); 1.378 + 1.379 + if (c < 0) 1.380 + break; 1.381 + 1.382 + switch (c) { 1.383 + case 'w': 1.384 + opt_working_set = true; 1.385 + break; 1.386 + 1.387 + case 'm': 1.388 + minpage = strtol(optarg, 0, 0) / PAGESIZE; 1.389 + opt_fixed = true; 1.390 + break; 1.391 + 1.392 + case 's': 1.393 + size = strtol(optarg, 0, 0) / PAGESIZE; 1.394 + break; 1.395 + 1.396 + case 'x': 1.397 + maxpage = strtol(optarg, 0, 0) / PAGESIZE; 1.398 + opt_fixed = true; 1.399 + break; 1.400 + 1.401 + default: 1.402 + usage(); 1.403 + return 1; 1.404 + } 1.405 + } 1.406 + 1.407 + if (minpage && !maxpage) { 1.408 + if (!size) { 1.409 + cerr << argv[0] << ": minpage specified without maxpage or size" << endl; 1.410 + return 1; 1.411 + } 1.412 + 1.413 + maxpage = minpage + size; 1.414 + } 1.415 + 1.416 + if (opt_fixed && minpage > maxpage) { 1.417 + cerr << argv[0] << ": invalid page range" << endl; 1.418 + return 1; 1.419 + } 1.420 + 1.421 + if (init()) 1.422 + run(); 1.423 + 1.424 + finish(); 1.425 + 1.426 + return 0; 1.427 +}