debuggers.hg

view tools/misc/gtraceview.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents 857d7b2dd8c7
children
line source
1 /*
2 * gtraceview.c: list Cx events in a ncurse way to help find abnormal behaviour.
3 * Copyright (c) 2009, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place - Suite 330, Boston, MA 02111-1307 USA.
17 */
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdarg.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
30 #include <xenctrl.h>
31 #include <xen/trace.h>
33 #ifdef __linux__
34 #include <ncurses.h>
35 #endif
36 #ifdef __NetBSD__
37 #include <curses.h>
38 #endif
40 /********** MACROS **********/
41 #define MAX_CPU_NR 32
42 #define MAX_MODE_NR 16
43 #define MAX_STRING_LEN 1024
45 /********** STRUCTURE DEFINITIONS **********/
46 enum {
47 FLAG_FUZZY = 0,
48 FLAG_LEVEL,
49 FLAG_EDGE,
50 FLAG_UNKNOWN,
51 NR_FLAGS
52 };
54 struct string {
55 int len;
56 char str[MAX_STRING_LEN+1];
57 };
59 int num_of_cpus(void);
60 void string_nr_addch(struct string *str, int nr, char ch)
61 {
62 int i;
63 for (i = 0; i < nr; i++)
64 str->str[str->len++] = ch;
65 str->str[str->len] = '\0';
66 }
68 int string_print(struct string *str, char *fmt, ...)
69 {
70 va_list ap;
71 int l = 0;
73 va_start(ap, fmt);
74 l = vsprintf(str->str + str->len, fmt, ap);
75 va_end(ap);
76 str->len += l;
77 str->str[str->len] = '\0';
78 return l;
79 }
81 struct cpu {
82 unsigned char cx;
83 // unsigned char cx_prev;
84 unsigned char flag;
85 unsigned char irqs[4];
86 unsigned int expected;
87 unsigned int predicted;
88 };
90 struct state {
91 uint64_t tsc;
92 struct cpu cpu[MAX_CPU_NR];
93 };
95 struct mode {
96 const char *name;
97 int offset;
98 int width;
99 int row;
100 int scroll_h;
101 struct state *state;
102 int state_nr;
103 uint64_t time_scale;
104 uint64_t start_time;
105 int cpu_bitmap[MAX_CPU_NR];
106 int initialized;
107 int (*init)(void);
108 void (*show)(void);
109 void (*exit)(void);
110 };
112 /* simplified xentrace record */
113 struct rec {
114 uint64_t tsc;
115 int cpu;
116 unsigned int expected;
117 unsigned int predicted;
118 unsigned char cx;
119 unsigned char irqs[4];
120 };
122 /********** FORWARD DECLARATION **********/
123 void show_help(void);
124 void show_version(void);
125 int load_file(char *fname);
126 void crt_init(void);
127 int mode_init(void);
128 void mode_show(void);
130 /* event mode handler */
131 int event_mode_init(void);
132 void event_mode_show(void);
133 void event_mode_exit(void);
135 /* time mode handler */
136 int time_mode_init(void);
137 int time_mode_rebuild(uint64_t start_time, uint64_t time_scale);
139 /********** GLOBAL VARIABLES **********/
140 /* store simplified xentrace data */
141 struct rec *data;
142 int64_t data_nr, data_cur;
143 /* store max cx state number and cpu number */
144 int max_cx_num = -1, max_cpu_num = -1;
145 int is_irq_enabled = -1;
146 int is_menu_gov_enabled = -1;
147 int is_link = 0;
148 uint64_t tsc2us = 2793UL;
150 struct rec *data_evt;
151 struct rec *evt[MAX_CPU_NR];
152 int evt_len[MAX_CPU_NR];
154 int cur_row = 0;
155 struct mode modes[] = {
156 {
157 .name = "Event",
158 .init = event_mode_init,
159 .show = event_mode_show,
160 .exit = event_mode_exit,
161 },
162 {
163 .name = "Time",
164 .init = time_mode_init,
165 /* use the same show and exit with event mode */
166 .show = event_mode_show,
167 .exit = event_mode_exit,
168 },
169 };
170 struct mode *this = NULL;
172 /* hand-crafted min() */
173 static inline int min(int a, int b)
174 {
175 return a < b ? a : b;
176 }
178 #define MIN(a, b) ((a) < (b) ? (a) : (b))
180 void choose_cpus(void);
181 void help_screen(void);
182 int main(int argc, char *argv[])
183 {
184 char *fname = NULL;
185 int arg;
186 int quit = 0;
187 uint64_t s_time = 0;
188 uint64_t last_tsc = 0;
190 for (arg = 1; arg < argc; arg++) {
191 if (!strcmp(argv[arg], "--version")) {
192 show_version();
193 exit(EXIT_SUCCESS);
194 } else if (!strcmp(argv[arg], "--help")) {
195 show_help();
196 exit(EXIT_SUCCESS);
197 } else {
198 /* assume it's a file */
199 fname = argv[arg];
200 break;
201 }
202 }
204 if (!fname) {
205 show_help();
206 exit(EXIT_FAILURE);
207 }
209 if (load_file(fname))
210 exit(EXIT_FAILURE);
212 if (!data_cur) {
213 fprintf(stderr, "file %s doesn't contain any valid record\n", fname);
214 exit(EXIT_FAILURE);
215 }
217 if (mode_init())
218 exit(EXIT_FAILURE);
220 crt_init();
222 cur_row = 1;
223 this = &modes[0];
224 while (!quit) {
225 int ch;
227 clear();
228 this->show();
229 ch = getch();
230 switch (ch) {
231 case '!':
232 is_link = !is_link;
233 break;
234 case 'u':
235 move(LINES-1, 0);
236 clrtoeol();
237 printw("us = ? TSCs (default: 2793):");
238 echo();
239 curs_set(1);
240 scanw("%"PRIu64, &tsc2us);
241 curs_set(0);
242 noecho();
243 if (tsc2us <= 0)
244 tsc2us = 2793UL;
245 break;
246 case '/':
247 move(LINES-1, 0);
248 clrtoeol();
249 printw("Input start time:");
250 echo();
251 curs_set(1);
252 scanw("%"PRIu64, &s_time);
253 curs_set(0);
254 noecho();
255 if (s_time >= this->state[0].tsc &&
256 s_time <= this->state[this->state_nr-1].tsc) {
257 int i = 0;
258 while (i < this->state_nr &&
259 this->state[i].tsc < s_time)
260 i++;
261 this->row = i;
262 cur_row = 1;
263 }
264 break;
265 case '+':
266 if (!strcmp(this->name, "Time")) {
267 this->time_scale -= this->time_scale/10;
268 this->start_time = this->state[this->row+cur_row-1].tsc - (cur_row-1)*this->time_scale;
269 if (this->start_time < data[0].tsc)
270 this->start_time = data[0].tsc;
271 time_mode_rebuild(this->start_time, this->time_scale);
272 }
273 break;
274 case '-':
275 if (!strcmp(this->name, "Time")) {
276 this->time_scale += this->time_scale/10;
277 this->start_time = this->state[this->row+cur_row-1].tsc - (cur_row-1)*this->time_scale;
278 if (this->start_time < data[0].tsc)
279 this->start_time = data[0].tsc;
280 time_mode_rebuild(this->start_time, this->time_scale);
281 }
282 break;
283 case KEY_RESIZE:
284 break;
285 case KEY_UP:
286 if (--cur_row < 1) {
287 cur_row = 1;
288 if (--this->row < 0)
289 this->row = 0;
290 }
291 break;
292 case KEY_DOWN:
293 if (++cur_row > LINES-2) {
294 cur_row = LINES-2;
295 this->row = min(this->state_nr-LINES+2, this->row+1);
296 }
297 break;
298 case KEY_LEFT:
299 this->scroll_h -= 3;
300 if (this->scroll_h < 0)
301 this->scroll_h = 0;
302 break;
303 case KEY_RIGHT:
304 this->scroll_h += 3;
305 if (this->scroll_h >= this->width*num_of_cpus())
306 this->scroll_h = this->width*num_of_cpus();
307 break;
308 case KEY_HOME:
309 cur_row = 1;
310 this->row = 0;
311 break;
312 case KEY_END:
313 cur_row = LINES-2;
314 this->row = this->state_nr-LINES+2;
315 break;
316 case KEY_NPAGE:
317 this->row = min(this->state_nr-LINES+2, this->row+20);
318 break;
319 case KEY_PPAGE:
320 if (this->row >= 20)
321 this->row -= 20;
322 break;
323 case KEY_F(2):
324 /* change to another mode */
325 if (is_link)
326 last_tsc = this->state[this->row+cur_row-1].tsc;
328 if (this == &modes[sizeof(modes)/sizeof(modes[0])-1])
329 this = &modes[0];
330 else
331 this++;
332 clear();
333 if (is_link) {
334 if (!strcmp(this->name, "Time")) {
335 this->start_time = last_tsc - (cur_row-1)*this->time_scale;
336 if (this->start_time < data[0].tsc)
337 this->start_time = data[0].tsc;
338 time_mode_rebuild(this->start_time, this->time_scale);
339 } else if (!strcmp(this->name, "Event")) {
340 int x;
341 for (x = 0; x < this->state_nr && this->state[x].tsc < last_tsc; x++)
342 ;
343 this->row = x-(cur_row-1);
344 }
345 }
346 break;
347 case KEY_F(3):
348 if (!strcmp(this->name, "Time")) {
349 /* only meaningful in Time mode */
350 move(LINES-1, 0);
351 clrtoeol();
352 printw("Input time scale and start time:");
353 echo();
354 curs_set(1);
355 scanw("%"PRIu64" %"PRIu64,
356 &this->time_scale, &this->start_time);
357 curs_set(0);
358 noecho();
359 time_mode_rebuild(this->start_time,
360 this->time_scale);
361 }
362 break;
363 case KEY_F(4):
364 /* quit */
365 quit = 1;
366 break;
367 case KEY_F(5):
368 /* choose which CPUs to display */
369 choose_cpus();
370 break;
371 case 'h':
372 help_screen();
373 break;
374 }
375 }
377 exit(EXIT_SUCCESS);
378 }
379 /* used for qsort() */
380 static int evt_data_cmp(const void *_a, const void *_b)
381 {
382 struct rec *a = (struct rec *)_a;
383 struct rec *b = (struct rec *)_b;
384 if (a->cpu == b->cpu)
385 return a->tsc > b->tsc ? 1 : -1;
386 return a->cpu > b->cpu ? 1 : -1;
387 }
389 static int data_cmp(const void *_a, const void *_b)
390 {
391 struct rec *a = (struct rec *)_a;
392 struct rec *b = (struct rec *)_b;
393 return a->tsc > b->tsc ? 1 : -1;
394 }
396 /* load file and make them a list of records
397 * update these following variables:
398 * data, data_cur, data_nr
399 * max_cpu_num, max_cx_num
400 */
401 int load_file(char *fname)
402 {
403 /* file descriptor for raw xentrace file */
404 int fd;
405 /* current cpu during xentrace data parse */
406 int cur_cpu = -1;
407 int i;
409 fd = open(fname, O_RDONLY);
410 if (fd < 0) {
411 fprintf(stderr, "file %s cannot open\n", fname);
412 return 1;
413 }
415 /* the initial number is 1024,
416 * and when it overflows, this number doubles.
417 */
418 data_nr = 1024;
419 data_cur = 0;
420 data = malloc(sizeof(struct rec) * data_nr);
421 if (!data) {
422 fprintf(stderr, "not enough memory\n");
423 close(fd);
424 return 1;
425 }
427 while (1) {
428 struct t_rec rec;
429 ssize_t ret, size;
431 ret = read(fd, &rec, sizeof(uint32_t));
432 if (!ret)
433 break;
434 if (ret != sizeof(uint32_t)) {
435 fprintf(stderr, "reading header error\n");
436 break;
437 }
439 size = 0;
440 if (rec.cycles_included)
441 size += sizeof(uint64_t);
442 size += sizeof(uint32_t) * rec.extra_u32;
444 ret = read(fd, (char *)&rec + sizeof(uint32_t), size);
445 if (!ret && size)
446 break;
447 if (ret != size) {
448 fprintf(stderr, "reading data error\n");
449 break;
450 }
452 if (rec.event == 0x1f003) {
453 /* cpu change event */
454 cur_cpu = 0;
455 if (rec.extra_u32 > 0)
456 cur_cpu = rec.u.nocycles.extra_u32[0];
457 continue;
458 } else if (!rec.cycles_included ||
459 (rec.event != TRC_PM_IDLE_ENTRY &&
460 rec.event != TRC_PM_IDLE_EXIT &&
461 rec.event != TRC_PM_FREQ_CHANGE)) {
462 continue;
463 }
465 /* add one record */
466 if (data_cur == data_nr) {
467 data_nr <<= 1;
468 if (data_nr < 0) {
469 fprintf(stderr, "too many entries\n");
470 close(fd);
471 return 1;
472 }
473 data = realloc(data, sizeof(struct rec) * data_nr);
474 if (!data) {
475 fprintf(stderr, "not enough memory\n");
476 close(fd);
477 return 1;
478 }
479 }
480 data[data_cur].tsc = rec.u.cycles.cycles_hi;
481 data[data_cur].tsc <<= 32;
482 data[data_cur].tsc |= rec.u.cycles.cycles_lo;
483 data[data_cur].cpu = cur_cpu;
484 /* extra_u32[1] is omitted, as it's pm ticks. */
485 if (rec.event == TRC_PM_IDLE_ENTRY) {
486 data[data_cur].cx = rec.u.cycles.extra_u32[0];
487 if (rec.extra_u32 >= 4) {
488 data[data_cur].expected = rec.u.cycles.extra_u32[2];
489 data[data_cur].predicted = rec.u.cycles.extra_u32[3];
490 is_menu_gov_enabled = 1;
491 } else
492 is_menu_gov_enabled = 0;
493 } else if (rec.event == TRC_PM_IDLE_EXIT) {
494 /* IDLE_EXIT default to C0 */
495 data[data_cur].cx = 0;
496 /* store the reasons why it exits */
497 if (rec.extra_u32 == 6) {
498 data[data_cur].irqs[0] = rec.u.cycles.extra_u32[2];
499 data[data_cur].irqs[1] = rec.u.cycles.extra_u32[3];
500 data[data_cur].irqs[2] = rec.u.cycles.extra_u32[4];
501 data[data_cur].irqs[3] = rec.u.cycles.extra_u32[5];
502 is_irq_enabled = 1;
503 } else
504 is_irq_enabled = 0;
505 } else {
506 /* FREQ CHANGE */
507 }
509 /* update max info */
510 if (data[data_cur].cx > max_cx_num)
511 max_cx_num = data[data_cur].cx;
512 if (data[data_cur].cpu > max_cpu_num)
513 max_cpu_num = data[data_cur].cpu;
515 data_cur++;
516 }
517 close(fd);
519 data_evt = malloc(sizeof(struct rec) * data_cur);
520 memcpy(data_evt, data, sizeof(struct rec) * data_cur);
522 qsort(data_evt, data_cur, sizeof(struct rec), evt_data_cmp);
523 for (i = 0; i < max_cpu_num; i++) {
524 evt_len[i] = 0;
525 evt[i] = NULL;
526 }
527 for (i = data_cur-1; i >= 0; i--) {
528 evt[data_evt[i].cpu] = data_evt+i;
529 evt_len[data_evt[i].cpu]++;
530 }
532 /* sort data array according to TSC time line */
533 qsort(data, data_cur, sizeof(struct rec), data_cmp);
535 max_cpu_num++;
536 max_cx_num++;
538 return 0;
539 }
541 void show_version(void)
542 {
543 printf("gtraceview - (C) 2009 Intel Corporation\n");
544 }
546 void show_help(void)
547 {
548 show_version();
549 printf("gtraceview <trace.data> [--version] [--help]\n");
550 printf(" trace.data raw data got by "
551 "'xentrace -e 0x80f000 trace.dat'\n");
552 printf(" --version show version information\n");
553 printf(" --help show this message\n");
554 printf("For more help messages, please press 'h' in the window\n");
555 }
557 void crt_done(void)
558 {
559 curs_set(1);
560 endwin();
561 }
563 void help_screen(void)
564 {
565 clear();
566 mvprintw(0, 0, " HELP SCREEN");
567 mvprintw(1, 0, "1. LEFT and RIGHT arrow key to move off-screen outputs");
568 mvprintw(2, 0, "2. UP and DOWN arrow key to move the highlighted line");
569 mvprintw(3, 0, "3. F2 to switch between Event and Time mode");
570 mvprintw(4, 0, "4. '/' to search the TSC stamp");
571 mvprintw(5, 0, "5. '+' to zoom in and '-' to zoom out");
572 mvprintw(6, 0, "6. F3 to set start time and time manually");
573 mvprintw(7, 0, "7. F4 to quit");
574 mvprintw(8, 0, "8. F5 to select which CPUs we want to see");
575 mvprintw(9, 0, "9. Irq exit reason shown on Cx exit record (patch needed)");
576 mvprintw(10, 0, "10. Menu governor criteria shown on bottom line (patch needed)");
577 mvprintw(11, 0, "11. PAGEDOWN, PAGEUP, HOME and END to navigate");
578 mvprintw(12, 0, "12. 'h' to show this screen");
579 mvprintw(13, 0, "13. 'u' to edit how many TSCs is a us unit");
581 mvprintw(LINES-1, 0, "Press any key to continue...");
582 getch();
583 }
585 void crt_init(void)
586 {
587 char *term;
589 initscr();
590 noecho();
591 nonl();
592 intrflush(stdscr, false);
593 keypad(stdscr, true);
594 curs_set(0);
595 /* hook exit() */
596 atexit(crt_done);
597 /* we love colorful screens :-) */
598 start_color();
599 init_pair(1, COLOR_BLACK, COLOR_CYAN);
600 init_pair(2, COLOR_BLACK, COLOR_GREEN);
601 init_pair(3, COLOR_BLACK, COLOR_RED);
603 /* some term tunings */
604 term = getenv("TERM");
605 if (!strcmp(term, "xterm") ||
606 !strcmp(term, "xterm-color") ||
607 !strcmp(term, "vt220")) {
608 define_key("\033[1~", KEY_HOME);
609 define_key("\033[4~", KEY_END);
610 define_key("\033OP", KEY_F(1));
611 define_key("\033OQ", KEY_F(2));
612 define_key("\033OR", KEY_F(3));
613 define_key("\033OS", KEY_F(4));
614 define_key("\033[11~", KEY_F(1));
615 define_key("\033[12~", KEY_F(2));
616 define_key("\033[13~", KEY_F(3));
617 define_key("\033[14~", KEY_F(4));
618 define_key("\033[[D", KEY_LEFT);
619 }
620 }
622 void nr_addch(int nr, int ch)
623 {
624 int i;
625 int y, x;
626 getyx(stdscr, y, x);
627 for (i = 0; i < nr; i++) {
628 if (x == COLS-1)
629 break;
630 addch(ch);
631 }
632 }
634 int event_mode_init(void)
635 {
636 int i, j;
637 struct state *state;
638 int index;
639 struct cpu cur_state[MAX_CPU_NR];
641 if (this->initialized)
642 free(this->state);
643 state = malloc(sizeof(struct state) * data_cur);
644 if (!state)
645 return 1;
646 this->state = state;
647 this->row = 0;
648 this->width = 9;
649 this->offset = 33;
650 this->scroll_h = 0;
652 /* otherwise, respect cpu_bitmap[] */
653 if (!this->initialized) {
654 this->initialized = 1;
655 for (i = 0; i < max_cpu_num; i++)
656 this->cpu_bitmap[i] = 1;
657 }
659 for (i = 0; i < max_cpu_num; i++)
660 if (this->cpu_bitmap[i])
661 cur_state[i].flag = FLAG_UNKNOWN;
663 for (i = 0, index = 0; i < data_cur; i++) {
664 /* data[i] */
665 int cpu = data[i].cpu;
666 if (cpu < 0)
667 continue;
668 if (!this->cpu_bitmap[cpu])
669 continue;
671 /* TODO: use the same structure */
672 /* copy cx, expected, predicted and irqs */
673 cur_state[cpu].cx = data[i].cx;
674 cur_state[cpu].expected = data[i].expected;
675 cur_state[cpu].predicted = data[i].predicted;
676 memcpy(cur_state[cpu].irqs, data[i].irqs,
677 sizeof(unsigned char) * 4);
678 /* as long as it comes here,
679 * it means that we have an event.
680 */
681 cur_state[cpu].flag = FLAG_EDGE;
683 state[index].tsc = data[i].tsc;
684 for (j = 0; j < max_cpu_num; j++) {
685 if (!this->cpu_bitmap[j])
686 continue;
688 /* copy cx, irqs and flags */
689 state[index].cpu[j].cx = cur_state[j].cx;
690 state[index].cpu[j].expected = cur_state[j].expected;
691 state[index].cpu[j].predicted = cur_state[j].predicted;
692 memcpy(state[index].cpu[j].irqs, cur_state[j].irqs,
693 sizeof(unsigned char) * 4);
694 state[index].cpu[j].flag = cur_state[j].flag;
696 /* chage flag in cur_state accordingly */
697 if (cur_state[j].flag == FLAG_EDGE)
698 cur_state[j].flag = FLAG_LEVEL;
699 }
700 index++;
701 }
703 this->state_nr = index;
704 return 0;
705 }
707 static inline int len_of_number(uint64_t n)
708 {
709 int l = 0;
710 if (!n)
711 return 1;
712 do {
713 l++;
714 n /= 10;
715 } while (n);
716 return l;
717 }
719 static inline void display_number(uint64_t n, int l)
720 {
721 static char sym[] = { ' ', 'K', 'M', 'G', 'T' };
722 int nr = 0;
724 if (len_of_number(n) <= l) {
725 nr_addch(l-len_of_number(n), ' ');
726 printw("%"PRIu64, n);
727 return;
728 }
729 do {
730 n /= 1000UL;
731 nr++;
732 } while (len_of_number(n) > l-1);
733 nr_addch(l-1-len_of_number(n), ' ');
734 printw("%"PRIu64, n);
735 nr_addch(1, sym[nr]);
736 }
738 void draw_cpu_state(struct string *s, struct cpu *c, int width)
739 {
740 int cx = c->cx;
741 int flag = c->flag;
743 switch (flag) {
744 case FLAG_FUZZY:
745 string_nr_addch(s, max_cx_num, '#');
746 string_nr_addch(s, width-max_cx_num, ' ');
747 break;
748 case FLAG_UNKNOWN:
749 string_nr_addch(s, 1, '?');
750 string_nr_addch(s, width-1, ' ');
751 break;
752 case FLAG_LEVEL:
753 string_nr_addch(s, cx, ' ');
754 string_nr_addch(s, 1, '|');
755 string_nr_addch(s, width-1-cx, ' ');
756 break;
757 case FLAG_EDGE:
758 if (cx > 0) {
759 /* ENTRY */
760 string_nr_addch(s, 1, '>');
761 string_nr_addch(s, cx-1, '-');
762 string_nr_addch(s, 1, '+');
763 string_nr_addch(s, width-cx-1, ' ');
764 } else {
765 /* EXIT */
766 string_nr_addch(s, 1, '<');
767 if (is_irq_enabled == 1) {
768 int k, len = 0;
769 for (k = 0; k < 4; k++) {
770 unsigned char irq = c->irqs[k];
771 if (irq) {
772 string_print(s, "%02x", irq);
773 len += 2;
774 }
775 }
776 if (len > 0)
777 string_nr_addch(s, width-len-1, ' ');
778 else {
779 string_print(s, "noirq");
780 string_nr_addch(s, width-1-5, ' ');
781 }
782 } else {
783 string_nr_addch(s, 1, '-');
784 string_nr_addch(s, width-2, ' ');
785 }
786 }
787 break;
788 }
789 }
791 void event_mode_show(void)
792 {
793 struct state *state = this->state;
794 struct string s;
795 int idx = this->row;
796 int idx_hl = 0;
797 int i, j, l;
799 /* draw headline */
800 s.len = 0;
801 move(0, 0);
802 attron(COLOR_PAIR(2));
803 nr_addch(this->offset, ' ');
804 for (i = 0; i < max_cpu_num; i++) {
805 if (this->cpu_bitmap[i]) {
806 string_print(&s, "CPU%d", i);
807 string_nr_addch(&s, this->width-len_of_number(i)-3, ' ');
808 }
809 }
810 mvaddnstr(0, this->offset, s.str+this->scroll_h,
811 MIN(s.len-this->scroll_h, this->width*num_of_cpus()));
812 attroff(COLOR_PAIR(2));
814 /* draw body */
815 for (i = 1; i < LINES-1; i++, idx++) {
816 move(i, 0);
817 /* highlight the current row */
818 if (i == cur_row) {
819 attron(COLOR_PAIR(1));
820 idx_hl = idx;
821 }
823 if (idx >= this->state_nr) {
824 /* do not show this line */
825 nr_addch(this->offset+this->width*num_of_cpus(), ' ');
826 } else {
827 if (!strcmp(this->name, "Event")) {
828 uint64_t delta = 0;
829 if (idx)
830 delta = (state[idx].tsc - state[idx-1].tsc)/tsc2us;
831 printw("%20"PRIu64"(", state[idx].tsc);
832 display_number(delta, 8);
833 printw("us) ");
834 } else if (!strcmp(this->name, "Time")) {
835 printw("%20"PRIu64" ", state[idx].tsc);
836 }
838 s.len = 0;
839 for (j = 0; j < max_cpu_num; j++) {
840 /* draw cpu state */
841 if (this->cpu_bitmap[j])
842 draw_cpu_state(&s, &state[idx].cpu[j], this->width);
843 }
844 /* draw the line accordingly */
845 mvaddnstr(i, this->offset, s.str+this->scroll_h,
846 MIN(s.len-this->scroll_h, this->width*num_of_cpus()));
847 }
848 /* pair of the highlight logics */
849 if (i == cur_row)
850 attroff(COLOR_PAIR(1));
851 }
853 /* draw tail line */
854 attron(COLOR_PAIR(2));
855 s.len = 0;
856 l = 0;
857 l += string_print(&s, "%s Mode [%sLINKED]", this->name, is_link ? "" : "NOT ");
858 if (!strcmp(this->name, "Time")) {
859 #if 0
860 l += string_print(&s, " [%"PRIu64":%"PRIu64"]",
861 this->start_time, this->time_scale);
862 #endif
863 l += string_print(&s, " [%"PRIu64"]",
864 this->time_scale);
865 }
866 if (is_menu_gov_enabled == 1) {
867 for (i = 0; i < max_cpu_num; i++) {
868 if (this->cpu_bitmap[i] &&
869 state[idx_hl].cpu[i].flag == FLAG_EDGE &&
870 state[idx_hl].cpu[i].cx > 0)
871 l += string_print(&s, " (CPU%d,%lu,%lu)",
872 i,
873 state[idx_hl].cpu[i].expected,
874 state[idx_hl].cpu[i].predicted);
875 }
876 }
877 /* add cx exit residency info */
878 for (i = 0; i < max_cpu_num; i++) {
879 if (this->cpu_bitmap[i] &&
880 state[idx_hl].cpu[i].flag == FLAG_EDGE &&
881 state[idx_hl].cpu[i].cx == 0) {
882 uint64_t tsc = state[idx_hl].tsc;
883 int k;
885 k = 0;
886 while (k < evt_len[i] &&
887 evt[i][k].tsc < tsc)
888 k++;
889 k--;
890 if (k >= 0 && k+1 < evt_len[i] && evt[i][k].cx > 0) {
891 l += string_print(&s, " (CPU%d, %"PRIu64"us)",
892 i,
893 (evt[i][k+1].tsc - evt[i][k].tsc)/tsc2us);
894 }
895 }
896 }
898 string_nr_addch(&s, this->offset+this->width*num_of_cpus()-l, ' ');
899 mvaddstr(LINES-1, 0, s.str);
900 attroff(COLOR_PAIR(2));
901 refresh();
902 }
904 void event_mode_exit(void)
905 {
906 free(this->state);
907 this->initialized = 0;
908 }
910 void mode_exit(void)
911 {
912 int nr = sizeof(modes)/sizeof(modes[0]);
913 int i;
915 for (i = 0; i < nr; i++) {
916 this = &modes[i];
917 if (this->initialized)
918 this->exit();
919 }
920 }
922 int mode_init(void)
923 {
924 int nr = sizeof(modes)/sizeof(modes[0]);
925 int i, r = 0;
927 for (i = 0; i < nr; i++) {
928 this = &modes[i];
929 this->initialized = 0;
930 r += this->init();
931 }
933 this = &modes[0];
935 /* hook into exit */
936 atexit(mode_exit);
938 return r;
939 }
941 int time_mode_rebuild(uint64_t start_time, uint64_t time_scale)
942 {
943 int i, j;
944 struct cpu cur_state[MAX_CPU_NR];
945 uint64_t tsc = start_time;
946 struct state *state;
947 uint64_t number, temp = 0;
948 int state_cur = 0;
950 for (i = 0; i < max_cpu_num; i++)
951 cur_state[i].flag = FLAG_UNKNOWN;
953 /* allocate spaces, it may be huge... */
954 if (time_scale)
955 temp = (data[data_cur-1].tsc - start_time)/time_scale;
956 number = 10000UL;
957 if (temp < number)
958 number = temp;
959 number += 2;
960 state = malloc(sizeof(struct state) * number);
961 if (!state)
962 return 1;
963 if (this->state)
964 free(this->state);
965 this->state = state;
966 this->width = 9;
967 this->row = 0;
969 /* determine the current Cx state */
970 /* check [data[0].tsc, tsc) */
971 i = 0;
972 while (i < data_cur && data[i].tsc < tsc) {
973 int cpu = data[i].cpu;
974 cur_state[cpu].cx = data[i].cx;
975 cur_state[cpu].flag = FLAG_LEVEL;
976 i++;
977 }
978 while (i < data_cur && state_cur < number) {
979 int num[MAX_CPU_NR];
980 int last_idx[MAX_CPU_NR];
982 #if 0
983 printf("XXXXX %d tsc: %"PRIu64" data[i].tsc: %"PRIu64"\n",
984 i, tsc, data[i].tsc);
985 #endif
986 /* ensure they are zero */
987 memset(num, 0, sizeof(int) * MAX_CPU_NR);
988 memset(last_idx, 0, sizeof(int) * MAX_CPU_NR);
990 /* check [tsc, tsc+time_scale) */
991 while (i < data_cur && data[i].tsc < tsc+time_scale) {
992 int cpu = data[i].cpu;
993 num[cpu]++;
994 last_idx[cpu] = i;
995 i++;
996 }
997 /* TODO */
998 if (i >= data_cur)
999 break;
1000 for (j = 0; j < max_cpu_num; j++) {
1001 if (num[j] == 1) {
1002 /* only one event, it's an edge*/
1003 cur_state[j].cx = data[last_idx[j]].cx;
1004 cur_state[j].flag = FLAG_EDGE;
1005 } else if (num[j] > 1) {
1006 /* more than one event, it's fuzzy */
1007 cur_state[j].cx = data[last_idx[j]].cx;
1008 cur_state[j].flag = FLAG_FUZZY;
1009 } else if (cur_state[j].flag == FLAG_FUZZY) {
1010 /* no event, fuzzy state can't be passed down
1011 * notice that cx is set in the fuzzy state,
1012 * it's not changed here afterwards.
1013 */
1014 cur_state[j].flag = FLAG_LEVEL;
1018 /* copy tsc */
1019 state[state_cur].tsc = tsc;
1020 for (j = 0; j < max_cpu_num; j++) {
1021 /* copy cx and flag */
1022 state[state_cur].cpu[j].cx = cur_state[j].cx;
1023 state[state_cur].cpu[j].flag = cur_state[j].flag;
1025 /* update flag in cur_state */
1026 if (cur_state[j].flag == FLAG_EDGE) {
1027 cur_state[j].flag = FLAG_LEVEL;
1028 if (cur_state[j].cx == 0) {
1029 /* EXIT */
1030 /* copy irqs conditionally */
1031 memcpy(state[state_cur].cpu[j].irqs,
1032 data[last_idx[j]].irqs,
1033 sizeof(unsigned char) * 4);
1034 } else {
1035 /* ENTRY */
1036 state[state_cur].cpu[j].expected =
1037 data[last_idx[j]].expected;
1038 state[state_cur].cpu[j].predicted =
1039 data[last_idx[j]].predicted;
1043 state_cur++;
1044 tsc += time_scale;
1046 this->state_nr = state_cur;
1047 this->row = 0;
1049 return 0;
1052 int time_mode_init(void)
1054 int i;
1055 this->offset = 21;
1056 this->scroll_h = 0;
1057 this->time_scale = (data[data_cur-1].tsc -data[0].tsc)/10000UL;
1058 this->start_time = data[0].tsc;
1059 for (i = 0; i < max_cpu_num; i++)
1060 this->cpu_bitmap[i] = 1;
1061 return time_mode_rebuild(this->start_time,
1062 this->time_scale);
1065 void choose_cpus(void)
1067 int i;
1068 int temp_row = 1;
1069 int ch;
1071 clear();
1072 mvprintw(0, 0, "How many CPUs to track? Press space to toggle. Press 'q' or 'Q' to quit.");
1074 while (1) {
1075 for (i = 0; i < max_cpu_num; i++) {
1076 if (temp_row == i+1)
1077 attron(COLOR_PAIR(2));
1078 mvprintw(i+1, 0, "[%s] CPU%d", this->cpu_bitmap[i] ? "x" : " ", i);
1079 if (temp_row == i+1)
1080 attroff(COLOR_PAIR(2));
1082 ch = getch();
1083 switch (ch) {
1084 case KEY_UP:
1085 if (--temp_row < 1)
1086 temp_row = 1;
1087 break;
1088 case KEY_DOWN:
1089 if (++temp_row > max_cpu_num)
1090 temp_row = max_cpu_num;
1091 break;
1092 case ' ':
1093 this->cpu_bitmap[temp_row-1] = !this->cpu_bitmap[temp_row-1];
1094 break;
1095 case 'q':
1096 case 'Q':
1097 if (num_of_cpus() >= 1) {
1098 if (!strcmp(this->name, "Event"))
1099 this->init();
1100 return;
1102 case KEY_F(4):
1103 exit(EXIT_SUCCESS);
1108 int num_of_cpus(void)
1110 int i, nr = 0;
1111 for (i = 0; i < max_cpu_num; i++)
1112 if (this->cpu_bitmap[i])
1113 nr++;
1114 return nr;