aboutsummaryrefslogtreecommitdiff
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
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>
-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);