#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 count_preemptions( struct evlink *release, struct evlink *completion, unsigned int *preemptions, unsigned int *migrations) { struct evlink *e; int seen_switched_away = 0; u8 last_cpu = 0; for (e = release; e != completion; e = e->next) { if (e->rec->hdr.job == release->rec->hdr.job) { switch (e->rec->hdr.type) { case ST_SWITCH_AWAY: seen_switched_away = 1; last_cpu = e->rec->hdr.cpu; break; case ST_SWITCH_TO: if (seen_switched_away) (*preemptions)++; if (seen_switched_away && e->rec->hdr.cpu != last_cpu) (*migrations)++; break; default: break; } } } } static void print_stats( struct task* t, struct evlink *release, struct evlink *completion) { int64_t lateness; u64 response; unsigned int preemptions = 0, migrations = 0; lateness = completion->rec->data.completion.when; lateness -= release->rec->data.release.deadline; response = completion->rec->data.completion.when; response -= release->rec->data.release.release; count_preemptions(release, completion, &preemptions, &migrations); if (want_ms) printf(" %5u, %5u, %10.2f, %10.2f, %8d, %10.2f, %10.2f, %7d" ", %10.2f, %12u, %12u\n", release->rec->hdr.pid, release->rec->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->rec->data.completion.forced, nano_to_ms(completion->rec->data.completion.exec_time), preemptions, migrations); else printf(" %5u, %5u, %10llu, %10llu, %8d, %10lld, %10lld, %7d" ", %10llu, %12u, %12u\n", release->rec->hdr.pid, release->rec->hdr.job, (unsigned long long) per(t), (unsigned long long) response, lateness > 0, (long long) lateness, lateness > 0 ? (long long) lateness : 0, completion->rec->data.completion.forced, (unsigned long long) completion->rec->data.completion.exec_time, preemptions, migrations); } 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, %12s, %12s\n", "Task", "Job", "Period", "Response", "DL Miss?", "Lateness", "Tardiness", "Forced?", "ACET", "Preemptions", "Migrations"); /* 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, e, pos); break; } else { pos = pos->next; count++; } } } } } return 0; }