This commit add regression test for idlestat.Please find bellowing message for how to use.
eg: import the mytrace file and compare it with previous mytrace_old file to see if they match each other ./idlestat --regression -f ./mytrace -t 10 -p -c -w -b ./mytrace_old -T
eg: import mytrace file and export the report file as myreport_new. compare the myreport_new with myreport_old to see if they match with each other ./idlestat --regression -f ./mytrace -t 10 -p -c -w -o ./myreport_new -b ./myreport_old -O
eg: set the store format of ftrace as bin and mmap it to userspace ./idlestat --trace -f ./mytrace -t 10 -m
Signed-off-by: Zhaoyang Huang zhaoyang.huang@linaro.org --- idlestat.c | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- idlestat.h | 7 +- topology.c | 19 +++++ topology.h | 1 + trace.h | 1 + utils.c | 10 +++ utils.h | 1 + 7 files changed, 270 insertions(+), 2 deletions(-)
diff --git a/idlestat.c b/idlestat.c index 4d773f4..cb481c9 100644 --- a/idlestat.c +++ b/idlestat.c @@ -41,6 +41,13 @@ #ifdef ANDROID #include <libgen.h> #endif +#include <sys/mman.h> /* for mmap and munmap */ +#include <sys/types.h> /* for open */ +#include <sys/stat.h> /* for open */ +#include <fcntl.h> /* for open */ +#include <unistd.h> /* for lseek and write */ +#include <stdio.h> +#include <string.h> /* for memcpy */
#include "idlestat.h" #include "utils.h" @@ -1074,6 +1081,7 @@ int getoptions(int argc, char *argv[], struct program_options *options) struct option long_options[] = { { "trace", no_argument, &options->mode, TRACE }, { "import", no_argument, &options->mode, IMPORT }, + { "regression", no_argument, &options->mode, REGRESSION }, { "baseline-trace", required_argument, NULL, 'b' }, { "idle", no_argument, NULL, 'c' }, { "energy-model-file", required_argument, NULL, 'e' }, @@ -1090,6 +1098,9 @@ int getoptions(int argc, char *argv[], struct program_options *options) { "poll-interval", required_argument, NULL, 'I' }, { "buffer-size", required_argument, NULL, 'S' }, { "version", no_argument, NULL, 'V' }, + { "mmap", optional_argument, NULL, 'm' }, + { "trace file compare", no_argument, NULL, 'T' }, + { "output file compare", no_argument, NULL, 'O' }, { 0, 0, 0, 0 } }; int c; @@ -1103,7 +1114,7 @@ int getoptions(int argc, char *argv[], struct program_options *options)
int optindex = 0;
- c = getopt_long(argc, argv, ":b:ce:f:ho:pr:t:vwBCI:S:V", + c = getopt_long(argc, argv, ":b:ce:f:ho:pr:t:vwBCI:S:VmTO", long_options, &optindex); if (c == -1) break; @@ -1174,6 +1185,15 @@ int getoptions(int argc, char *argv[], struct program_options *options) case 'S': options->tbs.percpu_buffer_size = atoi(optarg); break; + case 'm': + options->mmap_flag = true; + break; + case 'T': + options->tracefile_comp_flag = true; + break; + case 'O': + options->output_comp_flag = true; + break; case 0: /* getopt_long() set a variable, just keep going */ break; case ':': /* missing option argument */ @@ -1197,6 +1217,21 @@ int getoptions(int argc, char *argv[], struct program_options *options) return -1; }
+ if (options->mode == REGRESSION) { + if ((options->filename == NULL) + &&(options->outfilename == NULL)) { + fprintf(stderr, "expected file name for regression test\n"); + return -1; + } + if (options->filename && bad_filename(options->filename)) + return -1; + + if (options->outfilename && bad_filename(options->outfilename)) + return -1; + + return optind; + } + if (NULL == options->filename) { fprintf(stderr, "expected -f <trace filename>\n"); return -1; @@ -1401,6 +1436,158 @@ static int execute(int argc, char *argv[], char *const envp[], return -1; }
+int binary_compare(struct program_options * options) +{ + FILE * fp; + FILE * fp_baseline; + char * file; + char * file_baseline; + int file_size; + int ret; + + printf("running binary_compare\n"); + fp = fopen(options->filename,"r"); + fseek( fp , 0 , SEEK_END ); + file_size = ftell( fp ); + printf( "%s %d\n" ,options->filename, file_size ); + fseek( fp , 0 , SEEK_SET); + file = (char *)malloc( file_size * sizeof( char ) ); + fread( file , file_size , sizeof(char) , fp); + + fp_baseline = fopen(options->baseline_filename,"r"); + fseek( fp_baseline , 0 , SEEK_END ); + file_size = ftell(fp_baseline); + printf( "%s %d\n" ,options->baseline_filename, file_size ); + fseek( fp_baseline , 0 , SEEK_SET); + file_baseline = (char *)malloc( file_size * sizeof( char ) ); + fread( file_baseline , file_size , sizeof(char) , fp_baseline); + + ret = strcmp(file,file_baseline); + if(ret) + fprintf(stderr, "[IDLE_STAT REGRESSION TEST] <binary_compare failed>\n"); + else + printf( "[IDLE_STAT REGRESSION TEST] <binary_compare success>\n"); + + fclose(fp); + fclose(fp_baseline); + free(file); + free(file_baseline); + return ret; +} + +int output_compare(struct program_options * options) +{ + struct report_ops *output_handler = NULL; + void *report_data = NULL; + struct cpu_topology *cpu_topo = NULL; + struct cpuidle_datas *datas; + char * tmp_filename; + int ret = 0; + /* Load the idle states information */ + datas = idlestat_load(options->filename); + + if (is_err(datas)) + return 1; + + cpu_topo = datas->topo; + + output_handler = get_report_ops(options->report_type_name); + if (is_err(output_handler)) + return 1; + + if (output_handler->open_report_file(options->outfilename, report_data)) + return 1; + + if (options->display & IDLE_DISPLAY) { + output_handler->cstate_table_header(report_data); + dump_cpu_topo_info(output_handler, report_data, + display_cstates, cpu_topo, 1); + output_handler->cstate_table_footer(report_data); + } + + if (options->display & FREQUENCY_DISPLAY) { + output_handler->pstate_table_header(report_data); + dump_cpu_topo_info(output_handler, report_data, + display_pstates, cpu_topo, 0); + output_handler->pstate_table_footer(report_data); + } + + if (options->display & WAKEUP_DISPLAY) { + output_handler->wakeup_table_header(report_data); + dump_cpu_topo_info(output_handler, report_data, + display_wakeup, cpu_topo, 1); + output_handler->wakeup_table_footer(report_data); + } + + restore_stdout(); + + /*backup the filename*/ + tmp_filename = options->filename; + + /*compare the new output file with the old one*/ + options->filename = options->outfilename; + ret = binary_compare(options); + + if(ret) + printf("output file comparsion of %s and %s failed\n",options->filename,options->baseline_filename); + else + printf("[IDLE_STAT REGRESSION TEST] <output_compare success>\n"); + /*restore the file name*/ + options->filename = tmp_filename; + + + return ret; + +} + +int regression(struct program_options * options) +{ + struct cpuidle_datas *datas; + struct cpu_topology *cpu_topo = NULL; + struct report_ops *output_handler = NULL; + int ret = 0; + + char topo_info[512]; + + output_handler = get_report_ops(options->report_type_name); + if (is_err(output_handler)) + return 1; + + if (output_handler->check_options && + output_handler->check_options(options) < 0) + return 1; + + /* Load the idle states information */ + datas = idlestat_load(options->filename); + + if (is_err(datas)){ + fprintf(stderr, "[IDLE_STAT REGRESSION TEST] <trace file import failed>\n"); + ret = -1; + } + else + printf("[IDLE_STAT REGRESSION TEST] <trace file import success>\n"); + + cpu_topo = read_sysfs_cpu_topo(); + if (is_err(cpu_topo)) { + fprintf(stderr, "Failed to read CPU topology info from" + " sysfs.\n"); + ret = -1; + } + else{ + outstr_topo_info(topo_info,cpu_topo); + printf("%s",topo_info); + printf("[IDLE_STAT REGRESSION TEST] <CPU topology info read success>\n"); + } + + if(options->tracefile_comp_flag) + ret = binary_compare(options); + + if(options->output_comp_flag) + ret = output_compare(options); + + return ret; +} + int main(int argc, char *argv[], char *const envp[]) { struct cpuidle_datas *datas; @@ -1414,10 +1601,24 @@ int main(int argc, char *argv[], char *const envp[]) struct trace_options *saved_trace_options = NULL; void *report_data = NULL;
+ int fd; + int flength; + struct stat statbuff; + args = getoptions(argc, argv, &options); if (args <= 0) return 1;
+ if (options.mode == REGRESSION){ + if (regression(&options)){ + printf("regression failed\n"); + } + else{ + printf("regression success\n"); + } + return 0; + } + /* Tracing requires manipulation of some files only accessible * to root */ if ((options.mode == TRACE) && getuid()) { @@ -1497,6 +1698,16 @@ int main(int argc, char *argv[], char *const envp[])
initp = build_init_pstates(cpu_topo);
+ /* + set the trace file format to bin + */ + if(options.mmap_flag){ + FILE * file; + file = fopen(TRACE_OPTIONS, "r+"); + perror(__func__); + printf("%s open %p\n",TRACE_OPTIONS,file); + store_line("bin",(void *)file); + } /* Start the recording */ if (idlestat_trace_enable(true)) goto err_restore_trace_options; @@ -1532,6 +1743,26 @@ int main(int argc, char *argv[], char *const envp[]) initp, cpu_topo)) goto err_restore_trace_options;
+ if(options.mmap_flag){ + fd = open(options.filename, O_RDONLY); + if((fd == -1) || fstat(fd, &statbuff)){ + perror(__func__); + printf("[idlestat] file length get failed\n"); + close(fd); + return 1; + } + flength = statbuff.st_size; + printf("[idlestat]: mmaped file is %s fd %x length %d\n",options.filename,fd,flength); + options.mapped_mem = mmap(NULL, flength, PROT_READ,MAP_PRIVATE,fd,0); + if((int)(void *)options.mapped_mem == (int)-1){ + perror(__func__); + close(fd); + return 1; + } + //printf("%s\n", options.mapped_mem); + close(fd); + } + /* Restore original kernel ftrace options */ if (idlestat_restore_trace_options(saved_trace_options)) return 1; diff --git a/idlestat.h b/idlestat.h index e030f6a..1ca9748 100644 --- a/idlestat.h +++ b/idlestat.h @@ -121,7 +121,8 @@ struct cpuidle_datas {
enum modes { TRACE = 0, - IMPORT + IMPORT, + REGRESSION };
struct trace_buffer_settings { @@ -140,6 +141,10 @@ struct program_options { int verbose; char *energy_model_filename; char *report_type_name; + bool mmap_flag; + bool tracefile_comp_flag; + bool output_comp_flag; + char * mapped_mem; };
#define IDLE_DISPLAY 0x1 diff --git a/topology.c b/topology.c index 39b07bd..ee00bfe 100644 --- a/topology.c +++ b/topology.c @@ -280,6 +280,25 @@ int outfile_topo_info(FILE *f, struct cpu_topology *topo_list) return 0; }
+int outstr_topo_info(char *s, struct cpu_topology *topo_list) +{ + struct cpu_physical *s_phy; + struct cpu_core *s_core; + struct cpu_cpu *s_cpu; + + list_for_each_entry(s_phy, &topo_list->physical_head, list_physical) { + sprintf(s, "cluster%c:\n", s_phy->physical_id + 'A'); + list_for_each_entry(s_core, &s_phy->core_head, list_core) { + sprintf(s, "\tcore%d\n", s_core->core_id); + list_for_each_entry(s_cpu, &s_core->cpu_head, list_cpu){ + sprintf(s, "\t\tcpu%d\n", s_cpu->cpu_id); + } + } + } + + return 0; +} + struct cpu_cpu *find_cpu_point(struct cpu_topology *topo_list, int cpuid) { struct cpu_physical *s_phy; diff --git a/topology.h b/topology.h index 84e5bc1..4c7964e 100644 --- a/topology.h +++ b/topology.h @@ -115,5 +115,6 @@ extern int core_get_highest_freq(struct cpu_core *core); core_get_highest_freq(cpu_to_core(cpuid, topo))
extern int setup_topo_states(struct cpuidle_datas *datas); +extern int outstr_topo_info(char *s, struct cpu_topology *topo_list);
#endif diff --git a/trace.h b/trace.h index a2f5867..29b4d93 100644 --- a/trace.h +++ b/trace.h @@ -34,6 +34,7 @@ #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_OPTIONS TRACE_PATH "/trace_options" #define TRACE_IDLE_NRHITS_PER_SEC 10000 #define TRACE_IDLE_LENGTH 196 #define TRACE_CPUFREQ_NRHITS_PER_SEC 100 diff --git a/utils.c b/utils.c index 48be965..1c6bc96 100644 --- a/utils.c +++ b/utils.c @@ -210,14 +210,17 @@ out_free: return ret; }
+int std_fd; int redirect_stdout_to_file(const char *path) { int ret = 0; int fd;
if (path) { + std_fd = dup(STDOUT_FILENO); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP |S_IROTH); + if (fd < 0) { fprintf(stderr, "%s: failed to open '%s'\n", __func__, path); return -1; @@ -237,6 +240,13 @@ int redirect_stdout_to_file(const char *path) return 0; }
+int restore_stdout() +{ + int ret = 0; + ret = dup2(std_fd,STDOUT_FILENO); + printf("%s %d \n",__func__,ret); +} + void display_factored_time(double time, int align) { char buffer[128]; diff --git a/utils.h b/utils.h index ee7a9a2..6b8808c 100644 --- a/utils.h +++ b/utils.h @@ -49,5 +49,6 @@ extern int check_window_size(void); extern int error(const char *str); extern void *ptrerror(const char *str); extern int is_err(const void *ptr); +extern int restore_stdout();
#endif