|
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/. */ |
|
4 |
|
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 */ |
|
9 |
|
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> |
|
20 |
|
21 #define GET_DISPLAY_FD(display_) ConnectionNumber(display_) |
|
22 |
|
23 static bool opt_working_set = false; |
|
24 static bool opt_fixed = false; |
|
25 |
|
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; |
|
33 |
|
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]; |
|
39 |
|
40 |
|
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; |
|
50 |
|
51 window = |
|
52 XCreateSimpleWindow(display, |
|
53 RootWindow(display, 0), |
|
54 1, 1, width, height, |
|
55 0, |
|
56 BlackPixel(display, 0), |
|
57 BlackPixel(display, 0)); |
|
58 |
|
59 if (! window) |
|
60 return 0; |
|
61 |
|
62 gc = XCreateGC(display, window, 0, 0); |
|
63 if (! gc) |
|
64 return 0; |
|
65 |
|
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; |
|
70 |
|
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 } |
|
79 |
|
80 // We want exposes and resizes. |
|
81 XSelectInput(display, window, ExposureMask | StructureNotifyMask); |
|
82 |
|
83 XMapWindow(display, window); |
|
84 XFlush(display); |
|
85 |
|
86 return 1; |
|
87 } |
|
88 |
|
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; |
|
96 |
|
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 } |
|
106 |
|
107 if (*page) { |
|
108 *page /= 8; |
|
109 *page *= 7; |
|
110 } |
|
111 } |
|
112 |
|
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 } |
|
121 |
|
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); |
|
129 |
|
130 int i = event.x / cellsize; |
|
131 int imost = i + event.width / cellsize + 1; |
|
132 |
|
133 int j = event.y / cellsize; |
|
134 int jmost = j + event.height / cellsize + 1; |
|
135 |
|
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 } |
|
150 |
|
151 XFlush(display); |
|
152 |
|
153 return 1; |
|
154 } |
|
155 |
|
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; |
|
166 |
|
167 handle_expose(event); |
|
168 } |
|
169 |
|
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 } |
|
181 |
|
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 } |
|
190 |
|
191 /** |
|
192 * An X event occurred. Process it and flush the queue. |
|
193 */ |
|
194 static int |
|
195 handle_xevents() |
|
196 { |
|
197 int ok; |
|
198 |
|
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; |
|
206 |
|
207 case ConfigureNotify: |
|
208 ok = handle_configure(reinterpret_cast<const XConfigureEvent&>(event)); |
|
209 break; |
|
210 |
|
211 default: |
|
212 ok = 1; |
|
213 } |
|
214 } while (ok && XCheckIfEvent(display, &event, any_event, 0)); |
|
215 |
|
216 return ok; |
|
217 } |
|
218 |
|
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; |
|
230 |
|
231 count /= sizeof buf[0]; |
|
232 |
|
233 unsigned int *addr = reinterpret_cast<unsigned int *>(buf); |
|
234 unsigned int *limit = addr + count; |
|
235 |
|
236 for (; addr < limit; ++addr) { |
|
237 // map the address to a page |
|
238 unsigned int page = *addr / PAGESIZE; |
|
239 |
|
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; |
|
245 |
|
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 } |
|
258 |
|
259 if (page > maxpage) |
|
260 maxpage = page; |
|
261 } |
|
262 |
|
263 page -= minpage; |
|
264 pages[page] = 255; |
|
265 } |
|
266 } |
|
267 |
|
268 if (count < 0 && errno != EWOULDBLOCK) { |
|
269 perror("read"); |
|
270 return 0; |
|
271 } |
|
272 |
|
273 return 1; |
|
274 } |
|
275 |
|
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); |
|
284 |
|
285 // The last time we refreshed the window. |
|
286 struct timeval last; |
|
287 gettimeofday(&last, 0); |
|
288 |
|
289 int ok; |
|
290 |
|
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); |
|
297 |
|
298 struct timeval tv; |
|
299 tv.tv_sec = 1; |
|
300 tv.tv_usec = 0; |
|
301 |
|
302 ok = select(GET_DISPLAY_FD(display) + 1, &fds, 0, 0, &tv); |
|
303 if (ok < 0) |
|
304 break; |
|
305 |
|
306 if (maxpage) { |
|
307 // See if we've waited long enough to refresh the window. |
|
308 struct timeval now; |
|
309 gettimeofday(&now, 0); |
|
310 |
|
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 } |
|
323 |
|
324 // Now check for X events and input. |
|
325 ok = 1; |
|
326 |
|
327 if (FD_ISSET(GET_DISPLAY_FD(display), &fds)) |
|
328 ok = handle_xevents(); |
|
329 |
|
330 if (FD_ISSET(STDIN_FILENO, &fds)) |
|
331 ok = read_addrs(); |
|
332 } while (ok); |
|
333 } |
|
334 |
|
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 } |
|
345 |
|
346 if (display) |
|
347 XCloseDisplay(display); |
|
348 } |
|
349 |
|
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 }; |
|
357 |
|
358 static void |
|
359 usage() |
|
360 { |
|
361 cerr << "thrashview [--working-set] [--min=<min>] [--max=<max>] [--size=<size>]" << endl; |
|
362 } |
|
363 |
|
364 /** |
|
365 * Program starts here. |
|
366 */ |
|
367 int |
|
368 main(int argc, char *argv[]) |
|
369 { |
|
370 int size = 0; |
|
371 |
|
372 while (1) { |
|
373 int option_index = 0; |
|
374 int c = getopt_long(argc, argv, "wm:x:s:", opts, &option_index); |
|
375 |
|
376 if (c < 0) |
|
377 break; |
|
378 |
|
379 switch (c) { |
|
380 case 'w': |
|
381 opt_working_set = true; |
|
382 break; |
|
383 |
|
384 case 'm': |
|
385 minpage = strtol(optarg, 0, 0) / PAGESIZE; |
|
386 opt_fixed = true; |
|
387 break; |
|
388 |
|
389 case 's': |
|
390 size = strtol(optarg, 0, 0) / PAGESIZE; |
|
391 break; |
|
392 |
|
393 case 'x': |
|
394 maxpage = strtol(optarg, 0, 0) / PAGESIZE; |
|
395 opt_fixed = true; |
|
396 break; |
|
397 |
|
398 default: |
|
399 usage(); |
|
400 return 1; |
|
401 } |
|
402 } |
|
403 |
|
404 if (minpage && !maxpage) { |
|
405 if (!size) { |
|
406 cerr << argv[0] << ": minpage specified without maxpage or size" << endl; |
|
407 return 1; |
|
408 } |
|
409 |
|
410 maxpage = minpage + size; |
|
411 } |
|
412 |
|
413 if (opt_fixed && minpage > maxpage) { |
|
414 cerr << argv[0] << ": invalid page range" << endl; |
|
415 return 1; |
|
416 } |
|
417 |
|
418 if (init()) |
|
419 run(); |
|
420 |
|
421 finish(); |
|
422 |
|
423 return 0; |
|
424 } |