diff options
author | Tuukka Tikkanen <tuukka.tikkanen@linaro.org> | 2014-12-08 16:59:47 +0200 |
---|---|---|
committer | Tuukka Tikkanen <tuukka.tikkanen@linaro.org> | 2014-12-12 08:56:44 +0200 |
commit | 55ecf2a4a7949ff323aaa7570744f845c2d5b731 (patch) | |
tree | d38aaa6b621af53c0f2f3ea7790d6b890bafd2d2 /tracefile_idlestat.c | |
parent | 663342ed3364d4855a967be5ebe680bb2b3ffdcf (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.c | 241 |
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); |