aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan <hermanjl@hermanjl-Aspire-5553G.(none)>2012-03-07 23:11:07 -0500
committerJonathan <hermanjl@hermanjl-Aspire-5553G.(none)>2012-03-07 23:11:07 -0500
commite9f207ed3c68bca111d0e21a2bf601b4564ed748 (patch)
tree3ec203a17d81814a0a5f2afa465e4db4ffb186d3
parent000249b6b36adb88e4fea5e020319b9361d0ca54 (diff)
rt-graph: real-time task plots divorced from regular task plots
-rw-r--r--rt-graph.c143
-rw-r--r--rt-graph.h12
-rw-r--r--rt-plot-task.c268
-rw-r--r--rt-plot-task.h5
-rw-r--r--trace-graph.h1
-rw-r--r--trace-plot-task.c53
-rw-r--r--trace-plot-task.h16
7 files changed, 256 insertions, 242 deletions
diff --git a/rt-graph.c b/rt-graph.c
index 3e9f248..d6b1847 100644
--- a/rt-graph.c
+++ b/rt-graph.c
@@ -1,7 +1,7 @@
1#include "rt-graph.h" 1#include "trace-graph.h"
2#include "trace-hash.h" 2#include "trace-hash.h"
3 3
4#define DEBUG_LEVEL 0 4#define DEBUG_LEVEL 4
5#if DEBUG_LEVEL > 0 5#if DEBUG_LEVEL > 0
6#define dprintf(l, x...) \ 6#define dprintf(l, x...) \
7 do { \ 7 do { \
@@ -17,6 +17,9 @@ static guint get_event_hash_key(gint eid)
17 return trace_hash(eid) % TS_HASH_SIZE; 17 return trace_hash(eid) % TS_HASH_SIZE;
18} 18}
19 19
20/*
21 * Returns cached field for @eid at @key.
22 */
20struct format_field* find_ts_hash(struct ts_list **events, 23struct format_field* find_ts_hash(struct ts_list **events,
21 gint key, gint eid) 24 gint key, gint eid)
22{ 25{
@@ -29,10 +32,11 @@ struct format_field* find_ts_hash(struct ts_list **events,
29} 32}
30 33
31/* 34/*
32 * Return format field for @eid, caching its location if this is the first try 35 * Return field for @eid at @key, caching if necessary.
33 */ 36 */
34static struct format_field* add_ts_hash(struct ts_list **events, gint eid, gint key, 37static struct format_field*
35 struct pevent *pevent, struct record *record) 38add_ts_hash(struct ts_list **events, gint eid, gint key,
39 struct pevent *pevent, struct record *record)
36{ 40{
37 struct ts_list *list; 41 struct ts_list *list;
38 struct format_field *field; 42 struct format_field *field;
@@ -59,16 +63,12 @@ static struct format_field* add_ts_hash(struct ts_list **events, gint eid, gint
59 */ 63 */
60int rt_graph_check_any(struct rt_graph_info *rtinfo, 64int rt_graph_check_any(struct rt_graph_info *rtinfo,
61 struct pevent *pevent, struct record *record, 65 struct pevent *pevent, struct record *record,
62 gint *epid, unsigned long long *ts) 66 gint *epid, gint *out_eid, unsigned long long *ts)
63{ 67{
64 guint key, eid; 68 guint key, eid;
65 struct format_field *field; 69 struct format_field *field;
66 70
67 eid = pevent_data_type(pevent, record); 71 eid = pevent_data_type(pevent, record);
68
69 if (eid == rtinfo->switch_away_id)
70 return 0;
71
72 key = get_event_hash_key(eid); 72 key = get_event_hash_key(eid);
73 field = find_ts_hash(rtinfo->events, key, eid); 73 field = find_ts_hash(rtinfo->events, key, eid);
74 74
@@ -80,6 +80,7 @@ int rt_graph_check_any(struct rt_graph_info *rtinfo,
80 80
81 dprintf(3, "Read (%d) record for task %d at %llu\n", 81 dprintf(3, "Read (%d) record for task %d at %llu\n",
82 eid, *epid, *ts); 82 eid, *epid, *ts);
83 *out_eid = eid;
83 return 1; 84 return 1;
84} 85}
85 86
@@ -222,7 +223,7 @@ int rt_graph_check_switch_away(struct rt_graph_info *rtinfo,
222 223
223/** 224/**
224 * rt_graph_check_task_release - check for litmus_task_release record 225 * rt_graph_check_task_release - check for litmus_task_release record
225 * Return 1 and @pid, @job, and @deadline if the record matches 226 * Return 1 and @pid, @job, @release, and @deadline if the record matches
226 */ 227 */
227int rt_graph_check_task_release(struct rt_graph_info *rtinfo, 228int rt_graph_check_task_release(struct rt_graph_info *rtinfo,
228 struct pevent *pevent, struct record *record, 229 struct pevent *pevent, struct record *record,
@@ -271,7 +272,7 @@ int rt_graph_check_task_release(struct rt_graph_info *rtinfo,
271 272
272/** 273/**
273 * rt_graph_check_task_completion - check for litmus_task_completion record 274 * rt_graph_check_task_completion - check for litmus_task_completion record
274 * Return 1 and @pid, @job if the record matches 275 * Return 1 and @pid, @job, and @ts if the record matches
275 */ 276 */
276int rt_graph_check_task_completion(struct rt_graph_info *rtinfo, 277int rt_graph_check_task_completion(struct rt_graph_info *rtinfo,
277 struct pevent *pevent, struct record *record, 278 struct pevent *pevent, struct record *record,
@@ -314,7 +315,7 @@ int rt_graph_check_task_completion(struct rt_graph_info *rtinfo,
314 315
315/** 316/**
316 * rt_graph_check_task_block - check for litmus_task_block record 317 * rt_graph_check_task_block - check for litmus_task_block record
317 * Return 1 and @pid if the record matches 318 * Return 1, @pid, and @ts if the record matches
318 */ 319 */
319int rt_graph_check_task_block(struct rt_graph_info *rtinfo, 320int rt_graph_check_task_block(struct rt_graph_info *rtinfo,
320 struct pevent *pevent, struct record *record, 321 struct pevent *pevent, struct record *record,
@@ -431,3 +432,119 @@ void init_rt_event_cache(struct rt_graph_info *rtinfo)
431 rtinfo->resume_pid_field = NULL; 432 rtinfo->resume_pid_field = NULL;
432 rtinfo->resume_ts_field = NULL; 433 rtinfo->resume_ts_field = NULL;
433} 434}
435
436/**
437 * get_rts - extract real-time timestamp from a record
438 *
439 * This will only have to extract the timestamp once; after the time
440 * is extracted it will be cached in the record itself.
441 */
442unsigned long long
443get_rts(struct graph_info *ginfo, struct record *record)
444{
445 gint epid, eid;
446 unsigned long long ts;
447 if (!record->cached_rts) {
448 rt_graph_check_any(&ginfo->rtinfo, ginfo->pevent, record,
449 &epid, &eid, &ts);
450 record->cached_rts = ts;
451 } else
452 ts = record->cached_rts;
453 return ts;
454}
455
456/**
457 * next_rts - find a real-time timestamp AROUND an FTRACE time
458 * @ginfo: Current state of the graph
459 * @cpu: CPU to search
460 * @ft_target: FTRACE time to seek towards
461 *
462 * Returns the RT time of a record CLOSELY BEFORE @ft_time.
463 */
464unsigned long long
465next_rts(struct graph_info *ginfo, int cpu, unsigned long long ft_target)
466{
467 struct record *record;
468 unsigned long long ts = 0ULL;
469 tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, ft_target);
470 record = tracecmd_read_data(ginfo->handle, cpu);
471 if (record) {
472 ts = get_rts(ginfo, record);
473 free_record(record);
474 return ts;
475 } else
476 return 0;
477}
478
479/**
480 * set_cpu_to_rts - seek CPU to a time closely preceding a real-time timestamp
481 * @ginfo: Current state o the graph
482 * @cpu: The CPU to seek
483 * @rt_target: RT time to seek towards
484 *
485 * This seeks to a real-time timestamp, not the default ftrace timestamps.
486 * The @cpu seek location will be placed before the given time, but will
487 * not necessarily be placed _right_ before the time.
488 */
489void
490set_cpu_to_rts(struct graph_info *ginfo, unsigned long long rt_target, int cpu)
491{
492 struct record *record;
493 unsigned long long last_rts, rts, seek_time, last_seek;
494 long long diff;
495
496 rts = next_rts(ginfo, cpu, rt_target);
497 diff = rt_target - rts;
498
499 /* "Guess" a new target based on difference */
500 seek_time = rt_target + diff;
501 rts = next_rts(ginfo, cpu, seek_time);
502 diff = rt_target - rts;
503
504 /* Zero in in 1.5x the difference increments */
505 if (rts && diff > 0) {
506 /* rts rt_target
507 * seek ?
508 * ---|---->>----|---
509 */
510 do {
511 last_seek = seek_time;
512 last_rts = rts;
513 seek_time = seek_time + 1.5 * (rt_target - rts);
514 rts = next_rts(ginfo, cpu, seek_time);
515 } while (rts < rt_target && last_rts != rts);
516 tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, last_seek);
517 seek_time = last_seek;
518 } else if (rts && diff < 0) {
519 /* rt_target rts
520 * ? seek
521 * ---|----<<----|---
522 */
523 do {
524 seek_time = seek_time - 1.5 * (rts - rt_target);
525 rts = next_rts(ginfo, cpu, seek_time);
526 } while (rts > rt_target);
527 }
528
529 /* Get to first record at or after time */
530 while ((record = tracecmd_read_data(ginfo->handle, cpu))) {
531 if (get_rts(ginfo, record) >= rt_target)
532 break;
533 free_record(record);
534 }
535 if (record) {
536 tracecmd_set_cursor(ginfo->handle, cpu, record->offset);
537 free_record(record);
538 } else
539 tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, seek_time);
540}
541
542/**
543 * set_cpus_to_time - seek all cpus to real-time @rt_target
544 */
545void set_cpus_to_rts(struct graph_info *ginfo, unsigned long long rt_target)
546{
547 int cpu;
548 for (cpu = 0; cpu < ginfo->cpus; cpu++)
549 set_cpu_to_rts(ginfo, rt_target, cpu);
550}
diff --git a/rt-graph.h b/rt-graph.h
index eeae270..1a6ef0c 100644
--- a/rt-graph.h
+++ b/rt-graph.h
@@ -65,7 +65,7 @@ struct ts_list {
65/* Event parsers */ 65/* Event parsers */
66int rt_graph_check_any(struct rt_graph_info *rtinfo, 66int rt_graph_check_any(struct rt_graph_info *rtinfo,
67 struct pevent *pevent, struct record *record, 67 struct pevent *pevent, struct record *record,
68 gint *pid, unsigned long long *ts); 68 gint *pid, gint *eid, unsigned long long *ts);
69int rt_graph_check_task_param(struct rt_graph_info *rtinfo, struct pevent *pevent, 69int rt_graph_check_task_param(struct rt_graph_info *rtinfo, struct pevent *pevent,
70 struct record *record, gint *pid, 70 struct record *record, gint *pid,
71 unsigned long long *wcet, 71 unsigned long long *wcet,
@@ -91,4 +91,14 @@ int rt_graph_check_task_resume(struct rt_graph_info *rtinfo, struct pevent *peve
91 unsigned long long *when); 91 unsigned long long *when);
92void init_rt_event_cache(struct rt_graph_info *rtinfo); 92void init_rt_event_cache(struct rt_graph_info *rtinfo);
93 93
94/* Methods for dealing with RT timestamps */
95unsigned long long get_rts(struct graph_info *ginfo,
96 struct record *record);
97unsigned long long next_rts(struct graph_info *ginfo, int cpu,
98 unsigned long long ft_target);
99void set_cpu_to_rts(struct graph_info *ginfo,
100 unsigned long long rt_target, int cpu);
101void set_cpus_to_rts(struct graph_info *ginfo,
102 unsigned long long rt_target);
103
94#endif 104#endif
diff --git a/rt-plot-task.c b/rt-plot-task.c
index 31f4c7e..f28b7b1 100644
--- a/rt-plot-task.c
+++ b/rt-plot-task.c
@@ -1,4 +1,5 @@
1#include "trace-graph.h" 1#include "trace-graph.h"
2#include "trace-filter.h"
2 3
3#define LLABEL 30 4#define LLABEL 30
4 5
@@ -13,105 +14,11 @@
13#define dprintf(l, x...) do { if (0) printf(x); } while (0) 14#define dprintf(l, x...) do { if (0) printf(x); } while (0)
14#endif 15#endif
15 16
16/*
17 * Extract timestamp from a record, attempting to use cache if possible
18 */
19static unsigned long long
20get_rts(struct graph_info *ginfo, struct record *record)
21{
22 gint epid;
23 unsigned long long ts;
24 if (!record->cached_rts) {
25 rt_graph_check_any(&ginfo->rtinfo, ginfo->pevent, record,
26 &epid, &ts);
27 record->cached_rts = ts;
28 } else
29 ts = record->cached_rts;
30 return ts;
31}
32
33/*
34 * Get the real-time timestamp of the next record at time
35 */
36static unsigned long long
37next_rts(struct graph_info *ginfo, int cpu, unsigned long long time)
38{
39 struct record *record;
40 unsigned long long ts;
41 tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time);
42 record = tracecmd_read_data(ginfo->handle, cpu);
43 if (record) {
44 ts = get_rts(ginfo, record);
45 free_record(record);
46 return ts;
47 } else
48 return 0;
49}
50
51static void set_cpu_to_time(int cpu, struct graph_info *ginfo, unsigned long long time)
52{
53 struct record *record;
54 unsigned long long last_rts, rts, seek_time, last_seek;
55 long long diff;
56
57 rts = next_rts(ginfo, cpu, time);
58 diff = time - rts;
59
60 /* "Guess" a new target based on difference */
61 seek_time = time + diff;
62 rts = next_rts(ginfo, cpu, seek_time);
63 diff = time - rts;
64
65 /* Zero in in 1.5x the difference increments */
66 if (rts && diff > 0) {
67 /* rts time
68 * seek ?
69 * ---|---->>----|---
70 */
71 do {
72 last_seek = seek_time;
73 last_rts = rts;
74 seek_time = seek_time + 1.5 * (time - rts);
75 rts = next_rts(ginfo, cpu, seek_time);
76 } while (rts < time && last_rts != rts);
77 tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, last_seek);
78 seek_time = last_seek;
79 } else if (rts && diff < 0) {
80 /* time rts
81 * ? seek
82 * ---|----<<----|---
83 */
84 do {
85 seek_time = seek_time - 1.5 * (rts - time);
86 rts = next_rts(ginfo, cpu, seek_time);
87 } while (rts > time);
88 }
89
90 /* Get to first record at or after time */
91 while ((record = tracecmd_read_data(ginfo->handle, cpu))) {
92 if (get_rts(ginfo, record) >= time)
93 break;
94 free_record(record);
95 }
96 if (record) {
97 tracecmd_set_cursor(ginfo->handle, cpu, record->offset);
98 free_record(record);
99 } else
100 tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, seek_time);
101}
102
103void set_cpus_to_time(struct graph_info *ginfo, unsigned long long time)
104{
105 int cpu;
106 for (cpu = 0; cpu < ginfo->cpus; cpu++)
107 set_cpu_to_time(cpu, ginfo, time);
108}
109
110static gboolean record_matches_pid(struct graph_info *ginfo, 17static gboolean record_matches_pid(struct graph_info *ginfo,
111 struct record *record, 18 struct record *record,
112 int match_pid) 19 int match_pid)
113{ 20{
114 gint dint, pid = 0, match = 0; 21 gint dint, pid = 0, match;
115 unsigned long long dull; 22 unsigned long long dull;
116 struct rt_graph_info *rtg_info = &ginfo->rtinfo; 23 struct rt_graph_info *rtg_info = &ginfo->rtinfo;
117 24
@@ -125,29 +32,46 @@ static gboolean record_matches_pid(struct graph_info *ginfo,
125 rt_graph_check_task_completion(MARGS, &dint, &dull) || 32 rt_graph_check_task_completion(MARGS, &dint, &dull) ||
126 rt_graph_check_task_block(MARGS, &dull) || 33 rt_graph_check_task_block(MARGS, &dull) ||
127 rt_graph_check_task_resume(MARGS, &dull) || 34 rt_graph_check_task_resume(MARGS, &dull) ||
128 rt_graph_check_any(MARGS, &dull); 35 rt_graph_check_any(MARGS, &dint, &dull);
129#undef MARGS 36#undef MARGS
130 return match && pid == match_pid; 37 return pid == match_pid;
131} 38}
132 39
133struct record* 40static struct record*
134find_record(struct graph_info *ginfo, gint pid, guint64 time) 41__find_record(struct graph_info *ginfo, gint pid, guint64 time, int display)
135{ 42{
136 int next_cpu, match; 43 int next_cpu, match, eid, is_sa = 0;
137 struct record *record = NULL; 44 struct record *record = NULL;
45 struct rt_graph_info *rtg_info = &ginfo->rtinfo;
138 46
139 set_cpus_to_time(ginfo, time); 47 set_cpus_to_rts(ginfo, time);
140 do { 48 do {
141 free_record(record); 49 free_record(record);
142 record = tracecmd_read_next_data(ginfo->handle, &next_cpu); 50 record = tracecmd_read_next_data(ginfo->handle, &next_cpu);
143 if (!record) 51 if (!record)
144 return NULL; 52 return NULL;
145 match = record_matches_pid(ginfo, record, pid); 53 match = record_matches_pid(ginfo, record, pid);
146 } while (!(get_rts(ginfo, record) > time && match)); 54 if (display) {
55 eid = pevent_data_type(ginfo->pevent, record);
56 is_sa = (eid == rtg_info->switch_away_id);
57 }
58 } while (!(get_rts(ginfo, record) > time && match && !is_sa));
147 59
148 return record; 60 return record;
149} 61}
150 62
63static inline struct record*
64find_record(struct graph_info *ginfo, gint pid, guint64 time)
65{
66 return __find_record(ginfo, pid, time, 0);
67}
68
69static inline struct record*
70find_display_record(struct graph_info *ginfo, gint pid, guint64 time)
71{
72 return __find_record(ginfo, pid, time, 1);
73}
74
151/* 75/*
152 * Update current job in @rtt_info, ensuring monotonic increase 76 * Update current job in @rtt_info, ensuring monotonic increase
153 */ 77 */
@@ -155,12 +79,12 @@ static int update_job(struct rt_task_info *rtt_info, int job)
155{ 79{
156 if (job < rtt_info->last_job) { 80 if (job < rtt_info->last_job) {
157 printf("Inconsistent job state for %d:%d -> %d\n", 81 printf("Inconsistent job state for %d:%d -> %d\n",
158 rtt_info->base.pid, rtt_info->last_job, job); 82 rtt_info->pid, rtt_info->last_job, job);
159 return 0; 83 return 0;
160 } else if (job > rtt_info->last_job) { 84 } else if (job > rtt_info->last_job) {
161 rtt_info->last_job = job; 85 rtt_info->last_job = job;
162 snprintf(rtt_info->label, LLABEL, "%d:%d", 86 snprintf(rtt_info->label, LLABEL, "%d:%d",
163 rtt_info->base.pid, rtt_info->last_job); 87 rtt_info->pid, rtt_info->last_job);
164 } 88 }
165 return 1; 89 return 1;
166} 90}
@@ -177,7 +101,7 @@ static int try_param(struct graph_info *ginfo, struct rt_task_info *rtt_info,
177 101
178 match = rt_graph_check_task_param(&ginfo->rtinfo, ginfo->pevent, 102 match = rt_graph_check_task_param(&ginfo->rtinfo, ginfo->pevent,
179 record, &pid, &wcet, &period); 103 record, &pid, &wcet, &period);
180 if (match && pid == rtt_info->base.pid) { 104 if (match && pid == rtt_info->pid) {
181 update_job(rtt_info, 0); 105 update_job(rtt_info, 0);
182 rtt_info->wcet = wcet; 106 rtt_info->wcet = wcet;
183 rtt_info->period = period; 107 rtt_info->period = period;
@@ -199,7 +123,7 @@ static int try_release(struct graph_info *ginfo, struct rt_task_info *rtt_info,
199 match = rt_graph_check_task_release(&ginfo->rtinfo, ginfo->pevent, 123 match = rt_graph_check_task_release(&ginfo->rtinfo, ginfo->pevent,
200 record, &pid, &job, 124 record, &pid, &job,
201 &release, &deadline); 125 &release, &deadline);
202 if (match && pid == rtt_info->base.pid) { 126 if (match && pid == rtt_info->pid) {
203 update_job(rtt_info, job); 127 update_job(rtt_info, job);
204 info->release = TRUE; 128 info->release = TRUE;
205 info->rtime = release; 129 info->rtime = release;
@@ -226,7 +150,7 @@ static int try_completion(struct graph_info *ginfo,
226 150
227 match = rt_graph_check_task_completion(&ginfo->rtinfo, ginfo->pevent, 151 match = rt_graph_check_task_completion(&ginfo->rtinfo, ginfo->pevent,
228 record, &pid, &job, &ts); 152 record, &pid, &job, &ts);
229 if (match && pid == rtt_info->base.pid) { 153 if (match && pid == rtt_info->pid) {
230 update_job(rtt_info, job); 154 update_job(rtt_info, job);
231 info->completion = TRUE; 155 info->completion = TRUE;
232 info->ctime = ts; 156 info->ctime = ts;
@@ -244,7 +168,7 @@ static int try_block(struct graph_info *ginfo, struct rt_task_info *rtt_info,
244 168
245 match = rt_graph_check_task_block(&ginfo->rtinfo, ginfo->pevent, 169 match = rt_graph_check_task_block(&ginfo->rtinfo, ginfo->pevent,
246 record, &pid, &ts); 170 record, &pid, &ts);
247 if (match && pid == rtt_info->base.pid) { 171 if (match && pid == rtt_info->pid) {
248 rtt_info->block_time = ts; 172 rtt_info->block_time = ts;
249 ret = 1; 173 ret = 1;
250 } 174 }
@@ -259,13 +183,13 @@ static int try_resume(struct graph_info *ginfo, struct rt_task_info *rtt_info,
259 183
260 match = rt_graph_check_task_resume(&ginfo->rtinfo, ginfo->pevent, 184 match = rt_graph_check_task_resume(&ginfo->rtinfo, ginfo->pevent,
261 record, &pid, &ts); 185 record, &pid, &ts);
262 if (match && pid == rtt_info->base.pid) { 186 if (match && pid == rtt_info->pid) {
263 info->box = TRUE; 187 /* info->box = TRUE; */
264 info->bcolor = 0x0; 188 /* info->bcolor = 0x0; */
265 info->bfill = TRUE; 189 /* info->bfill = TRUE; */
266 info->bthin = TRUE; 190 /* info->bthin = TRUE; */
267 info->bstart = rtt_info->block_time; 191 /* info->bstart = rtt_info->block_time; */
268 info->bend = ts; 192 /* info->bend = ts; */
269 193
270 rtt_info->block_time = 0ULL; 194 rtt_info->block_time = 0ULL;
271 195
@@ -283,12 +207,12 @@ try_switch_away(struct graph_info *ginfo, struct rt_task_info *rtt_info,
283 207
284 match = rt_graph_check_switch_away(&ginfo->rtinfo, ginfo->pevent, 208 match = rt_graph_check_switch_away(&ginfo->rtinfo, ginfo->pevent,
285 record, &pid, &job, &ts); 209 record, &pid, &job, &ts);
286 if (match && pid == rtt_info->base.pid) { 210 if (match && pid == rtt_info->pid) {
287 update_job(rtt_info, job); 211 update_job(rtt_info, job);
288 212
289 if (rtt_info->run_time && rtt_info->run_time < ts) { 213 if (rtt_info->run_time && rtt_info->run_time < ts) {
290 dprintf(3, "Box for %d:%d, %llu to %llu on CPU %d\n", 214 dprintf(3, "Box for %d:%d, %llu to %llu on CPU %d\n",
291 rtt_info->base.pid, rtt_info->last_job, 215 rtt_info->pid, rtt_info->last_job,
292 rtt_info->run_time, ts, rtt_info->last_cpu); 216 rtt_info->run_time, ts, rtt_info->last_cpu);
293 info->box = TRUE; 217 info->box = TRUE;
294 info->bcolor = hash_cpu(rtt_info->last_cpu); 218 info->bcolor = hash_cpu(rtt_info->last_cpu);
@@ -298,6 +222,7 @@ try_switch_away(struct graph_info *ginfo, struct rt_task_info *rtt_info,
298 info->blabel = rtt_info->label; 222 info->blabel = rtt_info->label;
299 } 223 }
300 224
225 dprintf(3, "Switch away at %llu\n", ts);
301 rtt_info->run_time = 0ULL; 226 rtt_info->run_time = 0ULL;
302 rtt_info->last_cpu = -1; 227 rtt_info->last_cpu = -1;
303 228
@@ -314,14 +239,14 @@ static int try_switch_to(struct graph_info *ginfo, struct rt_task_info *rtt_info
314 239
315 match = rt_graph_check_switch_to(&ginfo->rtinfo, ginfo->pevent, 240 match = rt_graph_check_switch_to(&ginfo->rtinfo, ginfo->pevent,
316 record, &pid, &job, &ts); 241 record, &pid, &job, &ts);
317 if (match && pid == rtt_info->base.pid) { 242 if (match && pid == rtt_info->pid) {
318 update_job(rtt_info, job); 243 update_job(rtt_info, job);
319 244
320 rtt_info->run_time = ts; 245 rtt_info->run_time = ts;
321 rtt_info->last_cpu = record->cpu; 246 rtt_info->last_cpu = record->cpu;
322 247
323 dprintf(3, "Switching to %d:%d at %llu on CPU %d\n", 248 dprintf(3, "Switching to %d:%d at %llu on CPU %d\n",
324 rtt_info->base.pid, rtt_info->last_job, 249 rtt_info->pid, rtt_info->last_job,
325 ts, rtt_info->last_cpu); 250 ts, rtt_info->last_cpu);
326 251
327 ret = 1; 252 ret = 1;
@@ -332,14 +257,17 @@ static int try_switch_to(struct graph_info *ginfo, struct rt_task_info *rtt_info
332static int try_other(struct graph_info *ginfo, struct rt_task_info *rtt_info, 257static int try_other(struct graph_info *ginfo, struct rt_task_info *rtt_info,
333 struct record *record, struct plot_info *info) 258 struct record *record, struct plot_info *info)
334{ 259{
335 int pid, epid, ret = 0; 260 int pid, eid, epid, my_pid, my_cpu, not_sa, ret = 0;
336 unsigned long long ts; 261 unsigned long long ts;
337 struct task_plot_info *task_info = &rtt_info->base;
338 262
339 pid = task_info->pid; 263 pid = rtt_info->pid;
340 rt_graph_check_any(&ginfo->rtinfo, ginfo->pevent, record, &epid, &ts); 264 rt_graph_check_any(&ginfo->rtinfo, ginfo->pevent, record,
265 &epid, &eid, &ts);
341 266
342 if (pid == epid || record->cpu == rtt_info->last_cpu) { 267 my_pid = (pid == epid);
268 my_cpu = (rtt_info->run_time && record->cpu == rtt_info->last_cpu);
269 not_sa = (eid != ginfo->rtinfo.switch_away_id);
270 if (not_sa && (my_pid || my_cpu)) {
343 info->line = TRUE; 271 info->line = TRUE;
344 info->lcolor = hash_pid(record->cpu); 272 info->lcolor = hash_pid(record->cpu);
345 info->ltime = ts; 273 info->ltime = ts;
@@ -385,7 +313,7 @@ get_previous_release(struct graph_info *ginfo, struct rt_task_info *rtt_info,
385 &release, &deadline); 313 &release, &deadline);
386 free_record(last_record); 314 free_record(last_record);
387 last_record = record; 315 last_record = record;
388 if (match && (pid == rtt_info->base.pid) && release <= time) { 316 if (match && (pid == rtt_info->pid) && release <= time) {
389 ret = record; 317 ret = record;
390 last_record = NULL; 318 last_record = NULL;
391 *out_job = job; 319 *out_job = job;
@@ -422,7 +350,7 @@ static int get_time_info(struct graph_info *ginfo,
422 350
423 /* Seek CPUs to first record after this time */ 351 /* Seek CPUs to first record after this time */
424 *out_job = *out_release = *out_deadline = 0; 352 *out_job = *out_release = *out_deadline = 0;
425 *out_record = find_record(ginfo, rtt_info->base.pid, time); 353 *out_record = find_record(ginfo, rtt_info->pid, time);
426 if (!*out_record) 354 if (!*out_record)
427 return 0; 355 return 0;
428 356
@@ -466,20 +394,16 @@ static int rt_task_plot_event(struct graph_info *ginfo, struct graph_plot *plot,
466 struct record *record, struct plot_info *info) 394 struct record *record, struct plot_info *info)
467{ 395{
468 struct rt_task_info *rtt_info = plot->private; 396 struct rt_task_info *rtt_info = plot->private;
469 struct task_plot_info *task_info = &rtt_info->base; 397 int match;
470 int match, cpu;
471 398
472 /* No more records, finish what we started */ 399 /* No more records, finish what we started */
473 if (!record) { 400 if (!record) {
474 if (task_info->last_cpu >= 0) { 401 if (rtt_info->last_cpu >= 0 &&
402 rtt_info->run_time) {
475 info->box = TRUE; 403 info->box = TRUE;
476 info->bstart = task_info->last_time; 404 info->bstart = rtt_info->last_time;
477 info->bend = ginfo->view_end_time; 405 info->bend = ginfo->view_end_time;
478 info->bcolor = hash_cpu(task_info->last_cpu); 406 info->bcolor = hash_cpu(rtt_info->last_cpu);
479 }
480 for (cpu = 0; cpu < ginfo->cpus; cpu++) {
481 free_record(task_info->last_records[cpu]);
482 task_info->last_records[cpu] = NULL;
483 } 407 }
484 return 0; 408 return 0;
485 } 409 }
@@ -493,25 +417,12 @@ static int rt_task_plot_event(struct graph_info *ginfo, struct graph_plot *plot,
493 try_resume(ginfo, rtt_info, record, info) || 417 try_resume(ginfo, rtt_info, record, info) ||
494 try_other(ginfo, rtt_info, record, info); 418 try_other(ginfo, rtt_info, record, info);
495 419
496 /* This record is neither on our CPU nor related to us, useless */ 420 if (match) {
497 if (!match && record->cpu != task_info->last_cpu) { 421 rtt_info->last_time = get_rts(ginfo, record);
498 if (!task_info->last_records[record->cpu]) { 422 rtt_info->last_cpu = record->cpu;
499 task_info->last_records[record->cpu] = record;
500 tracecmd_record_ref(record);
501 }
502 return 0;
503 }
504
505 if (!match) {
506 cpu = record->cpu;
507 /* We need some record, use this if none exist */
508 if (!task_info->last_records[cpu]) {
509 free_record(task_info->last_records[cpu]);
510 task_info->last_records[cpu] = record;
511 }
512 } 423 }
513 424
514 return 1; 425 return match;
515} 426}
516 427
517static void rt_task_plot_start(struct graph_info *ginfo, struct graph_plot *plot, 428static void rt_task_plot_start(struct graph_info *ginfo, struct graph_plot *plot,
@@ -520,8 +431,6 @@ static void rt_task_plot_start(struct graph_info *ginfo, struct graph_plot *plot
520 int i; 431 int i;
521 struct rt_task_info *rtt_info = plot->private; 432 struct rt_task_info *rtt_info = plot->private;
522 433
523 task_plot_start(ginfo, plot, time);
524
525 rtt_info->wcet = 0ULL; 434 rtt_info->wcet = 0ULL;
526 rtt_info->period = 0ULL; 435 rtt_info->period = 0ULL;
527 rtt_info->run_time = 0ULL; 436 rtt_info->run_time = 0ULL;
@@ -537,7 +446,7 @@ static void rt_task_plot_start(struct graph_info *ginfo, struct graph_plot *plot
537static void rt_task_plot_destroy(struct graph_info *ginfo, struct graph_plot *plot) 446static void rt_task_plot_destroy(struct graph_info *ginfo, struct graph_plot *plot)
538{ 447{
539 struct rt_task_info *rtt_info = plot->private; 448 struct rt_task_info *rtt_info = plot->private;
540 printf("Destroying plot %d\n", rtt_info->base.pid); 449 printf("Destroying plot %d\n", rtt_info->pid);
541 free(rtt_info->label); 450 free(rtt_info->label);
542 task_plot_destroy(ginfo, plot); 451 task_plot_destroy(ginfo, plot);
543} 452}
@@ -554,7 +463,7 @@ static int rt_task_plot_display_last_event(struct graph_info *ginfo,
554 struct rt_task_info *rtt_info = plot->private; 463 struct rt_task_info *rtt_info = plot->private;
555 464
556 offsets = save_offsets(ginfo); 465 offsets = save_offsets(ginfo);
557 record = find_record(ginfo, rtt_info->base.pid, time); 466 record = find_display_record(ginfo, rtt_info->pid, time);
558 restore_offsets(ginfo, offsets); 467 restore_offsets(ginfo, offsets);
559 if (!record) 468 if (!record)
560 return 0; 469 return 0;
@@ -592,14 +501,14 @@ static int rt_task_plot_display_info(struct graph_info *ginfo,
592 restore_offsets(ginfo, offsets); 501 restore_offsets(ginfo, offsets);
593 502
594 /* Show real-time data about time */ 503 /* Show real-time data about time */
595 pid = rtt_info->base.pid; 504 pid = rtt_info->pid;
596 comm = pevent_data_comm_from_pid(ginfo->pevent, pid); 505 comm = pevent_data_comm_from_pid(ginfo->pevent, pid);
597 trace_seq_printf(s, "%s - %d:%d\n", comm, pid, job); 506 trace_seq_printf(s, "%s - %d:%d\n", comm, pid, job);
598 507
599 if (record) { 508 if (record) {
600 rts = get_rts(ginfo, record); 509 rts = get_rts(ginfo, record);
510 eid = pevent_data_type(ginfo->pevent, record);
601 if (in_res(ginfo, rts, time)) { 511 if (in_res(ginfo, rts, time)) {
602 eid = pevent_data_type(ginfo->pevent, record);
603 event = pevent_data_event_from_type(ginfo->pevent, eid); 512 event = pevent_data_event_from_type(ginfo->pevent, eid);
604 if (event) { 513 if (event) {
605 trace_seq_puts(s, event->name); 514 trace_seq_puts(s, event->name);
@@ -626,14 +535,14 @@ static int rt_task_plot_match_time(struct graph_info *ginfo,
626 struct rt_task_info *rtt_info = plot->private; 535 struct rt_task_info *rtt_info = plot->private;
627 int next_cpu, match, ret; 536 int next_cpu, match, ret;
628 537
629 set_cpus_to_time(ginfo, time); 538 set_cpus_to_rts(ginfo, time);
630 539
631 do { 540 do {
632 free_record(record); 541 free_record(record);
633 record = tracecmd_read_next_data(ginfo->handle, &next_cpu); 542 record = tracecmd_read_next_data(ginfo->handle, &next_cpu);
634 if (!record) 543 if (!record)
635 return 0; 544 return 0;
636 match = record_matches_pid(ginfo, record, rtt_info->base.pid); 545 match = record_matches_pid(ginfo, record, rtt_info->pid);
637 } while ((!match && get_rts(ginfo, record) < time + 1) || 546 } while ((!match && get_rts(ginfo, record) < time + 1) ||
638 (match && get_rts(ginfo, record) < time)); 547 (match && get_rts(ginfo, record) < time));
639 548
@@ -649,14 +558,13 @@ rt_task_plot_find_record(struct graph_info *ginfo, struct graph_plot *plot,
649 unsigned long long time) 558 unsigned long long time)
650{ 559{
651 struct rt_task_info *rtt_info = plot->private; 560 struct rt_task_info *rtt_info = plot->private;
652 return find_record(ginfo, rtt_info->base.pid, time); 561 return find_record(ginfo, rtt_info->pid, time);
653} 562}
654 563
655 564
656static const struct plot_callbacks rt_task_cb = { 565static const struct plot_callbacks rt_task_cb = {
657 .start = rt_task_plot_start, 566 .start = rt_task_plot_start,
658 .destroy = rt_task_plot_destroy, 567 .destroy = rt_task_plot_destroy,
659
660 .plot_event = rt_task_plot_event, 568 .plot_event = rt_task_plot_event,
661 .display_last_event = rt_task_plot_display_last_event, 569 .display_last_event = rt_task_plot_display_last_event,
662 .display_info = rt_task_plot_display_info, 570 .display_info = rt_task_plot_display_info,
@@ -669,13 +577,25 @@ void rt_plot_task_update_callback(gboolean accept,
669 gint *non_select, 577 gint *non_select,
670 gpointer data) 578 gpointer data)
671{ 579{
672 graph_tasks_update_callback(TASK_PLOT_RT, rt_plot_task, 580 graph_tasks_update_callback(PLOT_TYPE_RT_TASK, rt_plot_task,
673 accept, selected, non_select, data); 581 accept, selected, non_select, data);
674} 582}
675 583
676void rt_plot_task_plotted(struct graph_info *ginfo, gint **plotted) 584void rt_plot_task_plotted(struct graph_info *ginfo, gint **plotted)
677{ 585{
678 graph_tasks_plotted(ginfo, TASK_PLOT_RT, plotted); 586 struct task_plot_info *task_info;
587 struct graph_plot *plot;
588 int count = 0;
589 int i;
590
591 *plotted = NULL;
592 for (i = 0; i < ginfo->plots; i++) {
593 plot = ginfo->plot_array[i];
594 if (plot->type != PLOT_TYPE_RT_TASK)
595 continue;
596 task_info = plot->private;
597 trace_array_add(plotted, &count, task_info->pid);
598 }
679} 599}
680 600
681void rt_plot_task(struct graph_info *ginfo, int pid, int pos) 601void rt_plot_task(struct graph_info *ginfo, int pid, int pos)
@@ -684,29 +604,27 @@ void rt_plot_task(struct graph_info *ginfo, int pid, int pos)
684 struct rt_task_info *rtt_info; 604 struct rt_task_info *rtt_info;
685 struct graph_plot *plot; 605 struct graph_plot *plot;
686 const char *comm; 606 const char *comm;
687 char *label; 607 char *plot_label;
688 int len; 608 int len;
689 609
690 if (!find_task_list(rtinfo->tasks, pid)) 610 if (!find_task_list(rtinfo->tasks, pid))
691 die("Cannot create RT plot of non-RT task %d!\n", pid); 611 die("Cannot create RT plot of non-RT task %d!\n", pid);
692 612
693 rtt_info = malloc_or_die(sizeof(*rtt_info)); 613 rtt_info = malloc_or_die(sizeof(*rtt_info));
614 rtt_info->pid = pid;
694 rtt_info->label = malloc_or_die(LLABEL); 615 rtt_info->label = malloc_or_die(LLABEL);
695 616
696 init_task_plot_info(ginfo, &rtt_info->base, TASK_PLOT_RT, pid); 617 /* Create plot */
697
698 comm = pevent_data_comm_from_pid(ginfo->pevent, pid); 618 comm = pevent_data_comm_from_pid(ginfo->pevent, pid);
699 len = strlen(comm) + 100; 619 len = strlen(comm) + 100;
700 label = malloc_or_die(len); 620 plot_label = malloc_or_die(len);
701 snprintf(label, len, "*%s-%d", comm, pid); 621 snprintf(plot_label, len, "*%s-%d", comm, pid);
702 rtt_info->pid = pid; 622 plot = trace_graph_plot_insert(ginfo, pos, plot_label, PLOT_TYPE_RT_TASK,
623 &rt_task_cb, rtt_info);
624 free(plot_label);
703 625
704 printf("Created plot for %s-%d / %d %p\n", comm, pid, rtt_info->base.pid, 626 printf("Created plot for %s-%d / %d %p\n", comm, pid, rtt_info->pid,
705 rtt_info); 627 rtt_info);
706 628
707 plot = trace_graph_plot_insert(ginfo, pos, label, PLOT_TYPE_TASK,
708 &rt_task_cb, rtt_info);
709 free(label);
710
711 trace_graph_plot_add_all_recs(ginfo, plot); 629 trace_graph_plot_add_all_recs(ginfo, plot);
712} 630}
diff --git a/rt-plot-task.h b/rt-plot-task.h
index a66de39..d3464e9 100644
--- a/rt-plot-task.h
+++ b/rt-plot-task.h
@@ -4,17 +4,18 @@
4#include "trace-plot-task.h" 4#include "trace-plot-task.h"
5 5
6struct rt_task_info { 6struct rt_task_info {
7 struct task_plot_info base;
8
9 int pid; 7 int pid;
10 unsigned long long wcet; 8 unsigned long long wcet;
11 unsigned long long period; 9 unsigned long long period;
12 10
11 /* For drawing squares */
13 unsigned long long run_time; 12 unsigned long long run_time;
14 unsigned long long block_time; 13 unsigned long long block_time;
15 14
15 /* For managing state */
16 int last_job; 16 int last_job;
17 int last_cpu; 17 int last_cpu;
18 unsigned long long last_time;
18 19
19 /* Used to get around bugs(ish) */ 20 /* Used to get around bugs(ish) */
20 unsigned long long first_rels[3]; 21 unsigned long long first_rels[3];
diff --git a/trace-graph.h b/trace-graph.h
index ee57be6..1cb77b9 100644
--- a/trace-graph.h
+++ b/trace-graph.h
@@ -41,6 +41,7 @@ enum graph_plot_type {
41 PLOT_TYPE_OTHER, 41 PLOT_TYPE_OTHER,
42 PLOT_TYPE_CPU, 42 PLOT_TYPE_CPU,
43 PLOT_TYPE_TASK, 43 PLOT_TYPE_TASK,
44 PLOT_TYPE_RT_TASK,
44}; 45};
45 46
46struct graph_plot; 47struct graph_plot;
diff --git a/trace-plot-task.c b/trace-plot-task.c
index abcdd89..d4b608b 100644
--- a/trace-plot-task.c
+++ b/trace-plot-task.c
@@ -713,10 +713,16 @@ static const struct plot_callbacks task_plot_cb = {
713 .destroy = task_plot_destroy 713 .destroy = task_plot_destroy
714}; 714};
715 715
716 716/**
717void graph_tasks_plotted(struct graph_info *ginfo, 717 * graph_plot_task_plotted - return what tasks are plotted
718 enum task_plot_type type, 718 * @ginfo: the graph info structure
719 gint **plotted) 719 * @plotted: returns an allocated array of gints holding the pids.
720 * the last pid is -1, NULL, if none are.
721 *
722 * @plotted must be freed with free() after this is called.
723 */
724void graph_plot_task_plotted(struct graph_info *ginfo,
725 gint **plotted)
720{ 726{
721 struct task_plot_info *task_info; 727 struct task_plot_info *task_info;
722 struct graph_plot *plot; 728 struct graph_plot *plot;
@@ -733,21 +739,7 @@ void graph_tasks_plotted(struct graph_info *ginfo,
733 } 739 }
734} 740}
735 741
736/** 742void graph_tasks_update_callback(enum graph_plot_type type,
737 * graph_plot_task_plotted - return what tasks are plotted
738 * @ginfo: the graph info structure
739 * @plotted: returns an allocated array of gints holding the pids.
740 * the last pid is -1, NULL, if none are.
741 *
742 * @plotted must be freed with free() after this is called.
743 */
744void graph_plot_task_plotted(struct graph_info *ginfo,
745 gint **plotted)
746{
747 graph_tasks_plotted(ginfo, TASK_PLOT_LINUX, plotted);
748}
749
750void graph_tasks_update_callback(enum task_plot_type type,
751 plot_task_cb plot_cb, 743 plot_task_cb plot_cb,
752 gboolean accept, 744 gboolean accept,
753 gint *selected, 745 gint *selected,
@@ -778,12 +770,9 @@ void graph_tasks_update_callback(enum task_plot_type type,
778 */ 770 */
779 for (i = ginfo->plots - 1; i >= 0; i--) { 771 for (i = ginfo->plots - 1; i >= 0; i--) {
780 plot = ginfo->plot_array[i]; 772 plot = ginfo->plot_array[i];
781 if (plot->type != PLOT_TYPE_TASK) 773 if (plot->type != type)
782 continue; 774 continue;
783
784 task_info = plot->private; 775 task_info = plot->private;
785 if (task_info->type != type)
786 continue;
787 776
788 /* If non are selected, then remove all */ 777 /* If non are selected, then remove all */
789 if (!select_size) { 778 if (!select_size) {
@@ -819,7 +808,7 @@ void graph_plot_task_update_callback(gboolean accept,
819 gint *non_select, 808 gint *non_select,
820 gpointer data) 809 gpointer data)
821{ 810{
822 graph_tasks_update_callback(TASK_PLOT_LINUX, graph_plot_task, 811 graph_tasks_update_callback(PLOT_TYPE_TASK, graph_plot_task,
823 accept, selected, non_select, data); 812 accept, selected, non_select, data);
824} 813}
825 814
@@ -850,17 +839,6 @@ void graph_plot_init_tasks(struct graph_info *ginfo)
850 &task_plot_cb, task_info); 839 &task_plot_cb, task_info);
851} 840}
852 841
853void init_task_plot_info(struct graph_info *ginfo,
854 struct task_plot_info *task_info,
855 enum task_plot_type type,
856 int pid)
857{
858 task_info->last_records =
859 malloc_or_die(sizeof(struct record *) * ginfo->cpus);
860 task_info->pid = pid;
861 task_info->type = type;
862}
863
864void graph_plot_task(struct graph_info *ginfo, int pid, int pos) 842void graph_plot_task(struct graph_info *ginfo, int pid, int pos)
865{ 843{
866 struct task_plot_info *task_info; 844 struct task_plot_info *task_info;
@@ -870,8 +848,9 @@ void graph_plot_task(struct graph_info *ginfo, int pid, int pos)
870 int len; 848 int len;
871 849
872 task_info = malloc_or_die(sizeof(*task_info)); 850 task_info = malloc_or_die(sizeof(*task_info));
873 851 task_info->last_records =
874 init_task_plot_info(ginfo, task_info, TASK_PLOT_LINUX, pid); 852 malloc_or_die(sizeof(struct record *) * ginfo->cpus);
853 task_info->pid = pid;
875 854
876 comm = pevent_data_comm_from_pid(ginfo->pevent, pid); 855 comm = pevent_data_comm_from_pid(ginfo->pevent, pid);
877 856
diff --git a/trace-plot-task.h b/trace-plot-task.h
index e050512..11cb4f8 100644
--- a/trace-plot-task.h
+++ b/trace-plot-task.h
@@ -8,12 +8,7 @@
8struct graph_info; 8struct graph_info;
9struct graph_plot; 9struct graph_plot;
10struct plot_info; 10struct plot_info;
11 11enum graph_plot_type;
12enum task_plot_type {
13 TASK_PLOT_OTHER,
14 TASK_PLOT_LINUX,
15 TASK_PLOT_RT
16};
17 12
18/** 13/**
19 * struct task_plot_info - information for plotting a single task 14 * struct task_plot_info - information for plotting a single task
@@ -25,7 +20,6 @@ enum task_plot_type {
25 * @display_wake_time: as above, but reset under some circumstances 20 * @display_wake_time: as above, but reset under some circumstances
26 * @wake_color: 21 * @wake_color:
27 * @last_cpu: cpu task is currently running on 22 * @last_cpu: cpu task is currently running on
28 * @type: type of task plot
29 */ 23 */
30struct task_plot_info { 24struct task_plot_info {
31 int pid; 25 int pid;
@@ -36,7 +30,6 @@ struct task_plot_info {
36 unsigned long long display_wake_time; 30 unsigned long long display_wake_time;
37 int wake_color; 31 int wake_color;
38 int last_cpu; 32 int last_cpu;
39 enum task_plot_type type;
40}; 33};
41 34
42/* Querying records */ 35/* Querying records */
@@ -95,15 +88,10 @@ void graph_plot_init_tasks(struct graph_info *ginfo);
95 88
96/* Shared functionality for inheriting structs */ 89/* Shared functionality for inheriting structs */
97typedef void (plot_task_cb)(struct graph_info *ginfo, int pid, int pos); 90typedef void (plot_task_cb)(struct graph_info *ginfo, int pid, int pos);
98void graph_tasks_update_callback(enum task_plot_type type, 91void graph_tasks_update_callback(enum graph_plot_type type,
99 plot_task_cb plot_cb, 92 plot_task_cb plot_cb,
100 gboolean accept, 93 gboolean accept,
101 gint *selected, 94 gint *selected,
102 gint *non_select, 95 gint *non_select,
103 gpointer data); 96 gpointer data);
104void init_task_plot_info(struct graph_info *ginfo,
105 struct task_plot_info *task_info,
106 enum task_plot_type type, int pid);
107void graph_tasks_plotted(struct graph_info *ginfo, enum task_plot_type type,
108 gint **plotted);
109#endif 97#endif