aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBjoern Brandenburg <bbb@mpi-sws.org>2016-03-23 18:22:01 -0400
committerBjoern Brandenburg <bbb@mpi-sws.org>2016-03-23 18:22:01 -0400
commitb06db0322429df80ee0fe09d257520f0c5b71901 (patch)
treea13f5669e2334737a611e03057c696ad0c7c13fd /src
parent37aea5e4e06d30267c01ae3d078be34fb07fcfd3 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/eheap.c38
-rw-r--r--src/job_stats.c220
-rw-r--r--src/load.c236
-rw-r--r--src/stdump.c98
-rw-r--r--src/util.c147
5 files changed, 739 insertions, 0 deletions
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 @@
1#include <stdlib.h>
2
3#include "sched_trace.h"
4#include "eheap.h"
5
6int earlier_event(struct heap_node* _a, struct heap_node* _b)
7{
8 struct st_event_record *a, *b;
9 a = heap_node_value(_a);
10 b = heap_node_value(_b);
11 if (event_time(a) == 0 && event_time(b) == 0)
12 /* tie break by PID for consistent ordering */
13 return a->hdr.pid < b->hdr.pid;
14 else
15 return event_time(a) < event_time(b);
16}
17
18
19struct heap* heapify_events(struct st_event_record *ev, unsigned int count)
20{
21 struct heap_node* hn;
22 struct heap* h;
23 h = malloc(sizeof(struct heap));
24 hn = malloc(sizeof(struct heap_node) * count);
25 if (!hn || !h)
26 return NULL;
27 heap_init(h);
28 while (count) {
29 heap_node_init(hn, ev);
30 heap_insert(earlier_event, h, hn);
31 hn++;
32 ev++;
33 count--;
34 }
35 return h;
36}
37
38
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 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <string.h>
5
6#include "load.h"
7#include "sched_trace.h"
8#include "eheap.h"
9
10/* limit search window in case of missing completions */
11#define MAX_COMPLETIONS_TO_CHECK 20
12
13int want_ms = 0;
14
15static double nano_to_ms(int64_t ns)
16{
17 return ns * 1E-6;
18}
19
20static void print_stats(
21 struct task* t,
22 struct st_event_record *release,
23 struct st_event_record *completion)
24{
25 int64_t lateness;
26 u64 response;
27
28 lateness = completion->data.completion.when;
29 lateness -= release->data.release.deadline;
30 response = completion->data.completion.when;
31 response -= release->data.release.release;
32
33 if (want_ms)
34 printf(" %5u, %5u, %10.2f, %10.2f, %8d, %10.2f, %10.2f, %7d"
35 ", %10.2f\n",
36 release->hdr.pid,
37 release->hdr.job,
38 nano_to_ms(per(t)),
39 nano_to_ms(response),
40 lateness > 0,
41 nano_to_ms(lateness),
42 lateness > 0 ? nano_to_ms(lateness) : 0,
43 completion->data.completion.forced,
44 nano_to_ms(completion->data.completion.exec_time));
45 else
46 printf(" %5u, %5u, %10llu, %10llu, %8d, %10lld, %10lld, %7d"
47 ", %10llu\n",
48 release->hdr.pid,
49 release->hdr.job,
50 (unsigned long long) per(t),
51 (unsigned long long) response,
52 lateness > 0,
53 (long long) lateness,
54 lateness > 0 ? (long long) lateness : 0,
55 completion->data.completion.forced,
56 (unsigned long long) completion->data.completion.exec_time);
57}
58
59static void print_task_info(struct task *t)
60{
61 if (want_ms)
62 printf("# task NAME=%s PID=%d COST=%.2f PERIOD=%.2f CPU=%d\n",
63 tsk_name(t),
64 t->pid,
65 nano_to_ms(exe(t)),
66 nano_to_ms(per(t)),
67 tsk_cpu(t));
68 else
69 printf("# task NAME=%s PID=%d COST=%lu PERIOD=%lu CPU=%d\n",
70 tsk_name(t),
71 t->pid,
72 (unsigned long) exe(t),
73 (unsigned long) per(t),
74 tsk_cpu(t));
75}
76
77static void usage(const char *str)
78{
79 fprintf(stderr,
80 "\n USAGE\n"
81 "\n"
82 " st_job_stats [opts] <file.st>+\n"
83 "\n"
84 " OPTIONS\n"
85 " -r -- skip jobs prior to task-system release\n"
86 " -m -- output milliseconds (default: nanoseconds)\n"
87 " -p PID -- show only data for the task with the given PID\n"
88 " -n NAME -- show only data for the task(s) with the given NAME\n"
89 " -t PERIOD -- show only data for the task(s) with the given PERIOD\n"
90 "\n\n"
91 );
92 if (str) {
93 fprintf(stderr, "Aborted: %s\n", str);
94 exit(1);
95 } else {
96 exit(0);
97 }
98}
99
100#define OPTSTR "rmp:n:t:h"
101
102int main(int argc, char** argv)
103{
104 unsigned int count;
105 struct heap *h;
106
107 struct task *t;
108 struct evlink *e, *pos;
109 struct st_event_record *rec;
110
111 int wait_for_release = 0;
112 u64 sys_release = 0;
113
114 unsigned int pid_filter = 0;
115 const char* name_filter = 0;
116 u32 period_filter = 0;
117
118 int opt;
119
120 while ((opt = getopt(argc, argv, OPTSTR)) != -1) {
121 switch (opt) {
122 case 'r':
123 wait_for_release = 1;
124 break;
125 case 'm':
126 want_ms = 1;
127 break;
128 case 'p':
129 pid_filter = atoi(optarg);
130 if (!pid_filter)
131 usage("Invalid PID.");
132 break;
133 case 't':
134 period_filter = atoi(optarg);
135 if (!period_filter)
136 usage("Invalid period.");
137 break;
138 case 'n':
139 name_filter = optarg;
140 break;
141 case 'h':
142 usage(NULL);
143 break;
144 case ':':
145 usage("Argument missing.");
146 break;
147 case '?':
148 default:
149 usage("Bad argument.");
150 break;
151 }
152 }
153
154 if (want_ms)
155 period_filter *= 1000000; /* ns per ms */
156
157 h = load(argv + optind, argc - optind, &count);
158 if (!h)
159 return 1;
160
161 init_tasks();
162 split(h, count, 1);
163
164 if (wait_for_release) {
165 rec = find_sys_event(ST_SYS_RELEASE);
166 if (rec)
167 sys_release = rec->data.sys_release.release;
168 else {
169 fprintf(stderr, "Could not find task system "
170 "release time.\n");
171 exit(1);
172 }
173 }
174
175 /* print header */
176 printf("#%5s, %5s, %10s, %10s, %8s, %10s, %10s, %7s, %10s\n",
177 "Task",
178 "Job",
179 "Period",
180 "Response",
181 "DL Miss?",
182 "Lateness",
183 "Tardiness",
184 "Forced?",
185 "ACET");
186
187 /* print stats for each task */
188 for_each_task(t) {
189 if (pid_filter && pid_filter != t->pid)
190 continue;
191 if (name_filter && strcmp(tsk_name(t), name_filter))
192 continue;
193 if (period_filter && period_filter != per(t))
194 continue;
195
196 print_task_info(t);
197 for_each_event(t, e) {
198 rec = e->rec;
199 if (rec->hdr.type == ST_RELEASE &&
200 (!wait_for_release ||
201 rec->data.release.release >= sys_release)) {
202 pos = e;
203 count = 0;
204 while (pos && count < MAX_COMPLETIONS_TO_CHECK) {
205 find(pos, ST_COMPLETION);
206 if (pos->rec->hdr.job == rec->hdr.job) {
207 print_stats(t, rec, pos->rec);
208 break;
209 } else {
210 pos = pos->next;
211 count++;
212 }
213 }
214
215 }
216 }
217 }
218
219 return 0;
220}
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 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <sys/mman.h>
4#include <fcntl.h>
5#include <unistd.h>
6
7#include <stdio.h>
8#include <stdlib.h>
9
10#include "sched_trace.h"
11#include "eheap.h"
12#include "load.h"
13
14static int map_file(const char* filename, void **addr, size_t *size)
15{
16 struct stat info;
17 int error = 0;
18 int fd;
19
20 error = stat(filename, &info);
21 if (!error) {
22 *size = info.st_size;
23 if (info.st_size > 0) {
24 fd = open(filename, O_RDONLY);
25 if (fd >= 0) {
26 *addr = mmap(NULL, *size,
27 PROT_READ | PROT_WRITE,
28 MAP_PRIVATE, fd, 0);
29 if (*addr == MAP_FAILED)
30 error = -1;
31 close(fd);
32 } else
33 error = fd;
34 } else
35 *addr = NULL;
36 }
37 return error;
38}
39
40int map_trace(const char *name, void **start, void **end, size_t *size)
41{
42 int ret;
43
44 ret = map_file(name, start, size);
45 if (!ret)
46 *end = *start + *size;
47 return ret;
48}
49
50
51static struct heap* heap_from_file(char* file, unsigned int* count)
52{
53 size_t s;
54 struct st_event_record *rec, *end;
55 if (map_trace(file, (void**) &rec, (void**) &end, &s) == 0) {
56 *count = ((unsigned int)((char*) end - (char*) rec))
57 / sizeof(struct st_event_record);
58 return heapify_events(rec, *count);
59 } else
60 fprintf(stderr, "mmap: %m (%s)\n", file);
61 return NULL;
62}
63
64struct heap* load(char **files, int no_files, unsigned int *count)
65{
66 int i;
67 unsigned int c;
68 struct heap *h = NULL, *h2;
69 *count = 0;
70 for (i = 0; i < no_files; i++) {
71 h2 = heap_from_file(files[i], &c);
72 if (!h2)
73 /* potential memory leak, don't care */
74 return NULL;
75 if (h)
76 heap_union(earlier_event, h, h2);
77 else
78 h = h2;
79 *count += c;
80 }
81 return h;
82}
83
84
85
86struct task tasks[MAX_TASKS];
87struct evlink *sys_events = NULL;
88struct evlink **sys_next = &sys_events;
89u64 time0 = 0;
90u32 g_max_task = MAX_TASKS;
91u32 g_min_task = 0;
92
93void init_tasks(void)
94{
95 int i;
96
97 for (i = 0; i < MAX_TASKS; i++) {
98 tasks[i] = (struct task) {0, 0, NULL, NULL, NULL, NULL};
99 tasks[i].next = &tasks[i].events;
100 }
101}
102
103void crop_events(struct task* t, double min, double max)
104{
105 struct evlink **p;
106 double time;
107 p = &t->events;
108 while (*p) {
109 time = evtime((*p)->rec);
110 if (time < min || time > max)
111 *p = (*p)->next;
112 else
113 p = &(*p)->next;
114 }
115}
116
117void crop_events_all(double min, double max)
118{
119 struct task* t;
120 for_each_task(t)
121 crop_events(t, min, max);
122}
123
124struct task* by_pid(int pid)
125{
126 struct task* t;
127 if (!pid)
128 return NULL;
129 /* slow, don't care for now */
130 for (t = tasks; t < tasks + MAX_TASKS; t++) {
131 if (!t->pid) /* end, allocate */
132 t->pid = pid;
133 if (t->pid == pid)
134 return t;
135 }
136 return NULL;
137}
138
139u32 count_tasks(void)
140
141{
142 struct task* t;
143 u32 i = 0;
144 for_each_task(t)
145 i++;
146 return i;
147}
148
149
150void split(struct heap* h, unsigned int count, int find_time0)
151{
152 struct evlink *lnk = malloc(count * sizeof(struct evlink));
153 struct heap_node *hn;
154 u64 time;
155 struct st_event_record *rec;
156 struct task* t;
157
158 if (!lnk) {
159 perror("malloc");
160 return;
161 }
162
163 while ((hn = heap_take(earlier_event, h))) {
164 rec = heap_node_value(hn);
165 time = event_time(rec);
166 if (find_time0 && !time0 && time)
167 time0 = time;
168 t = by_pid(rec->hdr.pid);
169 switch (rec->hdr.type) {
170 case ST_PARAM:
171 if (t)
172 t->param = rec;
173 else
174 fprintf(stderr, "Dropped ST_PARAM record "
175 "for PID %d.\n", rec->hdr.pid);
176 break;
177 case ST_NAME:
178 if (t)
179 t->name = rec;
180 else
181 fprintf(stderr, "Dropped ST_NAME record "
182 "for PID %d.\n", rec->hdr.pid);
183 break;
184 default:
185 lnk->rec = rec;
186 lnk->next = NULL;
187 if (t) {
188 *(t->next) = lnk;
189 t->next = &lnk->next;
190 t->no_events++;
191 } else {
192 *(sys_next) = lnk;
193 sys_next = &lnk->next;
194 }
195 lnk++;
196 break;
197 }
198 }
199}
200
201int tsk_cpu(struct task *t)
202{
203 if (t->param)
204 return t->param->data.param.partition;
205 else
206 return -1;
207}
208
209const char* tsk_name(struct task* t)
210{
211 if (t->name)
212 return t->name->data.name.cmd;
213 else
214 return "<unknown>";
215}
216
217u32 per(struct task* t)
218{
219 if (t->param)
220 return t->param->data.param.period;
221 else
222 return 0;
223}
224
225u32 exe(struct task* t)
226{
227 if (t->param)
228 return t->param->data.param.wcet;
229 else
230 return 0;
231}
232
233u32 idx(struct task* t)
234{
235 return (t - tasks);
236}
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 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4
5#include "load.h"
6#include "sched_trace.h"
7#include "eheap.h"
8
9static void usage(const char *str)
10{
11 fprintf(stderr,
12 "\n USAGE\n"
13 "\n"
14 " stdump [opts] <file.st>+\n"
15 "\n"
16 " OPTIONS\n"
17 " -r -- find task system release and exit\n"
18 " -f -- use first non-zero event as system release\n"
19 " if no system release event is found\n"
20 " -c -- display a count of the number of events\n"
21 "\n\n"
22 );
23 if (str) {
24 fprintf(stderr, "Aborted: %s\n", str);
25 exit(1);
26 } else {
27 exit(0);
28 }
29}
30
31#define OPTSTR "rcfh"
32
33int main(int argc, char** argv)
34{
35 unsigned int count;
36 struct heap *h;
37 struct heap_node *hn, *first = NULL;
38 u64 time;
39 struct st_event_record *rec;
40 int find_release = 0;
41 int show_count = 0;
42 int use_first_nonzero = 0;
43 int opt;
44
45 while ((opt = getopt(argc, argv, OPTSTR)) != -1) {
46 switch (opt) {
47 case 'r':
48 find_release = 1;
49 break;
50 case 'c':
51 show_count = 1;
52 break;
53 case 'f':
54 use_first_nonzero = 1;
55 break;
56 case 'h':
57 usage(NULL);
58 break;
59 case ':':
60 usage("Argument missing.");
61 break;
62 case '?':
63 default:
64 usage("Bad argument.");
65 break;
66 }
67 }
68
69
70 h = load(argv + optind, argc - optind, &count);
71 if (!h)
72 return 1;
73 if (show_count)
74 printf("Loaded %u events.\n", count);
75 while ((hn = heap_take(earlier_event, h))) {
76 time = event_time(heap_node_value(hn));
77 if (time != 0 && !first)
78 first = hn;
79 time /= 1000000; /* convert to milliseconds */
80 if (!find_release) {
81 printf("[%10llu] ", (unsigned long long) time);
82 print_event(heap_node_value(hn));
83 } else {
84 rec = heap_node_value(hn);
85 if (rec->hdr.type == ST_SYS_RELEASE) {
86 printf("%6.2fms\n", rec->data.raw[1] / 1000000.0);
87 find_release = 0;
88 break;
89 }
90 }
91 }
92 if (find_release && use_first_nonzero && first) {
93 rec = heap_node_value(first);
94 printf("%6.2fms\n", event_time(rec) / 1000000.0);
95 }
96
97 return 0;
98}
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 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <assert.h>
4
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <sys/mman.h>
8#include <fcntl.h>
9#include <unistd.h>
10
11#include "sched_trace.h"
12
13static const char* event_names[] = {
14 "INVALID",
15 "NAME",
16 "PARAM",
17 "RELEASE",
18 "ASSIGNED",
19 "SWITCH_TO",
20 "SWITCH_FROM",
21 "COMPLETION",
22 "BLOCK",
23 "RESUME",
24 "ACTION",
25 "SYS_RELEASE",
26 "NP_ENTER",
27 "NP_EXIT",
28 "INVALID"
29};
30
31const char* event2name(unsigned int id)
32{
33 if (id >= ST_INVALID)
34 id = ST_INVALID;
35 return event_names[id];
36}
37
38
39u64 event_time(struct st_event_record* rec)
40{
41 u64 when;
42 switch (rec->hdr.type) {
43 /* the time stamp is encoded in the first payload u64 */
44 case ST_RELEASE:
45 case ST_ASSIGNED:
46 case ST_SWITCH_TO:
47 case ST_SWITCH_AWAY:
48 case ST_COMPLETION:
49 case ST_BLOCK:
50 case ST_RESUME:
51 case ST_NP_ENTER:
52 case ST_NP_EXIT:
53 case ST_ACTION:
54 case ST_SYS_RELEASE:
55 when = rec->data.raw[0];
56 break;
57 default:
58 /* stuff that doesn't have a time stamp should occur "early" */
59 when = 0;
60 break;
61 };
62 return when;
63}
64
65void print_header(struct st_trace_header* hdr)
66{
67 printf("%-14s %5u/%-5u on CPU%3u ",
68 event2name(hdr->type),
69 hdr->pid, hdr->job,
70 hdr->cpu);
71}
72
73typedef void (*print_t)(struct st_event_record* rec);
74
75static void print_nothing(struct st_event_record* _)
76{
77}
78
79static void print_raw(struct st_event_record* rec)
80{
81 printf(" type=%u", rec->hdr.type);
82}
83
84static void print_name(struct st_event_record* rec)
85{
86 /* terminate in all cases */
87 rec->data.name.cmd[ST_NAME_LEN - 1] = 0;
88 printf("%s", rec->data.name.cmd);
89}
90
91static void print_param(struct st_event_record* rec)
92{
93 printf("T=(cost:%6.2fms, period:%6.2fms, phase:%6.2fms), part=%d",
94 rec->data.param.wcet / 1000000.0,
95 rec->data.param.period / 1000000.0,
96 rec->data.param.phase / 1000000.0,\
97 rec->data.param.partition);
98}
99
100static void print_time_data2(struct st_event_record* rec)
101{
102 printf("%6.2fms", rec->data.raw[1] / 1000000.0);
103}
104
105static void print_action(struct st_event_record* rec)
106{
107 printf(" action=%u", rec->data.action.action);
108}
109
110static print_t print_detail[] = {
111 print_raw, /* invalid */
112 print_name, /* NAME */
113 print_param, /* PARAM */
114 print_time_data2, /* RELEASE */
115 print_nothing, /* ASSIGNED */
116 print_nothing, /* SWITCH_TO */
117 print_nothing, /* SWITCH_FROM */
118 print_nothing, /* COMPLETION */
119 print_nothing, /* BLOCK */
120 print_nothing, /* RESUME */
121 print_action, /* ACTION */
122 print_time_data2, /* SYS_RELEASE */
123 print_nothing, /* NP_ENTER */
124 print_nothing, /* NP_EXIT */
125 print_raw, /* invalid */
126};
127
128void print_event(struct st_event_record *rec)
129{
130 unsigned int id = rec->hdr.type;
131
132 /* ensure there's a handler for each ID */
133 assert(sizeof(print_detail) / sizeof(print_detail[0]) == ST_INVALID + 1);
134
135 if (id >= ST_INVALID)
136 id = ST_INVALID;
137 print_header(&rec->hdr);
138 print_detail[id](rec);
139 printf("\n");
140}
141
142void print_all(struct st_event_record *rec, unsigned int count)
143{
144 unsigned int i;
145 for (i = 0; i < count; i++)
146 print_event(&rec[i]);
147}