aboutsummaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--.gitignore2
-rw-r--r--Makefile10
-rw-r--r--include/eheap.h12
-rw-r--r--include/heap.h379
-rw-r--r--include/load.h92
-rw-r--r--include/sched_trace.h149
-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
11 files changed, 1382 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 8cdbb63..44c51a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,5 @@ ftcat
10ft2csv 10ft2csv
11ftdump 11ftdump
12ftsort 12ftsort
13st-dump
14st-job-stats
diff --git a/Makefile b/Makefile
index 246dd7c..3fcfc38 100644
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ CPPFLAGS += -Iinclude/
20# ############################################################################## 20# ##############################################################################
21# Targets 21# Targets
22 22
23all = ftcat ft2csv ftdump ftsort 23all = ftcat ft2csv ftdump ftsort st-dump st-job-stats
24 24
25.PHONY: all clean 25.PHONY: all clean
26all: ${all} 26all: ${all}
@@ -39,5 +39,13 @@ ftdump: ${obj-ftdump}
39obj-ftsort = ftsort.o timestamp.o mapping.o 39obj-ftsort = ftsort.o timestamp.o mapping.o
40ftsort: ${obj-ftsort} 40ftsort: ${obj-ftsort}
41 41
42obj-st-dump = stdump.o load.o eheap.o util.o
43st-dump: ${obj-st-dump}
44 $(CC) $(LDFLAGS) -o $@ ${obj-st-dump} # $(LOADLIBES) $(LDLIBS)
45
46obj-st-job-stats = job_stats.o load.o eheap.o util.o
47st-job-stats: ${obj-st-job-stats}
48 $(CC) $(LDFLAGS) -o $@ ${obj-st-job-stats} # $(LOADLIBES) $(LDLIBS)
49
42# dependency discovery 50# dependency discovery
43include ${LIBLITMUS}/inc/depend.makefile 51include ${LIBLITMUS}/inc/depend.makefile
diff --git a/include/eheap.h b/include/eheap.h
new file mode 100644
index 0000000..323fa30
--- /dev/null
+++ b/include/eheap.h
@@ -0,0 +1,12 @@
1#ifndef EHEAP_H
2#define EHEAP_H
3
4#include <limits.h>
5#include "heap.h"
6
7int earlier_event(struct heap_node* _a, struct heap_node* _b);
8
9struct heap* heapify_events(struct st_event_record *ev, unsigned int count);
10
11
12#endif
diff --git a/include/heap.h b/include/heap.h
new file mode 100644
index 0000000..e46b0eb
--- /dev/null
+++ b/include/heap.h
@@ -0,0 +1,379 @@
1/* heap.h -- Binomial Heaps
2 *
3 * Copyright (c) 2008, Bjoern B. Brandenburg <bbb [at] cs.unc.edu>
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of the University of North Carolina nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY COPYRIGHT OWNER AND CONTRIBUTERS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTERS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#ifndef HEAP_H
32#define HEAP_H
33
34#define NOT_IN_HEAP UINT_MAX
35
36struct heap_node {
37 struct heap_node* parent;
38 struct heap_node* next;
39 struct heap_node* child;
40
41 unsigned int degree;
42 void* value;
43 struct heap_node** ref;
44};
45
46struct heap {
47 struct heap_node* head;
48 /* We cache the minimum of the heap.
49 * This speeds up repeated peek operations.
50 */
51 struct heap_node* min;
52};
53
54/* item comparison function:
55 * return 1 if a has higher prio than b, 0 otherwise
56 */
57typedef int (*heap_prio_t)(struct heap_node* a, struct heap_node* b);
58
59static inline void heap_init(struct heap* heap)
60{
61 heap->head = NULL;
62 heap->min = NULL;
63}
64
65static inline void heap_node_init_ref(struct heap_node** _h, void* value)
66{
67 struct heap_node* h = *_h;
68 h->parent = NULL;
69 h->next = NULL;
70 h->child = NULL;
71 h->degree = NOT_IN_HEAP;
72 h->value = value;
73 h->ref = _h;
74}
75
76static inline void heap_node_init(struct heap_node* h, void* value)
77{
78 h->parent = NULL;
79 h->next = NULL;
80 h->child = NULL;
81 h->degree = NOT_IN_HEAP;
82 h->value = value;
83 h->ref = NULL;
84}
85
86static inline void* heap_node_value(struct heap_node* h)
87{
88 return h->value;
89}
90
91static inline int heap_node_in_heap(struct heap_node* h)
92{
93 return h->degree != NOT_IN_HEAP;
94}
95
96static inline int heap_empty(struct heap* heap)
97{
98 return heap->head == NULL && heap->min == NULL;
99}
100
101/* make child a subtree of root */
102static inline void __heap_link(struct heap_node* root,
103 struct heap_node* child)
104{
105 child->parent = root;
106 child->next = root->child;
107 root->child = child;
108 root->degree++;
109}
110
111/* merge root lists */
112static inline struct heap_node* __heap_merge(struct heap_node* a,
113 struct heap_node* b)
114{
115 struct heap_node* head = NULL;
116 struct heap_node** pos = &head;
117
118 while (a && b) {
119 if (a->degree < b->degree) {
120 *pos = a;
121 a = a->next;
122 } else {
123 *pos = b;
124 b = b->next;
125 }
126 pos = &(*pos)->next;
127 }
128 if (a)
129 *pos = a;
130 else
131 *pos = b;
132 return head;
133}
134
135/* reverse a linked list of nodes. also clears parent pointer */
136static inline struct heap_node* __heap_reverse(struct heap_node* h)
137{
138 struct heap_node* tail = NULL;
139 struct heap_node* next;
140
141 if (!h)
142 return h;
143
144 h->parent = NULL;
145 while (h->next) {
146 next = h->next;
147 h->next = tail;
148 tail = h;
149 h = next;
150 h->parent = NULL;
151 }
152 h->next = tail;
153 return h;
154}
155
156static inline void __heap_min(heap_prio_t higher_prio, struct heap* heap,
157 struct heap_node** prev, struct heap_node** node)
158{
159 struct heap_node *_prev, *cur;
160 *prev = NULL;
161
162 if (!heap->head) {
163 *node = NULL;
164 return;
165 }
166
167 *node = heap->head;
168 _prev = heap->head;
169 cur = heap->head->next;
170 while (cur) {
171 if (higher_prio(cur, *node)) {
172 *node = cur;
173 *prev = _prev;
174 }
175 _prev = cur;
176 cur = cur->next;
177 }
178}
179
180static inline void __heap_union(heap_prio_t higher_prio, struct heap* heap,
181 struct heap_node* h2)
182{
183 struct heap_node* h1;
184 struct heap_node *prev, *x, *next;
185 if (!h2)
186 return;
187 h1 = heap->head;
188 if (!h1) {
189 heap->head = h2;
190 return;
191 }
192 h1 = __heap_merge(h1, h2);
193 prev = NULL;
194 x = h1;
195 next = x->next;
196 while (next) {
197 if (x->degree != next->degree ||
198 (next->next && next->next->degree == x->degree)) {
199 /* nothing to do, advance */
200 prev = x;
201 x = next;
202 } else if (higher_prio(x, next)) {
203 /* x becomes the root of next */
204 x->next = next->next;
205 __heap_link(x, next);
206 } else {
207 /* next becomes the root of x */
208 if (prev)
209 prev->next = next;
210 else
211 h1 = next;
212 __heap_link(next, x);
213 x = next;
214 }
215 next = x->next;
216 }
217 heap->head = h1;
218}
219
220static inline struct heap_node* __heap_extract_min(heap_prio_t higher_prio,
221 struct heap* heap)
222{
223 struct heap_node *prev, *node;
224 __heap_min(higher_prio, heap, &prev, &node);
225 if (!node)
226 return NULL;
227 if (prev)
228 prev->next = node->next;
229 else
230 heap->head = node->next;
231 __heap_union(higher_prio, heap, __heap_reverse(node->child));
232 return node;
233}
234
235/* insert (and reinitialize) a node into the heap */
236static inline void heap_insert(heap_prio_t higher_prio, struct heap* heap,
237 struct heap_node* node)
238{
239 struct heap_node *min;
240 node->child = NULL;
241 node->parent = NULL;
242 node->next = NULL;
243 node->degree = 0;
244 if (heap->min && higher_prio(node, heap->min)) {
245 /* swap min cache */
246 min = heap->min;
247 min->child = NULL;
248 min->parent = NULL;
249 min->next = NULL;
250 min->degree = 0;
251 __heap_union(higher_prio, heap, min);
252 heap->min = node;
253 } else
254 __heap_union(higher_prio, heap, node);
255}
256
257static inline void __uncache_min(heap_prio_t higher_prio, struct heap* heap)
258{
259 struct heap_node* min;
260 if (heap->min) {
261 min = heap->min;
262 heap->min = NULL;
263 heap_insert(higher_prio, heap, min);
264 }
265}
266
267/* merge addition into target */
268static inline void heap_union(heap_prio_t higher_prio,
269 struct heap* target, struct heap* addition)
270{
271 /* first insert any cached minima, if necessary */
272 __uncache_min(higher_prio, target);
273 __uncache_min(higher_prio, addition);
274 __heap_union(higher_prio, target, addition->head);
275 /* this is a destructive merge */
276 addition->head = NULL;
277}
278
279static inline struct heap_node* heap_peek(heap_prio_t higher_prio,
280 struct heap* heap)
281{
282 if (!heap->min)
283 heap->min = __heap_extract_min(higher_prio, heap);
284 return heap->min;
285}
286
287static inline struct heap_node* heap_take(heap_prio_t higher_prio,
288 struct heap* heap)
289{
290 struct heap_node *node;
291 if (!heap->min)
292 heap->min = __heap_extract_min(higher_prio, heap);
293 node = heap->min;
294 heap->min = NULL;
295 if (node)
296 node->degree = NOT_IN_HEAP;
297 return node;
298}
299
300static inline void heap_decrease(heap_prio_t higher_prio, struct heap* heap,
301 struct heap_node* node)
302{
303 struct heap_node *parent;
304 struct heap_node** tmp_ref;
305 void* tmp;
306
307 /* node's priority was decreased, we need to update its position */
308 if (!node->ref)
309 return;
310 if (heap->min != node) {
311 /* bubble up */
312 parent = node->parent;
313 while (parent && higher_prio(node, parent)) {
314 /* swap parent and node */
315 tmp = parent->value;
316 parent->value = node->value;
317 node->value = tmp;
318 /* swap references */
319 if (parent->ref)
320 *(parent->ref) = node;
321 *(node->ref) = parent;
322 tmp_ref = parent->ref;
323 parent->ref = node->ref;
324 node->ref = tmp_ref;
325 /* step up */
326 node = parent;
327 parent = node->parent;
328 }
329 }
330}
331
332static inline void heap_delete(heap_prio_t higher_prio, struct heap* heap,
333 struct heap_node* node)
334{
335 struct heap_node *parent, *prev, *pos;
336 struct heap_node** tmp_ref;
337 void* tmp;
338
339 if (!node->ref) /* can only delete if we have a reference */
340 return;
341 if (heap->min != node) {
342 /* bubble up */
343 parent = node->parent;
344 while (parent) {
345 /* swap parent and node */
346 tmp = parent->value;
347 parent->value = node->value;
348 node->value = tmp;
349 /* swap references */
350 if (parent->ref)
351 *(parent->ref) = node;
352 *(node->ref) = parent;
353 tmp_ref = parent->ref;
354 parent->ref = node->ref;
355 node->ref = tmp_ref;
356 /* step up */
357 node = parent;
358 parent = node->parent;
359 }
360 /* now delete:
361 * first find prev */
362 prev = NULL;
363 pos = heap->head;
364 while (pos != node) {
365 prev = pos;
366 pos = pos->next;
367 }
368 /* we have prev, now remove node */
369 if (prev)
370 prev->next = node->next;
371 else
372 heap->head = node->next;
373 __heap_union(higher_prio, heap, __heap_reverse(node->child));
374 } else
375 heap->min = NULL;
376 node->degree = NOT_IN_HEAP;
377}
378
379#endif /* HEAP_H */
diff --git a/include/load.h b/include/load.h
new file mode 100644
index 0000000..c0d10aa
--- /dev/null
+++ b/include/load.h
@@ -0,0 +1,92 @@
1#ifndef LOAD_H
2#define LOAD_H
3
4#include "sched_trace.h"
5
6int map_trace(const char *name, void **start, void **end, size_t *size);
7struct heap* load(char **files, int no_files, unsigned int *count);
8
9struct evlink {
10 struct evlink *next;
11 struct st_event_record* rec;
12};
13
14struct task {
15 int pid;
16 unsigned int no_events;
17 struct st_event_record* name;
18 struct st_event_record* param;
19 struct evlink* events;
20 struct evlink** next;
21};
22
23#define MAX_TASKS 512
24
25extern struct task tasks[MAX_TASKS];
26extern struct evlink* sys_events;
27extern u64 time0;
28extern u32 g_min_task;
29extern u32 g_max_task;
30
31static inline double ns2ms(u64 ns)
32{
33 return ns / 1000000.0;
34}
35
36static inline double ns2ms_adj(u64 ns)
37{
38 return ns2ms(ns - time0);
39}
40
41static inline double evtime(struct st_event_record* rec)
42{
43 return ns2ms_adj(event_time(rec));
44}
45
46
47void crop_events(struct task* t, double min, double max);
48void crop_events_all(double min, double max);
49
50struct task* by_pid(int pid);
51
52void init_tasks(void);
53void split(struct heap* h, unsigned int count, int find_time0);
54
55const char* tsk_name(struct task* t);
56int tsk_cpu(struct task *t);
57u32 per(struct task* t);
58u32 exe(struct task* t);
59u32 idx(struct task* t);
60
61
62u32 count_tasks(void);
63
64#define CHECK(_e, test) if (test) fprintf(stderr, "'%s' failed.\n", #test);
65
66#define for_each_task(t) \
67 for (t = tasks + g_min_task; t < tasks + g_max_task && t->pid; t++)
68
69#define for_each_event(t, e) \
70 for (e = t->events; e; e = e->next)
71
72#define for_each_event_t(t, e, evtype) \
73 for_each_event(t, e) if (e->rec->hdr.type == evtype)
74
75#define find_evtype(e, evtype) \
76 while (e && e->rec->hdr.type != evtype) e = e->next;
77
78#define find(e, evtype) \
79 while (e && e->rec->hdr.type != evtype) e = e->next; if (!e) break;
80
81
82static inline struct st_event_record *find_sys_event(u8 type)
83{
84 struct evlink* pos = sys_events;
85 find_evtype(pos, type);
86 if (pos)
87 return pos->rec;
88 else
89 return NULL;
90}
91
92#endif
diff --git a/include/sched_trace.h b/include/sched_trace.h
new file mode 100644
index 0000000..0f6828c
--- /dev/null
+++ b/include/sched_trace.h
@@ -0,0 +1,149 @@
1#ifndef __SCHED_TRACE_H_
2#define __SCHED_TRACE_H_
3
4#include <stdint.h>
5
6typedef uint8_t u8;
7typedef uint32_t u32;
8typedef uint16_t u16;
9typedef uint64_t u64;
10
11/* A couple of notes about the format:
12 *
13 * - make sure it is in sync with the kernel
14 * - all times in nanoseconds
15 * - endianess issues are the problem of the app
16 */
17
18struct st_trace_header {
19 u8 type; /* Of what type is this record? */
20 u8 cpu; /* On which CPU was it recorded? */
21 u16 pid; /* PID of the task. */
22 u32 job; /* The job sequence number. */
23};
24
25#define ST_NAME_LEN 16
26struct st_name_data {
27 char cmd[ST_NAME_LEN];/* The name of the executable of this process. */
28};
29
30struct st_param_data { /* regular params */
31 u32 wcet;
32 u32 period;
33 u32 phase;
34 u8 partition;
35 u8 __unused[3];
36};
37
38struct st_release_data { /* A job is was/is going to be released. */
39 u64 release; /* What's the release time? */
40 u64 deadline; /* By when must it finish? */
41};
42
43struct st_assigned_data { /* A job was asigned to a CPU. */
44 u64 when;
45 u8 target; /* Where should it execute? */
46 u8 __unused[3];
47};
48
49struct st_switch_to_data { /* A process was switched to on a given CPU. */
50 u64 when; /* When did this occur? */
51 u32 exec_time; /* Time the current job has executed. */
52
53};
54
55struct st_switch_away_data { /* A process was switched away from on a given CPU. */
56 u64 when;
57 u64 exec_time;
58};
59
60struct st_completion_data { /* A job completed. */
61 u64 when;
62 u64 forced:1; /* Set to 1 if job overran and kernel advanced to the
63 * next job automatically; set to 0 otherwise.
64 */
65 u64 exec_time:63; /* Actual execution time of job. */
66};
67
68struct st_block_data { /* A task blocks. */
69 u64 when;
70 u64 __unused;
71};
72
73struct st_resume_data { /* A task resumes. */
74 u64 when;
75 u64 __unused;
76};
77
78struct st_np_enter_data { /* A task becomes non-preemptable. */
79 u64 when;
80 u64 __unused;
81};
82
83struct st_np_exit_data { /* A task becomes preemptable again. */
84 u64 when;
85 u64 __unused;
86};
87
88struct st_action_data { /* Catch-all for misc. events. */
89 u64 when;
90 u8 action;
91 u8 __unused[7];
92};
93
94struct st_sys_release_data {
95 u64 when;
96 u64 release;
97};
98
99#define DATA(x) struct st_ ## x ## _data x;
100
101typedef enum {
102 ST_NAME = 1, /* Start at one, so that we can spot
103 * uninitialized records. */
104 ST_PARAM,
105 ST_RELEASE,
106 ST_ASSIGNED,
107 ST_SWITCH_TO,
108 ST_SWITCH_AWAY,
109 ST_COMPLETION,
110 ST_BLOCK,
111 ST_RESUME,
112 ST_ACTION,
113 ST_SYS_RELEASE,
114 ST_NP_ENTER,
115 ST_NP_EXIT,
116
117 ST_INVALID /* Dummy ID to catch too-large IDs. */
118} st_event_record_type_t;
119
120struct st_event_record {
121 struct st_trace_header hdr;
122 union {
123 u64 raw[2];
124
125 DATA(name);
126 DATA(param);
127 DATA(release);
128 DATA(assigned);
129 DATA(switch_to);
130 DATA(switch_away);
131 DATA(completion);
132 DATA(block);
133 DATA(resume);
134 DATA(action);
135 DATA(sys_release);
136 DATA(np_enter);
137 DATA(np_exit);
138 } data;
139};
140
141#undef DATA
142
143const char* event2name(unsigned int id);
144u64 event_time(struct st_event_record* rec);
145
146void print_event(struct st_event_record *rec);
147void print_all(struct st_event_record *rec, unsigned int count);
148
149#endif
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}