From: Daniel Lezcano <daniel.lezc...@linaro.org>

Add functionality to record the current runtime cpuidle statistics
and show the same.

Signed-off-by: Sanjay Singh Rawat <sanjay.ra...@linaro.com>
---
 README                  |    6 +
 Test.mk                 |    6 +
 cpuidle/cpuidle_04.sh   |   41 ++++
 cpuidle/cpuidle_04.txt  |    1 +
 cpuidle/cpuidle_stats.c |  472 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 526 insertions(+)
 create mode 100755 cpuidle/cpuidle_04.sh
 create mode 100644 cpuidle/cpuidle_04.txt
 create mode 100644 cpuidle/cpuidle_stats.c

diff --git a/README b/README
index a22a3c8..41c5f41 100644
--- a/README
+++ b/README
@@ -10,3 +10,9 @@ If you want to run a subset of the tests, do:
 
        make -C sched_mc check
        make -C cpufreq check
+
+For running cpuidle-stats test, which is 4th subtest in cpuidle; run:
+
+       make -C cpuidle check TEST=4
+
+Note: Any other value for TEST will not run any other subtest
diff --git a/Test.mk b/Test.mk
index 671bbf5..19d7346 100644
--- a/Test.mk
+++ b/Test.mk
@@ -36,7 +36,13 @@ SANITY_STATUS:= $(shell if test $(SNT) && test -f $(SNT); 
then \
                echo 1; fi; else echo 1; fi)
 
 ifeq "$(SANITY_STATUS)" "1"
+
+ifeq "$(TEST)" "4"
+TST=cpuidle_04.sh
+run_tests: uncheck $(EXEC) $(LOG)
+else
 run_tests: uncheck $(EXEC) $(LOG)
+endif
 
 %.log: %.sh
        @echo "###"
diff --git a/cpuidle/cpuidle_04.sh b/cpuidle/cpuidle_04.sh
new file mode 100755
index 0000000..42d6de5
--- /dev/null
+++ b/cpuidle/cpuidle_04.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# PM-QA validation test suite for the power management on Linux
+#
+# Copyright (C) 2011, Linaro Limited.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
USA.
+#
+# Contributors:
+#     Daniel Lezcano <daniel.lezc...@linaro.org> (IBM Corporation)
+#       - initial API and implementation
+#
+
+source ../include/functions.sh
+
+CPUIDLE_STATS=./cpuidle_stats
+
+if [ $(id -u) != 0 ]; then
+    log_skip "run as non-root"
+    exit 0
+fi
+
+check_cpuidle_stats() {
+       trace-cmd record -e cpu_idle
+       trace-cmd report trace.dat > trace-cpuidle.dat
+       check "Running cpuidle_stats on collected data" "./$CPUIDLE_STATS" 
trace-cpuidle.dat
+}
+
+check_cpuidle_stats
diff --git a/cpuidle/cpuidle_04.txt b/cpuidle/cpuidle_04.txt
new file mode 100644
index 0000000..8cf6bb1
--- /dev/null
+++ b/cpuidle/cpuidle_04.txt
@@ -0,0 +1 @@
+Run cpuidle_stats program to check runtime cpuidle statistics.
diff --git a/cpuidle/cpuidle_stats.c b/cpuidle/cpuidle_stats.c
new file mode 100644
index 0000000..971fcc9
--- /dev/null
+++ b/cpuidle/cpuidle_stats.c
@@ -0,0 +1,472 @@
+#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("\ncluster");
+                       else
+                               printf("\ncpu%d", i);
+
+                       printf("/state%d, %d hits, total %.2lfus, "\
+                              "avg %.2lfus, min %.2lfus, max %.2lfus",
+                              j, cstate->nrdata, cstate->duration,
+                              cstate->avg_time, cstate->min_time,
+                              cstate->max_time);
+               }
+
+               i++;
+
+       } while (i < nrcpus && nrcpus != -1);
+       printf("\n");
+       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;
+}
-- 
1.7.9.5


_______________________________________________
linaro-dev mailing list
linaro-dev@lists.linaro.org
http://lists.linaro.org/mailman/listinfo/linaro-dev

Reply via email to