#include "topology.h" #include "trace_ops.h" #include "utils.h" #include "idlestat.h" #include #include #include #include #include #include #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);