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