| 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"); |