aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--idlestat.c222
-rw-r--r--idlestat.h7
-rw-r--r--trace_ops.h24
-rw-r--r--trace_ops_head.c6
-rw-r--r--trace_ops_tail.c6
-rw-r--r--tracefile_ftrace.c124
-rw-r--r--tracefile_idlestat.c241
8 files changed, 438 insertions, 198 deletions
diff --git a/Makefile b/Makefile
index e83130e..0de28be 100644
--- a/Makefile
+++ b/Makefile
@@ -25,7 +25,11 @@ CFLAGS?=-g -Wall
CC=gcc
OBJS = idlestat.o topology.o trace.o utils.o energy_model.o \
- default_report.o csv_report.o
+ default_report.o csv_report.o \
+ trace_ops_head.o \
+ tracefile_idlestat.o \
+ tracefile_ftrace.o \
+ trace_ops_tail.o
default: idlestat
diff --git a/idlestat.c b/idlestat.c
index 4ee6f57..8a58050 100644
--- a/idlestat.c
+++ b/idlestat.c
@@ -45,6 +45,7 @@
#include "topology.h"
#include "energy_model.h"
#include "report_ops.h"
+#include "trace_ops.h"
#define IDLESTAT_VERSION "0.4-rc1"
#define USEC_PER_SEC 1000000
@@ -381,11 +382,12 @@ int cpuidle_get_target_residency(int cpu, int state)
/**
* build_cstate_info - parse cpuidle sysfs entries and build per-CPU
* structs to maintain statistics of C-state transitions
+ *
* @nrcpus: number of CPUs
*
- * Return: per-CPU array of structs (success) or ptrerror() (error)
+ * @return: per-CPU array of structs (success) or ptrerror() (error)
*/
-static struct cpuidle_cstates *build_cstate_info(int nrcpus)
+struct cpuidle_cstates *build_cstate_info(int nrcpus)
{
int cpu;
struct cpuidle_cstates *cstates;
@@ -534,78 +536,6 @@ static int alloc_pstate(struct cpufreq_pstates *pstates, unsigned int freq)
}
/**
- * 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, int nrcpus)
-{
- int cpu;
- struct cpuidle_cstates *cstates;
-
- 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;
-}
-
-/**
* release_pstate_info - free all P-state related structs
* @pstates: per-cpu array of P-state statistics structs
* @nrcpus: number of CPUs
@@ -632,11 +562,12 @@ static void release_pstate_info(struct cpufreq_pstates *pstates, int nrcpus)
/**
* build_pstate_info - allocate and initialize per-CPU structs to maintain
* statistics of P-state transitions
+ *
* @nrcpus: number of CPUs
*
- * Return: per-CPU array of structs (success) or NULL (error)
+ * @return: per-CPU array of structs (success) or NULL (error)
*/
-static struct cpufreq_pstates *build_pstate_info(int nrcpus)
+struct cpufreq_pstates *build_pstate_info(int nrcpus)
{
int cpu;
struct cpufreq_pstates *pstates;
@@ -720,7 +651,7 @@ static void close_current_pstate(struct cpufreq_pstates *ps, double time)
p->count++;
}
-static void cpu_change_pstate(struct cpuidle_datas *datas, int cpu,
+void cpu_change_pstate(struct cpuidle_datas *datas, int cpu,
unsigned int freq, double time)
{
struct cpufreq_pstates *ps;
@@ -847,8 +778,8 @@ static int cstate_end(double time, struct cpuidle_cstates *cstates)
return 0;
}
-static int store_data(double time, int state, int cpu,
- struct cpuidle_datas *datas, int count)
+int store_data(double time, int state, int cpu,
+ struct cpuidle_datas *datas, int count)
{
struct cpuidle_cstates *cstates = &datas->cstates[cpu];
struct cpufreq_pstate *pstate = datas->pstates[cpu].pstate;
@@ -954,7 +885,7 @@ void output_cstate_info(FILE *f, int nrcpus) {
#define TRACECMD_REPORT_FORMAT "%*[^]]] %lf:%*[^=]=%u%*[^=]=%d"
#define TRACE_FORMAT "%*[^]]] %*s %lf:%*[^=]=%u%*[^=]=%d"
-static int get_wakeup_irq(struct cpuidle_datas *datas, char *buffer, int count)
+int get_wakeup_irq(struct cpuidle_datas *datas, char *buffer, int count)
{
int cpu, irqid;
char irqname[NAMELEN+1];
@@ -988,129 +919,26 @@ static int get_wakeup_irq(struct cpuidle_datas *datas, char *buffer, int count)
struct cpuidle_datas *idlestat_load(const char *filename)
{
- FILE *f;
- unsigned int state = 0, freq = 0, cpu = 0, nrcpus = 0;
- double time, begin = 0, end = 0;
- size_t count = 0, start = 1;
- struct cpuidle_datas *datas;
+ const struct trace_ops **ops_it;
int ret;
- enum formats format;
-
- f = fopen(filename, "r");
- if (!f) {
- fprintf(stderr, "%s: failed to open '%s': %m\n", __func__,
- filename);
- return ptrerror(NULL);
- }
-
- /* version line */
- fgets(buffer, BUFSIZE, f);
- if (strstr(buffer, "idlestat")) {
- format = IDLESTAT_HEADER;
- fgets(buffer, BUFSIZE, f);
- if (sscanf(buffer, "cpus=%u", &nrcpus) != 1)
- nrcpus = 0;
- fgets(buffer, BUFSIZE, f);
- } else if (strstr(buffer, "# tracer")) {
- format = TRACE_CMD_HEADER;
- while(!feof(f)) {
- if (buffer[0] != '#')
- break;
- if (strstr(buffer, "#P:") &&
- sscanf(buffer, "#%*[^#]#P:%u", &nrcpus) != 1)
- nrcpus = 0;
- fgets(buffer, BUFSIZE, f);
- }
- } else {
- fprintf(stderr, "%s: unrecognized import format in '%s'\n",
- __func__, filename);
- fclose(f);
- return ptrerror(NULL);
- }
-
- if (!nrcpus) {
- fclose(f);
- return ptrerror("read error for 'cpus=' in trace file");
- }
-
- datas = calloc(sizeof(*datas), 1);
- if (!datas) {
- fclose(f);
- return ptrerror(__func__);
- }
-
- /* read topology information */
- datas->topo = read_cpu_topo_info(f, buffer);
- if (is_err(datas->topo)) {
- fclose(f);
- free(datas);
- return ptrerror(NULL);
- }
-
- datas->nrcpus = nrcpus;
- /* read c-state information */
- if (format == IDLESTAT_HEADER)
- datas->cstates = load_and_build_cstate_info(f, nrcpus);
- else
- datas->cstates = build_cstate_info(nrcpus);
- if (is_err(datas->cstates)) {
- free(datas);
- fclose(f);
- return ptrerror(NULL);
- }
+ for (ops_it = (&trace_ops_head)+1 ; *ops_it ; ++ops_it) {
+ assert((*ops_it)->name);
+ assert((*ops_it)->check_magic);
+ assert((*ops_it)->load);
+ ret = (*ops_it)->check_magic(filename);
- datas->pstates = build_pstate_info(nrcpus);
- if (!datas->pstates) {
- free(datas->cstates);
- free(datas);
- fclose(f);
- return ptrerror("build_pstate_info: out of memory");
- }
-
- 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;
+ if (ret == -1)
+ return ptrerror(NULL);
- 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;
+ /* File format supported by these ops? */
+ if (ret > 0) {
+ return (*ops_it)->load(filename);
}
+ }
- ret = get_wakeup_irq(datas, buffer, count);
- count += (0 == ret) ? 1 : 0;
-
- } while (fgets(buffer, BUFSIZE, f));
-
- fclose(f);
-
- fprintf(stderr, "Log is %lf secs long with %zd events\n",
- end - begin, count);
-
- return datas;
+ fprintf(stderr, "Trace file format not recognized\n");
+ return ptrerror(NULL);
}
struct cpuidle_datas *cluster_data(struct cpuidle_datas *datas)
diff --git a/idlestat.h b/idlestat.h
index 0c2b60e..909d868 100644
--- a/idlestat.h
+++ b/idlestat.h
@@ -186,6 +186,13 @@ struct init_pstates {
unsigned int *freqs;
};
+extern int store_data(double time, int state, int cpu, struct cpuidle_datas *datas, int count);
+extern struct cpuidle_cstates *build_cstate_info(int nrcpus);
+extern struct cpufreq_pstates *build_pstate_info(int nrcpus);
+extern void cpu_change_pstate(struct cpuidle_datas *datas, int cpu, unsigned int freq, double time);
+extern int get_wakeup_irq(struct cpuidle_datas *datas, char *buffer, int count);
+
+
struct report_ops {
int (*check_output)(struct program_options *, void *);
diff --git a/trace_ops.h b/trace_ops.h
new file mode 100644
index 0000000..8885263
--- /dev/null
+++ b/trace_ops.h
@@ -0,0 +1,24 @@
+#ifndef __TRACE_OPS_H
+#define __TRACE_OPS_H
+
+#include <stdio.h>
+
+struct cpuidle_datas;
+
+struct trace_ops {
+ const char *name;
+ int (*check_magic)(const char *filename);
+ struct cpuidle_datas *(*load)(const char *filename);
+};
+
+extern void load_text_data_lines(FILE *f, char *buffer, struct cpuidle_datas *datas);
+
+#define EXPORT_TRACE_OPS(tracetype_name) \
+ static const struct trace_ops \
+ __attribute__ ((__used__)) \
+ __attribute__ ((__section__ ("__trace_ops"))) \
+ * tracetype_name ## _trace_ptr = &tracetype_name##_trace_ops
+
+extern const struct trace_ops *trace_ops_head;
+
+#endif
diff --git a/trace_ops_head.c b/trace_ops_head.c
new file mode 100644
index 0000000..74fb091
--- /dev/null
+++ b/trace_ops_head.c
@@ -0,0 +1,6 @@
+#include "trace_ops.h"
+#include <stddef.h>
+
+const struct trace_ops
+ __attribute__((__used__)) __attribute__ ((__section__ ("__trace_ops")))
+ *trace_ops_head = NULL;
diff --git a/trace_ops_tail.c b/trace_ops_tail.c
new file mode 100644
index 0000000..7ef187b
--- /dev/null
+++ b/trace_ops_tail.c
@@ -0,0 +1,6 @@
+#include "trace_ops.h"
+#include <stddef.h>
+
+static const struct trace_ops
+ __attribute__((__used__)) __attribute__ ((__section__ ("__trace_ops")))
+ *trace_ops_tail = NULL;
diff --git a/tracefile_ftrace.c b/tracefile_ftrace.c
new file mode 100644
index 0000000..ab07bb4
--- /dev/null
+++ b/tracefile_ftrace.c
@@ -0,0 +1,124 @@
+#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"
+
+static int ftrace_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, "# tracer", 8);
+}
+
+static struct cpuidle_datas * ftrace_load(const char *filename)
+{
+ FILE *f;
+ unsigned int nrcpus;
+ struct cpuidle_datas *datas;
+ int ret;
+ 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 */
+ nrcpus = 0;
+ while(!feof(f)) {
+ if (buffer[0] != '#')
+ break;
+ if (strncmp(buffer, "#P:", 3)) {
+ ret = sscanf(buffer, "#%*[^#]#P:%u", &nrcpus);
+ if (ret != 1)
+ nrcpus = 0;
+ }
+ line = fgets(buffer, BUFSIZE, f);
+ }
+
+ if (!line)
+ goto error_close;
+
+ if (!nrcpus) {
+ fclose(f);
+ return ptrerror("Cannot load trace file (nrcpus == 0)");
+ }
+
+ 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;
+
+ datas->topo = read_sysfs_cpu_topo();
+ if (is_err(datas->topo))
+ goto propagate_error_free_datas;
+
+ /* Build C-state information from current host sysfs */
+ datas->cstates = build_cstate_info(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 ftrace_trace_ops = {
+ .name = "ftrace",
+ .check_magic = ftrace_magic,
+ .load = ftrace_load
+};
+
+EXPORT_TRACE_OPS(ftrace);
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);