[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] x86 Cx tracing: adds gtraceview & gtracestat utilities
# HG changeset patch # User Keir Fraser <keir.fraser@xxxxxxxxxx> # Date 1246269804 -3600 # Node ID 388578e1626e78f50b1d9ca377ba38d592610cc1 # Parent 41317562d3329826ad1fadca16b519af335e7e72 x86 Cx tracing: adds gtraceview & gtracestat utilities Signed-off-by: Lu Guanqun <guanqun.lu@xxxxxxxxx> --- .hgignore | 2 tools/misc/Makefile | 9 tools/misc/gtracestat.c | 1209 ++++++++++++++++++++++++++++++++++++++++++++++++ tools/misc/gtraceview.c | 1103 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2320 insertions(+), 3 deletions(-) diff -r 41317562d332 -r 388578e1626e .hgignore --- a/.hgignore Mon Jun 29 11:01:50 2009 +0100 +++ b/.hgignore Mon Jun 29 11:03:24 2009 +0100 @@ -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 -r 41317562d332 -r 388578e1626e tools/misc/Makefile --- a/tools/misc/Makefile Mon Jun 29 11:01:50 2009 +0100 +++ b/tools/misc/Makefile Mon Jun 29 11:03:24 2009 +0100 @@ -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-$(CONFIG_X86) += xen-detect 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 -r 41317562d332 -r 388578e1626e tools/misc/gtracestat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/misc/gtracestat.c Mon Jun 29 11:03:24 2009 +0100 @@ -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 -r 41317562d332 -r 388578e1626e tools/misc/gtraceview.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/misc/gtraceview.c Mon Jun 29 11:03:24 2009 +0100 @@ -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; +} + _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |