On Tue, Oct 21, 2014 at 3:42 PM, Daniel Lezcano <daniel.lezc...@linaro.org> wrote: > On 10/04/2014 06:33 AM, pi-cheng.chen wrote: >> >> Initialize struct cpufreq_pstates with initial P-state of CPUs and >> allocate >> struct cpufreq_pstate dynamically when parsing trace file to solve the >> issue >> caused by missing "scaling_avaialable_freqs" attr when using intel_pstate >> driver. >> >> Changes v2 to v3: >> Initialize struct cpufreq_pstates with initial P-state of all CPUs and >> beginning timestamp before trace acquisition and close all P-states with >> ending timestamp >> >> hanges v1 to v2: >> Sort the cpufreq_pstate list when parsing events >> >> Signed-off-by: Pi-Cheng Chen <pi-cheng.c...@linaro.org> > > > At the first glance it looks ok for me. > > Thanks > -- Daniel
Good enough for a reviewed-by with your name? Tuukka > > >> --- >> idlestat.c | 259 >> ++++++++++++++++++++++++++++++++++++++++++++----------------- >> idlestat.h | 7 ++ >> trace.h | 1 + >> 3 files changed, 195 insertions(+), 72 deletions(-) >> >> diff --git a/idlestat.c b/idlestat.c >> index da615cb..8230067 100644 >> --- a/idlestat.c >> +++ b/idlestat.c >> @@ -536,6 +536,121 @@ static struct cpuidle_cstates *build_cstate_info(int >> nrcpus) >> return cstates; >> } >> >> +#define TRACE_STAT_FORMAT "%*[^:]:%lf" >> + >> +static double get_trace_ts(void) >> +{ >> + FILE *f; >> + double ts; >> + >> + f = fopen(TRACE_STAT_FILE, "r"); >> + if (!f) >> + return -1; >> + >> + while (fgets(buffer, BUFSIZE, f)) { >> + if (!strstr(buffer, "now ts")) >> + continue; >> + if (!sscanf(buffer, TRACE_STAT_FORMAT, &ts)) >> + ts = -1; >> + break; >> + } >> + fclose(f); >> + >> + return ts; >> +} >> + >> +static void release_init_pstates(struct init_pstates *init) >> +{ >> + free(init->freqs); >> + free(init); >> +} >> + >> +static struct init_pstates *build_init_pstates(void) >> +{ >> + struct init_pstates *init; >> + int nr_cpus, cpu; >> + unsigned int *freqs; >> + >> + nr_cpus = sysconf(_SC_NPROCESSORS_CONF); >> + if (nr_cpus < 0) >> + return NULL; >> + >> + init = malloc(sizeof(*init)); >> + if (!init) >> + return NULL; >> + >> + freqs = calloc(nr_cpus, sizeof(*freqs)); >> + if (!freqs) { >> + free(init); >> + return NULL; >> + } >> + memset(freqs, 0, sizeof(*freqs) * nr_cpus); >> + >> + for (cpu = 0; cpu < nr_cpus; cpu++) { >> + char *fpath; >> + unsigned int *freq = &(freqs[cpu]); >> + >> + if (asprintf(&fpath, CPUFREQ_CURFREQ_PATH_FORMAT, cpu) < >> 0) { >> + release_init_pstates(init); >> + return NULL; >> + } >> + if (read_int(fpath, (int *)freq)) >> + *freq = 0; >> + free(fpath); >> + } >> + init->nr_cpus = nr_cpus; >> + init->freqs = freqs; >> + >> + return init; >> +} >> + >> +/** >> + * alloc_pstate - allocate, sort, and initialize pstate struct >> + * to maintain statistics of P-state transitions >> + * @pstates: per-CPU P-state statistics struct >> + * @freq: frequency for which the newly pstate is allocated >> + * >> + * Return: the index of the newly allocated pstate struct >> + */ >> +static int alloc_pstate(struct cpufreq_pstates *pstates, unsigned int >> freq) >> +{ >> + struct cpufreq_pstate *pstate, *tmp; >> + int nrfreq, i, next = 0; >> + >> + pstate = pstates->pstate; >> + nrfreq = pstates->max; >> + >> + tmp = realloc(pstate, sizeof(*pstate) * (nrfreq + 1)); >> + if (!tmp) { >> + perror("realloc pstate"); >> + return -1; >> + } >> + pstate = tmp; >> + pstates->pstate = tmp; >> + pstates->max = nrfreq + 1; >> + >> + for (i = 0; i < nrfreq && freq <= pstate[i].freq; i++) >> + ; >> + >> + next = i; >> + for (i = nrfreq; i > next && i > 0; i--) { >> + pstate[i] = pstate[i - 1]; >> + pstate[i].id = i; >> + pstates->current = (pstates->current == i - 1)? >> + i : pstates->current; >> + } >> + >> + pstate[next].id = next; >> + pstate[next].freq = freq; >> + pstate[next].count = 0; >> + pstate[next].min_time = DBL_MAX; >> + pstate[next].max_time = 0; >> + pstate[next].avg_time = 0; >> + pstate[next].duration = 0; >> + >> + return next; >> +} >> + >> /** >> * release_pstate_info - free all P-state related structs >> * @pstates: per-cpu array of P-state statistics structs >> @@ -560,14 +675,16 @@ static void release_pstate_info(struct >> cpufreq_pstates *pstates, int nrcpus) >> return; >> } >> >> -/** >> - * build_pstate_info - parse cpufreq sysfs entries and build per-CPU >> - * structs to maintain statistics of P-state transitions >> +/* build_pstate_info - allocate and initialize per-CPU structs to >> + * maintain statistics of P-state transitions >> * @nrcpus: number of CPUs >> + * @initp: initial P-state of CPUs before trace acquistion >> + * @start_ts: timestamp when trace acquisition started >> * >> * Return: per-CPU array of structs (success) or NULL (error) >> */ >> -static struct cpufreq_pstates *build_pstate_info(int nrcpus) >> +static struct cpufreq_pstates *build_pstate_info(int nrcpus, >> + struct init_pstates *initp, double start_ts) >> { >> int cpu; >> struct cpufreq_pstates *pstates; >> @@ -577,67 +694,28 @@ static struct cpufreq_pstates *build_pstate_info(int >> nrcpus) >> return NULL; >> memset(pstates, 0, sizeof(*pstates) * nrcpus); >> >> - for (cpu = 0; cpu < nrcpus; cpu++) { >> - struct cpufreq_pstate *pstate; >> - int nrfreq; >> - char *fpath, *freq, line[256]; >> - FILE *sc_av_freq; >> - >> - if (asprintf(&fpath, CPUFREQ_AVFREQ_PATH_FORMAT, cpu) < 0) >> - goto clean_exit; >> - >> - /* read scaling_available_frequencies for the CPU */ >> - sc_av_freq = fopen(fpath, "r"); >> - free(fpath); >> - if (!sc_av_freq) { >> - fprintf(stderr, "warning: P-states not supported >> for " >> - "CPU%d\n", cpu); >> - continue; >> - } >> - freq = fgets(line, sizeof(line)/sizeof(line[0]), >> sc_av_freq); >> - fclose(sc_av_freq); >> - if (!freq) { >> - /* unlikely to be here, but just in case... */ >> - fprintf(stderr, "warning: P-state info not found >> for " >> - "CPU%d\n", cpu); >> - continue; >> - } >> + if (initp) >> + assert(initp->nr_cpus == nrcpus); >> >> - /* tokenize line and populate each frequency */ >> - nrfreq = 0; >> - pstate = NULL; >> - while ((freq = strtok(freq, "\n ")) != NULL) { >> - struct cpufreq_pstate *tmp = realloc(pstate, >> sizeof(*pstate) * (nrfreq+1)); >> - if (!tmp) >> - goto clean_exit; >> - pstate = tmp; >> - >> - /* initialize pstate record */ >> - pstate[nrfreq].id = nrfreq; >> - pstate[nrfreq].freq = atol(freq); >> - pstate[nrfreq].count = 0; >> - pstate[nrfreq].min_time = DBL_MAX; >> - pstate[nrfreq].max_time = 0.; >> - pstate[nrfreq].avg_time = 0.; >> - pstate[nrfreq].duration = 0.; >> - nrfreq++; >> - freq = NULL; >> + for (cpu = 0; cpu < nrcpus; cpu++) { >> + struct cpufreq_pstates *ps = &(pstates[cpu]); >> + >> + ps->pstate = NULL; >> + ps->max = 0; >> + ps->current = -1; /* unknown */ >> + ps->idle = -1; /* unknown */ >> + ps->time_enter = 0.; >> + ps->time_exit = 0.; >> + >> + if (initp && initp->freqs[cpu] > 0 && start_ts > 0) { >> + ps->current = alloc_pstate(ps, initp->freqs[cpu]); >> + assert(ps->current >= 0); >> + ps->time_enter = start_ts; >> + ps->idle = 0; >> } >> - >> - /* now populate cpufreq_pstates for this CPU */ >> - pstates[cpu].pstate = pstate; >> - pstates[cpu].max = nrfreq; >> - pstates[cpu].current = -1; /* unknown */ >> - pstates[cpu].idle = -1; /* unknown */ >> - pstates[cpu].time_enter = 0.; >> - pstates[cpu].time_exit = 0.; >> } >> >> return pstates; >> - >> -clean_exit: >> - release_pstate_info(pstates, nrcpus); >> - return NULL; >> } >> >> static int get_current_pstate(struct cpuidle_datas *datas, int cpu, >> @@ -712,6 +790,9 @@ static void cpu_change_pstate(struct cpuidle_datas >> *datas, int cpu, >> >> cur = get_current_pstate(datas, cpu, &ps, &p); >> next = freq_to_pstate_index(ps, freq); >> + if (next < 0) >> + next = alloc_pstate(ps, freq); >> + assert(next >= 0); >> >> switch (cur) { >> case 1: >> @@ -742,6 +823,19 @@ static void cpu_change_pstate(struct cpuidle_datas >> *datas, int cpu, >> } >> } >> >> +static void cpu_close_all_pstate(struct cpuidle_datas *datas, double >> time) >> +{ >> + struct cpufreq_pstates *ps; >> + struct cpufreq_pstate *p; >> + int cpu, cur; >> + >> + for (cpu = 0; cpu < datas->nrcpus; cpu++) { >> + cur = get_current_pstate(datas, cpu, &ps, &p); >> + if (p && !cur && time > 0) >> + close_current_pstate(ps, time); >> + } >> +} >> + >> static void cpu_pstate_idle(struct cpuidle_datas *datas, int cpu, double >> time) >> { >> struct cpufreq_pstates *ps = &(datas->pstates[cpu]); >> @@ -763,7 +857,6 @@ static 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; >> struct cpuidle_cstate *cstate; >> struct cpuidle_data *data, *tmp; >> int nrdata, last_cstate = cstates->last_cstate; >> @@ -826,9 +919,8 @@ static int store_data(double time, int state, int cpu, >> /* need indication if CPU is idle or not */ >> cstates->last_cstate = -1; >> >> - /* update P-state stats if supported */ >> - if (pstate) >> - cpu_pstate_running(datas, cpu, time); >> + /* update P-state stats */ >> + cpu_pstate_running(datas, cpu, time); >> >> return 0; >> } >> @@ -846,9 +938,8 @@ static int store_data(double time, int state, int cpu, >> cstates->cstate_max = MAX(cstates->cstate_max, state); >> cstates->last_cstate = state; >> cstates->wakeirq = NULL; >> - /* update P-state stats if supported */ >> - if (pstate) >> - cpu_pstate_idle(datas, cpu, time); >> + /* update P-state stats*/ >> + cpu_pstate_idle(datas, cpu, time); >> >> return 0; >> } >> @@ -932,7 +1023,8 @@ static int get_wakeup_irq(struct cpuidle_datas >> *datas, char *buffer, int count) >> return -1; >> } >> >> -static struct cpuidle_datas *idlestat_load(struct program_options >> *options) >> +static struct cpuidle_datas *idlestat_load(struct program_options >> *options, >> + struct init_pstates *initp, double start_ts, >> double end_ts) >> { >> FILE *f; >> unsigned int state = 0, freq = 0, cpu = 0, nrcpus = 0; >> @@ -988,14 +1080,13 @@ static struct cpuidle_datas *idlestat_load(struct >> program_options *options) >> return ptrerror("build_cstate_info: out of memory"); >> } >> >> - datas->pstates = build_pstate_info(nrcpus); >> + datas->pstates = build_pstate_info(nrcpus, initp, start_ts); >> if (!datas->pstates) { >> free(datas->cstates); >> free(datas); >> fclose(f); >> return ptrerror("build_pstate_info: out of memory"); >> } >> - >> datas->nrcpus = nrcpus; >> >> /* read topology information */ >> @@ -1018,7 +1109,6 @@ static struct cpuidle_datas *idlestat_load(struct >> program_options *options) >> } else if (strstr(buffer, "cpu_frequency")) { >> assert(sscanf(buffer, TRACE_FORMAT, &time, &freq, >> &cpu) == 3); >> - assert(datas->pstates[cpu].pstate != NULL); >> cpu_change_pstate(datas, cpu, freq, time); >> count++; >> continue; >> @@ -1031,6 +1121,9 @@ static struct cpuidle_datas *idlestat_load(struct >> program_options *options) >> >> fclose(f); >> >> + /* close all p-state with end timestamp */ >> + cpu_close_all_pstate(datas, end_ts); >> + >> fprintf(stderr, "Log is %lf secs long with %zd events\n", >> end - begin, count); >> >> @@ -1481,7 +1574,9 @@ int main(int argc, char *argv[], char *const envp[]) >> { >> struct cpuidle_datas *datas; >> struct program_options options; >> + struct init_pstates *initp = NULL; >> int args; >> + double start_ts, end_ts; >> >> args = getoptions(argc, argv, &options); >> if (args <= 0) >> @@ -1527,6 +1622,14 @@ int main(int argc, char *argv[], char *const >> envp[]) >> if (idlestat_flush_trace()) >> return -1; >> >> + /* Get current P-state of all CPUs */ >> + if (options.display & FREQUENCY_DISPLAY) >> + initp = build_init_pstates(); >> + >> + /* Get timestamp before trace acquisition */ >> + if (options.display & FREQUENCY_DISPLAY) >> + start_ts = get_trace_ts(); >> + >> /* Start the recording */ >> if (idlestat_trace_enable(true)) >> return -1; >> @@ -1549,6 +1652,17 @@ int main(int argc, char *argv[], char *const >> envp[]) >> if (idlestat_trace_enable(false)) >> return -1; >> >> + /* Get timestamp after trace acquisition */ >> + if (options.display & FREQUENCY_DISPLAY) >> + end_ts = get_trace_ts(); >> + >> + /* Emit warning messages when P-state info might be >> partial */ >> + if (options.display & FREQUENCY_DISPLAY && >> + (!initp || start_ts < 0 || end_ts < 0)) >> + fprintf(stderr, "Unable to get initial P-state," >> + " beginning timestamp, or ending >> timestamp!\n" >> + "P-state statistics data might be >> partial!\n"); >> + >> /* At this point we should have some spurious wake up >> * at the beginning of the traces and at the end (wake >> * up all cpus and timer expiration for the timer >> @@ -1559,7 +1673,7 @@ int main(int argc, char *argv[], char *const envp[]) >> } >> >> /* Load the idle states information */ >> - datas = idlestat_load(&options); >> + datas = idlestat_load(&options, initp, start_ts, end_ts); >> >> if (!datas) >> return 1; >> @@ -1590,6 +1704,7 @@ int main(int argc, char *argv[], char *const envp[]) >> } >> } >> >> + release_init_pstates(initp); >> release_cpu_topo_cstates(); >> release_cpu_topo_info(); >> release_pstate_info(datas->pstates, datas->nrcpus); >> diff --git a/idlestat.h b/idlestat.h >> index 735f0fe..39ba4d7 100644 >> --- a/idlestat.h >> +++ b/idlestat.h >> @@ -41,6 +41,8 @@ >> "/sys/devices/system/cpu/cpu%d/cpuidle/state%d/residency" >> #define CPUFREQ_AVFREQ_PATH_FORMAT \ >> >> "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_available_frequencies" >> +#define CPUFREQ_CURFREQ_PATH_FORMAT \ >> + "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq" >> #define CPUIDLE_STATENAME_PATH_FORMAT \ >> "/sys/devices/system/cpu/cpu%d/cpuidle/state%d/name" >> >> @@ -63,6 +65,11 @@ struct cpuidle_cstate { >> int target_residency; /* -1 if not available */ >> }; >> >> +struct init_pstates { >> + int nr_cpus; >> + unsigned int *freqs; >> +}; >> + >> enum IRQ_TYPE { >> HARD_IRQ = 0, >> IPI_IRQ, >> diff --git a/trace.h b/trace.h >> index bef6703..90b4a6a 100644 >> --- a/trace.h >> +++ b/trace.h >> @@ -33,6 +33,7 @@ >> #define TRACE_EVENT_PATH TRACE_PATH "/events/enable" >> #define TRACE_FREE TRACE_PATH "/free_buffer" >> #define TRACE_FILE TRACE_PATH "/trace" >> +#define TRACE_STAT_FILE TRACE_PATH "/per_cpu/cpu0/stats" >> #define TRACE_IDLE_NRHITS_PER_SEC 10000 >> #define TRACE_IDLE_LENGTH 196 >> #define TRACE_CPUFREQ_NRHITS_PER_SEC 100 >> > > > -- > <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs > > Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | > <http://twitter.com/#!/linaroorg> Twitter | > <http://www.linaro.org/linaro-blog/> Blog > > > > _______________________________________________ > linaro-dev mailing list > linaro-dev@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/linaro-dev _______________________________________________ linaro-dev mailing list linaro-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-dev