diff options
author | Keir Fraser <keir.fraser@citrix.com> | 2009-06-29 11:03:24 +0100 |
---|---|---|
committer | Keir Fraser <keir.fraser@citrix.com> | 2009-06-29 11:03:24 +0100 |
commit | 5dc9ac92ad2fea4161694c509130d64c247f4135 (patch) | |
tree | 4b8e203a323ec28c3c2f0545a120fed0f76d5618 /tools/misc/gtraceview.c | |
parent | 71eb0dfc66b858528a97b9fbc92d55f13d378cf5 (diff) | |
download | xen-5dc9ac92ad2fea4161694c509130d64c247f4135.tar.gz xen-5dc9ac92ad2fea4161694c509130d64c247f4135.tar.bz2 xen-5dc9ac92ad2fea4161694c509130d64c247f4135.zip |
x86 Cx tracing: adds gtraceview & gtracestat utilities
Signed-off-by: Lu Guanqun <guanqun.lu@intel.com>
Diffstat (limited to 'tools/misc/gtraceview.c')
-rw-r--r-- | tools/misc/gtraceview.c | 1103 |
1 files changed, 1103 insertions, 0 deletions
diff --git a/tools/misc/gtraceview.c b/tools/misc/gtraceview.c new file mode 100644 index 0000000000..5c91db38fe --- /dev/null +++ b/tools/misc/gtraceview.c @@ -0,0 +1,1103 @@ +/* + * gtraceview.c: list Cx events in a ncurse way to help find abnormal behaviour. + * Copyright (c) 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <xenctrl.h> +#include <xen/trace.h> + +#include <ncurses.h> + +/********** MACROS **********/ +#define MAX_CPU_NR 32 +#define MAX_MODE_NR 16 +#define MAX_STRING_LEN 1024 + +/********** STRUCTURE DEFINITIONS **********/ +enum { + FLAG_FUZZY = 0, + FLAG_LEVEL, + FLAG_EDGE, + FLAG_UNKNOWN, + NR_FLAGS +}; + +struct string { + int len; + char str[MAX_STRING_LEN+1]; +}; + +int num_of_cpus(void); +void string_nr_addch(struct string *str, int nr, char ch) +{ + int i; + for (i = 0; i < nr; i++) + str->str[str->len++] = ch; + str->str[str->len] = '\0'; +} + +int string_print(struct string *str, char *fmt, ...) +{ + va_list ap; + int l = 0; + + va_start(ap, fmt); + l = vsprintf(str->str + str->len, fmt, ap); + va_end(ap); + str->len += l; + str->str[str->len] = '\0'; + return l; +} + +struct cpu { + unsigned char cx; + // unsigned char cx_prev; + unsigned char flag; + unsigned char irqs[4]; + unsigned int expected; + unsigned int predicted; +}; + +struct state { + uint64_t tsc; + struct cpu cpu[MAX_CPU_NR]; +}; + +struct mode { + const char *name; + int offset; + int width; + int row; + int scroll_h; + struct state *state; + int state_nr; + uint64_t time_scale; + uint64_t start_time; + int cpu_bitmap[MAX_CPU_NR]; + int initialized; + int (*init)(void); + void (*show)(void); + void (*exit)(void); +}; + +/* simplified xentrace record */ +struct rec { + uint64_t tsc; + int cpu; + unsigned int expected; + unsigned int predicted; + unsigned char cx; + unsigned char irqs[4]; +}; + +/********** FORWARD DECLARATION **********/ +void show_help(void); +void show_version(void); +int load_file(char *fname); +void crt_init(void); +int mode_init(void); +void mode_show(void); + +/* event mode handler */ +int event_mode_init(void); +void event_mode_show(void); +void event_mode_exit(void); + +/* time mode handler */ +int time_mode_init(void); +int time_mode_rebuild(uint64_t start_time, uint64_t time_scale); + +/********** GLOBAL VARIABLES **********/ +/* store simplified xentrace data */ +struct rec *data; +int64_t data_nr, data_cur; +/* store max cx state number and cpu number */ +int max_cx_num = -1, max_cpu_num = -1; +int is_irq_enabled = -1; +int is_menu_gov_enabled = -1; +int is_link = 0; +uint64_t tsc2us = 2793UL; + +struct rec *data_evt; +struct rec *evt[MAX_CPU_NR]; +int evt_len[MAX_CPU_NR]; + +int cur_row = 0; +struct mode modes[] = { + { + .name = "Event", + .init = event_mode_init, + .show = event_mode_show, + .exit = event_mode_exit, + }, + { + .name = "Time", + .init = time_mode_init, + /* use the same show and exit with event mode */ + .show = event_mode_show, + .exit = event_mode_exit, + }, +}; +struct mode *this = NULL; + +/* hand-crafted min() */ +static inline int min(int a, int b) +{ + return a < b ? a : b; +} + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +void choose_cpus(void); +void help_screen(void); +int main(int argc, char *argv[]) +{ + char *fname = NULL; + int arg; + int quit = 0; + uint64_t s_time = 0; + uint64_t last_tsc = 0; + + for (arg = 1; arg < argc; arg++) { + if (!strcmp(argv[arg], "--version")) { + show_version(); + exit(EXIT_SUCCESS); + } else if (!strcmp(argv[arg], "--help")) { + show_help(); + exit(EXIT_SUCCESS); + } else { + /* assume it's a file */ + fname = argv[arg]; + break; + } + } + + if (!fname) { + show_help(); + exit(EXIT_FAILURE); + } + + if (load_file(fname)) + exit(EXIT_FAILURE); + + if (mode_init()) + exit(EXIT_FAILURE); + + crt_init(); + + cur_row = 1; + this = &modes[0]; + while (!quit) { + int ch; + + clear(); + this->show(); + ch = getch(); + switch (ch) { + case '!': + is_link = !is_link; + break; + case 'u': + move(LINES-1, 0); + clrtoeol(); + printw("us = ? TSCs (default: 2793):"); + echo(); + curs_set(1); + scanw("%"PRIu64, &tsc2us); + curs_set(0); + noecho(); + if (tsc2us <= 0) + tsc2us = 2793UL; + break; + case '/': + move(LINES-1, 0); + clrtoeol(); + printw("Input start time:"); + echo(); + curs_set(1); + scanw("%"PRIu64, &s_time); + curs_set(0); + noecho(); + if (s_time >= this->state[0].tsc && + s_time <= this->state[this->state_nr-1].tsc) { + int i = 0; + while (i < this->state_nr && + this->state[i].tsc < s_time) + i++; + this->row = i; + cur_row = 1; + } + break; + case '+': + if (!strcmp(this->name, "Time")) { + this->time_scale -= this->time_scale/10; + this->start_time = this->state[this->row+cur_row-1].tsc - (cur_row-1)*this->time_scale; + if (this->start_time < data[0].tsc) + this->start_time = data[0].tsc; + time_mode_rebuild(this->start_time, this->time_scale); + } + break; + case '-': + if (!strcmp(this->name, "Time")) { + this->time_scale += this->time_scale/10; + this->start_time = this->state[this->row+cur_row-1].tsc - (cur_row-1)*this->time_scale; + if (this->start_time < data[0].tsc) + this->start_time = data[0].tsc; + time_mode_rebuild(this->start_time, this->time_scale); + } + break; + case KEY_RESIZE: + break; + case KEY_UP: + if (--cur_row < 1) { + cur_row = 1; + if (--this->row < 0) + this->row = 0; + } + break; + case KEY_DOWN: + if (++cur_row > LINES-2) { + cur_row = LINES-2; + this->row = min(this->state_nr-LINES+2, this->row+1); + } + break; + case KEY_LEFT: + this->scroll_h -= 3; + if (this->scroll_h < 0) + this->scroll_h = 0; + break; + case KEY_RIGHT: + this->scroll_h += 3; + if (this->scroll_h >= this->width*num_of_cpus()) + this->scroll_h = this->width*num_of_cpus(); + break; + case KEY_HOME: + cur_row = 1; + this->row = 0; + break; + case KEY_END: + cur_row = LINES-2; + this->row = this->state_nr-LINES+2; + break; + case KEY_NPAGE: + this->row = min(this->state_nr-LINES+2, this->row+20); + break; + case KEY_PPAGE: + if (this->row >= 20) + this->row -= 20; + break; + case KEY_F(2): + /* change to another mode */ + if (is_link) + last_tsc = this->state[this->row+cur_row-1].tsc; + + if (this == &modes[sizeof(modes)/sizeof(modes[0])-1]) + this = &modes[0]; + else + this++; + clear(); + if (is_link) { + if (!strcmp(this->name, "Time")) { + this->start_time = last_tsc - (cur_row-1)*this->time_scale; + if (this->start_time < data[0].tsc) + this->start_time = data[0].tsc; + time_mode_rebuild(this->start_time, this->time_scale); + } else if (!strcmp(this->name, "Event")) { + int x; + for (x = 0; x < this->state_nr && this->state[x].tsc < last_tsc; x++) + ; + this->row = x-(cur_row-1); + } + } + break; + case KEY_F(3): + if (!strcmp(this->name, "Time")) { + /* only meaningful in Time mode */ + move(LINES-1, 0); + clrtoeol(); + printw("Input time scale and start time:"); + echo(); + curs_set(1); + scanw("%"PRIu64" %"PRIu64, + &this->time_scale, &this->start_time); + curs_set(0); + noecho(); + time_mode_rebuild(this->start_time, + this->time_scale); + } + break; + case KEY_F(4): + /* quit */ + quit = 1; + break; + case KEY_F(5): + /* choose which CPUs to display */ + choose_cpus(); + break; + case 'h': + help_screen(); + break; + } + } + + exit(EXIT_SUCCESS); +} +/* used for qsort() */ +static int evt_data_cmp(const void *_a, const void *_b) +{ + struct rec *a = (struct rec *)_a; + struct rec *b = (struct rec *)_b; + if (a->cpu == b->cpu) + return a->tsc > b->tsc ? 1 : -1; + return a->cpu > b->cpu ? 1 : -1; +} + +static int data_cmp(const void *_a, const void *_b) +{ + struct rec *a = (struct rec *)_a; + struct rec *b = (struct rec *)_b; + return a->tsc > b->tsc ? 1 : -1; +} + +/* load file and make them a list of records + * update these following variables: + * data, data_cur, data_nr + * max_cpu_num, max_cx_num + */ +int load_file(char *fname) +{ + /* file descriptor for raw xentrace file */ + int fd; + /* current cpu during xentrace data parse */ + int cur_cpu = -1; + int i; + + fd = open(fname, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "file %s cannot open\n", fname); + return 1; + } + + /* the initial number is 1024, + * and when it overflows, this number doubles. + */ + data_nr = 1024; + data_cur = 0; + data = malloc(sizeof(struct rec) * data_nr); + if (!data) { + fprintf(stderr, "not enough memory\n"); + close(fd); + return 1; + } + + while (1) { + struct t_rec rec; + ssize_t ret, size; + + ret = read(fd, &rec, sizeof(uint32_t)); + if (!ret) + break; + if (ret != sizeof(uint32_t)) { + fprintf(stderr, "reading header error\n"); + break; + } + + size = 0; + if (rec.cycles_included) + size += sizeof(uint64_t); + size += sizeof(uint32_t) * rec.extra_u32; + + ret = read(fd, (char *)&rec + sizeof(uint32_t), size); + if (!ret && size) + break; + if (ret != size) { + fprintf(stderr, "reading data error\n"); + break; + } + + if (rec.event == 0x1f003) { + /* cpu change event */ + cur_cpu = 0; + if (rec.extra_u32 > 0) + cur_cpu = rec.u.nocycles.extra_u32[0]; + continue; + } else if (!rec.cycles_included || + (rec.event != TRC_PM_IDLE_ENTRY && + rec.event != TRC_PM_IDLE_EXIT && + rec.event != TRC_PM_FREQ_CHANGE)) { + continue; + } + + /* add one record */ + if (data_cur == data_nr) { + data_nr <<= 1; + if (data_nr < 0) { + fprintf(stderr, "too many entries\n"); + close(fd); + return 1; + } + data = realloc(data, sizeof(struct rec) * data_nr); + if (!data) { + fprintf(stderr, "not enough memory\n"); + close(fd); + return 1; + } + } + data[data_cur].tsc = rec.u.cycles.cycles_hi; + data[data_cur].tsc <<= 32; + data[data_cur].tsc |= rec.u.cycles.cycles_lo; + data[data_cur].cpu = cur_cpu; + /* extra_u32[1] is omitted, as it's pm ticks. */ + if (rec.event == TRC_PM_IDLE_ENTRY) { + data[data_cur].cx = rec.u.cycles.extra_u32[0]; + if (rec.extra_u32 >= 4) { + data[data_cur].expected = rec.u.cycles.extra_u32[2]; + data[data_cur].predicted = rec.u.cycles.extra_u32[3]; + is_menu_gov_enabled = 1; + } else + is_menu_gov_enabled = 0; + } else if (rec.event == TRC_PM_IDLE_EXIT) { + /* IDLE_EXIT default to C0 */ + data[data_cur].cx = 0; + /* store the reasons why it exits */ + if (rec.extra_u32 == 6) { + data[data_cur].irqs[0] = rec.u.cycles.extra_u32[2]; + data[data_cur].irqs[1] = rec.u.cycles.extra_u32[3]; + data[data_cur].irqs[2] = rec.u.cycles.extra_u32[4]; + data[data_cur].irqs[3] = rec.u.cycles.extra_u32[5]; + is_irq_enabled = 1; + } else + is_irq_enabled = 0; + } else { + /* FREQ CHANGE */ + } + + /* update max info */ + if (data[data_cur].cx > max_cx_num) + max_cx_num = data[data_cur].cx; + if (data[data_cur].cpu > max_cpu_num) + max_cpu_num = data[data_cur].cpu; + + data_cur++; + } + close(fd); + + data_evt = malloc(sizeof(struct rec) * data_cur); + memcpy(data_evt, data, sizeof(struct rec) * data_cur); + + qsort(data_evt, data_cur, sizeof(struct rec), evt_data_cmp); + for (i = 0; i < max_cpu_num; i++) { + evt_len[i] = 0; + evt[i] = NULL; + } + for (i = data_cur-1; i >= 0; i--) { + evt[data_evt[i].cpu] = data_evt+i; + evt_len[data_evt[i].cpu]++; + } + + /* sort data array according to TSC time line */ + qsort(data, data_cur, sizeof(struct rec), data_cmp); + + max_cpu_num++; + max_cx_num++; + + return 0; +} + +void show_version(void) +{ + printf("gtraceview - (C) 2009 Intel Corporation\n"); +} + +void show_help(void) +{ + show_version(); + printf("gtraceview <trace.data> [--version] [--help]\n"); + printf(" trace.data raw data from xentrace\n"); + printf(" --version show version information\n"); + printf(" --help show this message\n"); + printf("For more help messages, please press 'h' in the window\n"); +} + +void crt_done(void) +{ + curs_set(1); + endwin(); +} + +void help_screen(void) +{ + clear(); + mvprintw(0, 0, " HELP SCREEN"); + mvprintw(1, 0, "1. LEFT and RIGHT arrow key to move off-screen outputs"); + mvprintw(2, 0, "2. UP and DOWN arrow key to move the highlighted line"); + mvprintw(3, 0, "3. F2 to switch between Event and Time mode"); + mvprintw(4, 0, "4. '/' to search the TSC stamp"); + mvprintw(5, 0, "5. '+' to zoom in and '-' to zoom out"); + mvprintw(6, 0, "6. F3 to set start time and time manually"); + mvprintw(7, 0, "7. F4 to quit"); + mvprintw(8, 0, "8. F5 to select which CPUs we want to see"); + mvprintw(9, 0, "9. Irq exit reason shown on Cx exit record (patch needed)"); + mvprintw(10, 0, "10. Menu governor criteria shown on bottom line (patch needed)"); + mvprintw(11, 0, "11. PAGEDOWN, PAGEUP, HOME and END to navigate"); + mvprintw(12, 0, "12. 'h' to show this screen"); + mvprintw(13, 0, "13. 'u' to edit how many TSCs is a us unit"); + + mvprintw(LINES-1, 0, "Press any key to continue..."); + getch(); +} + +void crt_init(void) +{ + char *term; + + initscr(); + noecho(); + nonl(); + intrflush(stdscr, false); + keypad(stdscr, true); + curs_set(0); + /* hook exit() */ + atexit(crt_done); + /* we love colorful screens :-) */ + start_color(); + init_pair(1, COLOR_BLACK, COLOR_CYAN); + init_pair(2, COLOR_BLACK, COLOR_GREEN); + init_pair(3, COLOR_BLACK, COLOR_RED); + + /* some term tunings */ + term = getenv("TERM"); + if (!strcmp(term, "xterm") || + !strcmp(term, "xterm-color") || + !strcmp(term, "vt220")) { + define_key("\033[1~", KEY_HOME); + define_key("\033[4~", KEY_END); + define_key("\033OP", KEY_F(1)); + define_key("\033OQ", KEY_F(2)); + define_key("\033OR", KEY_F(3)); + define_key("\033OS", KEY_F(4)); + define_key("\033[11~", KEY_F(1)); + define_key("\033[12~", KEY_F(2)); + define_key("\033[13~", KEY_F(3)); + define_key("\033[14~", KEY_F(4)); + define_key("\033[[D", KEY_LEFT); + } +} + +void nr_addch(int nr, int ch) +{ + int i; + int y, x; + getyx(stdscr, y, x); + for (i = 0; i < nr; i++) { + if (x == COLS-1) + break; + addch(ch); + } +} + +int event_mode_init(void) +{ + int i, j; + struct state *state; + int index; + struct cpu cur_state[MAX_CPU_NR]; + + if (this->initialized) + free(this->state); + state = malloc(sizeof(struct state) * data_cur); + if (!state) + return 1; + this->state = state; + this->row = 0; + this->width = 9; + this->offset = 33; + this->scroll_h = 0; + + /* otherwise, respect cpu_bitmap[] */ + if (!this->initialized) { + this->initialized = 1; + for (i = 0; i < max_cpu_num; i++) + this->cpu_bitmap[i] = 1; + } + + for (i = 0; i < max_cpu_num; i++) + if (this->cpu_bitmap[i]) + cur_state[i].flag = FLAG_UNKNOWN; + + for (i = 0, index = 0; i < data_cur; i++) { + /* data[i] */ + int cpu = data[i].cpu; + if (cpu < 0) + continue; + if (!this->cpu_bitmap[cpu]) + continue; + + /* TODO: use the same structure */ + /* copy cx, expected, predicted and irqs */ + cur_state[cpu].cx = data[i].cx; + cur_state[cpu].expected = data[i].expected; + cur_state[cpu].predicted = data[i].predicted; + memcpy(cur_state[cpu].irqs, data[i].irqs, + sizeof(unsigned char) * 4); + /* as long as it comes here, + * it means that we have an event. + */ + cur_state[cpu].flag = FLAG_EDGE; + + state[index].tsc = data[i].tsc; + for (j = 0; j < max_cpu_num; j++) { + if (!this->cpu_bitmap[j]) + continue; + + /* copy cx, irqs and flags */ + state[index].cpu[j].cx = cur_state[j].cx; + state[index].cpu[j].expected = cur_state[j].expected; + state[index].cpu[j].predicted = cur_state[j].predicted; + memcpy(state[index].cpu[j].irqs, cur_state[j].irqs, + sizeof(unsigned char) * 4); + state[index].cpu[j].flag = cur_state[j].flag; + + /* chage flag in cur_state accordingly */ + if (cur_state[j].flag == FLAG_EDGE) + cur_state[j].flag = FLAG_LEVEL; + } + index++; + } + + this->state_nr = index; + return 0; +} + +static inline int len_of_number(uint64_t n) +{ + int l = 0; + if (!n) + return 1; + do { + l++; + n /= 10; + } while (n); + return l; +} + +static inline void display_number(uint64_t n, int l) +{ + static char sym[] = { ' ', 'K', 'M', 'G', 'T' }; + int nr = 0; + + if (len_of_number(n) <= l) { + nr_addch(l-len_of_number(n), ' '); + printw("%"PRIu64, n); + return; + } + do { + n /= 1000UL; + nr++; + } while (len_of_number(n) > l-1); + nr_addch(l-1-len_of_number(n), ' '); + printw("%"PRIu64, n); + nr_addch(1, sym[nr]); +} + +void draw_cpu_state(struct string *s, struct cpu *c, int width) +{ + int cx = c->cx; + int flag = c->flag; + + switch (flag) { + case FLAG_FUZZY: + string_nr_addch(s, max_cx_num, '#'); + string_nr_addch(s, width-max_cx_num, ' '); + break; + case FLAG_UNKNOWN: + string_nr_addch(s, 1, '?'); + string_nr_addch(s, width-1, ' '); + break; + case FLAG_LEVEL: + string_nr_addch(s, cx, ' '); + string_nr_addch(s, 1, '|'); + string_nr_addch(s, width-1-cx, ' '); + break; + case FLAG_EDGE: + if (cx > 0) { + /* ENTRY */ + string_nr_addch(s, 1, '>'); + string_nr_addch(s, cx-1, '-'); + string_nr_addch(s, 1, '+'); + string_nr_addch(s, width-cx-1, ' '); + } else { + /* EXIT */ + string_nr_addch(s, 1, '<'); + if (is_irq_enabled == 1) { + int k, len = 0; + for (k = 0; k < 4; k++) { + unsigned char irq = c->irqs[k]; + if (irq) { + string_print(s, "%02x", irq); + len += 2; + } + } + if (len > 0) + string_nr_addch(s, width-len-1, ' '); + else { + string_print(s, "noirq"); + string_nr_addch(s, width-1-5, ' '); + } + } else { + string_nr_addch(s, 1, '-'); + string_nr_addch(s, width-2, ' '); + } + } + break; + } +} + +void event_mode_show(void) +{ + struct state *state = this->state; + struct string s; + int idx = this->row; + int idx_hl = 0; + int i, j, l; + + /* draw headline */ + s.len = 0; + move(0, 0); + attron(COLOR_PAIR(2)); + nr_addch(this->offset, ' '); + for (i = 0; i < max_cpu_num; i++) { + if (this->cpu_bitmap[i]) { + string_print(&s, "CPU%d", i); + string_nr_addch(&s, this->width-len_of_number(i)-3, ' '); + } + } + mvaddnstr(0, this->offset, s.str+this->scroll_h, + MIN(s.len-this->scroll_h, this->width*num_of_cpus())); + attroff(COLOR_PAIR(2)); + + /* draw body */ + for (i = 1; i < LINES-1; i++, idx++) { + move(i, 0); + /* highlight the current row */ + if (i == cur_row) { + attron(COLOR_PAIR(1)); + idx_hl = idx; + } + + if (idx >= this->state_nr) { + /* do not show this line */ + nr_addch(this->offset+this->width*num_of_cpus(), ' '); + } else { + if (!strcmp(this->name, "Event")) { + uint64_t delta = 0; + if (idx) + delta = (state[idx].tsc - state[idx-1].tsc)/tsc2us; + printw("%20"PRIu64"(", state[idx].tsc); + display_number(delta, 8); + printw("us) "); + } else if (!strcmp(this->name, "Time")) { + printw("%20"PRIu64" ", state[idx].tsc); + } + + s.len = 0; + for (j = 0; j < max_cpu_num; j++) { + /* draw cpu state */ + if (this->cpu_bitmap[j]) + draw_cpu_state(&s, &state[idx].cpu[j], this->width); + } + /* draw the line accordingly */ + mvaddnstr(i, this->offset, s.str+this->scroll_h, + MIN(s.len-this->scroll_h, this->width*num_of_cpus())); + } + /* pair of the highlight logics */ + if (i == cur_row) + attroff(COLOR_PAIR(1)); + } + + /* draw tail line */ + attron(COLOR_PAIR(2)); + s.len = 0; + l = 0; + l += string_print(&s, "%s Mode [%sLINKED]", this->name, is_link ? "" : "NOT "); + if (!strcmp(this->name, "Time")) { +#if 0 + l += string_print(&s, " [%"PRIu64":%"PRIu64"]", + this->start_time, this->time_scale); +#endif + l += string_print(&s, " [%"PRIu64"]", + this->time_scale); + } + if (is_menu_gov_enabled == 1) { + for (i = 0; i < max_cpu_num; i++) { + if (this->cpu_bitmap[i] && + state[idx_hl].cpu[i].flag == FLAG_EDGE && + state[idx_hl].cpu[i].cx > 0) + l += string_print(&s, " (CPU%d,%lu,%lu)", + i, + state[idx_hl].cpu[i].expected, + state[idx_hl].cpu[i].predicted); + } + } + /* add cx exit residency info */ + for (i = 0; i < max_cpu_num; i++) { + if (this->cpu_bitmap[i] && + state[idx_hl].cpu[i].flag == FLAG_EDGE && + state[idx_hl].cpu[i].cx == 0) { + uint64_t tsc = state[idx_hl].tsc; + int k; + + k = 0; + while (k < evt_len[i] && + evt[i][k].tsc < tsc) + k++; + k--; + if (k >= 0 && k+1 < evt_len[i] && evt[i][k].cx > 0) { + l += string_print(&s, " (CPU%d, %"PRIu64"us)", + i, + (evt[i][k+1].tsc - evt[i][k].tsc)/tsc2us); + } + } + } + + string_nr_addch(&s, this->offset+this->width*num_of_cpus()-l, ' '); + mvaddstr(LINES-1, 0, s.str); + attroff(COLOR_PAIR(2)); + refresh(); +} + +void event_mode_exit(void) +{ + free(this->state); + this->initialized = 0; +} + +void mode_exit(void) +{ + int nr = sizeof(modes)/sizeof(modes[0]); + int i; + + for (i = 0; i < nr; i++) { + this = &modes[i]; + if (this->initialized) + this->exit(); + } +} + +int mode_init(void) +{ + int nr = sizeof(modes)/sizeof(modes[0]); + int i, r = 0; + + for (i = 0; i < nr; i++) { + this = &modes[i]; + this->initialized = 0; + r += this->init(); + } + + this = &modes[0]; + + /* hook into exit */ + atexit(mode_exit); + + return r; +} + +int time_mode_rebuild(uint64_t start_time, uint64_t time_scale) +{ + int i, j; + struct cpu cur_state[MAX_CPU_NR]; + uint64_t tsc = start_time; + struct state *state; + uint64_t number, temp; + int state_cur = 0; + + for (i = 0; i < max_cpu_num; i++) + cur_state[i].flag = FLAG_UNKNOWN; + + /* allocate spaces, it may be huge... */ + temp = (data[data_cur-1].tsc - start_time)/time_scale; + number = 10000UL; + if (temp < number) + number = temp; + number += 2; + state = malloc(sizeof(struct state) * number); + if (!state) + return 1; + if (this->state) + free(this->state); + this->state = state; + this->width = 9; + this->row = 0; + + /* determine the current Cx state */ + /* check [data[0].tsc, tsc) */ + i = 0; + while (i < data_cur && data[i].tsc < tsc) { + int cpu = data[i].cpu; + cur_state[cpu].cx = data[i].cx; + cur_state[cpu].flag = FLAG_LEVEL; + i++; + } + while (i < data_cur && state_cur < number) { + int num[MAX_CPU_NR]; + int last_idx[MAX_CPU_NR]; + +#if 0 + printf("XXXXX %d tsc: %"PRIu64" data[i].tsc: %"PRIu64"\n", + i, tsc, data[i].tsc); +#endif + /* ensure they are zero */ + memset(num, 0, sizeof(int) * MAX_CPU_NR); + memset(last_idx, 0, sizeof(int) * MAX_CPU_NR); + + /* check [tsc, tsc+time_scale) */ + while (i < data_cur && data[i].tsc < tsc+time_scale) { + int cpu = data[i].cpu; + num[cpu]++; + last_idx[cpu] = i; + i++; + } + /* TODO */ + if (i >= data_cur) + break; + for (j = 0; j < max_cpu_num; j++) { + if (num[j] == 1) { + /* only one event, it's an edge*/ + cur_state[j].cx = data[last_idx[j]].cx; + cur_state[j].flag = FLAG_EDGE; + } else if (num[j] > 1) { + /* more than one event, it's fuzzy */ + cur_state[j].cx = data[last_idx[j]].cx; + cur_state[j].flag = FLAG_FUZZY; + } else if (cur_state[j].flag == FLAG_FUZZY) { + /* no event, fuzzy state can't be passed down + * notice that cx is set in the fuzzy state, + * it's not changed here afterwards. + */ + cur_state[j].flag = FLAG_LEVEL; + } + } + + /* copy tsc */ + state[state_cur].tsc = tsc; + for (j = 0; j < max_cpu_num; j++) { + /* copy cx and flag */ + state[state_cur].cpu[j].cx = cur_state[j].cx; + state[state_cur].cpu[j].flag = cur_state[j].flag; + + /* update flag in cur_state */ + if (cur_state[j].flag == FLAG_EDGE) { + cur_state[j].flag = FLAG_LEVEL; + if (cur_state[j].cx == 0) { + /* EXIT */ + /* copy irqs conditionally */ + memcpy(state[state_cur].cpu[j].irqs, + data[last_idx[j]].irqs, + sizeof(unsigned char) * 4); + } else { + /* ENTRY */ + state[state_cur].cpu[j].expected = + data[last_idx[j]].expected; + state[state_cur].cpu[j].predicted = + data[last_idx[j]].predicted; + } + } + } + state_cur++; + tsc += time_scale; + } + this->state_nr = state_cur; + this->row = 0; + + return 0; +} + +int time_mode_init(void) +{ + int i; + this->offset = 21; + this->scroll_h = 0; + this->time_scale = (data[data_cur-1].tsc -data[0].tsc)/10000UL; + this->start_time = data[0].tsc; + for (i = 0; i < max_cpu_num; i++) + this->cpu_bitmap[i] = 1; + return time_mode_rebuild(this->start_time, + this->time_scale); +} + +void choose_cpus(void) +{ + int i; + int temp_row = 1; + int ch; + + clear(); + mvprintw(0, 0, "How many CPUs to track? Press space to toggle. Press 'q' or 'Q' to quit."); + + while (1) { + for (i = 0; i < max_cpu_num; i++) { + if (temp_row == i+1) + attron(COLOR_PAIR(2)); + mvprintw(i+1, 0, "[%s] CPU%d", this->cpu_bitmap[i] ? "x" : " ", i); + if (temp_row == i+1) + attroff(COLOR_PAIR(2)); + } + ch = getch(); + switch (ch) { + case KEY_UP: + if (--temp_row < 1) + temp_row = 1; + break; + case KEY_DOWN: + if (++temp_row > max_cpu_num) + temp_row = max_cpu_num; + break; + case ' ': + this->cpu_bitmap[temp_row-1] = !this->cpu_bitmap[temp_row-1]; + break; + case 'q': + case 'Q': + if (num_of_cpus() >= 1) { + if (!strcmp(this->name, "Event")) + this->init(); + return; + } + case KEY_F(4): + exit(EXIT_SUCCESS); + } + } +} + +int num_of_cpus(void) +{ + int i, nr = 0; + for (i = 0; i < max_cpu_num; i++) + if (this->cpu_bitmap[i]) + nr++; + return nr; +} + |