/* * 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 output from configure */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* get curses header from configure */ #include INCLUDE_CURSES_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 (!data_cur) { fprintf(stderr, "file %s doesn't contain any valid record\n", 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 [--version] [--help]\n"); printf(" trace.data raw data got by " "'xentrace -e 0x80f000 trace.dat'\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 = 0; int state_cur = 0; for (i = 0; i < max_cpu_num; i++) cur_state[i].flag = FLAG_UNKNOWN; /* allocate spaces, it may be huge... */ if (time_scale) 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; }