On 02/20/2013 12:54 PM, Amit Kucheria wrote: > On Tue, Feb 19, 2013 at 10:11 AM, Sanjay Singh Rawat > <sanjay.ra...@linaro.org> wrote: >> Add functionality to record the overall and current runtime cpuidle >> statistics. > > I'd like the output to be something list this: > > [state1]: [number of times entered]: [average residency]: [std. > deviation(residency)]: [variance] > [state2]: [number of times entered]: [average residency]: [std. > deviation(residency)]: [variance] > . > . > [Total transitions (sum of all states entered)] > > This could then be compared with results from previous runs (scripted) > to flag any major changes. Of course, the environment in which this > test is run will have to be rigidly controlled for the results to be > comparable. > > I'm not sure if capturing the 'to' and 'from' states is required here. > It should reflect in the number of times a particular state is entered > above. > > Is it possible to come up with a single 'idleness' number that tells > us at a glance if idleness has decreased or increased on that SoC due > to changes in the environment (kernel, userspace)? This calculation > would presumably give much higher weight to a C3 compared to a C1. The > weight could even be the power draw of the SoC at that state. That > way, if we suddenly start staying in shallower states, our idleness > number should go down. > > Daniel? > > >> Signed-off-by: Sanjay Singh Rawat <sanjay.ra...@linaro.com> >> --- >> cpuidle/Readme.txt | 9 + >> cpuidle/cpuidle_03.sh | 9 + >> cpuidle/cpuidle_stats.c | 443 >> +++++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 461 insertions(+) >> create mode 100644 cpuidle/Readme.txt >> create mode 100644 cpuidle/cpuidle_stats.c >> >> diff --git a/cpuidle/Readme.txt b/cpuidle/Readme.txt >> new file mode 100644 >> index 0000000..9eacae0 >> --- /dev/null >> +++ b/cpuidle/Readme.txt >> @@ -0,0 +1,9 @@ >> +# cpuidle_stats execution: >> + - can compile and run separately. >> + - can run as part of cpuidle test : "sudo make -C cpuidle check" >> + >> +# info: >> +- Need sudo permission to run. >> +- saves trace in cpuidle/cpuidle_trace.log. >> +- Gives complete and current sampling time(in/out and timespent) cpuidle >> +state information. >> diff --git a/cpuidle/cpuidle_03.sh b/cpuidle/cpuidle_03.sh >> index 22bca10..0295555 100755 >> --- a/cpuidle/cpuidle_03.sh >> +++ b/cpuidle/cpuidle_03.sh >> @@ -56,5 +56,14 @@ fi >> >> trap "restore_cpus; sigtrap" SIGHUP SIGINT SIGTERM >> >> +check_cpuidle_trace() { >> + echo "### cpuidle state transition check:" >> + echo "## Collect the overall and current runtime cpuidle stats" > > > What environment does this test run in? We probably want to run this > on a stock ubuntu devel root filesystem and android after allowing it > to idle for 30 seconds or so. It is probably worth discarding several > seconds on data and the beginning and end of the capture too. > > It would also be useful to pass a command-line parameter to control > how long to run this measurement.
I don't remember if I already mentioned it, but it is worth to have the pm-qa to be a standalone test suite, that is init + minimal configuration (no daemons) + pm-qa. eg: a custom busybox system. Why ? we don't have the guarantee the userspace programs behave the same across the different versions. We may see improvements because of a new version of an application using a mainloop instead of polling or because a service is no longer set, or irqbalance is there, etc ... I already saw some cpufreq tests failing because the standard deviation was too high due to a daemon running a job or a flush to disk. That would be nice to boot the board on the pm-qa tests and then exits with a summary of the results. I don't know if that could be compatible with Lava, but with this approach, we are sure we are testing the kernel only. >> + check "cpuidle stats execution" "./cpuidle_stats" >> + return 0 >> +} >> + >> for_each_cpu check_cpuidle_kill >> restore_cpus >> + >> +check_cpuidle_trace >> diff --git a/cpuidle/cpuidle_stats.c b/cpuidle/cpuidle_stats.c >> new file mode 100644 >> index 0000000..8cc5436 >> --- /dev/null >> +++ b/cpuidle/cpuidle_stats.c >> @@ -0,0 +1,443 @@ >> +#include <stdio.h> >> +#include <unistd.h> >> +#include <string.h> >> +#include <stdlib.h> >> +#include <sys/types.h> >> +#include <fcntl.h> >> +#include <getopt.h> >> +#include <sched.h> >> +#include <linux/unistd.h> >> +#include <signal.h> >> +#include <time.h> >> + >> +#define path "./cpuidle_trace.log" >> + >> +unsigned int num_of_cpus; >> +int signal_handled = 0; >> +struct timeval start_time, stop_time; >> +FILE *log_file; >> +struct cpu_idle_stats { >> + int num_states, exit,active_state; >> + struct state_stats{ >> + char name[25],desc[25],latency[25]; >> + char power[25],time[25], usage[25]; >> + int in; >> + double time_spent, entry_time; >> + } **state; >> + double exit_time; >> +} **cpuidle_stats; >> + >> +double traceline_time_stamp(char *line) >> +{ >> + char *ptr; >> + char time_stamp[14]; >> + >> + ptr = strchr(line,'.'); >> + if( ptr == NULL) { >> + printf("Funtion-%s : error, line format not >> recognized\n",__func__); >> + return -1; //todo - proper return value >> + } >> + strncpy(time_stamp,(ptr-6),13); //6 digits before and after decimal >> + time_stamp[14]=0; //set EOS >> + >> + return atof(time_stamp); >> +} >> + >> +void print_stat() >> +{ >> + int cpu,state; >> + >> + fprintf(log_file,"=================== Current cpuidle statistics >> ================\n"); >> + >> + for(cpu=0 ; cpu<4 ; cpu++) { >> + fprintf(log_file,"cpu-%d : [State] [In-Count] >> [Time Spent]\n",cpu); >> + for(state=0 ; state<5 ; state++) { >> + fprintf(log_file,"\t[states-%d] in-count = %d >> time-spent = %lf\n", >> + state, cpuidle_stats[cpu]->state[state]->in, >> + >> cpuidle_stats[cpu]->state[state]->time_spent); >> + } >> + fprintf(log_file,"\t\t[Out-Count] = >> %d\n\n",cpuidle_stats[cpu]->exit); >> + } >> +} >> + >> +/* function: scan the trace file >> + */ >> +int scan_trace_file(void) >> +{ >> + char line[1024],str[50]; >> + FILE *fp; >> + >> + printf("Scanning trace file ...\n"); >> + fp = fopen (path, "r"); >> + if (fp == NULL) { >> + printf("function-%s: error fopen\n",__func__); >> + return -1; >> + } >> + >> + while( fgets(line, sizeof(line), fp) != NULL ) { >> + if(strstr(line,"cpu_idle")) { >> + int cpu_num; >> + for(cpu_num=0 ; cpu_num<4 ; cpu_num++) { >> + int ret; >> + >> + sprintf(str,"cpu_id=%d",cpu_num); >> + if(strstr(line,str) == NULL) >> + continue; >> + >> + for(ret=0 ; >> ret<cpuidle_stats[cpu_num]->num_states ; ret++) { >> + sprintf(str,"state=%d",ret); >> + if(strstr(line,"state=4294967295")) >> { //todo: do it better >> + >> cpuidle_stats[cpu_num]->exit_time = >> + >> traceline_time_stamp(line); >> + >> if(cpuidle_stats[cpu_num]->state[cpuidle_stats[cpu_num]->active_state]->entry_time) >> + >> cpuidle_stats[cpu_num]->state[cpuidle_stats[cpu_num]->active_state]->time_spent >> += >> + >> cpuidle_stats[cpu_num]->exit_time - >> + >> cpuidle_stats[cpu_num]->state[cpuidle_stats[cpu_num]->active_state]->entry_time; >> + >> + >> cpuidle_stats[cpu_num]->exit++; >> + break; >> + } >> + if(strstr(line,str) == NULL) >> + continue; >> + >> + cpuidle_stats[cpu_num]->active_state >> = ret; >> + >> cpuidle_stats[cpu_num]->state[ret]->entry_time = >> + traceline_time_stamp(line); >> + >> cpuidle_stats[cpu_num]->state[ret]->in++; >> + } >> + >> + } >> + } >> + } >> + print_stat(); >> + printf("Logs saved in cpuidle_stats.log\n\n"); >> +} >> + >> +/* function: allocate and setup few structure for you >> + */ >> +void print_cpus_states_info(struct cpu_idle_stats **cpuidle_stats) >> +{ >> + int cpus, states; >> + char line_buf[1024],*ptr; >> + >> + for(cpus=0 ; cpus<num_of_cpus ; cpus++) >> + { >> + memset(line_buf,'\0',sizeof(line_buf)); >> + ptr=line_buf; >> + >> fprintf(log_file,"=========================================================\n"); >> + fprintf(log_file,"\nCPU%d - Overall cpuidle state info: >> [STATE-0 ---> STATE-%d]\n" >> + ,cpus,(cpuidle_stats[cpus]->num_states-1)); >> + for(states=0 ; states< cpuidle_stats[cpus]->num_states ; >> states++) >> + { >> + int str_len= >> strlen(cpuidle_stats[cpus]->state[states]->desc); >> + >> memcpy(ptr,cpuidle_stats[cpus]->state[states]->desc,str_len); >> + ptr += (str_len - 1); >> + *ptr++ = '/'; >> + *ptr++ = '\t'; >> + } >> + fprintf(log_file," desc[0->4] - >> %s\n",line_buf); >> + >> + memset(line_buf,'\0',sizeof(line_buf)); >> + ptr=line_buf; >> + for(states=0 ; states< cpuidle_stats[cpus]->num_states ; >> states++) >> + { >> + int str_len= >> strlen(cpuidle_stats[cpus]->state[states]->latency); >> + >> memcpy(ptr,cpuidle_stats[cpus]->state[states]->latency,str_len); >> + ptr += (str_len - 1); >> + *ptr++ = '\t'; >> + } >> + fprintf(log_file," latency[0->4] - >> %s\n",line_buf); >> + >> + memset(line_buf,'\0',sizeof(line_buf)); >> + ptr=line_buf; >> + for(states=0 ; states< cpuidle_stats[cpus]->num_states ; >> states++) >> + { >> + int str_len= >> strlen(cpuidle_stats[cpus]->state[states]->name); >> + >> memcpy(ptr,cpuidle_stats[cpus]->state[states]->name,str_len); >> + ptr += (str_len - 1); >> + *ptr++ = '\t'; >> + } >> + fprintf(log_file," name[0->4] - >> %s\n",line_buf); >> + >> + memset(line_buf,'\0',sizeof(line_buf)); >> + ptr=line_buf; >> + for(states=0 ; states< cpuidle_stats[cpus]->num_states ; >> states++) >> + { >> + int str_len= >> strlen(cpuidle_stats[cpus]->state[states]->power); >> + >> memcpy(ptr,cpuidle_stats[cpus]->state[states]->power,str_len); >> + ptr += (str_len - 1); >> + *ptr++ = '\t'; >> + } >> + fprintf(log_file," power[0->4] - >> %s\n",line_buf); >> + >> + memset(line_buf,'\0',sizeof(line_buf)); >> + ptr=line_buf; >> + for(states=0 ; states< cpuidle_stats[cpus]->num_states ; >> states++) >> + { >> + int str_len= >> strlen(cpuidle_stats[cpus]->state[states]->time); >> + >> memcpy(ptr,cpuidle_stats[cpus]->state[states]->time,str_len); >> + ptr += (str_len - 1); >> + *ptr++ = '\t'; >> + } >> + fprintf(log_file," time[0->4] - >> %s\n",line_buf); >> + >> + memset(line_buf,'\0',sizeof(line_buf)); >> + ptr=line_buf; >> + for(states=0 ; states< cpuidle_stats[cpus]->num_states ; >> states++) >> + { >> + int str_len= >> strlen(cpuidle_stats[cpus]->state[states]->usage); >> + >> memcpy(ptr,cpuidle_stats[cpus]->state[states]->usage,str_len); >> + ptr += (str_len - 1); >> + *ptr++ = '\t'; >> + } >> + fprintf(log_file," usage[0->4] - >> %s\n",line_buf); >> + } >> + >> fprintf(log_file,"=========================================================\n"); >> +} >> + >> +/* function: read sys attribute in char buffer >> + */ >> +int read_attribute(char *state_path, char *buf) >> +{ >> + int fd, ret; >> + >> + fd = open(state_path, O_RDONLY, 0666); >> + if(fd < 0) { >> + ret = fd; >> + goto fail; >> + } >> + ret = read(fd, buf, 255); >> + if(ret < 0) { >> + close(fd); >> + goto fail; >> + } >> + close(fd); >> + return 0; >> +fail: >> + printf("function-%s: error/fail\n",__func__); >> + return ret; >> +} >> + >> +/* Function: Read the cpu-state attributes from sysfs >> + */ >> +int read_state_stats(struct state_stats *state, char *state_path) >> +{ >> + char state_attr_path[256]; >> + int ret; >> + >> + snprintf(state_attr_path, sizeof(state_attr_path),"%s/%s", >> + state_path,"desc"); >> + ret = read_attribute(state_attr_path,state->desc); >> + if(ret < 0) >> + goto fail; >> + >> + snprintf(state_attr_path, sizeof(state_attr_path),"%s/%s", >> + state_path,"latency"); >> + ret = read_attribute(state_attr_path,state->latency); >> + if(ret < 0) >> + goto fail; >> + >> + snprintf(state_attr_path, sizeof(state_attr_path),"%s/%s", >> + state_path,"name"); >> + ret = read_attribute(state_attr_path,state->name); >> + if(ret < 0) >> + goto fail; >> + >> + snprintf(state_attr_path, sizeof(state_attr_path),"%s/%s", >> + state_path,"power"); >> + ret = read_attribute(state_attr_path,state->power); >> + if(ret < 0) >> + goto fail; >> + >> + snprintf(state_attr_path, sizeof(state_attr_path),"%s/%s", >> + state_path,"time"); >> + ret = read_attribute(state_attr_path,state->time); >> + if(ret < 0) >> + goto fail; >> + >> + snprintf(state_attr_path, sizeof(state_attr_path),"%s/%s", >> + state_path,"usage"); >> + ret = read_attribute(state_attr_path,state->usage); >> + if(ret < 0) >> + goto fail; >> + >> + return 0; >> +fail: >> + printf("Function-%s: error\n",__func__); >> + return ret; >> +} >> + >> +/* function: print complete stats present in sysfs from epoch time >> + */ >> +int get_states_info(struct cpu_idle_stats **cpuidle_stats, int num_of_cpus) >> +{ >> + char cpu_path[256],state_path[256]; >> + int cpu,state,ret; >> + FILE *file; >> + >> + for(cpu=0 ; cpu<num_of_cpus ; cpu++) { >> + snprintf(cpu_path, sizeof(state_path), >> + "/sys/devices/system/cpu/cpu%d/cpuidle",cpu); >> + for(state=0 ; state<256 ; state++){ >> + snprintf(state_path, sizeof(state_path),"%s/state%d", >> + cpu_path,state); >> + file = fopen(state_path,"r"); >> + if(file != NULL) { >> + fclose(file); >> + } >> + else { >> + fprintf(log_file,"cpu-%d total states = >> %d\n",cpu,state); >> + cpuidle_stats[cpu]->num_states = state; >> + break; >> + } >> + } >> + >> + cpuidle_stats[cpu]->state = >> + malloc(cpuidle_stats[cpu]->num_states * sizeof(int >> *)); >> + for(state=0 ; state<cpuidle_stats[cpu]->num_states ; >> state++) { >> + cpuidle_stats[cpu]->state[state] = >> + malloc(sizeof(struct state_stats)); >> + memset(cpuidle_stats[cpu]->state[state],'\0', >> + sizeof(struct state_stats)); >> + snprintf(state_path, sizeof(state_path),"%s/state%d", >> + cpu_path,state); >> + ret = >> read_state_stats(cpuidle_stats[cpu]->state[state], >> + state_path); >> + } >> + } >> + print_cpus_states_info(cpuidle_stats); >> +} >> + >> +/* function: checks cpu and state information and allocate structure >> accordingly >> + */ >> +int setup_cpuidle_info() >> +{ >> + int ret,retb; >> + >> + num_of_cpus = sysconf(_SC_NPROCESSORS_CONF); >> + fprintf(log_file,"number of cpus = %d\n",num_of_cpus); >> + >> + cpuidle_stats = malloc(num_of_cpus * sizeof(int *)); >> + for( ret=0 ; ret<num_of_cpus ; ret++) { >> + int i; >> + >> + cpuidle_stats[ret] = (struct cpu_idle_stats*) >> + malloc(sizeof(struct cpu_idle_stats)); >> + if(!cpuidle_stats[ret]) { >> + printf("functioin:%s memory allocation >> failure\n",__func__); >> + } >> + memset(cpuidle_stats[ret],0,sizeof(struct cpu_idle_stats)); >> + } >> + ret = get_states_info(cpuidle_stats, num_of_cpus); >> + if(ret < 0) { >> + printf("function:%s: error\n",__func__); >> + } >> + >> + return ret; >> +} >> + >> +/* function: Handler to stop tracing >> + */ >> +void handle_tracing_stop(int signum) >> +{ >> + char buf[256]; >> + int ret; >> + double duration; >> + >> + snprintf(buf,sizeof(buf), >> + "echo 0 > >> /sys/kernel/debug/tracing/events/power/cpu_idle/enable"); >> + ret = system(buf); > > a bit ugly... perhaps replace with a function that opens the file and > writes a value into it? > >> + if(ret != 0) >> + goto fail; >> + >> + snprintf(buf,sizeof(buf), >> + "cat /sys/kernel/debug/tracing/trace > >> ./cpuidle_trace.log"); >> + ret = system(buf); > > ditto > >> + if(ret != 0) >> + goto fail; >> + >> + printf("\nCapturing cpu_idle trace : end, saved in file:" >> + "cpuidle_trace.log\n"); >> + signal_handled = 1; >> + return; >> +fail: >> + printf("san-%s: fail/error\n",__func__); >> +} >> + >> +/* function: Generate cpuidle trace and save in file >> + */ >> +int capture_trace() >> +{ >> + char buf[256]; >> + char p[]="adkfh"; >> + int ret; >> + >> + printf("\n\nCapturing cpu_idle trace : start\n"); >> + signal(SIGINT, handle_tracing_stop); >> + >> + /* prepare */ >> + snprintf(buf,sizeof(buf), >> + "echo 0 > >> /sys/kernel/debug/tracing/events/power/cpu_idle/enable"); >> + ret = system(buf); > > ditto > >> + if(ret != 0) >> + goto fail; >> + snprintf(buf,sizeof(buf),"echo > /sys/kernel/debug/tracing/trace"); >> + ret = system(buf); >> + if(ret != 0) >> + goto fail; >> + /* enable tracing */ >> + snprintf(buf,sizeof(buf), >> + "echo nop > /sys/kernel/debug/tracing/current_tracer"); >> + ret = system(buf); >> + if(ret != 0) >> + goto fail; >> + snprintf(buf,sizeof(buf), >> + "echo 1 > /sys/kernel/debug/tracing/tracing_enabled"); >> + ret = system(buf); >> + if(ret != 0) >> + goto fail; >> + snprintf(buf,sizeof(buf),"echo 1 > >> /sys/kernel/debug/tracing/tracing_on"); >> + ret = system(buf); >> + if(ret != 0) >> + goto fail; >> + >> + gettimeofday(&start_time,NULL); >> + snprintf(buf,sizeof(buf), >> + "echo 1 > >> /sys/kernel/debug/tracing/events/power/cpu_idle/enable"); >> + ret = system(buf); >> + if(ret != 0) >> + goto fail; >> + >> + printf("\nPress Ctrl + c to Exit\n"); >> + while(!signal_handled) >> + sleep(1); >> + >> + return 0; >> +fail: >> + printf("%s: fail/error\n",__func__); >> + return ret; >> +} >> + >> +int main() >> +{ >> + int ret; >> + >> + log_file = fopen("cpuidle_stats.log","w"); >> + if(log_file == NULL) { >> + printf("error: can't create log file\n"); >> + return -1; >> + } >> + >> + ret = setup_cpuidle_info(); >> + if(ret < 0) { >> + printf("function: %s: error\n",__func__); >> + } >> + >> + ret = capture_trace(); >> + if(ret != 0) >> + return ret; >> + >> + scan_trace_file(); >> + >> + return 0; >> +} >> -- >> 1.7.9.5 >> >> >> _______________________________________________ >> linaro-dev mailing list >> linaro-dev@lists.linaro.org >> http://lists.linaro.org/mailman/listinfo/linaro-dev -- <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