From b06db0322429df80ee0fe09d257520f0c5b71901 Mon Sep 17 00:00:00 2001 From: Bjoern Brandenburg Date: Wed, 23 Mar 2016 23:22:01 +0100 Subject: Port st-dump and st-job-stats Include st-dump (formerly 'st_show') and st-job-stats (formerly 'st_job_stats') from https://github.com/brandenburg/sched-trace-tools in this repository. --- src/eheap.c | 38 +++++++++ src/job_stats.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/load.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/stdump.c | 98 +++++++++++++++++++++++ src/util.c | 147 +++++++++++++++++++++++++++++++++++ 5 files changed, 739 insertions(+) create mode 100644 src/eheap.c create mode 100644 src/job_stats.c create mode 100644 src/load.c create mode 100644 src/stdump.c create mode 100644 src/util.c (limited to 'src') diff --git a/src/eheap.c b/src/eheap.c new file mode 100644 index 0000000..7fe9a6f --- /dev/null +++ b/src/eheap.c @@ -0,0 +1,38 @@ +#include + +#include "sched_trace.h" +#include "eheap.h" + +int earlier_event(struct heap_node* _a, struct heap_node* _b) +{ + struct st_event_record *a, *b; + a = heap_node_value(_a); + b = heap_node_value(_b); + if (event_time(a) == 0 && event_time(b) == 0) + /* tie break by PID for consistent ordering */ + return a->hdr.pid < b->hdr.pid; + else + return event_time(a) < event_time(b); +} + + +struct heap* heapify_events(struct st_event_record *ev, unsigned int count) +{ + struct heap_node* hn; + struct heap* h; + h = malloc(sizeof(struct heap)); + hn = malloc(sizeof(struct heap_node) * count); + if (!hn || !h) + return NULL; + heap_init(h); + while (count) { + heap_node_init(hn, ev); + heap_insert(earlier_event, h, hn); + hn++; + ev++; + count--; + } + return h; +} + + diff --git a/src/job_stats.c b/src/job_stats.c new file mode 100644 index 0000000..156be60 --- /dev/null +++ b/src/job_stats.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include + +#include "load.h" +#include "sched_trace.h" +#include "eheap.h" + +/* limit search window in case of missing completions */ +#define MAX_COMPLETIONS_TO_CHECK 20 + +int want_ms = 0; + +static double nano_to_ms(int64_t ns) +{ + return ns * 1E-6; +} + +static void print_stats( + struct task* t, + struct st_event_record *release, + struct st_event_record *completion) +{ + int64_t lateness; + u64 response; + + lateness = completion->data.completion.when; + lateness -= release->data.release.deadline; + response = completion->data.completion.when; + response -= release->data.release.release; + + if (want_ms) + printf(" %5u, %5u, %10.2f, %10.2f, %8d, %10.2f, %10.2f, %7d" + ", %10.2f\n", + release->hdr.pid, + release->hdr.job, + nano_to_ms(per(t)), + nano_to_ms(response), + lateness > 0, + nano_to_ms(lateness), + lateness > 0 ? nano_to_ms(lateness) : 0, + completion->data.completion.forced, + nano_to_ms(completion->data.completion.exec_time)); + else + printf(" %5u, %5u, %10llu, %10llu, %8d, %10lld, %10lld, %7d" + ", %10llu\n", + release->hdr.pid, + release->hdr.job, + (unsigned long long) per(t), + (unsigned long long) response, + lateness > 0, + (long long) lateness, + lateness > 0 ? (long long) lateness : 0, + completion->data.completion.forced, + (unsigned long long) completion->data.completion.exec_time); +} + +static void print_task_info(struct task *t) +{ + if (want_ms) + printf("# task NAME=%s PID=%d COST=%.2f PERIOD=%.2f CPU=%d\n", + tsk_name(t), + t->pid, + nano_to_ms(exe(t)), + nano_to_ms(per(t)), + tsk_cpu(t)); + else + printf("# task NAME=%s PID=%d COST=%lu PERIOD=%lu CPU=%d\n", + tsk_name(t), + t->pid, + (unsigned long) exe(t), + (unsigned long) per(t), + tsk_cpu(t)); +} + +static void usage(const char *str) +{ + fprintf(stderr, + "\n USAGE\n" + "\n" + " st_job_stats [opts] +\n" + "\n" + " OPTIONS\n" + " -r -- skip jobs prior to task-system release\n" + " -m -- output milliseconds (default: nanoseconds)\n" + " -p PID -- show only data for the task with the given PID\n" + " -n NAME -- show only data for the task(s) with the given NAME\n" + " -t PERIOD -- show only data for the task(s) with the given PERIOD\n" + "\n\n" + ); + if (str) { + fprintf(stderr, "Aborted: %s\n", str); + exit(1); + } else { + exit(0); + } +} + +#define OPTSTR "rmp:n:t:h" + +int main(int argc, char** argv) +{ + unsigned int count; + struct heap *h; + + struct task *t; + struct evlink *e, *pos; + struct st_event_record *rec; + + int wait_for_release = 0; + u64 sys_release = 0; + + unsigned int pid_filter = 0; + const char* name_filter = 0; + u32 period_filter = 0; + + int opt; + + while ((opt = getopt(argc, argv, OPTSTR)) != -1) { + switch (opt) { + case 'r': + wait_for_release = 1; + break; + case 'm': + want_ms = 1; + break; + case 'p': + pid_filter = atoi(optarg); + if (!pid_filter) + usage("Invalid PID."); + break; + case 't': + period_filter = atoi(optarg); + if (!period_filter) + usage("Invalid period."); + break; + case 'n': + name_filter = optarg; + break; + case 'h': + usage(NULL); + break; + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + + if (want_ms) + period_filter *= 1000000; /* ns per ms */ + + h = load(argv + optind, argc - optind, &count); + if (!h) + return 1; + + init_tasks(); + split(h, count, 1); + + if (wait_for_release) { + rec = find_sys_event(ST_SYS_RELEASE); + if (rec) + sys_release = rec->data.sys_release.release; + else { + fprintf(stderr, "Could not find task system " + "release time.\n"); + exit(1); + } + } + + /* print header */ + printf("#%5s, %5s, %10s, %10s, %8s, %10s, %10s, %7s, %10s\n", + "Task", + "Job", + "Period", + "Response", + "DL Miss?", + "Lateness", + "Tardiness", + "Forced?", + "ACET"); + + /* print stats for each task */ + for_each_task(t) { + if (pid_filter && pid_filter != t->pid) + continue; + if (name_filter && strcmp(tsk_name(t), name_filter)) + continue; + if (period_filter && period_filter != per(t)) + continue; + + print_task_info(t); + for_each_event(t, e) { + rec = e->rec; + if (rec->hdr.type == ST_RELEASE && + (!wait_for_release || + rec->data.release.release >= sys_release)) { + pos = e; + count = 0; + while (pos && count < MAX_COMPLETIONS_TO_CHECK) { + find(pos, ST_COMPLETION); + if (pos->rec->hdr.job == rec->hdr.job) { + print_stats(t, rec, pos->rec); + break; + } else { + pos = pos->next; + count++; + } + } + + } + } + } + + return 0; +} diff --git a/src/load.c b/src/load.c new file mode 100644 index 0000000..994ac6d --- /dev/null +++ b/src/load.c @@ -0,0 +1,236 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "sched_trace.h" +#include "eheap.h" +#include "load.h" + +static int map_file(const char* filename, void **addr, size_t *size) +{ + struct stat info; + int error = 0; + int fd; + + error = stat(filename, &info); + if (!error) { + *size = info.st_size; + if (info.st_size > 0) { + fd = open(filename, O_RDONLY); + if (fd >= 0) { + *addr = mmap(NULL, *size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE, fd, 0); + if (*addr == MAP_FAILED) + error = -1; + close(fd); + } else + error = fd; + } else + *addr = NULL; + } + return error; +} + +int map_trace(const char *name, void **start, void **end, size_t *size) +{ + int ret; + + ret = map_file(name, start, size); + if (!ret) + *end = *start + *size; + return ret; +} + + +static struct heap* heap_from_file(char* file, unsigned int* count) +{ + size_t s; + struct st_event_record *rec, *end; + if (map_trace(file, (void**) &rec, (void**) &end, &s) == 0) { + *count = ((unsigned int)((char*) end - (char*) rec)) + / sizeof(struct st_event_record); + return heapify_events(rec, *count); + } else + fprintf(stderr, "mmap: %m (%s)\n", file); + return NULL; +} + +struct heap* load(char **files, int no_files, unsigned int *count) +{ + int i; + unsigned int c; + struct heap *h = NULL, *h2; + *count = 0; + for (i = 0; i < no_files; i++) { + h2 = heap_from_file(files[i], &c); + if (!h2) + /* potential memory leak, don't care */ + return NULL; + if (h) + heap_union(earlier_event, h, h2); + else + h = h2; + *count += c; + } + return h; +} + + + +struct task tasks[MAX_TASKS]; +struct evlink *sys_events = NULL; +struct evlink **sys_next = &sys_events; +u64 time0 = 0; +u32 g_max_task = MAX_TASKS; +u32 g_min_task = 0; + +void init_tasks(void) +{ + int i; + + for (i = 0; i < MAX_TASKS; i++) { + tasks[i] = (struct task) {0, 0, NULL, NULL, NULL, NULL}; + tasks[i].next = &tasks[i].events; + } +} + +void crop_events(struct task* t, double min, double max) +{ + struct evlink **p; + double time; + p = &t->events; + while (*p) { + time = evtime((*p)->rec); + if (time < min || time > max) + *p = (*p)->next; + else + p = &(*p)->next; + } +} + +void crop_events_all(double min, double max) +{ + struct task* t; + for_each_task(t) + crop_events(t, min, max); +} + +struct task* by_pid(int pid) +{ + struct task* t; + if (!pid) + return NULL; + /* slow, don't care for now */ + for (t = tasks; t < tasks + MAX_TASKS; t++) { + if (!t->pid) /* end, allocate */ + t->pid = pid; + if (t->pid == pid) + return t; + } + return NULL; +} + +u32 count_tasks(void) + +{ + struct task* t; + u32 i = 0; + for_each_task(t) + i++; + return i; +} + + +void split(struct heap* h, unsigned int count, int find_time0) +{ + struct evlink *lnk = malloc(count * sizeof(struct evlink)); + struct heap_node *hn; + u64 time; + struct st_event_record *rec; + struct task* t; + + if (!lnk) { + perror("malloc"); + return; + } + + while ((hn = heap_take(earlier_event, h))) { + rec = heap_node_value(hn); + time = event_time(rec); + if (find_time0 && !time0 && time) + time0 = time; + t = by_pid(rec->hdr.pid); + switch (rec->hdr.type) { + case ST_PARAM: + if (t) + t->param = rec; + else + fprintf(stderr, "Dropped ST_PARAM record " + "for PID %d.\n", rec->hdr.pid); + break; + case ST_NAME: + if (t) + t->name = rec; + else + fprintf(stderr, "Dropped ST_NAME record " + "for PID %d.\n", rec->hdr.pid); + break; + default: + lnk->rec = rec; + lnk->next = NULL; + if (t) { + *(t->next) = lnk; + t->next = &lnk->next; + t->no_events++; + } else { + *(sys_next) = lnk; + sys_next = &lnk->next; + } + lnk++; + break; + } + } +} + +int tsk_cpu(struct task *t) +{ + if (t->param) + return t->param->data.param.partition; + else + return -1; +} + +const char* tsk_name(struct task* t) +{ + if (t->name) + return t->name->data.name.cmd; + else + return ""; +} + +u32 per(struct task* t) +{ + if (t->param) + return t->param->data.param.period; + else + return 0; +} + +u32 exe(struct task* t) +{ + if (t->param) + return t->param->data.param.wcet; + else + return 0; +} + +u32 idx(struct task* t) +{ + return (t - tasks); +} diff --git a/src/stdump.c b/src/stdump.c new file mode 100644 index 0000000..c67b2ad --- /dev/null +++ b/src/stdump.c @@ -0,0 +1,98 @@ +#include +#include +#include + +#include "load.h" +#include "sched_trace.h" +#include "eheap.h" + +static void usage(const char *str) +{ + fprintf(stderr, + "\n USAGE\n" + "\n" + " stdump [opts] +\n" + "\n" + " OPTIONS\n" + " -r -- find task system release and exit\n" + " -f -- use first non-zero event as system release\n" + " if no system release event is found\n" + " -c -- display a count of the number of events\n" + "\n\n" + ); + if (str) { + fprintf(stderr, "Aborted: %s\n", str); + exit(1); + } else { + exit(0); + } +} + +#define OPTSTR "rcfh" + +int main(int argc, char** argv) +{ + unsigned int count; + struct heap *h; + struct heap_node *hn, *first = NULL; + u64 time; + struct st_event_record *rec; + int find_release = 0; + int show_count = 0; + int use_first_nonzero = 0; + int opt; + + while ((opt = getopt(argc, argv, OPTSTR)) != -1) { + switch (opt) { + case 'r': + find_release = 1; + break; + case 'c': + show_count = 1; + break; + case 'f': + use_first_nonzero = 1; + break; + case 'h': + usage(NULL); + break; + case ':': + usage("Argument missing."); + break; + case '?': + default: + usage("Bad argument."); + break; + } + } + + + h = load(argv + optind, argc - optind, &count); + if (!h) + return 1; + if (show_count) + printf("Loaded %u events.\n", count); + while ((hn = heap_take(earlier_event, h))) { + time = event_time(heap_node_value(hn)); + if (time != 0 && !first) + first = hn; + time /= 1000000; /* convert to milliseconds */ + if (!find_release) { + printf("[%10llu] ", (unsigned long long) time); + print_event(heap_node_value(hn)); + } else { + rec = heap_node_value(hn); + if (rec->hdr.type == ST_SYS_RELEASE) { + printf("%6.2fms\n", rec->data.raw[1] / 1000000.0); + find_release = 0; + break; + } + } + } + if (find_release && use_first_nonzero && first) { + rec = heap_node_value(first); + printf("%6.2fms\n", event_time(rec) / 1000000.0); + } + + return 0; +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..f3d52f1 --- /dev/null +++ b/src/util.c @@ -0,0 +1,147 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "sched_trace.h" + +static const char* event_names[] = { + "INVALID", + "NAME", + "PARAM", + "RELEASE", + "ASSIGNED", + "SWITCH_TO", + "SWITCH_FROM", + "COMPLETION", + "BLOCK", + "RESUME", + "ACTION", + "SYS_RELEASE", + "NP_ENTER", + "NP_EXIT", + "INVALID" +}; + +const char* event2name(unsigned int id) +{ + if (id >= ST_INVALID) + id = ST_INVALID; + return event_names[id]; +} + + +u64 event_time(struct st_event_record* rec) +{ + u64 when; + switch (rec->hdr.type) { + /* the time stamp is encoded in the first payload u64 */ + case ST_RELEASE: + case ST_ASSIGNED: + case ST_SWITCH_TO: + case ST_SWITCH_AWAY: + case ST_COMPLETION: + case ST_BLOCK: + case ST_RESUME: + case ST_NP_ENTER: + case ST_NP_EXIT: + case ST_ACTION: + case ST_SYS_RELEASE: + when = rec->data.raw[0]; + break; + default: + /* stuff that doesn't have a time stamp should occur "early" */ + when = 0; + break; + }; + return when; +} + +void print_header(struct st_trace_header* hdr) +{ + printf("%-14s %5u/%-5u on CPU%3u ", + event2name(hdr->type), + hdr->pid, hdr->job, + hdr->cpu); +} + +typedef void (*print_t)(struct st_event_record* rec); + +static void print_nothing(struct st_event_record* _) +{ +} + +static void print_raw(struct st_event_record* rec) +{ + printf(" type=%u", rec->hdr.type); +} + +static void print_name(struct st_event_record* rec) +{ + /* terminate in all cases */ + rec->data.name.cmd[ST_NAME_LEN - 1] = 0; + printf("%s", rec->data.name.cmd); +} + +static void print_param(struct st_event_record* rec) +{ + printf("T=(cost:%6.2fms, period:%6.2fms, phase:%6.2fms), part=%d", + rec->data.param.wcet / 1000000.0, + rec->data.param.period / 1000000.0, + rec->data.param.phase / 1000000.0,\ + rec->data.param.partition); +} + +static void print_time_data2(struct st_event_record* rec) +{ + printf("%6.2fms", rec->data.raw[1] / 1000000.0); +} + +static void print_action(struct st_event_record* rec) +{ + printf(" action=%u", rec->data.action.action); +} + +static print_t print_detail[] = { + print_raw, /* invalid */ + print_name, /* NAME */ + print_param, /* PARAM */ + print_time_data2, /* RELEASE */ + print_nothing, /* ASSIGNED */ + print_nothing, /* SWITCH_TO */ + print_nothing, /* SWITCH_FROM */ + print_nothing, /* COMPLETION */ + print_nothing, /* BLOCK */ + print_nothing, /* RESUME */ + print_action, /* ACTION */ + print_time_data2, /* SYS_RELEASE */ + print_nothing, /* NP_ENTER */ + print_nothing, /* NP_EXIT */ + print_raw, /* invalid */ +}; + +void print_event(struct st_event_record *rec) +{ + unsigned int id = rec->hdr.type; + + /* ensure there's a handler for each ID */ + assert(sizeof(print_detail) / sizeof(print_detail[0]) == ST_INVALID + 1); + + if (id >= ST_INVALID) + id = ST_INVALID; + print_header(&rec->hdr); + print_detail[id](rec); + printf("\n"); +} + +void print_all(struct st_event_record *rec, unsigned int count) +{ + unsigned int i; + for (i = 0; i < count; i++) + print_event(&rec[i]); +} -- cgit v1.2.2