tools/footprint/thrashview.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /*
michael@0 6 * ``thrashview'' is a program that reads a binary stream of addresses
michael@0 7 * from stdin and displays the pattern graphically in a window.
michael@0 8 */
michael@0 9
michael@0 10 #include <errno.h>
michael@0 11 #include <fcntl.h>
michael@0 12 #include <stdio.h>
michael@0 13 #include <stdlib.h>
michael@0 14 #include <string.h>
michael@0 15 #include <sys/time.h>
michael@0 16 #include <unistd.h>
michael@0 17 #include <X11/Xlib.h>
michael@0 18 #include <fstream>
michael@0 19 #include <getopt.h>
michael@0 20
michael@0 21 #define GET_DISPLAY_FD(display_) ConnectionNumber(display_)
michael@0 22
michael@0 23 static bool opt_working_set = false;
michael@0 24 static bool opt_fixed = false;
michael@0 25
michael@0 26 static Display *display;
michael@0 27 static Window window;
michael@0 28 static GC gc;
michael@0 29 static XColor colors[256];
michael@0 30 const unsigned int cellsize = 4;
michael@0 31 static unsigned int width = 64 * cellsize;
michael@0 32 static unsigned int height = 64 * cellsize;
michael@0 33
michael@0 34 #define PAGESIZE 4096
michael@0 35 #define MAXPAGES 4096 // should hold 16MB worth of code
michael@0 36 static unsigned int minpage = static_cast<unsigned int>(-1);
michael@0 37 static unsigned int maxpage = 0;
michael@0 38 static unsigned char pages[MAXPAGES];
michael@0 39
michael@0 40
michael@0 41 /**
michael@0 42 * Create a simple window and the X objects we'll need to talk with it.
michael@0 43 */
michael@0 44 static int
michael@0 45 init()
michael@0 46 {
michael@0 47 display = XOpenDisplay(0);
michael@0 48 if (! display)
michael@0 49 return 0;
michael@0 50
michael@0 51 window =
michael@0 52 XCreateSimpleWindow(display,
michael@0 53 RootWindow(display, 0),
michael@0 54 1, 1, width, height,
michael@0 55 0,
michael@0 56 BlackPixel(display, 0),
michael@0 57 BlackPixel(display, 0));
michael@0 58
michael@0 59 if (! window)
michael@0 60 return 0;
michael@0 61
michael@0 62 gc = XCreateGC(display, window, 0, 0);
michael@0 63 if (! gc)
michael@0 64 return 0;
michael@0 65
michael@0 66 // Set up a grayscale
michael@0 67 const unsigned int ncolors = sizeof colors / sizeof colors[0];
michael@0 68 const unsigned short step = 65536 / ncolors;
michael@0 69 unsigned short brightness = 0;
michael@0 70
michael@0 71 XColor *color = colors;
michael@0 72 XColor *limit = colors + ncolors;
michael@0 73 for (; color < limit; ++color, brightness += step) {
michael@0 74 color->red = brightness;
michael@0 75 color->green = brightness;
michael@0 76 color->blue = brightness;
michael@0 77 XAllocColor(display, DefaultColormap(display, 0), color);
michael@0 78 }
michael@0 79
michael@0 80 // We want exposes and resizes.
michael@0 81 XSelectInput(display, window, ExposureMask | StructureNotifyMask);
michael@0 82
michael@0 83 XMapWindow(display, window);
michael@0 84 XFlush(display);
michael@0 85
michael@0 86 return 1;
michael@0 87 }
michael@0 88
michael@0 89 /**
michael@0 90 * Age pages that haven't been recently touched.
michael@0 91 */
michael@0 92 static void
michael@0 93 decay()
michael@0 94 {
michael@0 95 int ws_immediate = 0, ws_longterm = 0;
michael@0 96
michael@0 97 unsigned char *page = pages;
michael@0 98 unsigned char *limit = pages + (maxpage - minpage) + 1;
michael@0 99 for (; page < limit; ++page) {
michael@0 100 if (opt_working_set) {
michael@0 101 if (*page == 255)
michael@0 102 ++ws_immediate;
michael@0 103 if (*page)
michael@0 104 ++ws_longterm;
michael@0 105 }
michael@0 106
michael@0 107 if (*page) {
michael@0 108 *page /= 8;
michael@0 109 *page *= 7;
michael@0 110 }
michael@0 111 }
michael@0 112
michael@0 113 if (opt_working_set) {
michael@0 114 dec(cout);
michael@0 115 cout << "immediate: " << ws_immediate << " pages, ";
michael@0 116 cout << "longterm: " << ws_longterm << " pages, ";
michael@0 117 cout << "mapped: " << ((maxpage - minpage) + 1) << " pages";
michael@0 118 cout << endl;
michael@0 119 }
michael@0 120 }
michael@0 121
michael@0 122 /**
michael@0 123 * Blast the state of our pages to the screen.
michael@0 124 */
michael@0 125 static int
michael@0 126 handle_expose(const XExposeEvent& event)
michael@0 127 {
michael@0 128 //printf("handle_expose(%d, %d, %d, %d)\n", event.x, event.y, event.width, event.height);
michael@0 129
michael@0 130 int i = event.x / cellsize;
michael@0 131 int imost = i + event.width / cellsize + 1;
michael@0 132
michael@0 133 int j = event.y / cellsize;
michael@0 134 int jmost = j + event.height / cellsize + 1;
michael@0 135
michael@0 136 unsigned char *last_cell = pages + maxpage - minpage;
michael@0 137 unsigned char *row = pages + j;
michael@0 138 for (int y = j * cellsize, ymost = jmost * cellsize;
michael@0 139 y < ymost;
michael@0 140 y += cellsize, row += width / cellsize) {
michael@0 141 unsigned char *cell = row + i;
michael@0 142 for (int x = i * cellsize, xmost = imost * cellsize;
michael@0 143 x < xmost;
michael@0 144 x += cellsize, ++cell) {
michael@0 145 unsigned int pixel = (cell <= last_cell) ? colors[*cell].pixel : colors[0].pixel;
michael@0 146 XSetForeground(display, gc, pixel);
michael@0 147 XFillRectangle(display, window, gc, x, y, cellsize - 1, cellsize - 1);
michael@0 148 }
michael@0 149 }
michael@0 150
michael@0 151 XFlush(display);
michael@0 152
michael@0 153 return 1;
michael@0 154 }
michael@0 155
michael@0 156 /**
michael@0 157 * Invalidate the entire window.
michael@0 158 */
michael@0 159 static void
michael@0 160 invalidate_window()
michael@0 161 {
michael@0 162 XExposeEvent event;
michael@0 163 event.x = event.y = 0;
michael@0 164 event.width = width;
michael@0 165 event.height = height;
michael@0 166
michael@0 167 handle_expose(event);
michael@0 168 }
michael@0 169
michael@0 170 /**
michael@0 171 * Handle a configure event.
michael@0 172 */
michael@0 173 static int
michael@0 174 handle_configure(const XConfigureEvent& event)
michael@0 175 {
michael@0 176 //printf("handle_resize(%d, %d)\n", event.width, event.height);
michael@0 177 width = event.width - event.width % cellsize;
michael@0 178 height = event.height;
michael@0 179 return 1;
michael@0 180 }
michael@0 181
michael@0 182 /**
michael@0 183 * Filter to select any message.
michael@0 184 */
michael@0 185 static Bool
michael@0 186 any_event(Display *display, XEvent *event, XPointer arg)
michael@0 187 {
michael@0 188 return 1;
michael@0 189 }
michael@0 190
michael@0 191 /**
michael@0 192 * An X event occurred. Process it and flush the queue.
michael@0 193 */
michael@0 194 static int
michael@0 195 handle_xevents()
michael@0 196 {
michael@0 197 int ok;
michael@0 198
michael@0 199 XEvent event;
michael@0 200 XNextEvent(display, &event);
michael@0 201 do {
michael@0 202 switch (event.type) {
michael@0 203 case Expose:
michael@0 204 ok = handle_expose(reinterpret_cast<const XExposeEvent&>(event));
michael@0 205 break;
michael@0 206
michael@0 207 case ConfigureNotify:
michael@0 208 ok = handle_configure(reinterpret_cast<const XConfigureEvent&>(event));
michael@0 209 break;
michael@0 210
michael@0 211 default:
michael@0 212 ok = 1;
michael@0 213 }
michael@0 214 } while (ok && XCheckIfEvent(display, &event, any_event, 0));
michael@0 215
michael@0 216 return ok;
michael@0 217 }
michael@0 218
michael@0 219 /**
michael@0 220 * Read address data from stdin.
michael@0 221 */
michael@0 222 static int
michael@0 223 read_addrs()
michael@0 224 {
michael@0 225 unsigned int buf[1024];
michael@0 226 ssize_t count;
michael@0 227 while ((count = read(0, buf, sizeof buf)) > 0) {
michael@0 228 if (count % sizeof(unsigned int))
michael@0 229 cerr << "truncating unaligned read" << endl;
michael@0 230
michael@0 231 count /= sizeof buf[0];
michael@0 232
michael@0 233 unsigned int *addr = reinterpret_cast<unsigned int *>(buf);
michael@0 234 unsigned int *limit = addr + count;
michael@0 235
michael@0 236 for (; addr < limit; ++addr) {
michael@0 237 // map the address to a page
michael@0 238 unsigned int page = *addr / PAGESIZE;
michael@0 239
michael@0 240 // XXX Don't let stray addresses bring us down. Should
michael@0 241 // really fix this by knowing what the ranges of addresses
michael@0 242 // we ought to expect are (e.g., by reading the symtab)
michael@0 243 if (maxpage && page > maxpage && page - maxpage > MAXPAGES)
michael@0 244 continue;
michael@0 245
michael@0 246 if (! opt_fixed) {
michael@0 247 // Potentially adjust minpage and maxpage to
michael@0 248 // accomodate an out-of-bounds address.
michael@0 249 if (page < minpage) {
michael@0 250 if (maxpage) {
michael@0 251 // everything needs to shift.
michael@0 252 unsigned int shift = minpage - page;
michael@0 253 memmove(pages + shift, pages, maxpage - minpage);
michael@0 254 memset(pages, 0, shift);
michael@0 255 }
michael@0 256 minpage = page;
michael@0 257 }
michael@0 258
michael@0 259 if (page > maxpage)
michael@0 260 maxpage = page;
michael@0 261 }
michael@0 262
michael@0 263 page -= minpage;
michael@0 264 pages[page] = 255;
michael@0 265 }
michael@0 266 }
michael@0 267
michael@0 268 if (count < 0 && errno != EWOULDBLOCK) {
michael@0 269 perror("read");
michael@0 270 return 0;
michael@0 271 }
michael@0 272
michael@0 273 return 1;
michael@0 274 }
michael@0 275
michael@0 276 /**
michael@0 277 * Run the program
michael@0 278 */
michael@0 279 static void
michael@0 280 run()
michael@0 281 {
michael@0 282 // We want non-blocking I/O on stdin so we can select on it.
michael@0 283 fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
michael@0 284
michael@0 285 // The last time we refreshed the window.
michael@0 286 struct timeval last;
michael@0 287 gettimeofday(&last, 0);
michael@0 288
michael@0 289 int ok;
michael@0 290
michael@0 291 do {
michael@0 292 // Select on stdin and the connection to the X server.
michael@0 293 fd_set fds;
michael@0 294 FD_ZERO(&fds);
michael@0 295 FD_SET(STDIN_FILENO, &fds);
michael@0 296 FD_SET(GET_DISPLAY_FD(display), &fds);
michael@0 297
michael@0 298 struct timeval tv;
michael@0 299 tv.tv_sec = 1;
michael@0 300 tv.tv_usec = 0;
michael@0 301
michael@0 302 ok = select(GET_DISPLAY_FD(display) + 1, &fds, 0, 0, &tv);
michael@0 303 if (ok < 0)
michael@0 304 break;
michael@0 305
michael@0 306 if (maxpage) {
michael@0 307 // See if we've waited long enough to refresh the window.
michael@0 308 struct timeval now;
michael@0 309 gettimeofday(&now, 0);
michael@0 310
michael@0 311 if (now.tv_sec != last.tv_sec) {
michael@0 312 // At least a second has gone by. Decay and refresh.
michael@0 313 last = now;
michael@0 314 decay();
michael@0 315 invalidate_window();
michael@0 316 }
michael@0 317 else if (now.tv_usec - last.tv_usec > 100000) {
michael@0 318 // At least 100msec have gone by. Refresh.
michael@0 319 last.tv_usec = now.tv_usec;
michael@0 320 invalidate_window();
michael@0 321 }
michael@0 322 }
michael@0 323
michael@0 324 // Now check for X events and input.
michael@0 325 ok = 1;
michael@0 326
michael@0 327 if (FD_ISSET(GET_DISPLAY_FD(display), &fds))
michael@0 328 ok = handle_xevents();
michael@0 329
michael@0 330 if (FD_ISSET(STDIN_FILENO, &fds))
michael@0 331 ok = read_addrs();
michael@0 332 } while (ok);
michael@0 333 }
michael@0 334
michael@0 335 /**
michael@0 336 * Tear down our window and stuff.
michael@0 337 */
michael@0 338 static void
michael@0 339 finish()
michael@0 340 {
michael@0 341 if (window) {
michael@0 342 XUnmapWindow(display, window);
michael@0 343 XDestroyWindow(display, window);
michael@0 344 }
michael@0 345
michael@0 346 if (display)
michael@0 347 XCloseDisplay(display);
michael@0 348 }
michael@0 349
michael@0 350 static struct option opts[] = {
michael@0 351 { "working-set", no_argument, 0, 'w' },
michael@0 352 { "min", required_argument, 0, 'm' },
michael@0 353 { "size", required_argument, 0, 's' },
michael@0 354 { "max", required_argument, 0, 'x' },
michael@0 355 { 0, 0, 0, 0 }
michael@0 356 };
michael@0 357
michael@0 358 static void
michael@0 359 usage()
michael@0 360 {
michael@0 361 cerr << "thrashview [--working-set] [--min=<min>] [--max=<max>] [--size=<size>]" << endl;
michael@0 362 }
michael@0 363
michael@0 364 /**
michael@0 365 * Program starts here.
michael@0 366 */
michael@0 367 int
michael@0 368 main(int argc, char *argv[])
michael@0 369 {
michael@0 370 int size = 0;
michael@0 371
michael@0 372 while (1) {
michael@0 373 int option_index = 0;
michael@0 374 int c = getopt_long(argc, argv, "wm:x:s:", opts, &option_index);
michael@0 375
michael@0 376 if (c < 0)
michael@0 377 break;
michael@0 378
michael@0 379 switch (c) {
michael@0 380 case 'w':
michael@0 381 opt_working_set = true;
michael@0 382 break;
michael@0 383
michael@0 384 case 'm':
michael@0 385 minpage = strtol(optarg, 0, 0) / PAGESIZE;
michael@0 386 opt_fixed = true;
michael@0 387 break;
michael@0 388
michael@0 389 case 's':
michael@0 390 size = strtol(optarg, 0, 0) / PAGESIZE;
michael@0 391 break;
michael@0 392
michael@0 393 case 'x':
michael@0 394 maxpage = strtol(optarg, 0, 0) / PAGESIZE;
michael@0 395 opt_fixed = true;
michael@0 396 break;
michael@0 397
michael@0 398 default:
michael@0 399 usage();
michael@0 400 return 1;
michael@0 401 }
michael@0 402 }
michael@0 403
michael@0 404 if (minpage && !maxpage) {
michael@0 405 if (!size) {
michael@0 406 cerr << argv[0] << ": minpage specified without maxpage or size" << endl;
michael@0 407 return 1;
michael@0 408 }
michael@0 409
michael@0 410 maxpage = minpage + size;
michael@0 411 }
michael@0 412
michael@0 413 if (opt_fixed && minpage > maxpage) {
michael@0 414 cerr << argv[0] << ": invalid page range" << endl;
michael@0 415 return 1;
michael@0 416 }
michael@0 417
michael@0 418 if (init())
michael@0 419 run();
michael@0 420
michael@0 421 finish();
michael@0 422
michael@0 423 return 0;
michael@0 424 }

mercurial