03d7fdf3 |
1 | /* This file is part of the Linux Trace Toolkit viewer |
2 | * Copyright (C) 2003-2004 Michel Dagenais |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License Version 2 as |
6 | * published by the Free Software Foundation; |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License |
14 | * along with this program; if not, write to the Free Software |
15 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
16 | * MA 02111-1307, USA. |
17 | */ |
18 | |
19 | #ifdef HAVE_CONFIG_H |
20 | #include <config.h> |
21 | #endif |
22 | |
23 | #include <stdio.h> |
24 | #include <lttv/module.h> |
25 | #include <lttv/xenoltt_sim.h> |
26 | #include <lttv/stats.h> |
27 | #include <lttv/lttv.h> |
28 | #include <lttv/attribute.h> |
29 | #include <ltt/facility.h> |
30 | #include <ltt/trace.h> |
31 | #include <ltt/event.h> |
32 | #include <ltt/type.h> |
33 | |
34 | |
35 | /****************************************************************************************************************************/ |
36 | gboolean save_event(void *hook_data, void *call_data); |
37 | /****************************************************************************************************************************/ |
38 | |
39 | gboolean sim_every_event(void *hook_data, void *call_data) |
40 | { |
41 | LttvTracefileStats *tfcs = (LttvTracefileStats *)call_data; |
42 | |
43 | LttEvent *e = ltt_tracefile_get_event(tfcs->parent.parent.tf); |
44 | |
45 | LttvAttributeValue v; |
46 | |
47 | /* The current branch corresponds to the tracefile/process/interrupt state. |
48 | Statistics are added within it, to count the number of events of this |
49 | type occuring in this context. A quark has been pre-allocated for each |
50 | event type and is used as name. */ |
51 | |
52 | lttv_attribute_find(tfcs->current_event_types_tree, |
53 | ltt_eventtype_name(ltt_event_eventtype(e)), |
54 | LTTV_UINT, &v); |
55 | (*(v.v_uint))++; |
56 | return FALSE; |
57 | } |
58 | |
59 | // Hook wrapper. call_data is a traceset context. |
60 | gboolean lttv_xenoltt_sim_hook_add_event_hooks(void *hook_data, void *call_data) |
61 | { |
62 | LttvTracesetStats *tss = (LttvTracesetStats*)call_data; |
63 | |
64 | lttv_xenoltt_sim_add_event_hooks(tss); |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | void lttv_xenoltt_sim_add_event_hooks(LttvTracesetStats *self) |
70 | { |
71 | LttvTraceset *traceset = self->parent.parent.ts; |
72 | |
73 | guint i, j, k, l, nb_trace, nb_tracefile; |
74 | |
75 | LttvTraceStats *ts; |
76 | |
77 | LttvTracefileStats *tfs; |
78 | |
79 | GArray *hooks, *before_hooks; |
80 | |
81 | LttvTraceHook *hook; |
82 | |
83 | LttvTraceHookByFacility *thf; |
84 | |
85 | LttvAttributeValue val; |
86 | |
87 | gint ret; |
88 | gint hn; |
89 | |
90 | nb_trace = lttv_traceset_number(traceset); |
91 | for(i = 0 ; i < nb_trace ; i++) { |
92 | ts = (LttvTraceStats *)self->parent.parent.traces[i]; |
93 | |
94 | /* Find the eventtype id for the following events and register the |
95 | associated by id hooks. */ |
96 | |
97 | hooks = g_array_sized_new(FALSE, FALSE, sizeof(LttvTraceHook), 16); |
98 | g_array_set_size(hooks, 16); |
99 | hn=0; |
100 | /* |
101 | LTT_EVENT_XENOLTT_THREAD_INIT, |
102 | LTT_EVENT_XENOLTT_THREAD_SET_PERIOD, |
103 | LTT_EVENT_XENOLTT_THREAD_WAIT_PERIOD, |
104 | LTT_EVENT_XENOLTT_THREAD_MISSED_PERIOD, |
105 | LTT_EVENT_XENOLTT_THREAD_SUSPEND, |
106 | LTT_EVENT_XENOLTT_THREAD_START, |
107 | LTT_EVENT_XENOLTT_THREAD_RESUME, |
108 | LTT_EVENT_XENOLTT_THREAD_DELETE, |
109 | LTT_EVENT_XENOLTT_THREAD_UNBLOCK, |
110 | LTT_EVENT_XENOLTT_THREAD_RENICE, |
111 | LTT_EVENT_XENOLTT_TIMER_TICK, |
112 | LTT_EVENT_XENOLTT_SYNCH_SET_OWNER, |
113 | LTT_EVENT_XENOLTT_SYNCH_UNLOCK, |
114 | LTT_EVENT_XENOLTT_SYNCH_WAKEUP1, |
115 | LTT_EVENT_XENOLTT_SYNCH_WAKEUPX, |
116 | LTT_EVENT_XENOLTT_SYNCH_SLEEP_ON, |
117 | LTT_EVENT_XENOLTT_SYNCH_FLUSH, |
118 | LTT_EVENT_XENOLTT_SYNCH_FORGET, |
119 | LTT_EVENT_XENOLTT_THREAD_SWITCH; |
120 | */ |
121 | |
122 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
123 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_THREAD_INIT, |
124 | LTT_FIELD_XENOLTT_ADDRESS, 0, 0, |
125 | save_event, NULL, |
126 | &g_array_index(hooks, LttvTraceHook, hn++)); |
127 | if(ret) hn--; |
128 | |
129 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
130 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_THREAD_SET_PERIOD, |
131 | LTT_FIELD_XENOLTT_ADDRESS, 0, 0, |
132 | save_event, NULL, |
133 | &g_array_index(hooks, LttvTraceHook, hn++)); |
134 | if(ret) hn--; |
135 | |
136 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
137 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_THREAD_WAIT_PERIOD, |
138 | LTT_FIELD_XENOLTT_ADDRESS, 0, 0, |
139 | save_event, NULL, |
140 | &g_array_index(hooks, LttvTraceHook, hn++)); |
141 | if(ret) hn--; |
142 | |
143 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
144 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_THREAD_MISSED_PERIOD, |
145 | LTT_FIELD_XENOLTT_ADDRESS, 0, 0, |
146 | save_event, NULL, |
147 | &g_array_index(hooks, LttvTraceHook, hn++)); |
148 | if(ret) hn--; |
149 | |
150 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
151 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_THREAD_SUSPEND, |
152 | LTT_FIELD_XENOLTT_ADDRESS, 0, 0, |
153 | save_event, NULL, |
154 | &g_array_index(hooks, LttvTraceHook, hn++)); |
155 | if(ret) hn--; |
156 | |
157 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
158 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_THREAD_START, |
159 | LTT_FIELD_XENOLTT_ADDRESS, 0, 0, |
160 | save_event, NULL, |
161 | &g_array_index(hooks, LttvTraceHook, hn++)); |
162 | if(ret) hn--; |
163 | |
164 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
165 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_THREAD_RESUME, |
166 | LTT_FIELD_XENOLTT_ADDRESS, 0, 0, |
167 | save_event, NULL, |
168 | &g_array_index(hooks, LttvTraceHook, hn++)); |
169 | if(ret) hn--; |
170 | |
171 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
172 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_THREAD_DELETE, |
173 | LTT_FIELD_XENOLTT_ADDRESS, 0, 0, |
174 | save_event, NULL, |
175 | &g_array_index(hooks, LttvTraceHook, hn++)); |
176 | if(ret) hn--; |
177 | |
178 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
179 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_THREAD_SWITCH, |
180 | LTT_FIELD_XENOLTT_ADDRESS, 0, 0, |
181 | save_event, NULL, |
182 | &g_array_index(hooks, LttvTraceHook, hn++)); |
183 | if(ret) hn--; |
184 | |
185 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
186 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_THREAD_SWITCH, |
187 | LTT_FIELD_XENOLTT_ADDRESS_OUT, 0, 0, |
188 | save_event, NULL, |
189 | &g_array_index(hooks, LttvTraceHook, hn++)); |
190 | if(ret) hn--; |
191 | |
192 | ret = lttv_trace_find_hook(ts->parent.parent.t, |
193 | LTT_FACILITY_XENOLTT, LTT_EVENT_XENOLTT_TIMER_TICK, |
194 | LTT_FIELD_XENOLTT_ADDRESS, 0, 0, |
195 | save_event, NULL, |
196 | &g_array_index(hooks, LttvTraceHook, hn++)); |
197 | if(ret) hn--; |
198 | |
199 | g_array_set_size(hooks, hn); |
200 | |
201 | before_hooks = hooks; |
202 | |
203 | /* Add these hooks to each event_by_id hooks list */ |
204 | |
205 | nb_tracefile = ts->parent.parent.tracefiles->len; |
206 | |
207 | for(j = 0 ; j < nb_tracefile ; j++) { |
208 | tfs = LTTV_TRACEFILE_STATS(g_array_index(ts->parent.parent.tracefiles, |
209 | LttvTracefileContext*, j)); |
210 | lttv_hooks_add(tfs->parent.parent.event, sim_every_event, NULL, |
211 | LTTV_PRIO_DEFAULT); |
212 | |
213 | for(k = 0 ; k < before_hooks->len ; k++) { |
214 | hook = &g_array_index(before_hooks, LttvTraceHook, k); |
215 | for(l = 0; l<hook->fac_list->len;l++) { |
216 | thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l); |
217 | lttv_hooks_add( |
218 | lttv_hooks_by_id_find(tfs->parent.parent.event_by_id, thf->id), |
219 | thf->h, |
220 | thf, |
221 | LTTV_PRIO_DEFAULT); |
222 | } |
223 | } |
224 | |
225 | } |
226 | lttv_attribute_find(self->parent.parent.a, LTTV_STATS_BEFORE_HOOKS, |
227 | LTTV_POINTER, &val); |
228 | *(val.v_pointer) = before_hooks; |
229 | } |
230 | } |
231 | |
232 | // Hook wrapper. call_data is a traceset context. |
233 | gboolean lttv_xenoltt_sim_hook_remove_event_hooks(void *hook_data, void *call_data) |
234 | { |
235 | LttvTracesetStats *tss = (LttvTracesetStats*)call_data; |
236 | |
237 | lttv_xenoltt_sim_remove_event_hooks(tss); |
238 | |
239 | return 0; |
240 | } |
241 | |
242 | void lttv_xenoltt_sim_remove_event_hooks(LttvTracesetStats *self) |
243 | { |
244 | LttvTraceset *traceset = self->parent.parent.ts; |
245 | |
246 | guint i, j, k, l, nb_trace, nb_tracefile; |
247 | |
248 | LttvTraceStats *ts; |
249 | |
250 | LttvTracefileStats *tfs; |
251 | |
252 | GArray *before_hooks; |
253 | |
254 | LttvTraceHook *hook; |
255 | |
256 | LttvTraceHookByFacility *thf; |
257 | |
258 | LttvAttributeValue val; |
259 | |
260 | nb_trace = lttv_traceset_number(traceset); |
261 | for(i = 0 ; i < nb_trace ; i++) { |
262 | ts = (LttvTraceStats*)self->parent.parent.traces[i]; |
263 | lttv_attribute_find(self->parent.parent.a, LTTV_STATS_BEFORE_HOOKS, |
264 | LTTV_POINTER, &val); |
265 | before_hooks = *(val.v_pointer); |
266 | |
267 | /* Remove these hooks from each event_by_id hooks list */ |
268 | |
269 | nb_tracefile = ts->parent.parent.tracefiles->len; |
270 | |
271 | for(j = 0 ; j < nb_tracefile ; j++) { |
272 | tfs = LTTV_TRACEFILE_STATS(g_array_index(ts->parent.parent.tracefiles, |
273 | LttvTracefileContext*, j)); |
274 | lttv_hooks_remove_data(tfs->parent.parent.event, sim_every_event, |
275 | NULL); |
276 | |
277 | for(k = 0 ; k < before_hooks->len ; k++) { |
278 | hook = &g_array_index(before_hooks, LttvTraceHook, k); |
279 | for(l = 0 ; l < hook->fac_list->len ; l++) { |
280 | thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l); |
281 | lttv_hooks_remove_data( |
282 | lttv_hooks_by_id_find(tfs->parent.parent.event_by_id, thf->id), |
283 | thf->h, |
284 | thf); |
285 | } |
286 | } |
287 | } |
288 | g_debug("lttv_stats_remove_event_hooks()"); |
289 | g_array_free(before_hooks, TRUE); |
290 | } |
291 | } |
292 | |
293 | |
294 | |
295 | /****************************************************************************************************************************/ |
296 | |
297 | |
298 | |
299 | /* |
300 | This function will look into the thread list to find the corresponding thread and returns it |
301 | This way we will be able to add a new event to this thread events list. |
302 | */ |
303 | ThreadEventData* lookup_or_create_thread(gulong address, guint prio, LttTime creation_time, GQuark name){ |
304 | int i, index = 0; |
305 | ThreadEventData *temp_thread; |
306 | ThreadEventData *temp_thread_2 = g_new(ThreadEventData, 1); |
307 | temp_thread_2->address = address; |
308 | temp_thread_2->prio = prio; |
309 | temp_thread_2->creation_time = creation_time; |
310 | temp_thread_2->name = name; |
311 | temp_thread_2->event_list = g_array_new(FALSE, FALSE, sizeof(EventData*)); |
312 | |
313 | |
314 | for(i=0;i<thread_event_list->len;i++){ |
315 | temp_thread = g_array_index(thread_event_list, ThreadEventData*, i); |
316 | if (temp_thread->address == temp_thread_2->address && |
317 | ltt_time_compare(temp_thread->creation_time,temp_thread_2->creation_time) == 0) |
318 | return temp_thread; // Thread is found we return it |
319 | /* Otherwise we check for the priority, this will help us to defined the |
320 | index where to insert the thread. This way we don't to sort the thread list */ |
321 | else if(temp_thread_2->prio <= temp_thread->prio) index++; |
322 | } |
323 | |
324 | g_array_insert_val(thread_event_list,index,temp_thread_2); |
325 | |
326 | return temp_thread_2; // New inserted thread is returned |
327 | } |
328 | |
329 | void calculate_event_time(guint index, ThreadEventData *temp_thread){ |
330 | LttTime next_tick = ltt_time_zero, delay, preempt_begin, preempt_time = ltt_time_zero, |
331 | last_write_event_time, last_read_event_time, original_event_time; |
332 | EventData *new_event, *event; |
333 | int i,j, overruns = 0; |
334 | gboolean wait_period_called = FALSE; |
335 | RunningThread *temp_running_thread = NULL; |
336 | gboolean first_thread_switch, running = FALSE; |
337 | LttTime new_period = ltt_time_from_double(temp_thread->period); |
338 | |
339 | temp_thread->new_event_list = g_array_new(FALSE, FALSE, sizeof(EventData*)); |
340 | |
341 | // We will iterate on all event of this thread |
342 | for(i=0;i<temp_thread->event_list->len;i++){ |
343 | // for the first event read |
344 | if (i == 0){ |
345 | new_event = g_array_index(temp_thread->event_list, EventData*, i); |
346 | last_write_event_time = new_event->event_time; |
347 | last_read_event_time = new_event->event_time; |
348 | original_event_time = new_event->event_time; |
349 | } |
350 | else{ |
351 | last_write_event_time = new_event->event_time; |
352 | last_read_event_time = original_event_time; |
353 | new_event = g_array_index(temp_thread->event_list, EventData*, i); |
354 | original_event_time = new_event->event_time; |
355 | } |
356 | |
357 | // Calculate the delay between to following events |
358 | delay = ltt_time_sub(original_event_time,last_read_event_time); |
359 | delay = ltt_time_sub(delay,preempt_time); |
360 | |
361 | // We need to save all events from the timer_tick until the wait_period event |
362 | // At the same time we can calculate the new time of the event |
363 | if (new_event->name == LTT_EVENT_XENOLTT_TIMER_TICK){ |
364 | //printf("NEW PERIOD\n"); |
365 | // The first tick will be unchanged |
366 | if(ltt_time_compare(ltt_time_zero,next_tick) != 0){ |
367 | new_event->event_time = next_tick; |
368 | } |
369 | next_tick = ltt_time_add(new_event->event_time,new_period); |
370 | wait_period_called = FALSE; // We prepare for next period that should begin now |
371 | |
372 | g_array_append_val(temp_thread->new_event_list,new_event); // insert the new timer_tick |
373 | //printf("\tTIMER_TICK - TIME: %lu.%lu - %lu.%lu\n", original_event_time.tv_sec,original_event_time.tv_nsec,new_event->event_time.tv_sec,new_event->event_time.tv_nsec); |
374 | |
375 | first_thread_switch = TRUE; |
376 | preempt_time = ltt_time_zero; |
377 | preempt_begin = ltt_time_zero; |
378 | |
379 | /************************************************************************ |
380 | * Beginning of a new period |
381 | * We must check for thread_switching (preemption) |
382 | * new timer tick to create |
383 | * overrun to create |
384 | * event missed_period to create |
385 | ************************************************************************/ |
386 | |
387 | while(new_event->name != LTT_EVENT_XENOLTT_THREAD_WAIT_PERIOD){ // Until the end of the period |
388 | i++; |
389 | last_write_event_time = new_event->event_time; |
390 | last_read_event_time = original_event_time; |
391 | new_event = g_array_index(temp_thread->event_list, EventData*, i); |
392 | original_event_time = new_event->event_time; |
393 | |
394 | // Calculate the delay between to following events |
395 | delay = ltt_time_sub(original_event_time,last_read_event_time); |
396 | delay = ltt_time_sub(delay,preempt_time); |
397 | |
398 | // Need to test if we have exceeded the new period |
399 | if(new_event->name != LTT_EVENT_XENOLTT_TIMER_TICK){ |
400 | if (ltt_time_compare(ltt_time_add(last_write_event_time,delay),next_tick) > 0){ |
401 | EventData *tick_event = g_new(EventData, 1); |
402 | tick_event->event_time = next_tick; |
403 | tick_event->name = LTT_EVENT_XENOLTT_TIMER_TICK; |
404 | g_array_append_val(temp_thread->new_event_list,tick_event); |
405 | next_tick = ltt_time_add(tick_event->event_time,new_period); |
406 | //printf("\t%s - TIME: \t%lu.%lu\n", g_quark_to_string(tick_event->name),tick_event->event_time.tv_sec,tick_event->event_time.tv_nsec); |
407 | overruns++; |
408 | } |
409 | } |
410 | |
411 | |
412 | // Check and treat every kind of event |
413 | if(new_event->name == LTT_EVENT_XENOLTT_THREAD_INIT || |
414 | new_event->name == LTT_EVENT_XENOLTT_THREAD_SET_PERIOD || |
415 | new_event->name == LTT_EVENT_XENOLTT_THREAD_START || |
416 | new_event->name == LTT_EVENT_XENOLTT_THREAD_RESUME || |
417 | new_event->name == LTT_EVENT_XENOLTT_THREAD_RENICE || |
418 | new_event->name == LTT_EVENT_XENOLTT_THREAD_SUSPEND){ |
419 | new_event->event_time = ltt_time_add(last_write_event_time,delay); // New time of the event |
420 | // Insert event in the period list |
421 | g_array_append_val(temp_thread->new_event_list,new_event); |
422 | //printf("\t%s - TIME: %lu.%lu - %lu.%lu\n", g_quark_to_string(new_event->name),original_event_time.tv_sec,original_event_time.tv_nsec,new_event->event_time.tv_sec,new_event->event_time.tv_nsec); |
423 | } |
424 | // the first Thread_Switch indicate that the thread is now running |
425 | else if(new_event->name == LTT_EVENT_XENOLTT_THREAD_SWITCH){ |
426 | if (first_thread_switch){ |
427 | running = TRUE; |
428 | first_thread_switch = FALSE; |
429 | new_event->event_time = ltt_time_add(last_write_event_time,delay); // New time of the event |
430 | // Insert event in the period list |
431 | g_array_append_val(temp_thread->new_event_list,new_event); |
432 | //printf("\t%s - TIME: %lu.%lu - %lu.%lu\n", g_quark_to_string(new_event->name),original_event_time.tv_sec,original_event_time.tv_nsec,new_event->event_time.tv_sec,new_event->event_time.tv_nsec); |
433 | } |
434 | // Not the first thread switch, we will delete this event and the previous one that should be thread_suspend |
435 | else if(running){ |
436 | running = FALSE; // Stop the thread |
437 | new_event = g_array_index(temp_thread->event_list, EventData*, (i-1)); |
438 | preempt_begin = new_event->event_time;// Save the time of the preemption (time of the suspend event |
439 | } |
440 | // Thread is suspended and want to restart, delete the thread_switch and the following event that should be thread_resume |
441 | else{ |
442 | running = TRUE; // restart thread |
443 | i++; |
444 | new_event = g_array_index(temp_thread->event_list, EventData*, i); |
445 | preempt_time = ltt_time_add(preempt_time,ltt_time_sub(new_event->event_time,preempt_begin));// ignore the time spent in ready state |
446 | } |
447 | } |
448 | // Thread going in overrun |
449 | else if(new_event->name == LTT_EVENT_XENOLTT_TIMER_TICK){ |
450 | new_event->event_time = next_tick; |
451 | next_tick = ltt_time_add(new_event->event_time,new_period); |
452 | overruns++; // If wait_period has not been called, this means we are going in overrun |
453 | // Insert event in the period list |
454 | g_array_append_val(temp_thread->new_event_list,new_event); |
455 | //printf("\t%s - TIME: %lu.%lu - %lu.%lu\n", g_quark_to_string(new_event->name),original_event_time.tv_sec,original_event_time.tv_nsec,new_event->event_time.tv_sec,new_event->event_time.tv_nsec); |
456 | } |
457 | |
458 | if(new_event->name == LTT_EVENT_XENOLTT_THREAD_WAIT_PERIOD){ |
459 | new_event->event_time = ltt_time_add(last_write_event_time,delay); // New time of the event |
460 | g_array_append_val(temp_thread->new_event_list,new_event); |
461 | //printf("\t%s - TIME: %lu.%lu - %lu.%lu\n", g_quark_to_string(new_event->name),original_event_time.tv_sec,original_event_time.tv_nsec,new_event->event_time.tv_sec,new_event->event_time.tv_nsec); |
462 | //printf("END PERIOD\n"); |
463 | wait_period_called = TRUE; |
464 | if(overruns > 0){ |
465 | EventData *missed_period_event = g_new(EventData, 1); |
466 | missed_period_event->event_time = new_event->event_time; // Same time ?? |
467 | missed_period_event->name = LTT_EVENT_XENOLTT_THREAD_MISSED_PERIOD; |
468 | g_array_append_val(temp_thread->new_event_list,missed_period_event); |
469 | //printf("\t%s - TIME: %lu.%lu\n", g_quark_to_string(missed_period_event->name),missed_period_event->event_time.tv_sec,missed_period_event->event_time.tv_nsec); |
470 | } |
471 | overruns = 0; |
472 | // Period is finished |
473 | running = FALSE; |
474 | } |
475 | |
476 | if(new_event->name == LTT_EVENT_XENOLTT_THREAD_DELETE){ |
477 | // Insert event in the period list |
478 | new_event->event_time = ltt_time_add(last_write_event_time,delay); // New time of the event |
479 | g_array_append_val(temp_thread->new_event_list,new_event); |
480 | //printf("\t%s - TIME: %lu.%lu - %lu.%lu\n", g_quark_to_string(new_event->name),original_event_time.tv_sec,original_event_time.tv_nsec,new_event->event_time.tv_sec,new_event->event_time.tv_nsec); |
481 | break; |
482 | } |
483 | } |
484 | } |
485 | // For other events, simply save them with new time |
486 | else{ |
487 | if (new_event->name != LTT_EVENT_XENOLTT_THREAD_MISSED_PERIOD){ |
488 | new_event->event_time = ltt_time_add(last_write_event_time,delay); // New time of the event |
489 | g_array_append_val(temp_thread->new_event_list,new_event); |
490 | //printf("NO_PERIOD %s - TIME: %lu.%lu - %lu.%lu\n", g_quark_to_string(new_event->name),original_event_time.tv_sec,original_event_time.tv_nsec,new_event->event_time.tv_sec,new_event->event_time.tv_nsec); |
491 | } |
492 | } |
493 | } |
494 | |
495 | // printf("fin phase 1\n"); |
496 | // Now we have a full list of events representing the simulation of the current task |
497 | // Last step consist of checking if this thread will be preempted by others |
498 | // To see that, we will check in the running_thread list to find some free time space |
499 | |
500 | // Iterate on the event_list and check for every thread_switch |
501 | gboolean not_running = TRUE; |
502 | overruns = 0; |
503 | wait_period_called = TRUE; |
504 | j=0; |
505 | delay = ltt_time_zero; |
506 | |
507 | // We will iterate on all event of this thread |
508 | for(i=0;i<temp_thread->new_event_list->len;i++){ |
509 | event = g_array_index(temp_thread->new_event_list, EventData*, i); |
510 | if (event->name == LTT_EVENT_XENOLTT_THREAD_SWITCH){ |
511 | // If thread is a switch in |
512 | if (not_running){ |
513 | not_running = FALSE; |
514 | // Check if cpu is free at this time, look for the nearest begin execution time period(before current time) |
515 | for(;j<running_thread->len;j++){ |
516 | temp_running_thread = g_array_index(running_thread, RunningThread*, j); |
517 | |
518 | if (ltt_time_compare(event->event_time,temp_running_thread->begin_time) >= 0){ |
519 | if (ltt_time_compare(event->event_time,temp_running_thread->end_time) <= 0){ |
520 | // Compute delay to insert in all following events |
521 | delay = ltt_time_add(delay,ltt_time_sub(temp_running_thread->end_time,event->event_time)); |
522 | // new event time is the time of the next switch out |
523 | event->event_time = temp_running_thread->end_time; |
524 | // event_time we be tested on next entry |
525 | } |
526 | } |
527 | else{ |
528 | break; |
529 | } |
530 | } |
531 | |
532 | // At this time we should have found a free starting position |
533 | RunningThread *new_running = g_new(RunningThread,1); |
534 | new_running->thread = temp_thread; |
535 | new_running->begin_time = event->event_time; |
536 | // printf("Begin: %lu.%lu\n",new_running->begin_time.tv_sec,new_running->begin_time.tv_nsec); |
537 | |
538 | for(i++;i<temp_thread->new_event_list->len;i++){ |
539 | event = g_array_index(temp_thread->new_event_list, EventData*, i); |
540 | |
541 | // Don't delay Timer_Tick |
542 | if (event->name == LTT_EVENT_XENOLTT_TIMER_TICK){ |
543 | // Beginning of a period |
544 | if (wait_period_called){ |
545 | wait_period_called = FALSE; |
546 | overruns = 0; |
547 | delay = ltt_time_zero; |
548 | } |
549 | // We are going in overrun |
550 | else{ |
551 | overruns++; |
552 | } |
553 | } |
554 | |
555 | // On Switch_Out event, save the thread running time in the running_thread list |
556 | else if(event->name == LTT_EVENT_XENOLTT_THREAD_SWITCH){ |
557 | new_running->end_time = event->event_time; |
558 | g_array_insert_val(running_thread,j,new_running); |
559 | not_running = TRUE; |
560 | break; |
561 | } |
562 | // All other events may be preempt by another task including the thread_switch |
563 | else{ |
564 | // insert the delay due to previous preemption in this period |
565 | event->event_time = ltt_time_add(event->event_time,delay); |
566 | |
567 | |
568 | // We must check the if the next running thread is beginning before the event_time |
569 | // Note that temp_running_thread can be the last running_thread of the list |
570 | // or the next running thread if we have found a free time space between |
571 | // two threads |
572 | if(temp_running_thread != NULL && running_thread->len > 0){ |
573 | // Another task is alreday running we will insert a thread switch out |
574 | if(ltt_time_compare(event->event_time,temp_running_thread->begin_time) >= 0 && |
575 | ltt_time_compare(event->event_time,temp_running_thread->end_time) < 0){ |
576 | // If running task ends before the event_time, no delay is needed |
577 | // but if taks finishes after the event, we must move the event at the end time |
578 | if(ltt_time_compare(event->event_time,temp_running_thread->end_time) < 0) |
579 | delay = ltt_time_add(delay,ltt_time_sub(temp_running_thread->end_time,event->event_time)); |
580 | |
581 | // Insert a thread switch in that will be check at next iteration |
582 | new_event = g_new(EventData, 1); |
583 | new_event->event_time = temp_running_thread->end_time; |
584 | new_event->name = LTT_EVENT_XENOLTT_THREAD_SWITCH; |
585 | g_array_insert_val(temp_thread->new_event_list,i,new_event); |
586 | |
587 | // Thread switch out |
588 | new_event = g_new(EventData, 1); |
589 | new_event->event_time = temp_running_thread->begin_time; |
590 | new_event->name = LTT_EVENT_XENOLTT_THREAD_SWITCH; |
591 | g_array_insert_val(temp_thread->new_event_list,i,new_event); |
592 | |
593 | // Insert the thread in the running thread |
594 | new_running->end_time = temp_running_thread->begin_time; |
595 | g_array_insert_val(running_thread,j,new_running); |
596 | // printf("%lu.%lu\n",new_running->begin_time.tv_sec,new_running->begin_time.tv_nsec); |
597 | not_running = TRUE; |
598 | |
599 | break; |
600 | } |
601 | } |
602 | } |
603 | } |
604 | } |
605 | } |
606 | } |
607 | |
608 | |
609 | |
610 | /* |
611 | //Print the new thread simulation |
612 | for(j=0;j<new_event_list->len;j++){ |
613 | event = g_array_index(new_event_list, EventData*, j); |
614 | printf("%s - TIME: %lu.%lu\n", g_quark_to_string(event->name),event->event_time.tv_sec,event->event_time.tv_nsec); |
615 | } |
616 | */ |
617 | } |
618 | |
619 | void simulate_high_priority_thread(ThreadEventData* thread){ |
620 | EventData *event; |
621 | gboolean running = FALSE; |
622 | RunningThread *run_thread = g_new(RunningThread, 1); |
623 | RunningThread *temp_thread; |
624 | int i,j; |
625 | LttTime begin_time = ltt_time_zero; |
626 | LttTime end_time = ltt_time_zero; |
627 | gboolean inserted; |
628 | |
629 | for(i=0;i<thread->event_list->len;i++){ |
630 | event = g_array_index(thread->event_list, EventData*, i); |
631 | |
632 | if(event->name == LTT_EVENT_XENOLTT_THREAD_SWITCH){ |
633 | if(running){ |
634 | running = FALSE; |
635 | end_time = event->event_time; |
636 | run_thread = g_new(RunningThread, 1); |
637 | run_thread->thread = thread; |
638 | run_thread->begin_time = begin_time; |
639 | run_thread->end_time = end_time; |
640 | |
641 | inserted = FALSE; |
642 | for(j=0;j<running_thread->len;j++){ |
643 | temp_thread = g_array_index(running_thread, RunningThread*, j); |
644 | if (ltt_time_compare(temp_thread->begin_time,run_thread->begin_time) > 0){ |
645 | g_array_insert_val(running_thread,j,run_thread); |
646 | inserted = TRUE; |
647 | break; |
648 | } |
649 | } |
650 | if (!inserted) g_array_append_val(running_thread,run_thread); |
651 | } |
652 | else{ |
653 | running = TRUE; |
654 | begin_time = event->event_time; |
655 | } |
656 | } |
657 | } |
658 | thread->new_event_list = thread->event_list; |
659 | } |
660 | |
661 | GArray* get_thread_list(){ |
662 | return thread_event_list; |
663 | } |
664 | |
665 | void compute_simulation(guint index, guint period, FILE *a_file){ |
666 | |
667 | int i,j; |
668 | ThreadEventData *temp_thread; |
669 | RunningThread *run_thread; |
670 | EventData *event; |
671 | |
672 | |
673 | // First, set the new period of the thread |
674 | temp_thread = g_array_index(thread_event_list, ThreadEventData*, index); |
675 | temp_thread->period = period; |
676 | |
677 | running_thread = g_array_new(FALSE, FALSE, sizeof(RunningThread*)); |
678 | |
679 | fprintf(a_file,"<EVENTS_LIST>\n"); |
680 | |
681 | /* |
682 | First, we need to ignore all task with higher priority |
683 | than the task we want to simulate that's why we begin the simulation |
684 | from the thread index which we will modify the period |
685 | */ |
686 | for(i=0;i<index;i++){ |
687 | temp_thread = g_array_index(thread_event_list, ThreadEventData*, i); |
688 | simulate_high_priority_thread(temp_thread); |
689 | fprintf(a_file,"\t<TASK NAME=\"%s\" ADDRESS=\"%p\" PRIORITY=\"%u\" PERIOD=\"%u\">\n",g_quark_to_string(temp_thread->name),(void *) temp_thread->address, temp_thread->prio, temp_thread->period); |
690 | for(j=0; j<temp_thread->new_event_list->len;j++){ |
691 | event = g_array_index(temp_thread->new_event_list, EventData*, j); |
692 | fprintf(a_file,"\t\t<EVENT NAME=\"%s\" TIME=\"%lu.%lu\">\n",g_quark_to_string(event->name), event->event_time.tv_sec,event->event_time.tv_nsec); |
693 | } |
694 | fprintf(a_file,"\t</TASK>\n"); |
695 | } |
696 | |
697 | for(i=index;i<thread_event_list->len;i++){ |
698 | temp_thread = g_array_index(thread_event_list, ThreadEventData*, i); |
699 | |
700 | // We will simulate this thread considering all higher priority threads |
701 | calculate_event_time(i, temp_thread); |
702 | fprintf(a_file,"\t<TASK NAME=\"%s\" ADDRESS=\"%p\" PRIORITY=\"%u\" PERIOD=\"%u\">\n",g_quark_to_string(temp_thread->name), (void *) temp_thread->address, temp_thread->prio, temp_thread->period); |
703 | for(j=0; j<temp_thread->new_event_list->len;j++){ |
704 | event = g_array_index(temp_thread->new_event_list, EventData*, j); |
705 | fprintf(a_file,"\t\t<EVENT NAME=\"%s\" TIME=\"%lu.%lu\">\n",g_quark_to_string(event->name), event->event_time.tv_sec,event->event_time.tv_nsec); |
706 | } |
707 | fprintf(a_file,"\t</TASK>\n"); |
708 | } |
709 | |
710 | |
711 | fprintf(a_file,"</EVENTS_LIST>\n"); |
712 | |
713 | fprintf(a_file,"<RUNNING_TASK>\n"); |
714 | |
715 | for(i=0;i<running_thread->len;i++){ |
716 | run_thread = g_array_index(running_thread, RunningThread*, i); |
717 | fprintf(a_file,"\t<TASK NAME=\"%s\" FROM=\"%lu.%lu\" TO=\"%lu.%lu\">\n",g_quark_to_string(run_thread->thread->name),run_thread->begin_time.tv_sec,run_thread->begin_time.tv_nsec, |
718 | run_thread->end_time.tv_sec,run_thread->end_time.tv_nsec); |
719 | } |
720 | |
721 | fprintf(a_file,"</RUNNING_TASK>\n"); |
722 | |
723 | } |
724 | |
725 | gboolean save_event(void *hook_data, void *call_data){ |
726 | LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; |
727 | LttvTracefileStats *tfcs = (LttvTracefileStats *)call_data; |
728 | LttvTraceState *ts = (LttvTraceState*)tfcs->parent.parent.t_context; |
729 | guint cpu = tfcs->parent.cpu; |
730 | LttvXenoThreadState *thread_info; |
731 | |
732 | LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; |
733 | LttEvent *e = ltt_tracefile_get_event(tfc->tf); |
734 | GQuark event_name = ltt_eventtype_name(ltt_event_eventtype(e)); |
735 | LttTime evtime = ltt_event_time(e); |
736 | |
737 | if (event_name == LTT_EVENT_XENOLTT_TIMER_TICK){ |
738 | gulong timer_address = ltt_event_get_long_unsigned(e, thf->f1); |
739 | thread_info = lttv_xeno_state_find_thread_from_timer(ts,cpu,timer_address); |
740 | } |
741 | else{ |
742 | gulong address = ltt_event_get_long_unsigned(e, thf->f1); |
743 | // First we need to lookup for the current thread in the list |
744 | thread_info = lttv_xeno_state_find_thread(ts,cpu,address); |
745 | } |
746 | |
747 | if (thread_info != NULL){ |
748 | ThreadEventData *thread = lookup_or_create_thread(thread_info->address, thread_info->prio, thread_info->creation_time, thread_info->name); |
749 | if (event_name == LTT_EVENT_XENOLTT_THREAD_SET_PERIOD) thread->period = thread_info->period; |
750 | //Thread is found in the table, we can insert the new event in the list |
751 | EventData *new_event = g_new(EventData, 1); |
752 | new_event->event_time = evtime; |
753 | new_event->name = event_name; |
754 | new_event->event = e; |
755 | g_array_append_val(thread->event_list,new_event); |
756 | } |
757 | |
758 | return FALSE; |
759 | } |
760 | |
761 | /****************************************************************************************************************************/ |
762 | |
763 | |
764 | |
765 | |
766 | static void module_init() |
767 | { |
768 | // Initialization of the 2 main lists used in this module |
769 | thread_event_list = g_array_new(FALSE, FALSE, sizeof(ThreadEventData*)); |
770 | running_thread = g_array_new(FALSE, FALSE, sizeof(RunningThread*)); |
771 | } |
772 | |
773 | static void module_destroy() |
774 | { |
775 | } |
776 | |
777 | |
778 | LTTV_MODULE("xenoltt_sim", "Compute Xenomai Tasks simulation", \ |
779 | "Simulate a task execution with a different period", \ |
780 | module_init, module_destroy, "state"); |