diff options
Diffstat (limited to 'rt-plot.c')
-rw-r--r-- | rt-plot.c | 379 |
1 files changed, 269 insertions, 110 deletions
@@ -1,5 +1,170 @@ | |||
1 | #include <gtk/gtk.h> | 1 | #include <gtk/gtk.h> |
2 | #include <string.h> | ||
2 | #include "trace-graph.h" | 3 | #include "trace-graph.h" |
4 | #include "list.h" | ||
5 | |||
6 | |||
7 | struct record_list { | ||
8 | struct record_list *next; | ||
9 | struct record *record; | ||
10 | }; | ||
11 | |||
12 | /* | ||
13 | * Insert @record into @list, storing the record in @node. | ||
14 | */ | ||
15 | void insert_record(struct graph_info *ginfo, struct record_list *list, | ||
16 | struct record *record, struct record_list *node, | ||
17 | int reverse) | ||
18 | { | ||
19 | if (node->next) | ||
20 | die("Node is in use!"); | ||
21 | if (!record) { | ||
22 | printf("No record\n"); | ||
23 | return; | ||
24 | } | ||
25 | |||
26 | node->record = record; | ||
27 | |||
28 | struct record_list *pos = list; | ||
29 | |||
30 | while (pos->next) { | ||
31 | unsigned long long next_rts = get_rts(ginfo, pos->next->record); | ||
32 | if (( reverse && !next_rts > get_rts(ginfo, record)) || | ||
33 | (!reverse && next_rts < get_rts(ginfo, record))){ | ||
34 | break; | ||
35 | } | ||
36 | pos = pos->next; | ||
37 | } | ||
38 | |||
39 | node->next = pos->next; | ||
40 | pos->next = node; | ||
41 | } | ||
42 | |||
43 | |||
44 | /* | ||
45 | * Remove the first record in @list and put into @node. | ||
46 | */ | ||
47 | int pop_record(struct graph_info *ginfo, struct record_list *list, | ||
48 | struct record_list **node) | ||
49 | { | ||
50 | if (!list->next) | ||
51 | return 0; | ||
52 | |||
53 | *node = list->next; | ||
54 | list->next = list->next->next; | ||
55 | (*node)->next = 0; | ||
56 | |||
57 | return 1; | ||
58 | } | ||
59 | |||
60 | |||
61 | /* For communication between get_previous_release and its iterator */ | ||
62 | struct prev_release_args { | ||
63 | unsigned long long min_ts; | ||
64 | struct rt_plot_common *common; | ||
65 | unsigned long long time; | ||
66 | int match_tid; | ||
67 | unsigned int *out_job; | ||
68 | unsigned long long *out_release; | ||
69 | unsigned long long *out_deadline; | ||
70 | }; | ||
71 | |||
72 | |||
73 | static int | ||
74 | prev_release_iterator(struct graph_info *ginfo, struct record *rec, void *data) | ||
75 | { | ||
76 | int tid, is_release, job; | ||
77 | unsigned long long release, deadline; | ||
78 | struct prev_release_args *args = data; | ||
79 | |||
80 | if (get_rts(ginfo, rec) < args->min_ts) { | ||
81 | args->common->last_job.release = get_rts(ginfo, rec); | ||
82 | args->common->last_job.deadline = args->time; | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | #define ARG ginfo, rec, &tid, &job, &release, &deadline | ||
87 | is_release = rt_graph_check_task_release(ARG) || | ||
88 | rt_graph_check_server_release(ARG); | ||
89 | #undef ARG | ||
90 | |||
91 | if (is_release && args->match_tid == tid && release <= args->time) { | ||
92 | *(args->out_job) = job; | ||
93 | *(args->out_release) = release; | ||
94 | *(args->out_deadline) = deadline; | ||
95 | |||
96 | /* Cache to minimize work later */ | ||
97 | args->common->last_job.no = job; | ||
98 | args->common->last_job.release = release; | ||
99 | args->common->last_job.deadline = deadline; | ||
100 | args->common->last_job.start = get_rts(ginfo, rec); | ||
101 | args->common->last_job.end = args->time; | ||
102 | return 0; | ||
103 | } else { | ||
104 | return 1; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | |||
109 | /* For communication between find_prev_display_record and its iterator */ | ||
110 | struct prev_display_args { | ||
111 | struct rt_plot_common *common; | ||
112 | struct record *result; | ||
113 | unsigned long long min_ts; | ||
114 | }; | ||
115 | |||
116 | |||
117 | static int | ||
118 | prev_display_iterator(struct graph_info *ginfo, struct record *record, void *data) | ||
119 | { | ||
120 | int eid, ignored; | ||
121 | struct prev_display_args *args = data; | ||
122 | |||
123 | if (get_rts(ginfo, record) < args->min_ts) { | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | eid = pevent_data_type(ginfo->pevent, record); | ||
128 | ignored = (eid == ginfo->event_sched_switch_id); | ||
129 | |||
130 | if (!ignored) { | ||
131 | ignored = args->common->is_drawn(ginfo, eid); | ||
132 | } | ||
133 | |||
134 | if (!ignored && args->common->record_matches(args->common, ginfo, record)) { | ||
135 | args->result = record; | ||
136 | ++record->ref_count; | ||
137 | printf("success!\n"); | ||
138 | return 0; | ||
139 | } else { | ||
140 | return 1; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | |||
145 | /* | ||
146 | * Return first displayed record before @time, abandoning search after @range. | ||
147 | */ | ||
148 | static struct record* | ||
149 | find_prev_display_record(struct graph_info *ginfo, struct rt_plot_common *rt_info, | ||
150 | unsigned long long time, unsigned long long range) | ||
151 | { | ||
152 | int eid, ignored, match, cpu; | ||
153 | struct record *prev, *next, *res = NULL; | ||
154 | struct prev_display_args args = {rt_info, NULL, 0}; | ||
155 | |||
156 | if (range) { | ||
157 | args.min_ts = time - range; | ||
158 | } else { | ||
159 | args.min_ts = time - max_rt_search(ginfo); | ||
160 | } | ||
161 | |||
162 | set_cpus_to_rts(ginfo, time); | ||
163 | iterate(ginfo, 1, prev_display_iterator, &args); | ||
164 | |||
165 | return args.result; | ||
166 | } | ||
167 | |||
3 | 168 | ||
4 | /** | 169 | /** |
5 | * Return first relevant record after @time. | 170 | * Return first relevant record after @time. |
@@ -10,7 +175,7 @@ __find_rt_record(struct graph_info *ginfo, struct rt_plot_common *rt_info, | |||
10 | guint64 time, int display, unsigned long long range) | 175 | guint64 time, int display, unsigned long long range) |
11 | { | 176 | { |
12 | int next_cpu, match, eid, ignored; | 177 | int next_cpu, match, eid, ignored; |
13 | struct record *record; | 178 | struct record *record = NULL; |
14 | 179 | ||
15 | set_cpus_to_rts(ginfo, time); | 180 | set_cpus_to_rts(ginfo, time); |
16 | while ((record = tracecmd_read_next_data(ginfo->handle, &next_cpu))) { | 181 | while ((record = tracecmd_read_next_data(ginfo->handle, &next_cpu))) { |
@@ -71,49 +236,6 @@ rt_plot_display_last_event(struct graph_info *ginfo, struct graph_plot *plot, | |||
71 | return 1; | 236 | return 1; |
72 | } | 237 | } |
73 | 238 | ||
74 | /* | ||
75 | * Return first displayed record before @time, abandoning search after @range. | ||
76 | */ | ||
77 | static struct record* | ||
78 | find_prev_display_record(struct graph_info *ginfo, struct rt_plot_common *rt_info, | ||
79 | unsigned long long time, unsigned long long range) | ||
80 | { | ||
81 | int eid, ignored, match, cpu; | ||
82 | struct record *prev, *res = NULL; | ||
83 | unsigned long long min_ts; | ||
84 | |||
85 | if (range) | ||
86 | min_ts = time - range; | ||
87 | else | ||
88 | min_ts = time - max_rt_search(ginfo); | ||
89 | |||
90 | set_cpus_to_rts(ginfo, time); | ||
91 | |||
92 | for (cpu = 0; cpu < ginfo->cpus; cpu++) { | ||
93 | prev = tracecmd_peek_data(ginfo->handle, cpu); | ||
94 | while ((prev = tracecmd_read_prev(ginfo->handle, prev)) && | ||
95 | get_rts(ginfo, prev) > min_ts) { | ||
96 | eid = pevent_data_type(ginfo->pevent, prev); | ||
97 | ignored = (eid == ginfo->event_sched_switch_id); | ||
98 | if (!ignored) { | ||
99 | ignored = rt_info->is_drawn(ginfo, eid); | ||
100 | } | ||
101 | match = !ignored && | ||
102 | rt_info->record_matches(rt_info, ginfo, prev); | ||
103 | if (match) { | ||
104 | if (!res || | ||
105 | get_rts(ginfo, prev) > get_rts(ginfo, res)) { | ||
106 | free_record(res); | ||
107 | res = prev; | ||
108 | } | ||
109 | break; | ||
110 | } | ||
111 | free_record(prev); | ||
112 | } | ||
113 | } | ||
114 | return res; | ||
115 | } | ||
116 | |||
117 | /** | 239 | /** |
118 | * rt_plot_display_info - write information about @time into @s | 240 | * rt_plot_display_info - write information about @time into @s |
119 | */ | 241 | */ |
@@ -128,14 +250,22 @@ rt_plot_display_info(struct graph_info *ginfo, struct graph_plot *plot, | |||
128 | long long pdiff, rdiff; | 250 | long long pdiff, rdiff; |
129 | int eid; | 251 | int eid; |
130 | 252 | ||
253 | /* printf("\nBegin display\n"); */ | ||
254 | |||
131 | /* Write plot-specific data */ | 255 | /* Write plot-specific data */ |
132 | data_record = rt_info->write_header(rt_info, ginfo, s, time); | 256 | data_record = rt_info->write_header(rt_info, ginfo, s, time); |
257 | /* printf("Data record: "); print_record(data_record); */ | ||
133 | 258 | ||
134 | /* Select closest relevant record */ | 259 | /* Select closest relevant record */ |
135 | range = 2 / ginfo->resolution; | 260 | range = 2 / ginfo->resolution; |
136 | 261 | ||
262 | |||
137 | record = __find_rt_record(ginfo, rt_info, time, 1, range); | 263 | record = __find_rt_record(ginfo, rt_info, time, 1, range); |
138 | prev_record = find_prev_display_record(ginfo, rt_info, time, range); | 264 | prev_record = find_prev_display_record(ginfo, rt_info, time, range); |
265 | //prev_record = NULL; | ||
266 | |||
267 | /* printf("Next: "); print_record(record); */ | ||
268 | /* printf("Prev: "); print_record(prev_record); */ | ||
139 | 269 | ||
140 | if (!record) { | 270 | if (!record) { |
141 | record = prev_record; | 271 | record = prev_record; |
@@ -144,34 +274,42 @@ rt_plot_display_info(struct graph_info *ginfo, struct graph_plot *plot, | |||
144 | rtime = get_rts(ginfo, record); | 274 | rtime = get_rts(ginfo, record); |
145 | pdiff = (ptime < time) ? time - ptime : ptime - time; | 275 | pdiff = (ptime < time) ? time - ptime : ptime - time; |
146 | rdiff = (rtime < time) ? time - rtime : rtime - time; | 276 | rdiff = (rtime < time) ? time - rtime : rtime - time; |
147 | record = (pdiff < rdiff) ? prev_record : record; | 277 | if (pdiff < rdiff) { |
278 | free_record(record); | ||
279 | record = prev_record; | ||
280 | } else { | ||
281 | free_record(prev_record); | ||
282 | } | ||
148 | } | 283 | } |
149 | 284 | ||
150 | /* Write event info */ | 285 | /* Write event info */ |
151 | if (record) { | 286 | if (record) { |
152 | rts = get_rts(ginfo, record); | 287 | /* rts = get_rts(ginfo, record); */ |
153 | eid = pevent_data_type(ginfo->pevent, record); | 288 | /* eid = pevent_data_type(ginfo->pevent, record); */ |
154 | 289 | ||
155 | if (in_res(ginfo, rts, time)) { | 290 | /* if (in_res(ginfo, rts, time)) { */ |
156 | event = pevent_data_event_from_type(ginfo->pevent, eid); | 291 | /* event = pevent_data_event_from_type(ginfo->pevent, eid); */ |
157 | if (event) { | 292 | /* if (event) { */ |
158 | trace_seq_putc(s, '\n'); | 293 | /* trace_seq_putc(s, '\n'); */ |
159 | trace_seq_puts(s, event->name); | 294 | /* trace_seq_puts(s, event->name); */ |
160 | trace_seq_putc(s, '\n'); | 295 | /* trace_seq_putc(s, '\n'); */ |
161 | pevent_event_info(s, event, record); | 296 | /* pevent_event_info(s, event, record); */ |
162 | } else | 297 | /* } else */ |
163 | trace_seq_printf(s, "\nUNKNOWN EVENT %d\n", eid); | 298 | /* trace_seq_printf(s, "\nUNKNOWN EVENT %d\n", eid); */ |
164 | } | 299 | /* } */ |
165 | free_record(record); | 300 | free_record(record); |
301 | /* printf("Freed record: "); print_record(record); */ | ||
166 | } | 302 | } |
167 | 303 | ||
168 | /* Metadata */ | 304 | /* Metadata */ |
169 | trace_seq_putc(s, '\n'); | 305 | trace_seq_putc(s, '\n'); |
170 | nano_to_milli(time, &msec, &nsec); | 306 | nano_to_milli(time, &msec, &nsec); |
171 | trace_seq_printf(s, "%llu.%06llu ms", msec, nsec); | 307 | trace_seq_printf(s, "%llu.%06llu ms", msec, nsec); |
308 | |||
172 | if (data_record) { | 309 | if (data_record) { |
173 | trace_seq_printf(s, " CPU: %03d", data_record->cpu); | 310 | trace_seq_printf(s, " CPU: %03d", data_record->cpu); |
174 | free_record(data_record); | 311 | free_record(data_record); |
312 | /* printf("Freed data: "); print_record(data_record); */ | ||
175 | } | 313 | } |
176 | 314 | ||
177 | return 1; | 315 | return 1; |
@@ -360,6 +498,64 @@ int is_task_running(struct graph_info *ginfo, | |||
360 | return running; | 498 | return running; |
361 | } | 499 | } |
362 | 500 | ||
501 | void iterate(struct graph_info *ginfo, int reverse, iterate_cb cb, void *data) | ||
502 | { | ||
503 | int proceed, cpu; | ||
504 | struct record *next, *prev; | ||
505 | struct record_list list; | ||
506 | struct record_list *nodes, *node; | ||
507 | |||
508 | nodes = malloc_or_die(sizeof(*nodes) * ginfo->cpus); | ||
509 | memset(nodes, 0, sizeof(*nodes) * ginfo->cpus); | ||
510 | memset(&list, 0, sizeof(list)); | ||
511 | |||
512 | /* Maintains a list of the next record on each cpu, sorted by | ||
513 | * timestamp. Start with the first record on each cpu. | ||
514 | */ | ||
515 | for (cpu = 0; cpu < ginfo->cpus; cpu++) { | ||
516 | next = tracecmd_peek_data(ginfo->handle, cpu); | ||
517 | if (next) { | ||
518 | if (reverse) { | ||
519 | prev = next; | ||
520 | next = tracecmd_read_prev(ginfo->handle, prev); | ||
521 | if (prev != next && prev->data) { | ||
522 | free_record(prev); | ||
523 | } | ||
524 | } | ||
525 | insert_record(ginfo, &list, next, &nodes[cpu], reverse); | ||
526 | } | ||
527 | } | ||
528 | |||
529 | /* Read sequentially from all cpus */ | ||
530 | while (pop_record(ginfo, &list, &node)) { | ||
531 | next = node->record; | ||
532 | |||
533 | proceed = cb(ginfo, next, data); | ||
534 | |||
535 | if (!proceed) { | ||
536 | free_record(next); | ||
537 | break; | ||
538 | } | ||
539 | |||
540 | prev = next; | ||
541 | |||
542 | if (!reverse) { | ||
543 | next = tracecmd_read_data(ginfo->handle, next->cpu); | ||
544 | } else { | ||
545 | next = tracecmd_read_prev(ginfo->handle, next); | ||
546 | } | ||
547 | |||
548 | free_record(prev); | ||
549 | |||
550 | insert_record(ginfo, &list, next, node, reverse); | ||
551 | } | ||
552 | |||
553 | while (pop_record(ginfo, &list, &node)) { | ||
554 | free_record(node->record); | ||
555 | } | ||
556 | } | ||
557 | |||
558 | |||
363 | /** | 559 | /** |
364 | * Find the information for the last release of @match_tid on @cpu before @time. | 560 | * Find the information for the last release of @match_tid on @cpu before @time. |
365 | * | 561 | * |
@@ -369,63 +565,26 @@ int is_task_running(struct graph_info *ginfo, | |||
369 | * Returns release record and @out_job, @out_release, and @out_deadline if a | 565 | * Returns release record and @out_job, @out_release, and @out_deadline if a |
370 | * release was found for @tid before @time. | 566 | * release was found for @tid before @time. |
371 | */ | 567 | */ |
372 | void get_previous_release(struct graph_info *ginfo, int match_tid, | 568 | void get_previous_release(struct graph_info *ginfo, struct rt_plot_common *common, |
569 | int match_tid, | ||
373 | unsigned long long time, | 570 | unsigned long long time, |
374 | int *out_job, | 571 | int *out_job, |
375 | unsigned long long *out_release, | 572 | unsigned long long *out_release, |
376 | unsigned long long *out_deadline) | 573 | unsigned long long *out_deadline) |
377 | { | 574 | { |
378 | int tid, cpu, match, job; | 575 | struct prev_release_args args = { |
379 | unsigned long long release, deadline, min_ts; | 576 | (time - max_rt_search(ginfo)), common, time, match_tid, |
380 | struct record *last_rec = NULL, *rec, *ret = NULL; | 577 | out_job, out_release, out_deadline}; |
381 | 578 | ||
382 | *out_job = -2; | 579 | /* Use cached job info, if possible */ |
383 | 580 | if (time >= common->last_job.start && | |
384 | min_ts = time - max_rt_search(ginfo); | 581 | time <= common->last_job.end) { |
385 | 582 | *out_job = common->last_job.no; | |
386 | /* The release record could have occurred on any CPU. Search all */ | 583 | *out_release = common->last_job.release; |
387 | for (cpu = 0; cpu < ginfo->cpus; cpu++) { | 584 | *out_deadline = common->last_job.deadline; |
388 | set_cpu_to_rts(ginfo, time, cpu); | 585 | return; |
389 | last_rec = tracecmd_peek_data(ginfo->handle, cpu); | ||
390 | |||
391 | /* Require a record to start with */ | ||
392 | if (!last_rec) { | ||
393 | goto loop_end; | ||
394 | } | ||
395 | last_rec->ref_count++; | ||
396 | |||
397 | while ((rec = tracecmd_read_prev(ginfo->handle, last_rec))) { | ||
398 | if (get_rts(ginfo, rec) < min_ts) { | ||
399 | free_record(rec); | ||
400 | |||
401 | goto loop_end; | ||
402 | } | ||
403 | |||
404 | #define ARG ginfo, rec, &tid, &job, &release, &deadline | ||
405 | match = rt_graph_check_task_release(ARG) || | ||
406 | rt_graph_check_server_release(ARG); | ||
407 | #undef ARG | ||
408 | |||
409 | free_record(last_rec); | ||
410 | last_rec = rec; | ||
411 | |||
412 | /* Only consider releases before the current time */ | ||
413 | if (match && tid == match_tid && release <= time) { | ||
414 | /* Return the lastest release */ | ||
415 | if (!ret || *out_job < job) { | ||
416 | free_record(ret); | ||
417 | ret = rec; | ||
418 | *out_job = job; | ||
419 | *out_release = release; | ||
420 | *out_deadline = deadline; | ||
421 | } | ||
422 | last_rec = NULL; | ||
423 | goto loop_end; | ||
424 | } | ||
425 | } | ||
426 | loop_end: | ||
427 | free_record(last_rec); | ||
428 | } | 586 | } |
429 | 587 | ||
430 | free_record(ret); | 588 | set_cpus_to_rts(ginfo, time); |
589 | iterate(ginfo, 1, prev_release_iterator, &args); | ||
431 | } | 590 | } |