aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.hgignore2
-rw-r--r--tools/misc/Makefile9
-rw-r--r--tools/misc/gtracestat.c1209
-rw-r--r--tools/misc/gtraceview.c1103
4 files changed, 2320 insertions, 3 deletions
diff --git a/.hgignore b/.hgignore
index ba0d0bb87a..77b68aef5a 100644
--- a/.hgignore
+++ b/.hgignore
@@ -197,6 +197,8 @@
^tools/misc/xen-tmem-list-parse$
^tools/misc/xenperf$
^tools/misc/xenpm$
+^tools/misc/gtraceview$
+^tools/misc/gtracestat$
^tools/pygrub/build/.*$
^tools/python/build/.*$
^tools/python/xen/util/path\.py$
diff --git a/tools/misc/Makefile b/tools/misc/Makefile
index b6a735bfdd..038fcef23d 100644
--- a/tools/misc/Makefile
+++ b/tools/misc/Makefile
@@ -10,7 +10,7 @@ CFLAGS += $(INCLUDES)
HDRS = $(wildcard *.h)
-TARGETS-y := xenperf xenpm xen-tmem-list-parse
+TARGETS-y := xenperf xenpm xen-tmem-list-parse gtraceview gtracestat
TARGETS-$(CONFIG_X86) += xen-detect
TARGETS := $(TARGETS-y)
@@ -22,7 +22,7 @@ INSTALL_BIN-y := xencons
INSTALL_BIN-$(CONFIG_X86) += xen-detect
INSTALL_BIN := $(INSTALL_BIN-y)
-INSTALL_SBIN-y := xm xen-bugtool xen-python-path xend xenperf xsview xenpm xen-tmem-list-parse
+INSTALL_SBIN-y := xm xen-bugtool xen-python-path xend xenperf xsview xenpm xen-tmem-list-parse gtraceview gtracestat
INSTALL_SBIN := $(INSTALL_SBIN-y)
DEFAULT_PYTHON_PATH := $(shell $(XEN_ROOT)/tools/python/get-path)
@@ -53,7 +53,10 @@ clean:
%.o: %.c $(HDRS) Makefile
$(CC) -c $(CFLAGS) -o $@ $<
-xenperf xenpm: %: %.o Makefile
+xenperf xenpm gtracestat: %: %.o Makefile
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LDFLAGS_libxenctrl)
+gtraceview: %: %.o Makefile
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -lncurses
+
-include $(DEPS)
diff --git a/tools/misc/gtracestat.c b/tools/misc/gtracestat.c
new file mode 100644
index 0000000000..fc3bd587d5
--- /dev/null
+++ b/tools/misc/gtracestat.c
@@ -0,0 +1,1209 @@
+/*
+ * gtracestat.c: list the statistics information for a dumped xentrace file.
+ * 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 <getopt.h>
+#include <inttypes.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <xenctrl.h>
+#include <xen/trace.h>
+
+#define CHECK_DUP_CX 0
+
+/********** MACROS **********/
+#define MAX_CPU_NR 32
+#define MAX_CX_NR 8
+#define MAX_MODE_NR 16
+#define MAX_PX_NR 100
+
+/* simplified xentrace record */
+struct rec {
+ uint64_t tsc;
+ int cpu;
+ unsigned char cx;
+ unsigned char irqs[4];
+ unsigned int predicted;
+ unsigned int expected;
+ int px;
+};
+
+/********** FORWARD DECLARATION **********/
+void show_help(void);
+void show_version(void);
+int load_file(char *fname);
+void do_digest(uint64_t start, uint64_t end, uint64_t scale);
+void do_breakevents(void);
+void do_count(void);
+void do_px_count(void);
+void do_maxmin(void);
+void do_average(void);
+void do_cstate(uint64_t start, uint64_t end);
+void do_exp_ratio(void);
+void do_exp_pred(void);
+
+/********** 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 px_freq_table[MAX_PX_NR];
+int max_px_num = 0;
+
+int is_menu_gov_enabled = 0;
+
+/* user specified translation unit */
+uint64_t tsc2ms = 2793000UL;
+uint64_t tsc2us = 2793UL;
+uint64_t tsc2phase = 55800000UL;
+
+/* each cpu column width */
+int width = 0;
+
+/* digest mode variables */
+struct rec *evt[MAX_CPU_NR];
+int evt_len[MAX_CPU_NR];
+
+/* hand-crafted min() */
+static inline uint64_t min(uint64_t a, uint64_t b)
+{
+ return a < b ? a : b;
+}
+static inline uint64_t max(uint64_t a, uint64_t b)
+{
+ return a > b ? a : b;
+}
+
+int is_px = 0;
+
+int main(int argc, char *argv[])
+{
+ char *fname = NULL;
+ /* operation flags */
+ int is_breakevents = 0;
+ int is_count = 0;
+ int is_maxmin = 0;
+ int is_average = 0;
+ int is_digest = 0;
+ int is_exp_ratio = 0;
+ int is_exp = 0;
+ uint64_t start_time = 0;
+ uint64_t time_scale = 0;
+ uint64_t end_time = 0;
+
+ struct option long_options [] = {
+ /* short options are listed correspondingly */
+ { "version", 0, NULL, 'v' },
+ { "help", 0, NULL, 'h' },
+ /* list Cx entires one by one */
+ { "digest", 0, NULL, 'd' },
+ /* ignored when digest is disabled */
+ { "start", 1, NULL, 's' },
+ { "end", 1, NULL, 'e' },
+ { "scale", 1, NULL, 'l' },
+ /* give summary about breakevents info */
+ { "breakevents", 0, NULL, 'b' },
+ { "count", 0, NULL, 'c' },
+ { "average", 0, NULL, 'a' },
+ /* list max/min residency for each Cx */
+ { "maxmin", 0, NULL, 'm' },
+ { "tsc2us", 1, NULL, 'u' },
+ { "px", 0, NULL, 'p' },
+ { "tsc2phase", 1, NULL, 'n' },
+ { "exp-ratio", 0, NULL, 'z' },
+ { "exp-pred", 0, NULL, 'x' },
+ { NULL, 0, NULL, 0 },
+ };
+
+ while (1) {
+ int ch, opt_idx;
+ ch = getopt_long(argc, argv, "vhds:e:l:bcmaupnzx",
+ long_options, &opt_idx);
+ if (ch == -1)
+ break;
+ switch (ch) {
+ case 'v':
+ show_version();
+ exit(EXIT_SUCCESS);
+ case 'h':
+ show_help();
+ exit(EXIT_SUCCESS);
+ case 'p':
+ is_px = 1;
+ break;
+ case 'x':
+ is_exp = 1;
+ break;
+ case 'z':
+ is_exp_ratio = 1;
+ break;
+ case 'n':
+ tsc2phase = atoll(optarg);
+ if (tsc2phase <= 0)
+ tsc2phase = 55800000UL;
+ case 'd':
+ is_digest = 1;
+ break;
+ case 's':
+ start_time = atoll(optarg);
+ break;
+ case 'e':
+ end_time = atoll(optarg);
+ break;
+ case 'l':
+ time_scale = atoll(optarg);
+ break;
+ case 'b':
+ is_breakevents = 1;
+ break;
+ case 'c':
+ is_count = 1;
+ break;
+ case 'm':
+ is_maxmin = 1;
+ break;
+ case 'a':
+ is_average = 1;
+ break;
+ case 'u':
+ tsc2us = atoll(optarg);
+ tsc2ms = tsc2us * 1000UL;
+ break;
+ case '?':
+ default:
+ show_help();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (argc - optind > 1) {
+ printf("Multiple file specified?\n");
+ show_help();
+ exit(EXIT_FAILURE);
+ }
+ fname = argv[optind];
+
+ if (load_file(fname))
+ exit(EXIT_FAILURE);
+
+ width = 10;
+ if (is_digest) {
+ /* if people not specify the time related number,
+ * use the default one from the record.
+ */
+ if (!start_time)
+ start_time = data[0].tsc;
+ if (!end_time)
+ end_time = data[data_cur-1].tsc;
+ if (!time_scale)
+ time_scale = 10UL * tsc2ms; /* default: 10 ms */
+ do_digest(start_time, end_time, time_scale);
+ }
+
+ if (is_breakevents)
+ do_breakevents();
+
+ if (is_count && !is_px)
+ do_count();
+ if (is_count && is_px)
+ do_px_count();
+
+ if (is_maxmin)
+ do_maxmin();
+
+ if (is_average)
+ do_average();
+
+ if (is_exp_ratio)
+ do_exp_ratio();
+
+ if (is_exp)
+ do_exp_pred();
+
+ exit(EXIT_SUCCESS);
+}
+
+/* used for qsort() */
+/* sort by cpu first, then by tsc */
+static int 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;
+}
+
+/* load file and make them a list of records
+ * update these following variables:
+ * data, data_cur, data_nr
+ * max_cpu_num, max_cx_num
+ */
+#define LIST_PX 0
+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)) {
+ /* we care about only idle events now */
+ 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;
+ if (is_px) {
+ if (rec.event != TRC_PM_FREQ_CHANGE)
+ continue;
+ /* FREQ_CHANGE */
+ if (rec.u.cycles.extra_u32[0] ==
+ rec.u.cycles.extra_u32[1])
+ continue;
+ data[data_cur].px = rec.u.cycles.extra_u32[1];
+ for (i = 0; i < max_px_num; i++)
+ if (px_freq_table[i] == data[data_cur].px)
+ break;
+ if (i == max_px_num)
+ px_freq_table[max_px_num++] = data[data_cur].px;
+ } else {
+ 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 */
+ 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];
+ } else
+ continue;
+ /* 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);
+
+ /* sort data array according to TSC time line */
+ qsort(data, data_cur, sizeof(struct rec), data_cmp);
+
+ max_cpu_num++;
+ max_cx_num++;
+
+ 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[i].cpu] = data+i;
+ evt_len[data[i].cpu]++;
+ }
+#if CHECK_DUP_CX
+ int xx, yy;
+ int err = 0;
+ printf("Checking %s...\n", fname);
+ for (xx = 0; xx < max_cpu_num; xx++) {
+ // printf("............ CPU %d .............\n", xx);
+ for (yy = 0; yy+1 < evt_len[xx]; yy++)
+ if ( evt[xx][yy].cx > 0 && evt[xx][yy+1].cx > 0) {
+ printf("same witht next one %"PRIu64" %d %d\n",
+ evt[xx][yy].tsc, evt[xx][yy].cpu, evt[xx][yy].cx);
+ err++;
+ }
+ }
+ exit(err);
+#endif
+#if LIST_PX
+ int x, y;
+ for (x = 0; x < max_cpu_num; x++) {
+ printf("CPU%d**************************************\n", x);
+ for (y = 0; y+1 < evt_len[x]; y++) {
+ printf("[%dHz]: phase: %d\n",
+ evt[x][y].px,
+ (int)((evt[x][y+1].tsc - evt[x][y].tsc)/tsc2phase));
+ }
+ }
+#endif
+ return 0;
+}
+
+void show_version(void)
+{
+ printf("gtracestat - (C) 2009 Intel Corporation\n");
+}
+
+void show_help(void)
+{
+ show_version();
+ printf("tracestat <trace.data> [-vhdselbcmau]\n");
+ printf(" trace.data raw data from xentrace\n");
+ printf(" -v / --version show version message\n");
+ printf(" -h / --help show this message\n");
+ printf(" -d / --digest digest mode, more variables to specify.\n");
+ printf(" -s / --start <start_time> specify start time (only in digest mode)\n");
+ printf(" -e / --end <end_time> specify end time (only in digest mode)\n");
+ printf(" -l / --scale <scale> specify time scale (only in digest mode)\n");
+ printf(" -b / --breakevents give breakevents summary info\n");
+ printf(" -c / --count give count summary info\n");
+ printf(" -a / --average give total/average residency info\n");
+ printf(" -m / --maxmin show man/min residency summary info\n");
+ printf(" -u / --tsc2us specify how many tsc is a us unit\n");
+ printf(" -p / --px operate on Px entries\n");
+ printf(" -n / --tsc2phase specify how many tsc is a phase unit (only in px)\n");
+ printf(" -z / --exp-ratio show the ratio of early break events\n");
+ printf(" -x / --exp-pred show the ratio of expected / predicted in Cx entry\n");
+}
+
+static inline int len_of_number(uint64_t n)
+{
+ int l = 0;
+ do {
+ l++;
+ n /= 10;
+ } while (n);
+ return l;
+}
+
+/* determine the cx at time t
+ * take advantage of evt and evt_len.
+ */
+int determine_cx(int c, uint64_t t)
+{
+ int i;
+
+ i = 0;
+ while (i < evt_len[c] && evt[c][i].tsc <= t)
+ i++;
+ /* if there are any events happening,
+ * it must be in a Cx state now.
+ */
+ if (i)
+ return evt[c][i-1].cx;
+ /* look forward to see whether it will enter
+ * a Cx state, if so, it must be in C0 state.
+ * we can't determine a Cx state from exit event.
+ */
+ if (i < evt_len[c] && evt[c][i].cx > 0)
+ return 0;
+ return -1;
+}
+
+/* c - cpu
+ * t - start time
+ * s - scale
+ * cx_i - number of cx index
+ * cx_r - residency of each cx entry
+ */
+int process(int c, uint64_t t, uint64_t s, int *cx_i, uint64_t *cx_r)
+{
+ int cx;
+ uint64_t len;
+ int i, n;
+
+ cx = determine_cx(c, t);
+ i = 0;
+ while (i < evt_len[c] && evt[c][i].tsc < t)
+ i++;
+ n = 0;
+ if (cx >= 0 && i < evt_len[c]) {
+ cx_i[n] = cx;
+ cx_r[n] = evt[c][i].tsc - t;
+ if (cx_r[n])
+ n++;
+ }
+ while (i < evt_len[c] && evt[c][i].tsc < t+s) {
+ /* we are now at [t, t+s) */
+ cx = evt[c][i].cx;
+ len = min((i+1 < evt_len[c] ? evt[c][i+1].tsc : t+s), t+s)
+ - evt[c][i].tsc;
+
+ cx_i[n] = cx;
+ cx_r[n] = len;
+ n++;
+
+ i++;
+ }
+
+ return n;
+}
+
+void nr_putchar(int nr, int ch)
+{
+ int i;
+ for (i = 0; i < nr; i++)
+ putchar(ch);
+}
+
+#define MAX_INTERVAL_ENTRY 1000
+/* process period [start_time, start_time + time_scale) */
+void single_digest(uint64_t start_time, uint64_t time_scale)
+{
+ int cpu;
+ int cx_i[MAX_CPU_NR][MAX_INTERVAL_ENTRY];
+ uint64_t cx_r[MAX_CPU_NR][MAX_INTERVAL_ENTRY];
+ int cx_n[MAX_CPU_NR];
+ int max_n;
+
+ memset(cx_i, 0, sizeof(int) * MAX_CPU_NR * MAX_INTERVAL_ENTRY);
+ memset(cx_r, 0, sizeof(uint64_t) * MAX_CPU_NR * MAX_INTERVAL_ENTRY);
+ memset(cx_n, 0, sizeof(int) * MAX_CPU_NR);
+
+ max_n = 0;
+ for (cpu = 0; cpu < max_cpu_num; cpu++) {
+ cx_n[cpu] = process(cpu, start_time, time_scale, cx_i[cpu], cx_r[cpu]);
+ if (cx_n[cpu] > max_n)
+ max_n = cx_n[cpu];
+ }
+
+ /* means how many lines will be consumed */
+ while (--max_n >= 0) {
+ for (cpu = 0; cpu < max_cpu_num; cpu++) {
+ if (cx_n[cpu] > 0) {
+ int i;
+ /* find the available cx index */
+ for (i = 0; i < MAX_INTERVAL_ENTRY && cx_i[cpu][i] == -1; i++)
+ ;
+ if (i < MAX_INTERVAL_ENTRY) {
+ int len;
+ /* print it */
+ len= printf("C%d,%"PRIu64".%d", cx_i[cpu][i],
+ cx_r[cpu][i]/tsc2ms,
+ (unsigned int)(cx_r[cpu][i]/(tsc2ms/10))%10);
+ nr_putchar(width-len, ' ');
+
+ cx_i[cpu][i] = -1;
+ } else
+ nr_putchar(width, ' ');
+
+ cx_n[cpu]--;
+ } else
+ nr_putchar(width, ' ');
+ }
+ nr_putchar(1, '\n');
+ }
+}
+
+void do_digest(uint64_t start, uint64_t end, uint64_t scale)
+{
+ int i;
+ uint64_t ms = 0;
+ uint64_t delta_ms = scale / tsc2ms;
+
+ for (i = 0; i < max_cpu_num; i++) {
+ int len = 0;
+ len = printf("CPU%d", i);
+ nr_putchar(width-len, ' ');
+ }
+ nr_putchar(1, '\n');
+ while (start < end) {
+ /* print --- xxx ms --- line */
+ int off = (max_cpu_num * width - len_of_number(ms) - 2)/2;
+ nr_putchar(off, '-');
+ off += printf("%"PRIu64"ms", ms);
+ off += printf(" (%"PRIu64")", start);
+ nr_putchar(max_cpu_num * width-off, '-');
+ nr_putchar(1, '\n');
+ /* print each digest entries */
+ single_digest(start, scale);
+
+ start += scale;
+ ms += delta_ms;
+ }
+}
+
+/* [min, max) */
+struct cond_rec {
+ uint64_t min;
+ uint64_t max;
+ uint64_t cnt;
+ uint64_t res;
+};
+
+void cond_rec_init(struct cond_rec *r, uint64_t min, uint64_t max)
+{
+ r->min = min;
+ r->max = max;
+ r->cnt = 0;
+}
+
+void cond_rec_inc(uint64_t cur, struct cond_rec *r)
+{
+ if (r->min <= cur && cur < r->max) {
+ r->cnt++;
+ r->res += cur;
+ }
+}
+
+/* c - current cpu to scan
+ * cx - cx state to track
+ * a - conditonal array
+ * n - how many entries there are
+ */
+void do_count_per_cpu(int c, int cx, struct cond_rec *a, int n)
+{
+ int i;
+ /* find Cx entry first */
+ i = 0;
+ while (i < evt_len[c] && evt[c][i].cx == 0)
+ i++;
+ /* check evt[c][i] and evt[c][i+1] */
+ while (i + 1 < evt_len[c]) {
+ if (evt[c][i].cx == cx) {
+ uint64_t len = evt[c][i+1].tsc - evt[c][i].tsc;
+ int j;
+ /* check for each condition */
+ for (j = 0; j < n; j++)
+ cond_rec_inc(len, a+j);
+ }
+ i++;
+ }
+}
+
+struct cond_rec *make_cond_rec(uint64_t *a, int n)
+{
+ int i;
+ struct cond_rec *t = malloc(sizeof(struct cond_rec) * (n+1));
+ if (!t)
+ return NULL;
+ for (i = 0; i < n; i++) {
+ t[i].max = a[i];
+ t[i+1].min = a[i];
+ t[i].cnt = 0;
+ t[i].res = 0;
+ }
+ t[0].min = 0;
+ t[n].max = (uint64_t) -1;
+ t[n].cnt = 0;
+ t[n].res = 0;
+
+ return t;
+}
+
+uint64_t max_res[MAX_CPU_NR][MAX_CX_NR];
+uint64_t min_res[MAX_CPU_NR][MAX_CX_NR];
+uint64_t max_tm[MAX_CPU_NR][MAX_CX_NR];
+uint64_t min_tm[MAX_CPU_NR][MAX_CX_NR];
+
+void do_maxmin_per_cpu(int c)
+{
+ int i;
+ /* find Cx entry first */
+ i = 0;
+ while (i < evt_len[c] && evt[c][i].cx == 0)
+ i++;
+ /* check evt[c][i] and evt[c][i+1] */
+ while (i + 1 < evt_len[c]) {
+ int cx = evt[c][i].cx;
+ uint64_t len = evt[c][i+1].tsc - evt[c][i].tsc;
+ if (len > max_res[c][cx]) {
+ max_res[c][cx] = len;
+ max_tm[c][cx] = evt[c][i].tsc;
+ }
+ if (len < min_res[c][cx]) {
+ min_res[c][cx] = len;
+ min_tm[c][cx] = evt[c][i].tsc;
+ }
+ i++;
+ }
+}
+
+void do_maxmin(void)
+{
+ int i, j;
+ /* init */
+ for (i = 0; i < max_cpu_num; i++)
+ for (j = 0; j < max_cx_num; j++) {
+ max_res[i][j] = 0;
+ min_res[i][j] = (uint64_t) -1;
+ }
+
+ for (i = 0; i < max_cpu_num; i++)
+ do_maxmin_per_cpu(i);
+
+ for (i = 0; i < max_cpu_num; i++) {
+ printf("********* CPU%d *********\n", i);
+ for (j = 0; j < max_cx_num; j++)
+ if (max_res[i][j] == 0)
+ printf(" not found ");
+ else
+ printf("%7"PRIu64"us (%15"PRIu64") ", max_res[i][j]/tsc2us, max_tm[i][j]);
+ printf("\n");
+ for (j = 0; j < max_cx_num; j++)
+ if (max_res[i][j] == 0)
+ printf(" not found ");
+ else
+ printf("%7"PRIu64"us (%15"PRIu64") ", min_res[i][j]/tsc2us, min_tm[i][j]);
+ printf("\n\n");
+ }
+}
+
+void do_count(void)
+{
+ uint64_t scale[100] = { 50000UL, 100000UL, 200000UL, 400000UL, 800000UL, 1000000UL };
+ int a[100];
+ int scale_len = 6;
+ int len = 0;
+ int i, j;
+
+ printf("Please input the period: (Ctrl+D to quit)\n");
+ printf("The default is 50us, 100us, 200us, 400us, 800us, 1000us.\n(unit is us, you DO NOT need to add us and specify ZERO us and please be in INCREASING order.)\n");
+ while (scanf("%d", &a[len]) == 1)
+ len++;
+ if (len) {
+ for (i = 0; i < len; i++)
+ scale[i] = a[i] * tsc2us;
+ scale_len = len;
+ }
+ for (i = 0; i < max_cpu_num; i++) {
+ struct cond_rec *r[MAX_CX_NR];
+ uint64_t sum[MAX_CX_NR];
+ int k;
+
+ printf("********** CPU%d *********\n", i);
+ for (j = 0; j < max_cx_num; j++) {
+ r[j] = make_cond_rec(scale, scale_len);
+ if (!r[j])
+ continue;
+ do_count_per_cpu(i, j, r[j], scale_len+1);
+
+ /* print */
+ sum[j] = 0;
+ for (k = 0; k < scale_len+1; k++)
+ sum[j] += r[j][k].cnt;
+ if (sum[j] == 0)
+ sum[j] = 1;
+ }
+ printf(" ");
+ for (j = 0; j < max_cx_num; j++)
+ printf(" C%d ", j);
+ printf("\n");
+ for (k = 0; k < scale_len+1; k++) {
+ if (k == scale_len)
+ printf("%5"PRIu64" us -> MAX us:", r[0][k].min/tsc2us);
+ else
+ printf("%5"PRIu64" us -> %5"PRIu64" us:",
+ r[0][k].min/tsc2us, r[0][k].max/tsc2us);
+ for (j = 0; j < max_cx_num; j++)
+ printf(" %10"PRIu64" (%5.2f%%)",
+ r[j][k].cnt, 100.0 * (double) r[j][k].cnt / (double)sum[j]);
+ printf("\n");
+ }
+ for (j = 0; j < max_cx_num; j++)
+ free(r[j]);
+ }
+}
+
+static void do_px_count_per_cpu(int c, int px, struct cond_rec *cond, int n)
+{
+ int i, j;
+ uint64_t len;
+
+ i = 0;
+ while (i+1 < evt_len[c]) {
+ if (evt[c][i].px == px) {
+ len = evt[c][i+1].tsc - evt[c][i].tsc;
+ /* check each condition */
+ for (j = 0; j < n; j++)
+ cond_rec_inc(len, cond+j);
+ }
+ i++;
+ }
+}
+
+void do_px_count(void)
+{
+ int a[100];
+ uint64_t scale[100];
+ int n, i, c, j;
+
+ printf("Please input phases series: (Ctrl+D to quit)\n");
+ printf("The default is 1, 2, 4, 8, 16, 32.\n");
+ printf("Please be in increasing order.\n");
+ scale[0] = tsc2phase;
+ scale[1] = 2 * tsc2phase;
+ scale[2] = 4 * tsc2phase;
+ scale[3] = 8 * tsc2phase;
+ scale[4] = 16 * tsc2phase;
+ scale[5] = 32 * tsc2phase;
+ n = 0;
+ while (scanf("%d", &a[n]) == 1)
+ n++;
+ if (n) {
+ for (i = 0; i < n; i++)
+ scale[i] = a[i] * tsc2phase;
+ } else
+ n = 6;
+ for (c = 0; c < max_cpu_num; c++) {
+ struct cond_rec *p[MAX_PX_NR];
+ int k;
+
+ printf("***** CPU%d *****\n", c);
+ for (i = 0; i < max_px_num; i++) {
+ p[i] = make_cond_rec(scale, n);
+ if (!p[i])
+ continue;
+ do_px_count_per_cpu(c, px_freq_table[i], p[i], n+1);
+ }
+ /* print */
+ nr_putchar(16, ' ');
+ for (j = 0; j < max_px_num; j++)
+ printf("P%d\t", px_freq_table[j]);
+ printf("\n");
+ for (k = 0; k < n+1; k++) {
+ if (k == n)
+ printf("%5"PRIu64" -> MAX : ", p[0][k].min/tsc2phase);
+ else
+ printf("%5"PRIu64" -> %5"PRIu64": ",
+ p[0][k].min/tsc2phase, p[0][k].max/tsc2phase);
+ for (j = 0; j < max_px_num; j++) {
+ printf("%"PRIu64"\t", p[j][k].cnt);
+ }
+ printf("\n");
+ }
+ printf("---\n");
+ printf("Count: ");
+ for (j = 0; j < max_px_num; j++) {
+ int sum = 0;
+ for (k = 0; k < n+1; k++) {
+ sum += (int)p[j][k].cnt;
+ }
+ /* print count */
+ printf("%d\t", sum);
+ }
+ printf("\nAverage: ");
+ for (j = 0; j < max_px_num; j++) {
+ int sum = 0;
+ int s_res = 0;
+ for (k = 0; k < n+1; k++) {
+ sum += (int)p[j][k].cnt;
+ s_res += (int)(p[j][k].res/tsc2phase);
+ }
+ /* print average */
+ if (sum == 0)
+ sum = 1;
+ printf("%.1f\t", (double)s_res/(double)sum);
+ }
+ printf("\nTotal: ");
+ for (j = 0; j < max_px_num; j++) {
+ int s_res = 0;
+ for (k = 0; k < n+1; k++) {
+ s_res += (int)(p[j][k].res/tsc2phase);
+ }
+ /* print total */
+ printf("%d\t", s_res);
+ }
+ printf("\n");
+ }
+}
+
+void do_breakevents(void)
+{
+ int br[MAX_CPU_NR][257];
+ float pc[MAX_CPU_NR][257];
+ int i, j, k, l;
+
+ memset(br, 0, sizeof(int) * MAX_CPU_NR * 257);
+ memset(pc, 0, sizeof(int) * MAX_CPU_NR * 257);
+
+ for (i = 0; i < max_cpu_num; i++) {
+ int sum = 0;
+ for (j = 0; j < evt_len[i]; j++) {
+ if (evt[i][j].cx == 0) {
+ /* EXIT */
+ /* collect breakevents information */
+ int xx = 0;
+ for (k = 0; k < 4; k++) {
+ int irq = evt[i][j].irqs[k];
+ if (irq) {
+ br[i][irq]++;
+ sum++;
+ xx++;
+ }
+ }
+ if (!xx) {
+ br[i][256]++;
+ sum++;
+ }
+ }
+ }
+ for (j = 0; j < 257; j++)
+ pc[i][j] = 100.0 * br[i][j]/sum;
+ }
+ /* print the results */
+ width = 13;
+ printf(" ");
+ for (i = 0; i < max_cpu_num; i++) {
+ l = 0;
+ l += printf("CPU%d", i);
+ nr_putchar(width-l, ' ');
+ }
+ printf("\n");
+
+ for (j = 0; j < 257; j++) {
+ int n = 0;
+ for (i = 0; i < max_cpu_num; i++)
+ if (br[i][j])
+ n++;
+ if (n) {
+ if (j == 256)
+ printf("[N/A] ");
+ else
+ printf("[%03x] ", j);
+ for (i = 0; i < max_cpu_num; i++) {
+ if (br[i][j]) {
+ l = 0;
+ l += printf("%.1f%%,%d ", pc[i][j], br[i][j]);
+ nr_putchar(width-l, ' ');
+ } else {
+ nr_putchar(width, ' ');
+ }
+ }
+ printf("\n");
+ }
+ }
+}
+
+void single_cstate(int c, uint64_t t, uint64_t e,
+ uint64_t *a,
+ uint64_t *max_res,
+ uint64_t *min_res,
+ uint64_t *num);
+void do_cstate(uint64_t start, uint64_t end)
+{
+ uint64_t cxtime[MAX_CX_NR];
+ uint64_t max_res[MAX_CX_NR];
+ uint64_t min_res[MAX_CX_NR];
+ uint64_t num[MAX_CX_NR];
+ int i, j;
+
+ width = 20;
+ printf(" ");
+ for (i = 0; i < max_cx_num; i++) {
+ int l = printf("C%d", i);
+ nr_putchar(width-l, ' ');
+ }
+ printf("\n");
+
+ for (i = 0; i < max_cpu_num; i++) {
+ uint64_t sum = 0;
+ single_cstate(i, start, end, cxtime, max_res, min_res, num);
+ printf("CPU%2d ", i);
+ for (j = 0; j < max_cx_num; j++)
+ sum += cxtime[i];
+ for (j = 0; j < max_cx_num; j++) {
+ int l = printf("%.1f%%, %"PRIu64".%d, %"PRIu64".%d, %"PRIu64,
+ 100.0 * cxtime[j]/sum,
+ max_res[j]/tsc2ms,
+ (unsigned int)(max_res[j]/(tsc2ms/10))%10,
+ min_res[j]/tsc2ms,
+ (unsigned int)(min_res[j]/(tsc2ms/10))%10,
+ cxtime[j]/num[j]/tsc2ms);
+ nr_putchar(width - l, ' ');
+ }
+ }
+}
+
+void single_cstate(int c, uint64_t t, uint64_t e,
+ uint64_t *a,
+ uint64_t *max_res,
+ uint64_t *min_res,
+ uint64_t *num)
+{
+ int cx;
+ int i;
+ int first = 1;
+
+ for (i = 0; i < max_cx_num; i++) {
+ a[i] = 0;
+ max_res[i] = 0;
+ min_res[i] = (uint64_t) -1;
+ num[i] = 0;
+ }
+
+ cx = determine_cx(c, t);
+ i = 0;
+ while (i < evt_len[c] && evt[c][i].tsc <= t)
+ i++;
+ for (; i+1 < evt_len[c] && evt[c][i].tsc <= e; i++) {
+ int cxidx = evt[c][i].cx;
+ uint64_t delta;
+
+ if (first && cx >= 0) {
+ /* Partial Cx, only once */
+ first = 0;
+
+ cxidx = cx;
+ delta = evt[c][i].tsc - max(evt[c][i-1].tsc, t);
+ a[cxidx] += delta;
+ num[cxidx]++;
+
+ /* update min and max residency */
+ if (delta > max_res[cxidx])
+ max_res[cxidx] = delta;
+ if (delta < min_res[cxidx])
+ min_res[cxidx] = delta;
+ }
+ delta = evt[c][i+1].tsc - evt[c][i].tsc;
+ a[cxidx] += delta;
+ num[cxidx]++;
+
+ /* update min and max residency */
+ if (delta > max_res[cxidx])
+ max_res[cxidx] = delta;
+ if (delta < min_res[cxidx])
+ min_res[cxidx] = delta;
+ }
+}
+
+void do_average_per_cpu(int c)
+{
+ int i;
+ uint64_t tot[MAX_CX_NR] = { 0 };
+ uint64_t cnt[MAX_CX_NR] = { 0 };
+ uint64_t sum = 0;
+
+ /* find Cx entry first */
+ i = 0;
+ while (i < evt_len[c] && evt[c][i].cx == 0)
+ i++;
+ /* check evt[c][i] and evt[c][i+1] */
+ while (i + 1 < evt_len[c]) {
+ uint64_t len = evt[c][i+1].tsc - evt[c][i].tsc;
+ int cx = evt[c][i].cx;
+ tot[cx] += len;
+ cnt[cx]++;
+ sum += len;
+ i++;
+ }
+ /* prevent divide zero error */
+ if (!sum)
+ sum = 1;
+ /* print */
+ printf("CPU%d:\tResidency(ms)\t\tAvg Res(ms)\n", c);
+ for (i = 0; i < max_cx_num; i++) {
+ /* prevent divide zero error */
+ if (!cnt[i])
+ cnt[i] = 1;
+ printf(" C%d\t%"PRIu64"\t(%6.2f%%)\t%.2f\n", i,
+ tot[i]/tsc2ms, 100.0 * tot[i] / (double)sum,
+ (double)tot[i]/cnt[i]/tsc2ms );
+ }
+ printf("\n");
+}
+
+void do_average(void)
+{
+ int i;
+
+ for (i = 0; i < max_cpu_num; i++)
+ do_average_per_cpu(i);
+}
+
+static void do_exp_ratio_per_cpu(int c)
+{
+ int i;
+ uint64_t expected[MAX_CX_NR] = { 0 }, sum[MAX_CX_NR] = { 0 };
+
+ i = 0;
+ while (i < evt_len[c] && evt[c][i].cx == 0)
+ i++;
+ /* check evt[c][i] and evt[c][i+1] */
+ while (i + 1 < evt_len[c]) {
+ uint64_t len;
+ int cx;
+
+ if ((evt[c][i].cx == 0 && evt[c][i+1].cx == 0) ||
+ (evt[c][i].cx > 0 && evt[c][i+1].cx > 0)) {
+ i++;
+ continue;
+ }
+ len = evt[c][i+1].tsc - evt[c][i].tsc;
+ cx = evt[c][i].cx;
+ if (cx > 0) {
+ if ((len/tsc2us) <= evt[c][i].expected)
+ expected[cx]++;
+ sum[cx]++;
+ }
+
+ i++;
+ }
+ printf("********** CPU%d **********\n", c);
+ for (i = 1; i < max_cx_num; i++) {
+ if (sum[i] == 0)
+ printf("C%d\t0\t0\t00.00%%\n", i);
+ else
+ printf("C%d\t%"PRIu64"\t%"PRIu64"\t%4.2f%%\n",
+ i, expected[i], sum[i], 100.0 * (double)expected[i]/(double)sum[i]);
+ }
+}
+
+void do_exp_ratio(void)
+{
+ int i;
+
+ if (!is_menu_gov_enabled) {
+ printf("The file seems doesn't consists the expected/predicted information.\n");
+ return;
+ }
+
+ printf("Cx\tearly\ttot\tratio(%%)\n");
+ for (i = 0; i < max_cpu_num; i++)
+ do_exp_ratio_per_cpu(i);
+}
+
+static void do_exp_pred_per_cpu(int c)
+{
+ int i;
+ uint64_t expected[MAX_CX_NR] = { 0 }, sum[MAX_CX_NR] = { 0 };
+
+ i = 0;
+ while (i < evt_len[c] && evt[c][i].cx == 0)
+ i++;
+ /* check evt[c][i] and evt[c][i+1] */
+ while (i + 1 < evt_len[c]) {
+ int cx;
+
+ if ((evt[c][i].cx == 0 && evt[c][i+1].cx == 0) ||
+ (evt[c][i].cx > 0 && evt[c][i+1].cx > 0)) {
+ i++;
+ continue;
+ }
+ cx = evt[c][i].cx;
+ if (cx > 0) {
+ if (evt[c][i].expected <= evt[c][i].predicted)
+ expected[cx]++;
+ sum[cx]++;
+ }
+
+ i++;
+ }
+ printf("********** CPU%d **********\n", c);
+ for (i = 1; i < max_cx_num; i++) {
+ if (sum[i] == 0)
+ printf("C%d\t0\t0\t00.00%%\n", i);
+ else
+ printf("C%d\t%"PRIu64"\t%"PRIu64"\t%4.2f%%\n",
+ i, expected[i], sum[i], 100.0 * (double)expected[i]/(double)sum[i]);
+ }
+}
+
+void do_exp_pred(void)
+{
+ int i;
+
+ if (!is_menu_gov_enabled) {
+ printf("The file seems doesn't consists the expected/predicted information.\n");
+ return;
+ }
+
+ printf("Cx\texp\ttot\tratio(%%)\n");
+ for (i = 0; i < max_cpu_num; i++)
+ do_exp_pred_per_cpu(i);
+}
+
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;
+}
+