cavatools: initialize repository
[cavatools.git] / erised / erised.c
1 /*
2 Copyright (c) 2020 Peter Hsu. All Rights Reserved. See LICENCE file for details.
3 */
4
5 #include <unistd.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <string.h>
11 #include <sys/time.h>
12 #include <sys/mman.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <curses.h>
16
17 #include "caveat.h"
18 #include "opcodes.h"
19 #include "insn.h"
20 #include "perfctr.h"
21
22
23 #define FRAMERATE 60 /* frames per second */
24 #define PERSISTENCE 30 /* frames per color decay */
25 #define HOT_COLOR (7-2)
26
27 int global_width = 17;
28 int local_width = 17;
29
30 struct perfCounters_t perf;
31
32 WINDOW *menu;
33 struct histogram_t global, local;
34 struct disasm_t disasm;
35
36 time_t start_tick;
37 long insn_count =0;
38
39 inline static int min(int x, int y) { return x < y ? x : y; }
40
41 #define EPSILON 0.001
42
43 MEVENT event;
44
45
46 /* Histogram functions. */
47
48 struct histogram_t {
49 WINDOW* win;
50 long* bin;
51 int* decay;
52 long base, bound;
53 long range;
54 long max_value;
55 int bins;
56 };
57
58 struct disasm_t {
59 WINDOW* win;
60 long* old;
61 int* decay;
62 long base, bound;
63 long max_value;
64 int lines;
65 };
66
67
68 #define max(a, b) ( (a) > (b) ? (a) : (b) )
69
70 void histo_create(struct histogram_t* histo, int lines, int cols, int starty, int startx)
71 {
72 histo->win = newwin(lines, cols, starty, startx);
73 histo->bins = --lines;
74 histo->bin = (long*)malloc(lines*sizeof(long));
75 memset(histo->bin, 0, lines*sizeof(long));
76 histo->decay = (int*)malloc(lines*sizeof(int));
77 memset(histo->decay, 0, lines*sizeof(int));
78 };
79
80 void histo_delete(struct histogram_t* histo)
81 {
82 if (histo->bin)
83 free(histo->bin);
84 if (histo->decay)
85 free(histo->decay);
86 memset(histo, 0, sizeof(struct histogram_t));
87 }
88
89 void histo_compute(struct histogram_t* histo, long base, long bound)
90 {
91 long range = (bound-base) / histo->bins; /* pc range per bin */
92 histo->base = base;
93 histo->bound = bound;
94 histo->range = range;
95 long pc = base;
96 struct count_t* c = count(pc);
97 long max_count = 0;
98 for (int i=0; i<histo->bins; i++) {
99 long mcount = 0;
100 long limit = pc + range;
101 while (pc < limit) {
102 // mcount = max(mcount, c->count);
103 mcount += c->count;
104 pc += shortOp(c->i.op_code) ? 2 : 4;
105 c += shortOp(c->i.op_code) ? 1 : 2;
106 }
107 if (mcount != histo->bin[i])
108 histo->decay[i] = HOT_COLOR*PERSISTENCE;
109 histo->bin[i] = mcount;
110 max_count = max(max_count, mcount);
111 }
112 histo->max_value = max_count;
113 }
114
115 void paint_count_color(WINDOW* win, int width, long count, int decay, int always)
116 {
117 int heat = (decay + PERSISTENCE-1)/PERSISTENCE;
118 wattron(win, COLOR_PAIR(heat + 2));
119 if (count > 0 || always)
120 wprintw(win, "%*ld", width, count);
121 else
122 wprintw(win, "%*s", width, "");
123 wattroff(win, COLOR_PAIR(heat + 2));
124 }
125
126 void histo_paint(struct histogram_t* histo, const char* title, long base, long bound)
127 {
128 werase(histo->win);
129 long rows, cols;
130 getmaxyx(histo->win, rows, cols);
131 rows--;
132 wmove(histo->win, 0, 0);
133 wprintw(histo->win, "%*s\n", cols-1, title);
134 long pc = histo->base;
135 for (int y=0; y<rows; y++) {
136 int highlight = (base <= pc && pc < bound || pc <= base && bound < pc+histo->range);
137 if (highlight) wattron(histo->win, A_REVERSE);
138 paint_count_color(histo->win, cols-1, histo->bin[y], histo->decay[y], 0);
139 wprintw(histo->win, "\n");
140 if (highlight) wattroff(histo->win, A_REVERSE);
141 if (histo->decay[y] > 0)
142 histo->decay[y]--;
143 pc += histo->range;
144 }
145 wnoutrefresh(histo->win);
146 }
147
148
149
150 void disasm_create(struct disasm_t* disasm, int lines, int cols, int starty, int startx)
151 {
152 disasm->win = newwin(lines, cols, starty, startx);
153 disasm->lines = lines;
154 disasm->old = (long*)malloc(lines*sizeof(long));
155 memset(disasm->old, 0, lines*sizeof(long));
156 disasm->decay = (int*)malloc(lines*sizeof(int));
157 memset(disasm->decay, 0, lines*sizeof(int));
158 }
159
160 void disasm_delete(struct disasm_t* disasm)
161 {
162 if (disasm->old)
163 free(disasm->old);
164 if (disasm->decay)
165 free(disasm->decay);
166 memset(disasm, 0, sizeof(struct disasm_t));
167 }
168
169 inline int fmtpercent(char* b, long num, long over)
170 {
171 if (num == 0) return sprintf(b, " %4s ", "");
172 double percent = 100.0 * num / over;
173 if (percent > 99.9) return sprintf(b, " %4d%%", (int)percent);
174 if (percent > 9.99) return sprintf(b, " %4.1f%%", percent);
175 if (percent > 0.99) return sprintf(b, " %4.2f%%", percent);
176 return sprintf(b, " .%03u%%", (unsigned)((percent+0.005)*100));
177 }
178
179 void disasm_paint(struct disasm_t* disasm)
180 {
181 WINDOW* win = disasm->win;
182 long pc = disasm->base;
183 wmove(win, 0, 0);
184 wprintw(win, "%16s %-6s %-5s %-5s %-5s", "Count", " CPI", "Ibuf", "I$", "D$");
185 wprintw(win, "%7.1fB insns CPI=%5.2f ", perf.h->insns/1e9, (double)perf.h->cycles/perf.h->insns);
186 wprintw(win, "%8s %8s %s\n", "PC", "Hex", "Assembly q=quit");
187 if (pc != 0) {
188 const struct count_t* c = count(pc);
189 const long* ibm = ibmiss(pc);
190 const long* icm = icmiss(pc);
191 const long* dcm = dcmiss(pc);
192 for (int y=1; y<getmaxy(win) && pc<perf.h->bound; y++) {
193 wmove(win, y, 0);
194 if (c->count != disasm->old[y])
195 disasm->decay[y] = HOT_COLOR*PERSISTENCE;
196 disasm->old[y] = c->count;
197 paint_count_color(win, 16, c->count, disasm->decay[y], 1);
198 if (disasm->decay[y] > 0)
199 disasm->decay[y]--;
200
201 double cpi = (double)c->cycles/c->count;
202 int dim = cpi < 1.0+EPSILON || c->count == 0;
203 if (dim) wattron(win, A_DIM);
204 if (c->count == 0) wprintw(win, " %-5s", "");
205 else if (c->cycles == c->count) wprintw(win, " %-5s", " 1");
206 else wprintw(win, " %5.2f", cpi);
207 char buf[1024];
208 char* b = buf;
209 b+=fmtpercent(b, *ibm, c->count);
210 b+=fmtpercent(b, *icm, c->count);
211 b+=fmtpercent(b, *dcm, c->count);
212 b+=sprintf(b, " ");
213 b+=format_pc(b, 28, pc);
214 b+=format_insn(b, &c->i, pc, *((unsigned int*)pc));
215 wprintw(win, "%s\n", buf);
216 if (dim) wattroff(win, A_DIM);
217
218 int sz = shortOp(c->i.op_code) ? 1 : 2;
219 pc += 2*sz;
220 c += sz, ibm += sz, icm += sz, dcm += sz;
221 }
222 }
223 disasm->bound = pc;
224 wclrtobot(win);
225 wnoutrefresh(win);
226 }
227
228 void resize_histos()
229 {
230 histo_delete(&global);
231 histo_delete(&local);
232 disasm_delete(&disasm);
233 histo_create(&global, LINES, global_width, 0, 0);
234 histo_compute(&global, perf.h->base, perf.h->bound);
235 histo_create(&local, LINES, local_width, 0, global_width+1);
236 histo_compute(&local, 0, 0);
237 disasm_create(&disasm, LINES, COLS-global_width-local_width, 0, global_width+local_width);
238 disasm.base = 0;
239 doupdate();
240 }
241
242 void interactive()
243 {
244 struct timeval t1, t2;
245 for (;;) {
246 gettimeofday(&t1, 0);
247 // histo_compute(&global, perf.h->base, perf.h->bound);
248 histo_compute(&global, insnSpace.base, insnSpace.bound);
249 histo_compute(&local, local.base, local.bound);
250 disasm_paint(&disasm);
251 histo_paint(&global, "Global", local.base, local.bound);
252 histo_paint(&local, "Local", disasm.base, disasm.bound);
253 doupdate();
254 int ch = wgetch(stdscr);
255 switch (ch) {
256 case ERR:
257 gettimeofday(&t2, 0);
258 double msec = (t2.tv_sec - t1.tv_sec)*1000;
259 msec += (t2.tv_usec - t1.tv_usec)/1000.0;
260 usleep((1000/FRAMERATE - msec) * 1000);
261 break;
262 //case KEY_F(1):
263 #if 0
264 case KEY_RESIZE:
265 resizeterm(LINES, COLS);
266 resize_histos();
267 break;
268 #endif
269 case 'q':
270 return;
271 //case KEY_DOWN:
272 //case KEY_UP:
273 case KEY_MOUSE:
274 dieif(getmouse(&event) != OK, "Got bad mouse event.");
275 if (wenclose(disasm.win, event.y, event.x)) {
276 if (event.bstate & BUTTON4_PRESSED) {
277 if (disasm.base > perf.h->base) {
278 disasm.base -= 2;
279 if (insn(disasm.base)->op_code == Op_zero)
280 disasm.base -= 2;
281 }
282 }
283 else if (event.bstate & BUTTON5_PRESSED) {
284 long npc = disasm.base + (shortOp(insn(disasm.base)->op_code) ? 2 : 4);
285 if (npc < perf.h->bound)
286 disasm.base = npc;
287 }
288 }
289 else if (wenclose(global.win, event.y, event.x)) {
290 if (event.bstate & BUTTON1_PRESSED) {
291 local.base = global.base + (event.y-1)*global.range;
292 local.bound = local.base + global.range;
293 }
294 }
295 else if (wenclose(local.win, event.y, event.x)) {
296 //int scroll = local.range * local.bins/2;
297 int scroll = local.range;
298 if (event.bstate & BUTTON1_PRESSED) {
299 disasm.base = local.base + (event.y-1)*local.range;
300 }
301 else if ((event.bstate & BUTTON4_PRESSED) && (event.bstate & BUTTON_SHIFT)) {
302 local.base += scroll;
303 local.bound -= scroll;
304 }
305 else if ((event.bstate & BUTTON5_PRESSED) && (event.bstate & BUTTON_SHIFT)) {
306 local.base -= scroll;
307 local.bound += scroll;
308 }
309 else if (event.bstate & BUTTON4_PRESSED) { /* without shift */
310 local.base -= scroll;
311 local.bound -= scroll;
312 }
313 else if (event.bstate & BUTTON5_PRESSED) { /* without shift */
314 local.base += scroll;
315 local.bound += scroll;
316 }
317
318 if (local.base < insnSpace.base)
319 local.base = insnSpace.base;
320 if (local.bound > insnSpace.bound)
321 local.bound = insnSpace.bound;
322 local.range = (local.bound-local.base)/local.bins;
323 }
324 break;
325 }
326 }
327 }
328
329
330 long atohex(const char* p)
331 {
332 for (long n=0; ; p++) {
333 long digit;
334 if ('0' <= *p && *p <= '9')
335 digit = *p - '0';
336 else if ('a' <= *p && *p <= 'f')
337 digit = 10 + (*p - 'a');
338 else if ('A' <= *p && *p <= 'F')
339 digit = 10 + (*p - 'F');
340 else
341 return n;
342 n = 16*n + digit;
343 }
344 }
345
346
347 static const char* perf_path =0;
348 static int list =0;
349
350 const char* usage = "erised --count=name [erised-options]";
351 const struct options_t opt[] =
352 { { "--perf=s", .s=&perf_path, .ds=0, .h="Shared memory counting structure =name" },
353 { 0 }
354 };
355
356
357 int main(int argc, const char** argv)
358 {
359 int numopts = parse_options(argv+1);
360 if (argc == numopts+1 || !perf_path)
361 help_exit();
362 long entry = load_elf_binary(argv[1+numopts], 0);
363 insnSpace_init();
364 perf_open(perf_path);
365
366 initscr(); /* Start curses mode */
367 start_color(); /* Start the color functionality */
368 cbreak(); /* Line buffering disabled */
369 noecho();
370 keypad(stdscr, TRUE); /* Need all keys */
371 mouseinterval(0); /* no mouse clicks, just button up/down */
372 // Don't mask any mouse events
373 mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
374 //mousemask(ALL_MOUSE_EVENTS, NULL);
375
376 nodelay(stdscr, TRUE);
377 //timeout(100);
378
379 if (has_colors() == FALSE) {
380 endwin();
381 puts("Your terminal does not support color");
382 exit(1);
383 }
384 start_color();
385 init_pair(2, COLOR_MAGENTA, COLOR_BLACK);
386 init_pair(3, COLOR_BLUE, COLOR_BLACK);
387 init_pair(4, COLOR_CYAN, COLOR_BLACK);
388 init_pair(5, COLOR_GREEN, COLOR_BLACK);
389 init_pair(6, COLOR_YELLOW, COLOR_BLACK);
390 init_pair(7, COLOR_RED, COLOR_BLACK);
391
392 resize_histos();
393 interactive();
394
395 // printf("\033[?1003l\n"); // Disable mouse movement events, as l = low
396 endwin();
397 perf_close();
398 return 0;
399 }