aboutsummaryrefslogtreecommitdiff
path: root/tracefile_idlestat.c
diff options
context:
space:
mode:
authorTuukka Tikkanen <tuukka.tikkanen@linaro.org>2014-12-08 16:59:47 +0200
committerTuukka Tikkanen <tuukka.tikkanen@linaro.org>2014-12-12 08:56:44 +0200
commit55ecf2a4a7949ff323aaa7570744f845c2d5b731 (patch)
treed38aaa6b621af53c0f2f3ea7790d6b890bafd2d2 /tracefile_idlestat.c
parent663342ed3364d4855a967be5ebe680bb2b3ffdcf (diff)
idlestat: Separate loading of different trace file formats
This patch separates loading of different trace file formats to independent functions. In order to call the correct function for each trace file processed, each load function comes with a match function. Pointers to these functions are stored in a new struct trace_ops. In order to avoid static lists of trace file formats (e.g. implemented as static arrays or a number of registration function calls in main), pointers to all trace_ops structures are collected by the linker into a private segment. This creates an implicit array of the pointers, which may then be enumerated starting right after a globally linked element trace_ops_head. A sentinel NULL pointer trails the array. A helper macro EXPORT_TRACE_OPS is added for creating the special pointers. Signed-off-by: Tuukka Tikkanen <tuukka.tikkanen@linaro.org> Reviewed-by: Koan-Sin Tan <freedom.tan@linaro.org>
Diffstat (limited to 'tracefile_idlestat.c')
-rw-r--r--tracefile_idlestat.c241
1 files changed, 241 insertions, 0 deletions
diff --git a/tracefile_idlestat.c b/tracefile_idlestat.c
new file mode 100644
index 0000000..dbf468b
--- /dev/null
+++ b/tracefile_idlestat.c
@@ -0,0 +1,241 @@
+#include "topology.h"
+#include "trace_ops.h"
+#include "utils.h"
+#include "idlestat.h"
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <assert.h>
+#include <values.h>
+
+#define TRACE_FORMAT "%*[^]]] %*s %lf:%*[^=]=%u%*[^=]=%d"
+
+/**
+ * load_and_build_cstate_info - load c-state info written to idlestat
+ * trace file.
+ *
+ * @f: the file handle of the idlestat trace file
+ * @nrcpus: number of CPUs
+ *
+ * @return: per-CPU array of structs (success) or ptrerror() (error)
+ */
+static struct cpuidle_cstates *load_and_build_cstate_info(FILE* f, char *buffer, int nrcpus)
+{
+ int cpu;
+ struct cpuidle_cstates *cstates;
+
+ assert(f != NULL);
+ assert(buffer != NULL);
+ assert(nrcpus > 0);
+
+ cstates = calloc(nrcpus, sizeof(*cstates));
+ if (!cstates)
+ return ptrerror(__func__);
+
+ for (cpu = 0; cpu < nrcpus; cpu++) {
+ int i, read_cpu;
+ struct cpuidle_cstate *c;
+
+ cstates[cpu].cstate_max = -1;
+ cstates[cpu].current_cstate = -1;
+
+ if (sscanf(buffer, "cpuid %d:\n", &read_cpu) != 1 ||
+ read_cpu != cpu) {
+ release_cstate_info(cstates, cpu);
+ fprintf(stderr,
+ "%s: Error reading trace file\n"
+ "Expected: cpuid %d:\n"
+ "Read: %s",
+ __func__, cpu, buffer);
+ return ptrerror(NULL);
+ }
+
+ for (i = 0; i < MAXCSTATE; i++) {
+ int residency;
+ char *name = malloc(128);
+ if (!name) {
+ release_cstate_info(cstates, cpu);
+ return ptrerror(__func__);
+ }
+
+ fgets(buffer, BUFSIZE, f);
+ sscanf(buffer, "\t%s\n", name);
+ fgets(buffer, BUFSIZE, f);
+ sscanf(buffer, "\t%d\n", &residency);
+
+ c = &(cstates[cpu].cstate[i]);
+ if (!strcmp(name, "(null)")) {
+ free(name);
+ c->name = NULL;
+ } else {
+ c->name = name;
+ }
+ c->data = NULL;
+ c->nrdata = 0;
+ c->early_wakings = 0;
+ c->late_wakings = 0;
+ c->avg_time = 0.;
+ c->max_time = 0.;
+ c->min_time = DBL_MAX;
+ c->duration = 0.;
+ c->target_residency = residency;
+ }
+ fgets(buffer, BUFSIZE, f);
+ }
+
+ return cstates;
+}
+
+void load_text_data_lines(FILE *f, char *buffer, struct cpuidle_datas *datas)
+{
+ unsigned int state, freq, cpu;
+ double time, begin = 0, end = 0;
+ size_t count = 0, start = 1;
+ int ret;
+
+ do {
+ if (strstr(buffer, "cpu_idle")) {
+ if (sscanf(buffer, TRACE_FORMAT, &time, &state, &cpu)
+ != 3) {
+ fprintf(stderr, "warning: Unrecognized cpuidle "
+ "record. The result of analysis might "
+ "be wrong.\n");
+ continue;
+ }
+
+ if (start) {
+ begin = time;
+ start = 0;
+ }
+ end = time;
+
+ store_data(time, state, cpu, datas, count);
+ count++;
+ continue;
+ } else if (strstr(buffer, "cpu_frequency")) {
+ if (sscanf(buffer, TRACE_FORMAT, &time, &freq, &cpu)
+ != 3) {
+ fprintf(stderr, "warning: Unrecognized cpufreq "
+ "record. The result of analysis might "
+ "be wrong.\n");
+ continue;
+ }
+ cpu_change_pstate(datas, cpu, freq, time);
+ count++;
+ continue;
+ }
+
+ ret = get_wakeup_irq(datas, buffer, count);
+ count += (0 == ret) ? 1 : 0;
+
+ } while (fgets(buffer, BUFSIZE, f));
+
+ fprintf(stderr, "Log is %lf secs long with %zd events\n",
+ end - begin, count);
+}
+
+static int idlestat_magic(const char *filename)
+{
+ FILE *f;
+ char *line;
+ char buffer[BUFSIZE];
+
+ f = fopen(filename, "r");
+ if (!f) {
+ fprintf(stderr, "%s: failed to open '%s': %m\n", __func__,
+ filename);
+ return -1;
+ }
+
+ line = fgets(buffer, BUFSIZE, f);
+ fclose(f);
+
+ return (line != NULL) && !strncmp(buffer, "idlestat version", 16);
+}
+
+static struct cpuidle_datas * idlestat_native_load(const char *filename)
+{
+ FILE *f;
+ unsigned int nrcpus;
+ struct cpuidle_datas *datas;
+ char *line;
+ char buffer[BUFSIZE];
+
+ f = fopen(filename, "r");
+ if (!f) {
+ fprintf(stderr, "%s: failed to open '%s': %m\n", __func__,
+ filename);
+ return ptrerror(NULL);
+ }
+
+ /* Version line */
+ line = fgets(buffer, BUFSIZE, f);
+ if (!line)
+ goto error_close;
+
+ /* Number of CPUs */
+ line = fgets(buffer, BUFSIZE, f);
+ if (!line)
+ goto error_close;
+
+ if (sscanf(buffer, "cpus=%u", &nrcpus) != 1 || nrcpus == 0) {
+ fclose(f);
+ return ptrerror("Cannot load trace file (nrcpus == 0)");
+ }
+
+ line = fgets(buffer, BUFSIZE, f);
+ if (!line)
+ goto error_close;
+
+ datas = calloc(sizeof(*datas), 1);
+ if (!datas) {
+ fclose(f);
+ return ptrerror(__func__);
+ }
+
+ datas->nrcpus = nrcpus;
+ datas->pstates = build_pstate_info(nrcpus);
+ if (!datas->pstates)
+ goto propagate_error_free_datas;
+
+ /* Read topology information */
+ datas->topo = read_cpu_topo_info(f, buffer);
+ if (is_err(datas->topo))
+ goto propagate_error_free_datas;
+
+ /* Read C-state information */
+ datas->cstates = load_and_build_cstate_info(f, buffer, nrcpus);
+ if (is_err(datas->cstates))
+ goto propagate_error_free_datas;
+
+ load_text_data_lines(f, buffer, datas);
+
+ fclose(f);
+
+ return datas;
+
+ propagate_error_free_datas:
+ fclose(f);
+ if (!is_err(datas->topo))
+ release_cpu_topo_info(datas->topo);
+ if (!is_err(datas->cstates))
+ release_cstate_info(datas->cstates, nrcpus);
+ free(datas);
+ return ptrerror(NULL);
+
+ error_close:
+ fclose(f);
+ fprintf(stderr, "%s: error or EOF while reading '%s': %m",
+ __func__, filename);
+ return ptrerror(NULL);
+}
+
+
+static const struct trace_ops idlestat_trace_ops = {
+ .name = "Idlestat native",
+ .check_magic = idlestat_magic,
+ .load = idlestat_native_load
+};
+
+EXPORT_TRACE_OPS(idlestat);