aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Lezcano <daniel.lezcano@linaro.org>2013-01-08 16:38:57 +0100
committerDaniel Lezcano <daniel.lezcano@linaro.org>2013-01-08 16:38:57 +0100
commitcc595d5f92bfe63f7f551ec9a0ebd7ed20961981 (patch)
tree2dc103fa808552768c201d946af7d048d794b2e3
downloadidlestat-cc595d5f92bfe63f7f551ec9a0ebd7ed20961981.tar.gz
initial import
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r--Makefile12
-rw-r--r--README13
-rw-r--r--idlestat.c478
3 files changed, 503 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..16f0267
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,12 @@
+CFLAGS?=-g -Wall
+CC?=gcc
+
+OBJS = idlestat.o
+
+default: idlestat
+
+idledebug: $(OBJS)
+ $(CC) ${CFLAGS} $(OBJS) -lncurses -o $@
+
+clean:
+ rm -f $(OBJS) idlestat
diff --git a/README b/README
new file mode 100644
index 0000000..bea714b
--- /dev/null
+++ b/README
@@ -0,0 +1,13 @@
+This tool gives some stats based on the result from ftrace.
+
+One example of usage:
+
+ trace-cmd record -e cpu_idle
+
+ ... acquire data ...
+
+ trace-cmd report trace.dat > trace-cpuidle.dat
+
+ idlestat ./trace-cpuidle.dat
+
+The tool does not handle the file correctly if there are non cpu_idle traces.
diff --git a/idlestat.c b/idlestat.c
new file mode 100644
index 0000000..a827f84
--- /dev/null
+++ b/idlestat.c
@@ -0,0 +1,478 @@
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <values.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#define BUFSIZE 256
+#define MAXCSTATE 8
+#define MAX(A,B) (A > B ? A : B)
+#define MIN(A,B) (A < B ? A : B)
+#define AVG(A,B,I) ((A) + ((B - A) / (I)))
+
+static char buffer[BUFSIZE];
+
+struct cpuidle_data {
+ double begin;
+ double end;
+ double duration;
+};
+
+struct cpuidle_cstate {
+ struct cpuidle_data *data;
+ int nrdata;
+ double avg_time;
+ double max_time;
+ double min_time;
+ double duration;
+};
+
+struct cpuidle_cstates {
+ struct cpuidle_cstate cstate[MAXCSTATE];
+ int last_cstate;
+ int cstate_max;
+};
+
+struct cpuidle_datas {
+ struct cpuidle_cstates *cstates;
+ int nrcpus;
+};
+
+static inline int error(const char *str)
+{
+ perror(str);
+ return -1;
+}
+
+static inline void *ptrerror(const char *str)
+{
+ perror(str);
+ return NULL;
+}
+
+static int dump_data(struct cpuidle_datas *datas, int state, int count)
+{
+ int i = 0, j, k, nrcpus = datas->nrcpus;
+ struct cpuidle_cstates *cstates;
+ struct cpuidle_cstate *cstate;
+
+ do {
+ cstates = &datas->cstates[i];
+
+ for (j = 0; j < cstates->cstate_max + 1; j++) {
+
+ if (state != -1 && state != j)
+ continue;
+
+ cstate = &cstates->cstate[j];
+
+ for (k = 0; k < MIN(count, cstate->nrdata); k++) {
+ printf("%lf %d\n", cstate->data[k].begin, j);
+ printf("%lf 0\n", cstate->data[k].end);
+ }
+
+ /* add a break */
+ printf("\n");
+ }
+
+ i++;
+
+ } while (i < nrcpus && nrcpus != -1);
+
+ return 0;
+}
+
+static int display_data(struct cpuidle_datas *datas, int state)
+{
+ int i = 0, j, nrcpus = datas->nrcpus;
+ struct cpuidle_cstates *cstates;
+ struct cpuidle_cstate *cstate;
+
+ do {
+ cstates = &datas->cstates[i];
+
+ for (j = 0; j < cstates->cstate_max + 1; j++) {
+
+ if (state != -1 && state != j)
+ continue;
+
+ cstate = &cstates->cstate[j];
+
+ if (nrcpus == -1)
+ printf("cluster");
+ else
+ printf("cpu%d", i);
+
+ printf("/state%d, %d hits, total %.2lfus, "\
+ "avg %.2lfus, min %.2lfus, max %.2lfus\n",
+ j, cstate->nrdata, cstate->duration,
+ cstate->avg_time, cstate->min_time,
+ cstate->max_time);
+ }
+
+ i++;
+
+ } while (i < nrcpus && nrcpus != -1);
+
+ return 0;
+}
+
+static struct cpuidle_data *intersection(struct cpuidle_data *data1,
+ struct cpuidle_data *data2)
+{
+ double begin, end;
+ struct cpuidle_data *data;
+
+ begin = MAX(data1->begin, data2->begin);
+ end = MIN(data1->end, data2->end);
+
+ if (begin >= end)
+ return NULL;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return NULL;
+
+ data->begin = begin;
+ data->end = end;
+ data->duration = end - begin;
+ data->duration *= 1000000;
+
+ return data;
+}
+
+static struct cpuidle_cstate *inter(struct cpuidle_cstate *c1,
+ struct cpuidle_cstate *c2)
+{
+ int i, j;
+ struct cpuidle_data *interval;
+ struct cpuidle_cstate *result;
+ struct cpuidle_data *data = NULL;
+ size_t index;
+
+ if (!c1)
+ return c2;
+ if (!c2)
+ return c1;
+
+ result = calloc(sizeof(*result), 1);
+ if (!result)
+ return NULL;
+
+ for (i = 0, index = 0; i < c1->nrdata; i++) {
+
+ for (j = index; j < c2->nrdata; j++) {
+
+ /* intervals are ordered, no need to go further */
+ if (c1->data[i].end < c2->data[j].begin)
+ break;
+
+ /* primary loop begins where we ended */
+ if (c1->data[i].begin > c2->data[j].end)
+ index = j;
+
+ interval = intersection(&c1->data[i], &c2->data[j]);
+ if (!interval)
+ continue;
+
+ result->min_time = MIN(!result->nrdata ? 999999.0 :
+ result->min_time,
+ interval->duration);
+
+ result->max_time = MAX(result->max_time,
+ interval->duration);
+
+ result->avg_time = AVG(result->avg_time,
+ interval->duration,
+ result->nrdata + 1);
+
+ result->duration += interval->duration;
+
+ result->nrdata++;
+
+ data = realloc(data, sizeof(*data) *
+ (result->nrdata + 1));
+ if (!data)
+ return NULL;
+
+ result->data = data;
+ result->data[result->nrdata - 1] = *interval;
+
+ free(interval);
+ }
+ }
+
+ return result;
+}
+
+static int store_data(double time, int state, int cpu,
+ struct cpuidle_datas *datas, int count)
+{
+ struct cpuidle_cstates *cstates = &datas->cstates[cpu];
+ struct cpuidle_cstate *cstate;
+ struct cpuidle_data *data;
+ int nrdata, last_cstate = cstates->last_cstate;
+
+ /* ignore when we got a "closing" state first */
+ if (state == -1 && !cstates->cstate_max)
+ return 0;
+
+ cstate = &cstates->cstate[state == -1 ? last_cstate : state ];
+ data = cstate->data;
+ nrdata = cstate->nrdata;
+
+ if (state == -1) {
+
+ data = &data[nrdata];
+
+ data->end = time;
+ data->duration = data->end - data->begin;
+
+ /* That happens when precision digit in the file exceed
+ * 7 (eg. xxx.1000000). Ignoring the result because I don't
+ * find a way to fix with the sscanf used in the caller
+ */
+ if (data->duration < 0)
+ return 0;
+
+ /* convert to us */
+ data->duration *= 1000000;
+
+ cstate->min_time = MIN(!nrdata ? 999999.0 : cstate->min_time,
+ data->duration);
+
+ cstate->max_time = MAX(cstate->max_time, data->duration);
+
+
+ cstate->avg_time = AVG(cstate->avg_time, data->duration,
+ cstate->nrdata + 1);
+
+ cstate->duration += data->duration;
+
+ cstate->nrdata++;
+
+ return 0;
+ }
+
+ data = realloc(data, sizeof(*data) * (nrdata + 1));
+ if (!data)
+ return error("realloc data");;
+
+ data[nrdata].begin = time;
+
+ cstates->cstate[state].data = data;
+ cstates->cstate_max = MAX(cstates->cstate_max, state);
+ cstates->last_cstate = state;
+
+ return 0;
+}
+
+static struct cpuidle_datas *load_data(const char *path)
+{
+ FILE *f;
+ unsigned int state = 0, cpu = 0, nrcpus= 0;
+ double time, begin, end;
+ size_t count;
+
+ struct cpuidle_datas *datas;
+
+ f = fopen(path, "r");
+ if (!f)
+ return ptrerror("fopen");
+
+ for (count = 0; count < 2; count++) {
+ fgets(buffer, BUFSIZE, f);
+ sscanf(buffer, "cpus=%u", &nrcpus);
+ }
+
+ if (!nrcpus)
+ return ptrerror("read error for 'cpus=' in trace file");
+
+ datas = malloc(sizeof(*datas));
+ if (!datas)
+ return ptrerror("malloc datas");
+
+ datas->cstates = calloc(sizeof(*datas->cstates), nrcpus);
+ if (!datas->cstates)
+ return ptrerror("calloc cstate");
+
+ datas->nrcpus = nrcpus;
+
+ for (; fgets(buffer, BUFSIZE, f); count++) {
+
+ sscanf(buffer, "%*[^]]] %lf:%*[^=]=%u%*[^=]=%d",
+ &time, &state, &cpu);
+
+ if (count == 2)
+ begin = time;
+ end = time;
+
+ store_data(time, state, cpu, datas, count);
+ }
+
+ fclose(f);
+
+ fprintf(stderr, "Log is %lf secs long with %d events\n",
+ end - begin, count);
+
+ return datas;
+}
+
+struct cpuidle_datas *cluster_data(struct cpuidle_datas *datas)
+{
+ struct cpuidle_cstate *c1, *cstates;
+ struct cpuidle_datas *result;
+ int i, j;
+ int cstate_max = -1;
+
+ result = malloc(sizeof(*result));
+ if (!result)
+ return NULL;
+
+ result->nrcpus = -1; /* the cluster */
+
+ result->cstates = calloc(sizeof(*result->cstates), 1);
+ if (!result->cstates)
+ return NULL;
+
+ /* hack but negligeable overhead */
+ for (i = 0; i < datas->nrcpus; i++)
+ cstate_max = MAX(cstate_max, datas->cstates[i].cstate_max);
+ result->cstates[0].cstate_max = cstate_max;
+
+ for (i = 0; i < cstate_max + 1; i++) {
+
+ for (j = 0, cstates = NULL; j < datas->nrcpus; j++) {
+
+ c1 = &datas->cstates[j].cstate[i];
+
+ cstates = inter(cstates, c1);
+ if (!cstates)
+ continue;
+ }
+
+ result->cstates[0].cstate[i] = *cstates;
+ }
+
+ return result;
+}
+
+static int help(const char *cmd)
+{
+ fprintf(stderr, "%s [-d/--dump] [-c/--cstate=x] <file>\n", cmd);
+ exit(0);
+}
+
+static struct option long_options[] = {
+ { "dump", 0, 0, 'd' },
+ { "iterations", 0, 0, 'i' },
+ { "cstate", 0, 0, 'c' },
+ { "debug", 0, 0, 'g' },
+ { "verbose", 0, 0, 'v' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+struct idledebug_options {
+ bool debug;
+ bool dump;
+ int cstate;
+ int iterations;
+};
+
+int getoptions(int argc, char *argv[], struct idledebug_options *options)
+{
+ int c;
+
+ memset(options, 0, sizeof(*options));
+ options->cstate = -1;
+
+ while (1) {
+
+ int optindex = 0;
+
+ c = getopt_long(argc, argv, "gdvhi:c:",
+ long_options, &optindex);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'g':
+ options->debug = true;
+ break;
+ case 'd':
+ options->dump = true;
+ break;
+ case 'i':
+ options->iterations = atoi(optarg);
+ break;
+ case 'c':
+ options->cstate = atoi(optarg);
+ break;
+ case 'h':
+ help(argv[0]);
+ break;
+ case '?':
+ fprintf(stderr, "%s: Unknown option %c'.\n",
+ argv[0], optopt);
+ default:
+ return -1;
+ }
+ }
+
+ if (options->cstate >= MAXCSTATE) {
+ fprintf(stderr, "C-state must be less than %d\n",
+ MAXCSTATE);
+ return -1;
+ }
+
+ if (options->iterations < 0) {
+ fprintf(stderr, "dump values must be a positive value\n");
+ }
+
+ if (optind == argc) {
+ fprintf(stderr, "expected filename\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct cpuidle_datas *datas;
+ struct cpuidle_datas *cluster;
+ struct idledebug_options options;
+ struct rusage rusage;
+
+
+ if (getoptions(argc, argv, &options))
+ return 1;
+
+ datas = load_data(argv[optind]);
+ if (!datas)
+ return 1;
+
+ cluster = cluster_data(datas);
+ if (!cluster)
+ return 1;
+
+ if (options.dump > 0) {
+ dump_data(datas, options.cstate, options.iterations);
+ dump_data(cluster, options.cstate, options.iterations);
+ } else {
+ display_data(datas, options.cstate);
+ display_data(cluster, options.cstate);
+ }
+
+ if (options.debug) {
+ getrusage(RUSAGE_SELF, &rusage);
+ printf("max rss : %ld kB\n", rusage.ru_maxrss);
+ }
+
+ return 0;
+}