2 Copyright (c) 2020 Peter Hsu. All Rights Reserved. See LICENCE file for details.
23 #define FRAMERATE 60 /* frames per second */
24 #define PERSISTENCE 30 /* frames per color decay */
25 #define HOT_COLOR (7-2)
27 int global_width
= 17;
30 struct perfCounters_t perf
;
33 struct histogram_t global
, local
;
34 struct disasm_t disasm
;
39 inline static int min(int x
, int y
) { return x
< y
? x
: y
; }
46 /* Histogram functions. */
68 #define max(a, b) ( (a) > (b) ? (a) : (b) )
70 void histo_create(struct histogram_t
* histo
, int lines
, int cols
, int starty
, int startx
)
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));
80 void histo_delete(struct histogram_t
* histo
)
86 memset(histo
, 0, sizeof(struct histogram_t
));
89 void histo_compute(struct histogram_t
* histo
, long base
, long bound
)
91 long range
= (bound
-base
) / histo
->bins
; /* pc range per bin */
96 struct count_t
* c
= count(pc
);
98 for (int i
=0; i
<histo
->bins
; i
++) {
100 long limit
= pc
+ range
;
102 // mcount = max(mcount, c->count);
104 pc
+= shortOp(c
->i
.op_code
) ? 2 : 4;
105 c
+= shortOp(c
->i
.op_code
) ? 1 : 2;
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
);
112 histo
->max_value
= max_count
;
115 void paint_count_color(WINDOW
* win
, int width
, long count
, int decay
, int always
)
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
);
122 wprintw(win
, "%*s", width
, "");
123 wattroff(win
, COLOR_PAIR(heat
+ 2));
126 void histo_paint(struct histogram_t
* histo
, const char* title
, long base
, long bound
)
130 getmaxyx(histo
->win
, rows
, cols
);
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)
145 wnoutrefresh(histo
->win
);
150 void disasm_create(struct disasm_t
* disasm
, int lines
, int cols
, int starty
, int startx
)
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));
160 void disasm_delete(struct disasm_t
* disasm
)
166 memset(disasm
, 0, sizeof(struct disasm_t
));
169 inline int fmtpercent(char* b
, long num
, long over
)
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));
179 void disasm_paint(struct disasm_t
* disasm
)
181 WINDOW
* win
= disasm
->win
;
182 long pc
= disasm
->base
;
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");
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
++) {
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)
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
);
209 b
+=fmtpercent(b
, *ibm
, c
->count
);
210 b
+=fmtpercent(b
, *icm
, c
->count
);
211 b
+=fmtpercent(b
, *dcm
, c
->count
);
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
);
218 int sz
= shortOp(c
->i
.op_code
) ? 1 : 2;
220 c
+= sz
, ibm
+= sz
, icm
+= sz
, dcm
+= sz
;
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
);
244 struct timeval t1
, t2
;
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
);
254 int ch
= wgetch(stdscr
);
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);
265 resizeterm(LINES
, COLS
);
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
) {
279 if (insn(disasm
.base
)->op_code
== Op_zero
)
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
)
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
;
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
;
301 else if ((event
.bstate
& BUTTON4_PRESSED
) && (event
.bstate
& BUTTON_SHIFT
)) {
302 local
.base
+= scroll
;
303 local
.bound
-= scroll
;
305 else if ((event
.bstate
& BUTTON5_PRESSED
) && (event
.bstate
& BUTTON_SHIFT
)) {
306 local
.base
-= scroll
;
307 local
.bound
+= scroll
;
309 else if (event
.bstate
& BUTTON4_PRESSED
) { /* without shift */
310 local
.base
-= scroll
;
311 local
.bound
-= scroll
;
313 else if (event
.bstate
& BUTTON5_PRESSED
) { /* without shift */
314 local
.base
+= scroll
;
315 local
.bound
+= scroll
;
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
;
330 long atohex(const char* p
)
332 for (long n
=0; ; p
++) {
334 if ('0' <= *p
&& *p
<= '9')
336 else if ('a' <= *p
&& *p
<= 'f')
337 digit
= 10 + (*p
- 'a');
338 else if ('A' <= *p
&& *p
<= 'F')
339 digit
= 10 + (*p
- 'F');
347 static const char* perf_path
=0;
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" },
357 int main(int argc
, const char** argv
)
359 int numopts
= parse_options(argv
+1);
360 if (argc
== numopts
+1 || !perf_path
)
362 long entry
= load_elf_binary(argv
[1+numopts
], 0);
364 perf_open(perf_path
);
366 initscr(); /* Start curses mode */
367 start_color(); /* Start the color functionality */
368 cbreak(); /* Line buffering disabled */
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);
376 nodelay(stdscr
, TRUE
);
379 if (has_colors() == FALSE
) {
381 puts("Your terminal does not support 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
);
395 // printf("\033[?1003l\n"); // Disable mouse movement events, as l = low