diff options
author | Bjoern Brandenburg <bbb@mpi-sws.org> | 2012-08-19 14:05:00 -0400 |
---|---|---|
committer | Bjoern Brandenburg <bbb@mpi-sws.org> | 2012-10-19 16:54:29 -0400 |
commit | 4531e293279cdc817f7ac37bff9472e74cb085ee (patch) | |
tree | 1f3d0ce8a4e49d1b33ad6891acc0bfc0b6ae0a2d | |
parent | 44da17bf24f2e67ab71ecb552ebf7b460e64999e (diff) |
Add filtering of IRQ-disturbed samples (on by default)
Interrupts can cause massive outliers. Instead of trying to filter
them afterwards, let's not even include such samples in the first
place.
-rw-r--r-- | src/ft2csv.c | 195 |
1 files changed, 143 insertions, 52 deletions
diff --git a/src/ft2csv.c b/src/ft2csv.c index ff3db23..d8e8e95 100644 --- a/src/ft2csv.c +++ b/src/ft2csv.c | |||
@@ -31,6 +31,7 @@ | |||
31 | 31 | ||
32 | static int want_interleaved = 1; | 32 | static int want_interleaved = 1; |
33 | static int want_best_effort = 0; | 33 | static int want_best_effort = 0; |
34 | static int want_interrupted = 0; | ||
34 | static int find_by_pid = AUTO_SELECT; | 35 | static int find_by_pid = AUTO_SELECT; |
35 | 36 | ||
36 | /* discard samples from a specific CPU */ | 37 | /* discard samples from a specific CPU */ |
@@ -40,22 +41,24 @@ static int only_cpu = -1; | |||
40 | 41 | ||
41 | static unsigned int complete = 0; | 42 | static unsigned int complete = 0; |
42 | static unsigned int incomplete = 0; | 43 | static unsigned int incomplete = 0; |
44 | static unsigned int interrupted = 0; | ||
43 | static unsigned int skipped = 0; | 45 | static unsigned int skipped = 0; |
44 | static unsigned int non_rt = 0; | 46 | static unsigned int non_rt = 0; |
45 | static unsigned int interleaved = 0; | 47 | static unsigned int interleaved = 0; |
46 | static unsigned int avoided = 0; | 48 | static unsigned int avoided = 0; |
47 | 49 | ||
48 | #define CYCLES_PER_US 2128 | 50 | static struct timestamp* next(struct timestamp* first, struct timestamp* end, |
49 | |||
50 | static struct timestamp* next(struct timestamp* start, struct timestamp* end, | ||
51 | int cpu) | 51 | int cpu) |
52 | { | 52 | { |
53 | struct timestamp* pos; | 53 | struct timestamp* pos; |
54 | unsigned int last_seqno = 0; | 54 | uint32_t last_seqno = 0, next_seqno = 0; |
55 | |||
55 | 56 | ||
56 | for (pos = start; pos != end; pos++) { | 57 | last_seqno = first->seq_no; |
58 | for (pos = first + 1; pos < end; pos++) { | ||
57 | /* check for for holes in the sequence number */ | 59 | /* check for for holes in the sequence number */ |
58 | if (last_seqno && last_seqno + 1 != pos->seq_no) { | 60 | next_seqno = last_seqno + 1; |
61 | if (next_seqno != pos->seq_no) { | ||
59 | /* stumbled across a hole */ | 62 | /* stumbled across a hole */ |
60 | return NULL; | 63 | return NULL; |
61 | } | 64 | } |
@@ -69,7 +72,8 @@ static struct timestamp* next(struct timestamp* start, struct timestamp* end, | |||
69 | 72 | ||
70 | static struct timestamp* next_id(struct timestamp* start, struct timestamp* end, | 73 | static struct timestamp* next_id(struct timestamp* start, struct timestamp* end, |
71 | int cpu, unsigned long id, | 74 | int cpu, unsigned long id, |
72 | unsigned long stop_id) | 75 | unsigned long stop_id, |
76 | int *interrupt_flag) | ||
73 | { | 77 | { |
74 | struct timestamp* pos = start; | 78 | struct timestamp* pos = start; |
75 | int restarts = 0; | 79 | int restarts = 0; |
@@ -79,45 +83,74 @@ static struct timestamp* next_id(struct timestamp* start, struct timestamp* end, | |||
79 | break; | 83 | break; |
80 | else if (pos->event == stop_id) | 84 | else if (pos->event == stop_id) |
81 | return NULL; | 85 | return NULL; |
82 | pos++; | 86 | |
83 | restarts++; | 87 | restarts++; |
84 | if (!want_interleaved) | 88 | if (!want_interleaved) |
85 | return NULL; | 89 | return NULL; |
90 | if (pos->irq_flag && !want_interrupted) { | ||
91 | *interrupt_flag = 1; | ||
92 | return NULL; | ||
93 | } | ||
86 | } | 94 | } |
87 | if (pos) | 95 | if (pos) |
88 | interleaved += restarts; | 96 | interleaved += restarts; |
97 | |||
98 | if (pos && pos->irq_flag) { | ||
99 | interrupted++; | ||
100 | if (!want_interrupted) { | ||
101 | *interrupt_flag = 1; | ||
102 | return NULL; | ||
103 | } | ||
104 | } | ||
105 | |||
89 | return pos; | 106 | return pos; |
90 | } | 107 | } |
91 | 108 | ||
92 | static struct timestamp* find_second_ts(struct timestamp* start, | 109 | static struct timestamp* find_second_ts(struct timestamp* start, |
93 | struct timestamp* end) | 110 | struct timestamp* end, |
111 | int *interrupt_flag) | ||
94 | { | 112 | { |
95 | /* convention: the end->event is start->event + 1 */ | 113 | /* convention: the end->event is start->event + 1 */ |
96 | return next_id(start + 1, end, start->cpu, start->event + 1, | 114 | return next_id(start, end, start->cpu, start->event + 1, |
97 | start->event); | 115 | start->event, interrupt_flag); |
98 | } | 116 | } |
99 | 117 | ||
100 | static struct timestamp* next_pid(struct timestamp* start, struct timestamp* end, | 118 | static struct timestamp* next_pid(struct timestamp* first, struct timestamp* end, |
101 | unsigned int pid, unsigned long id1, unsigned long id2) | 119 | unsigned long id1, unsigned long id2, |
120 | int interrupts_significant, int *interrupted_flag) | ||
102 | { | 121 | { |
103 | struct timestamp* pos; | 122 | struct timestamp* pos; |
104 | unsigned int last_seqno = 0; | 123 | uint32_t last_seqno = 0, next_seqno = 0; |
105 | 124 | ||
106 | for (pos = start; pos != end; pos++) { | 125 | |
126 | last_seqno = first->seq_no; | ||
127 | for (pos = first + 1; pos < end; pos++) { | ||
107 | /* check for for holes in the sequence number */ | 128 | /* check for for holes in the sequence number */ |
108 | if (last_seqno && last_seqno + 1 != pos->seq_no) { | 129 | next_seqno = last_seqno + 1; |
130 | if (next_seqno != pos->seq_no) { | ||
109 | /* stumbled across a hole */ | 131 | /* stumbled across a hole */ |
110 | return NULL; | 132 | return NULL; |
111 | } | 133 | } |
112 | last_seqno = pos->seq_no; | 134 | last_seqno = pos->seq_no; |
113 | 135 | ||
136 | if (interrupts_significant | ||
137 | && pos->cpu == first->cpu | ||
138 | && pos->irq_flag) { | ||
139 | /* did an interrupt get in the way? */ | ||
140 | interrupted++; | ||
141 | if (!want_interrupted) { | ||
142 | *interrupted_flag = 1; | ||
143 | return NULL; | ||
144 | } | ||
145 | } | ||
146 | |||
114 | /* only care about this PID */ | 147 | /* only care about this PID */ |
115 | if (pos->pid == pid) { | 148 | if (pos->pid == first->pid) { |
116 | /* is it the right one? */ | 149 | /* is it the right one? */ |
117 | if (pos->event == id1 || pos->event == id2) | 150 | if (pos->event == id1 || pos->event == id2) |
118 | return pos; | 151 | return pos; |
119 | else | 152 | else |
120 | /* Don't allow unexptected IDs interleaved. | 153 | /* Don't allow unexpected IDs interleaved. |
121 | * Tasks are sequential, there shouldn't be | 154 | * Tasks are sequential, there shouldn't be |
122 | * anything else. */ | 155 | * anything else. */ |
123 | return NULL; | 156 | return NULL; |
@@ -126,21 +159,55 @@ static struct timestamp* next_pid(struct timestamp* start, struct timestamp* end | |||
126 | return NULL; | 159 | return NULL; |
127 | } | 160 | } |
128 | 161 | ||
162 | static struct timestamp* skip_over_suspension(struct timestamp *pos, | ||
163 | struct timestamp *end, | ||
164 | uint64_t *last_time) | ||
165 | { | ||
166 | /* Find matching resume. */ | ||
167 | pos = next_pid(pos, end, | ||
168 | TS_LOCK_RESUME, TS_SCHED_START, | ||
169 | 0, NULL); | ||
170 | |||
171 | if (!pos || pos->timestamp < *last_time) | ||
172 | /* broken stream */ | ||
173 | return NULL; | ||
174 | |||
175 | *last_time = pos->timestamp; | ||
176 | |||
177 | if (pos->event == TS_SCHED_START) { | ||
178 | /* Was scheduled out, find TS_SCHED_END. */ | ||
179 | pos = next_pid(pos, end, TS_SCHED_END, 0, 0, NULL); | ||
180 | if (!pos || pos->timestamp < *last_time) | ||
181 | return NULL; | ||
182 | |||
183 | /* next find TS_LOCK_RESUME */ | ||
184 | pos = next_pid(pos, end, TS_LOCK_RESUME, 0, 0, NULL); | ||
185 | } | ||
186 | |||
187 | |||
188 | return pos; | ||
189 | } | ||
190 | |||
129 | static struct timestamp* accumulate_exec_time( | 191 | static struct timestamp* accumulate_exec_time( |
130 | struct timestamp* start, struct timestamp* end, | 192 | struct timestamp* start, struct timestamp* end, |
131 | unsigned int pid, unsigned long stop_id, uint64_t *sum) | 193 | unsigned long stop_id, uint64_t *sum, |
194 | int *interrupted_flag) | ||
132 | { | 195 | { |
133 | struct timestamp* pos = start; | 196 | struct timestamp *pos = start; |
134 | uint64_t exec_start; | 197 | uint64_t exec_start; |
198 | uint64_t last_time = start->timestamp; | ||
135 | 199 | ||
136 | *sum = 0; | 200 | *sum = 0; |
137 | 201 | ||
138 | while (1) { | 202 | while (1) { |
139 | exec_start = pos->timestamp; | 203 | exec_start = pos->timestamp; |
140 | 204 | ||
141 | /* find a suspension */ | 205 | /* Find a suspension or the proper end. */ |
142 | pos = next_pid(pos + 1, end, pid, TS_LOCK_SUSPEND, stop_id); | 206 | pos = next_pid(pos, end, |
143 | if (!pos) | 207 | TS_LOCK_SUSPEND, stop_id, |
208 | 1, interrupted_flag); | ||
209 | |||
210 | if (!pos || pos->timestamp < last_time) | ||
144 | /* broken stream */ | 211 | /* broken stream */ |
145 | return NULL; | 212 | return NULL; |
146 | 213 | ||
@@ -148,16 +215,25 @@ static struct timestamp* accumulate_exec_time( | |||
148 | *sum += pos->timestamp - exec_start; | 215 | *sum += pos->timestamp - exec_start; |
149 | 216 | ||
150 | if (pos->event == stop_id) | 217 | if (pos->event == stop_id) |
151 | /* no suspension */ | 218 | /* no suspension or preemption */ |
152 | return pos; | 219 | return pos; |
220 | else { | ||
221 | last_time = pos->timestamp; | ||
153 | 222 | ||
154 | /* find matching resume */ | 223 | /* handle self-suspension */ |
155 | pos = next_pid(pos + 1, end, pid, TS_LOCK_RESUME, 0); | 224 | pos = skip_over_suspension(pos, end, &last_time); |
156 | if (!pos) | 225 | |
157 | /* broken stream */ | 226 | /* Must be a resume => start over. If a resume sample is |
158 | return NULL; | 227 | * affected by interrupts we don't care since it does not |
228 | * contribute to the reported execution cost. | ||
229 | */ | ||
159 | 230 | ||
160 | /* must be a resume => start over */ | 231 | if (!pos || pos->timestamp < last_time) |
232 | /* broken stream */ | ||
233 | return NULL; | ||
234 | |||
235 | last_time = pos->timestamp; | ||
236 | } | ||
161 | } | 237 | } |
162 | } | 238 | } |
163 | 239 | ||
@@ -183,22 +259,26 @@ static void find_event_by_pid(struct timestamp* first, struct timestamp* end) | |||
183 | { | 259 | { |
184 | struct timestamp *second; | 260 | struct timestamp *second; |
185 | uint64_t exec_time = 0; | 261 | uint64_t exec_time = 0; |
262 | int interrupted = 0; | ||
186 | 263 | ||
187 | /* special case: take suspensions into account */ | 264 | /* special case: take suspensions into account */ |
188 | if (first->event >= SUSPENSION_RANGE) { | 265 | if (first->event >= SUSPENSION_RANGE) { |
189 | second = accumulate_exec_time(first, end, | 266 | second = accumulate_exec_time(first, end, |
190 | first->pid, | 267 | first->event + 1, &exec_time, |
191 | first->event + 1, &exec_time); | 268 | &interrupted); |
192 | } else { | 269 | } else { |
193 | second = next_pid(first + 1, end, first->pid, | 270 | second = next_pid(first, end, |
194 | first->event + 1, 0); | 271 | first->event + 1, 0, |
195 | if (second) | 272 | 1, &interrupted); |
273 | if (second && second->timestamp > first->timestamp) | ||
196 | exec_time = second->timestamp - first->timestamp; | 274 | exec_time = second->timestamp - first->timestamp; |
275 | else | ||
276 | second = NULL; | ||
197 | } | 277 | } |
198 | if (second) { | 278 | if (second) { |
199 | format_pair(first, second, exec_time); | 279 | format_pair(first, second, exec_time); |
200 | complete++; | 280 | complete++; |
201 | } else | 281 | } else if (!interrupted) |
202 | incomplete++; | 282 | incomplete++; |
203 | } | 283 | } |
204 | 284 | ||
@@ -206,8 +286,9 @@ static void find_event_by_eid(struct timestamp *first, struct timestamp* end) | |||
206 | { | 286 | { |
207 | struct timestamp *second; | 287 | struct timestamp *second; |
208 | uint64_t exec_time; | 288 | uint64_t exec_time; |
289 | int interrupted = 0; | ||
209 | 290 | ||
210 | second = find_second_ts(first, end); | 291 | second = find_second_ts(first, end, &interrupted); |
211 | if (second) { | 292 | if (second) { |
212 | exec_time = second->timestamp - first->timestamp; | 293 | exec_time = second->timestamp - first->timestamp; |
213 | if (first->task_type != TSK_RT && | 294 | if (first->task_type != TSK_RT && |
@@ -217,7 +298,7 @@ static void find_event_by_eid(struct timestamp *first, struct timestamp* end) | |||
217 | format_pair(first, second, exec_time); | 298 | format_pair(first, second, exec_time); |
218 | complete++; | 299 | complete++; |
219 | } | 300 | } |
220 | } else | 301 | } else if (!interrupted) |
221 | incomplete++; | 302 | incomplete++; |
222 | } | 303 | } |
223 | 304 | ||
@@ -295,6 +376,7 @@ static void show_single_records(struct timestamp* start, struct timestamp* end, | |||
295 | " -r: raw binary format -- don't produce .csv output \n" \ | 376 | " -r: raw binary format -- don't produce .csv output \n" \ |
296 | " -a: avoid CPU -- skip samples from a specific CPU\n" \ | 377 | " -a: avoid CPU -- skip samples from a specific CPU\n" \ |
297 | " -o: only CPU -- skip all samples from other CPUs\n" \ | 378 | " -o: only CPU -- skip all samples from other CPUs\n" \ |
379 | " -x: allow interrupts -- don't skip samples with IRQ-happned flag \n" \ | ||
298 | "" | 380 | "" |
299 | 381 | ||
300 | static void die(char* msg) | 382 | static void die(char* msg) |
@@ -306,7 +388,7 @@ static void die(char* msg) | |||
306 | exit(1); | 388 | exit(1); |
307 | } | 389 | } |
308 | 390 | ||
309 | #define OPTS "ibra:o:pe" | 391 | #define OPTS "ibra:o:pex" |
310 | 392 | ||
311 | int main(int argc, char** argv) | 393 | int main(int argc, char** argv) |
312 | { | 394 | { |
@@ -323,13 +405,17 @@ int main(int argc, char** argv) | |||
323 | want_interleaved = 0; | 405 | want_interleaved = 0; |
324 | fprintf(stderr, "Discarging interleaved samples.\n"); | 406 | fprintf(stderr, "Discarging interleaved samples.\n"); |
325 | break; | 407 | break; |
408 | case 'x': | ||
409 | want_interrupted = 1; | ||
410 | fprintf(stderr, "Not filtering disturbed-by-interrupt samples.\n"); | ||
411 | break; | ||
326 | case 'b': | 412 | case 'b': |
327 | fprintf(stderr,"Not filtering samples from best-effort" | 413 | fprintf(stderr,"Not filtering samples from best-effort" |
328 | " tasks.\n"); | 414 | " tasks.\n"); |
329 | want_best_effort = 1; | 415 | want_best_effort = 1; |
330 | break; | 416 | break; |
331 | case 'r': | 417 | case 'r': |
332 | fprintf(stderr, "Generating binary (raw) output.\n"); | 418 | fprintf(stderr, "Generating binary, NumPy-compatible output.\n"); |
333 | single_fmt = print_single_bin; | 419 | single_fmt = print_single_bin; |
334 | format_pair = print_pair_bin; | 420 | format_pair = print_pair_bin; |
335 | break; | 421 | break; |
@@ -382,18 +468,23 @@ int main(int argc, char** argv) | |||
382 | else | 468 | else |
383 | show_id(ts, end, id); | 469 | show_id(ts, end, id); |
384 | 470 | ||
385 | fprintf(stderr, | 471 | if (count == skipped) |
386 | "Total : %10d\n" | 472 | fprintf(stderr, "Event %s not present.\n", |
387 | "Skipped : %10d\n" | 473 | argv[optind]); |
388 | "Avoided : %10d\n" | 474 | else |
389 | "Complete : %10d\n" | 475 | fprintf(stderr, |
390 | "Incomplete : %10d\n" | 476 | "Total : %10d\n" |
391 | "Non RT : %10d\n" | 477 | "Skipped : %10d\n" |
392 | "Interleaved : %10d\n", | 478 | "Avoided : %10d\n" |
393 | (int) count, | 479 | "Complete : %10d\n" |
394 | skipped, avoided, complete, | 480 | "Incomplete : %10d\n" |
395 | incomplete, non_rt, | 481 | "Non RT : %10d\n" |
396 | interleaved); | 482 | "Interleaved : %10d\n" |
483 | "Interrupted : %10d\n", | ||
484 | (int) count, | ||
485 | skipped, avoided, complete, | ||
486 | incomplete, non_rt, | ||
487 | interleaved, interrupted); | ||
397 | 488 | ||
398 | return 0; | 489 | return 0; |
399 | } | 490 | } |