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.

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

mercurial