From 41358857592f1908d0c0f9898b6c9acabc1ad161 Mon Sep 17 00:00:00 2001 From: Joshua Bakita Date: Sun, 18 Oct 2020 00:24:06 -0400 Subject: Rewrite extra.h for task baselines to be easier to use New macro `for_each_job` simplifies instrumentation. Backwards-compatible. --- baseline/source/extra.h | 275 ++++++++++++++++++++++++++++-------------------- 1 file changed, 163 insertions(+), 112 deletions(-) diff --git a/baseline/source/extra.h b/baseline/source/extra.h index 02e97af..e8f3d18 100644 --- a/baseline/source/extra.h +++ b/baseline/source/extra.h @@ -53,45 +53,60 @@ extern int sched_getcpu(); #define LOAD_PARAMS LOAD_PARAMS_ITRL #endif +// Store state globally so that the job can be outside main() +// Arrays use float as a comprimise between overflow and size +float *_rt_exec_time; +#if MMDC_PERF +float *_rt_mmdc_read; +float *_rt_mmdc_write; +#endif +long _rt_jobs_complete; +long _rt_max_jobs; +int _rt_core; +int _rt_will_output; +struct timespec _rt_start, _rt_end; + +char *_rt_run_id; +char *_rt_our_prog_name; +#define _RT_FILENAME_LEN 64 + #define LOAD_PARAMS_ITRL \ if (argc != 6) { \ fprintf(stderr, "Usage: %s \n", argv[0]);\ fprintf(stderr, " integer number of iterations. -1 for infitite.\n");\ fprintf(stderr, " 1 to save results, 0 to discard.\n");\ + fprintf(stderr, " UNUSED. Core is now auto-detected.\n");\ + exit(1);\ + }\ + _rt_our_prog_name = argv[1];\ + _rt_max_jobs = atol(argv[2]);\ + _rt_core = sched_getcpu();\ + _rt_run_id = argv[4];\ + _rt_will_output = atoi(argv[5]);\ + if (_rt_max_jobs < 0 && _rt_will_output != 0) {\ + fprintf(stderr, "Infinite loops only supported when _rt_will_output is disabled!\n");\ exit(1);\ }\ - char *thisProgram=argv[1];\ - int parsedMaxJobs=atoi(argv[2]);\ - unsigned int thisCore=atoi(argv[3]);\ - thisCore = sched_getcpu();\ - char *runID=argv[4];\ - int output=atoi(argv[5]);\ - if (parsedMaxJobs < 0 && output != 0){\ - fprintf(stderr, "Infinite loops only supported when output is disabled!\n");\ + if (strlen(_rt_run_id) + 5 > _RT_FILENAME_LEN) {\ + fprintf(stderr, "Run ID is too large! Keep it to less than %d characters.\n", _RT_FILENAME_LEN);\ exit(1);\ }\ - /* Cheat. -1 is larger than jobsComplete can ever reach. */\ - unsigned int maxJobs = parsedMaxJobs;\ - struct timespec _start, _end;\ - int jobsComplete;\ - int jobs_complete = -1;\ - float *progTime = malloc(sizeof(float)*maxJobs*output);\ - memset(progTime, 0, sizeof(float)*maxJobs*output);\ - char fileName[64];\ - float *mmdc_read = malloc(sizeof(float)*maxJobs*output);\ - float *mmdc_write = malloc(sizeof(float)*maxJobs*output);\ - memset(mmdc_read, 0, sizeof(float)*maxJobs*output);\ - memset(mmdc_write, 0, sizeof(float)*maxJobs*output);\ - if (strlen(runID) + 5 > sizeof(fileName)) {\ - fprintf(stderr, "Run ID is too large! Keep it to less than 60 characters.\n");\ + _rt_exec_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\ + if (!_rt_exec_time) {\ + perror("Unable to allocate buffer for execution times");\ exit(1);\ }\ - strcpy(fileName, runID);\ - strcat(fileName, ".txt");\ + _rt_jobs_complete = 0;\ mlockall(MCL_CURRENT || MCL_FUTURE); #define SETUP_MMDC \ - MMDC_PROFILE_RES_t mmdc_res; \ + _rt_mmdc_read = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\ + _rt_mmdc_write = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\ + if (!_rt_mmdc_read || !_rt_mmdc_write) {\ + perror("Unable to allocate buffer for MMDC data");\ + exit(1);\ + }\ + MMDC_PROFILE_RES_t mmdc_res;\ memset(&mmdc_res, 0, sizeof(MMDC_PROFILE_RES_t));\ int fd = open("/dev/mem", O_RDWR, 0);\ if (fd < 0) {\ @@ -107,9 +122,8 @@ extern int sched_getcpu(); msync(&(mmdc->madpcr1),4,MS_SYNC); #define SETUP_LITMUS \ - unsigned int cpu = atoi(thisCore); \ unsigned int wait = 0; \ - if (be_migrate_to_domain(cpu) < 0) { \ + if (be_migrate_to_domain(_rt_core) < 0) { \ perror("Unable to migrate to specified CPU"); \ exit(1); \ } \ @@ -176,54 +190,6 @@ extern int sched_getcpu(); } \ reservation_destroy(gettid(), rt_param.cpu); -#if MMDC_PROF -#define SAVE_RESULTS \ - if(jobs_complete >= maxJobs) {\ - fprintf(stderr, "Max jobs setting too small! Trying to record job #%d when we only have space for %d jobs. Exiting...\n", jobs_complete, maxJobs);\ - exit(1);\ - }\ - if(jobs_complete > -1 && output) {\ - progTime[jobs_complete] = _end.tv_sec - _start.tv_sec;\ - progTime[jobs_complete] *= 1000000000;\ - progTime[jobs_complete] += _end.tv_nsec - _start.tv_nsec;\ - mmdc_read[jobs_complete] = mmdc_res.read_bytes;\ - mmdc_write[jobs_complete] = mmdc_res.write_bytes;\ - } -#else -#define SAVE_RESULTS \ - if(jobs_complete >= maxJobs) {\ - fprintf(stderr, "Max jobs setting too small! Trying to record job #%d when we only have space for %d jobs. Exiting...\n", jobs_complete, maxJobs);\ - exit(1);\ - }\ - if(jobs_complete > -1 && output) {\ - progTime[jobs_complete] = _end.tv_sec - _start.tv_sec;\ - progTime[jobs_complete] *= 1000000000;\ - progTime[jobs_complete] += _end.tv_nsec - _start.tv_nsec;\ - } -#endif - - -#if LITMUS -#define WRITE_TO_FILE WRITE_TO_FILE_ITRNL CLEANUP_LITMUS -#else -#define WRITE_TO_FILE WRITE_TO_FILE_ITRNL -#endif - -#define WRITE_TO_FILE_ITRNL if (output){\ - munlockall();\ - FILE *fp=fopen(fileName, "a");\ - if (fp == NULL) {\ - perror("Unable to open output file");\ - exit(1);\ - }\ - for (int i = 0; i <= jobs_complete; i++){\ - fprintf(fp, "%s none %u none %d %.f %s %d %.f %.f \n",\ - thisProgram, thisCore, maxJobs,\ - progTime[i], runID, i, mmdc_read[i], mmdc_write[i]);\ - }\ - fclose(fp);\ -} - #if __arm__ // On ARM, manually flush the cache #define FLUSH_CACHES \ @@ -247,53 +213,138 @@ extern int sched_getcpu(); fclose(fp); #endif +// Buffer timing result from a single job +static void _rt_save_job_result() { + if (_rt_jobs_complete >= _rt_max_jobs) { + fprintf(stderr, "Max jobs setting too small! Trying to record job #%ld when we only have space for %ld jobs. Exiting...\n", _rt_jobs_complete, _rt_max_jobs); + exit(1); + } + if (_rt_jobs_complete > -1 && _rt_will_output) { + _rt_exec_time[_rt_jobs_complete] = _rt_end.tv_sec - _rt_start.tv_sec; + _rt_exec_time[_rt_jobs_complete] *= 1e9; + _rt_exec_time[_rt_jobs_complete] += _rt_end.tv_nsec - _rt_start.tv_nsec; #if MMDC_PROF -#define START_TIMER \ - /* This disables profiling, resets the counters, clears the overflow bit, and enables profiling */ \ - start_mmdc_profiling(mmdc); \ - /*nanosleep(&(struct timespec){0, ms2ns(999)}, NULL);*/ \ - clock_gettime(CLOCK_MONOTONIC, &_start); -#else -#define START_TIMER clock_gettime(CLOCK_MONOTONIC, &_start); + _rt_mmdc_read[_rt_jobs_complete] = mmdc_res.read_bytes; + _rt_mmdc_write[_rt_jobs_complete] = mmdc_res.write_bytes; #endif + } +} +// Save all buffered timing results to disk +static void _rt_write_to_file() { + char fileName[_RT_FILENAME_LEN]; + FILE *fp; + if (!_rt_will_output) + return; + munlockall(); + strcpy(fileName, _rt_run_id); + strcat(fileName, ".txt"); + fp = fopen(fileName, "a"); + if (fp == NULL) { + perror("Unable to open _rt_will_output file"); + exit(1); + } + // Same format as the paired results with "none" for unused fields + for (int i = 0; i < _rt_jobs_complete; i++){ + fprintf(fp, "%s none %u none %ld %.f %s %d %.f %.f \n", + _rt_our_prog_name, _rt_core, _rt_max_jobs, + _rt_exec_time[i], _rt_run_id, i, #if MMDC_PROF -#define STOP_TIMER \ - clock_gettime(CLOCK_MONOTONIC, &_end); \ - /* This freezes the profiling and makes results available */ \ - pause_mmdc_profiling(mmdc); \ - get_mmdc_profiling_results(mmdc, &mmdc_res); + _rt_mmdc_read[i], _rt_mmdc_write[i]); #else -#define STOP_TIMER clock_gettime(CLOCK_MONOTONIC, &_end); + 0.0, 0.0); #endif + } + fclose(fp); +#if LITMUS + CLEANUP_LITMUS +#endif +} -#define SLEEP nanosleep((const struct timespec[]){{0, 1000000}}, NULL); - +// Start a job +static void _rt_start_loop() { #if LITMUS -#define START_LOOP \ - if (sleep_next_period() != 0) { \ - perror("Unable to sleep for next period"); \ - } \ - FLUSH_CACHES START_TIMER + if (sleep_next_period() != 0) { + perror("Unable to sleep for next period"); + } #else -#define START_LOOP sched_yield(); FLUSH_CACHES START_TIMER + sched_yield(); #endif + FLUSH_CACHES +#if MMDC_PROF + /* This disables profiling, resets the counters, clears the overflow bit, and enables profiling */ + start_mmdc_profiling(mmdc); +#endif + clock_gettime(CLOCK_MONOTONIC, &_rt_start); +} -#define STOP_LOOP STOP_TIMER jobs_complete++; SAVE_RESULTS - +// Complete a job +static void _rt_stop_loop() { + clock_gettime(CLOCK_MONOTONIC, &_rt_end); +#if MMDC_PROF + /* This freezes the profiling and makes results available */ + pause_mmdc_profiling(mmdc); + get_mmdc_profiling_results(mmdc, &mmdc_res); +#endif + _rt_save_job_result(); + _rt_jobs_complete++; +} -/* -Intended structure +/****** New API ****** + * Intended structure: + * + * |int main(int argc, char **argv) { + * | SET_UP + * | ... + * | for_each_job { + * | tacleInit(); + * | tacleMain(); + * | } + * | WRITE_TO_FILE + * |} + * + * The main() function must call its parameters argc and argv for SET_UP to be + * able to read them. + * Only SET_UP necessarily has to be in main(). + * + * We use some niche C features, here's a quick explaination: + * 1. The && operator doesn't evaluate the right-hand side of the expression + * unless the left side evaluated to true. We use this to only execute + * _rt_start_loop() when the loop will actually run. + * 2. The comma operator executes the first expression and then throws away the + * result. We use this to call our void function from inside a comparison. + */ +#define for_each_job \ + for (; _rt_jobs_complete < _rt_max_jobs && (_rt_start_loop(),1); \ + _rt_stop_loop()) -main -SET_UP -notice that STOP LOOP negates the ++ if outout=0 -for (jobsComplete=0; jobsComplete