gdunlap/sched-sim.hg

annotate simulator.c @ 11:31f36108fc43

c02: Initial commit (clone of c01)
author George Dunlap <gdunlap@xensource.com>
date Tue Oct 20 18:14:37 2009 +0100 (2009-10-20)
parents 403bd7680df6
children f289f886cbcc
rev   line source
gdunlap@0 1 #include <stdlib.h>
gdunlap@0 2 #include <stdio.h>
gdunlap@0 3 #include <assert.h>
gdunlap@0 4
gdunlap@0 5 #define ASSERT assert
gdunlap@0 6
gdunlap@0 7 #include "stats.h"
gdunlap@0 8 #include "list.h"
gdunlap@0 9 #include "sim.h"
gdunlap@0 10 #include "workload.h"
gdunlap@2 11 #include "sched.h"
gdunlap@2 12 #include "options.h"
gdunlap@0 13
gdunlap@0 14 FILE *warn;
gdunlap@0 15
gdunlap@0 16 enum event_type {
gdunlap@0 17 EVT_BLOCK,
gdunlap@0 18 EVT_WAKE,
gdunlap@0 19 EVT_TIMER,
gdunlap@0 20 EVT_MAX
gdunlap@0 21 };
gdunlap@0 22
gdunlap@0 23 char *event_name[EVT_MAX] = {
gdunlap@0 24 [EVT_BLOCK]="block",
gdunlap@0 25 [EVT_WAKE] ="wake ",
gdunlap@0 26 [EVT_TIMER]="timer"
gdunlap@0 27 };
gdunlap@0 28
gdunlap@0 29 struct event {
gdunlap@0 30 struct list_head event_list;
gdunlap@0 31 enum event_type type;
gdunlap@0 32 int time;
gdunlap@0 33 int param; /* Usually VM ID */
gdunlap@0 34 };
gdunlap@0 35
gdunlap@0 36 char * state_name[STATE_MAX] = {
gdunlap@0 37 [STATE_RUN]= "run ",
gdunlap@0 38 [STATE_PREEMPT]="preempt",
gdunlap@0 39 [STATE_WAKE]= "wake ",
gdunlap@0 40 [STATE_BLOCK]= "block ",
gdunlap@0 41 };
gdunlap@0 42
gdunlap@0 43 struct {
gdunlap@0 44 int now;
gdunlap@0 45 struct list_head events;
gdunlap@0 46 struct list_head *timer;
gdunlap@0 47 const struct sched_ops *sched_ops;
gdunlap@0 48 } sim;
gdunlap@0 49
gdunlap@0 50
gdunlap@1 51 #ifndef VM_DATA_PUBLIC
gdunlap@1 52 struct global_vm_data {
gdunlap@0 53 int count;
gdunlap@0 54 struct vm vms[MAX_VMS];
gdunlap@1 55 };
gdunlap@1 56 #endif
gdunlap@1 57 struct global_vm_data V;
gdunlap@0 58
gdunlap@0 59 extern struct scheduler sched_rr;
gdunlap@5 60 extern struct scheduler sched_credit01;
gdunlap@11 61 extern struct scheduler sched_credit02;
gdunlap@0 62 int default_scheduler = 0;
gdunlap@0 63 struct scheduler *schedulers[] =
gdunlap@0 64 {
gdunlap@0 65 &sched_rr,
gdunlap@5 66 &sched_credit01,
gdunlap@11 67 &sched_credit02,
gdunlap@2 68 NULL
gdunlap@0 69 };
gdunlap@0 70
gdunlap@0 71 /* Options */
gdunlap@0 72
gdunlap@0 73 struct global_pcpu_data P;
gdunlap@0 74
gdunlap@0 75 /* Sim list interface */
gdunlap@0 76 /* NB: Caller must free if they're not going to use it! */
gdunlap@0 77 #define list_event(_l) (list_entry((_l), struct event, event_list))
gdunlap@0 78
gdunlap@0 79 struct event* sim_remove_event(int type, int param)
gdunlap@0 80 {
gdunlap@0 81 struct event* ret = NULL;
gdunlap@0 82 struct list_head *pos, *tmp;
gdunlap@0 83
gdunlap@0 84 /* Look for an event that matches this one and remove it */
gdunlap@0 85 list_for_each_safe(pos, tmp, &sim.events)
gdunlap@0 86 {
gdunlap@0 87 struct event *tevt = list_event(pos);
gdunlap@0 88 if ( tevt->type == type
gdunlap@0 89 && tevt->param == param )
gdunlap@0 90 {
gdunlap@0 91 list_del(pos);
gdunlap@0 92 ret = tevt;
gdunlap@0 93 break;
gdunlap@0 94 }
gdunlap@0 95 }
gdunlap@0 96
gdunlap@0 97 return ret;
gdunlap@0 98 }
gdunlap@0 99
gdunlap@0 100 void sim_insert_event(int time, int type, int param, int reset)
gdunlap@0 101 {
gdunlap@0 102 struct list_head *pos = NULL;
gdunlap@0 103 struct event *evt=NULL;
gdunlap@0 104
gdunlap@0 105 ASSERT(time >= sim.now);
gdunlap@0 106
gdunlap@0 107 if ( reset )
gdunlap@0 108 evt=sim_remove_event(type, param);
gdunlap@0 109
gdunlap@0 110 if ( !evt )
gdunlap@0 111 evt = (struct event *)malloc(sizeof(*evt));
gdunlap@0 112
gdunlap@0 113 evt->time = time;
gdunlap@0 114 evt->type = type;
gdunlap@0 115 evt->param = param;
gdunlap@0 116
gdunlap@1 117 printf(" [insert t%d %s param%d]\n",
gdunlap@1 118 evt->time, event_name[evt->type], evt->param);
gdunlap@1 119
gdunlap@0 120 INIT_LIST_HEAD(&evt->event_list);
gdunlap@0 121
gdunlap@0 122 list_for_each(pos, &sim.events)
gdunlap@0 123 {
gdunlap@0 124 if ( list_event(pos)->time > evt->time )
gdunlap@0 125 break;
gdunlap@0 126 }
gdunlap@0 127 list_add_tail(&evt->event_list, pos);
gdunlap@0 128 }
gdunlap@0 129
gdunlap@0 130 struct event sim_next_event(void)
gdunlap@0 131 {
gdunlap@0 132 struct event *evt;
gdunlap@0 133 struct list_head *next;
gdunlap@0 134
gdunlap@0 135 ASSERT(!list_empty(&sim.events));
gdunlap@0 136
gdunlap@0 137 next=sim.events.next;
gdunlap@0 138
gdunlap@0 139 list_del(next);
gdunlap@0 140
gdunlap@0 141 evt=list_event(next);
gdunlap@0 142
gdunlap@0 143 printf("%d: evt %s param%d\n",
gdunlap@0 144 evt->time, event_name[evt->type], evt->param);
gdunlap@0 145
gdunlap@0 146 free(evt);
gdunlap@0 147
gdunlap@0 148 /* XXX */
gdunlap@0 149 return *evt;
gdunlap@0 150 }
gdunlap@0 151
gdunlap@0 152 /*
gdunlap@0 153 * VM simulation
gdunlap@0 154 */
gdunlap@0 155 void vm_next_event(struct vm *v)
gdunlap@0 156 {
gdunlap@0 157 v->phase_index = ( v->phase_index + 1 ) % v->workload->phase_count;
gdunlap@0 158
gdunlap@0 159 v->e = v->workload->list + v->phase_index;
gdunlap@0 160 }
gdunlap@0 161
gdunlap@0 162 struct vm* vm_from_vid(int vid)
gdunlap@0 163 {
gdunlap@1 164 if ( vid >= V.count )
gdunlap@1 165 {
gdunlap@1 166 fprintf(stderr, "%s: v%d >= V.count %d!\n",
gdunlap@1 167 __func__, vid, V.count);
gdunlap@1 168 exit(1);
gdunlap@1 169 }
gdunlap@0 170
gdunlap@0 171 return V.vms + vid;
gdunlap@0 172 }
gdunlap@0 173
gdunlap@0 174 void vm_block(int now, struct vm *v)
gdunlap@0 175 {
gdunlap@0 176 ASSERT(v->e->type == PHASE_RUN);
gdunlap@0 177 v->time_this_phase += now - v->state_start_time;
gdunlap@3 178 printf("%s: v%d time_this_phase %d (evt %d)\n",
gdunlap@3 179 __func__, v->vid, v->time_this_phase, v->e->time);
gdunlap@0 180
gdunlap@0 181 ASSERT(v->time_this_phase == v->e->time);
gdunlap@0 182
gdunlap@0 183 vm_next_event(v);
gdunlap@0 184
gdunlap@0 185 ASSERT(v->e->type == PHASE_BLOCK);
gdunlap@0 186
gdunlap@0 187 sim_insert_event(now + v->e->time, EVT_WAKE, v->vid, 0);
gdunlap@0 188 v->time_this_phase = 0;
gdunlap@0 189 v->was_preempted = 0;
gdunlap@0 190 }
gdunlap@0 191
gdunlap@0 192 /* Called when wake event happens; increment timer and reset state */
gdunlap@0 193 void vm_wake(int now, struct vm *v)
gdunlap@0 194 {
gdunlap@0 195 ASSERT(v->e->type == PHASE_BLOCK);
gdunlap@0 196 ASSERT(v->time_this_phase == 0);
gdunlap@0 197
gdunlap@0 198 v->time_this_phase = now - v->state_start_time;
gdunlap@0 199
gdunlap@0 200 if ( now != 0 )
gdunlap@0 201 ASSERT(v->time_this_phase == v->e->time);
gdunlap@0 202
gdunlap@0 203 vm_next_event(v);
gdunlap@0 204
gdunlap@0 205 v->time_this_phase = 0;
gdunlap@0 206 }
gdunlap@0 207
gdunlap@0 208 /* Called when actually starting to run; make block event and set state */
gdunlap@0 209 void vm_run(int now, struct vm *v)
gdunlap@0 210 {
gdunlap@0 211 ASSERT(v->e->type == PHASE_RUN);
gdunlap@3 212 ASSERT(v->time_this_phase <= v->e->time);
gdunlap@0 213
gdunlap@0 214 sim_insert_event(now + v->e->time - v->time_this_phase, EVT_BLOCK, v->vid, 0);
gdunlap@0 215 v->state_start_time = now;
gdunlap@0 216 }
gdunlap@0 217
gdunlap@0 218 /* Preempt: Remove block event, update amount of runtime (so that when it runs again we can accurately
gdunlap@0 219 * generate a new block event) */
gdunlap@0 220 void vm_preempt(int now, struct vm *v)
gdunlap@0 221 {
gdunlap@0 222 struct event* evt;
gdunlap@0 223
gdunlap@3 224 v->time_this_phase += now - v->state_start_time;
gdunlap@3 225 printf("%s: v%d time_this_phase %d (evt %d)\n",
gdunlap@3 226 __func__, v->vid, v->time_this_phase, v->e->time);
gdunlap@3 227
gdunlap@3 228 ASSERT( v->time_this_phase <= v->e->time );
gdunlap@3 229
gdunlap@3 230 /* Only remove block event if we still have more runtime left */
gdunlap@0 231 if ( ( evt = sim_remove_event(EVT_BLOCK, v->vid) ) )
gdunlap@0 232 free(evt);
gdunlap@0 233
gdunlap@0 234 v->was_preempted = 1;
gdunlap@0 235 }
gdunlap@0 236
gdunlap@0 237
gdunlap@0 238 /* Callbacks the scheduler may make */
gdunlap@0 239 void sim_sched_timer(int time, int pid)
gdunlap@0 240 {
gdunlap@3 241 if ( time < 0 )
gdunlap@3 242 {
gdunlap@3 243 fprintf(stderr, "%s: Time %d < 0!\n",
gdunlap@3 244 __func__, time);
gdunlap@3 245 abort();
gdunlap@3 246 }
gdunlap@3 247
gdunlap@1 248 if ( pid >= P.count )
gdunlap@1 249 {
gdunlap@1 250 fprintf(stderr, "%s: p%d >= P.count %d\n",
gdunlap@1 251 __func__, pid, P.count);
gdunlap@1 252 exit(1);
gdunlap@1 253 }
gdunlap@1 254
gdunlap@1 255 if ( P.pcpus[pid].idle )
gdunlap@1 256 {
gdunlap@1 257 P.pcpus[pid].idle = 0;
gdunlap@1 258 P.idle--;
gdunlap@1 259 }
gdunlap@0 260 sim_insert_event(sim.now + time, EVT_TIMER, pid, 1);
gdunlap@0 261 }
gdunlap@0 262
gdunlap@6 263 void dump_running(int now, struct vm *v)
gdunlap@6 264 {
gdunlap@6 265 printf("runtime v%d %d %llu\n",
gdunlap@6 266 v->vid,
gdunlap@6 267 now,
gdunlap@6 268 v->stats.state[RUNSTATE_RUNNING].cycles);
gdunlap@6 269 }
gdunlap@6 270
gdunlap@0 271 void sim_runstate_change(int now, struct vm *v, int new_runstate)
gdunlap@0 272 {
gdunlap@0 273 int ostate, nstate;
gdunlap@0 274 int stime = now - v->state_start_time;
gdunlap@0 275
gdunlap@0 276 /* Valid transitions:
gdunlap@0 277 * + R->A (preemption): remove block event
gdunlap@0 278 * + R->B (block) : Insert wake event
gdunlap@0 279 * + A->R (run) : Insert block event
gdunlap@0 280 * + B->A (wake) : No action necessary
gdunlap@0 281 */
gdunlap@0 282
gdunlap@0 283 switch ( v->runstate )
gdunlap@0 284 {
gdunlap@0 285 case RUNSTATE_RUNNING:
gdunlap@0 286 ostate = STATE_RUN;
gdunlap@0 287 break;
gdunlap@0 288 case RUNSTATE_RUNNABLE:
gdunlap@0 289 if ( v->was_preempted )
gdunlap@0 290 ostate = STATE_PREEMPT;
gdunlap@0 291 else
gdunlap@0 292 ostate = STATE_WAKE;
gdunlap@0 293 break;
gdunlap@0 294 case RUNSTATE_BLOCKED:
gdunlap@0 295 ostate = STATE_BLOCK;
gdunlap@0 296 break;
gdunlap@0 297 }
gdunlap@0 298
gdunlap@0 299 update_cycles(&v->stats.state[ostate], stime);
gdunlap@0 300
gdunlap@6 301 if ( v->runstate == RUNSTATE_RUNNING
gdunlap@6 302 || new_runstate == RUNSTATE_RUNNING )
gdunlap@6 303 dump_running(now, v);
gdunlap@6 304
gdunlap@0 305
gdunlap@0 306 if ( v->runstate == RUNSTATE_RUNNING
gdunlap@0 307 && new_runstate == RUNSTATE_RUNNABLE )
gdunlap@0 308 {
gdunlap@0 309 nstate = STATE_PREEMPT;
gdunlap@0 310 vm_preempt(now, v);
gdunlap@0 311 }
gdunlap@0 312 else if ( v->runstate == RUNSTATE_RUNNING
gdunlap@0 313 && new_runstate == RUNSTATE_BLOCKED )
gdunlap@0 314 {
gdunlap@0 315 nstate = STATE_BLOCK;
gdunlap@0 316 vm_block(now, v);
gdunlap@0 317 }
gdunlap@0 318 else if ( v->runstate == RUNSTATE_RUNNABLE
gdunlap@0 319 && new_runstate == RUNSTATE_RUNNING )
gdunlap@0 320 {
gdunlap@0 321 nstate = STATE_RUN;
gdunlap@0 322 vm_run(now, v);
gdunlap@0 323 }
gdunlap@0 324 else if ( v->runstate == RUNSTATE_BLOCKED
gdunlap@0 325 && new_runstate == RUNSTATE_RUNNABLE )
gdunlap@0 326 {
gdunlap@0 327 nstate = STATE_WAKE;
gdunlap@0 328 vm_wake(now, v);
gdunlap@0 329 }
gdunlap@0 330 else
gdunlap@0 331 goto unexpected_transition;
gdunlap@0 332
gdunlap@0 333 printf("%d: v%d %s %d -> %s\n",
gdunlap@0 334 now, v->vid, state_name[ostate], stime, state_name[nstate]);
gdunlap@0 335
gdunlap@0 336 v->runstate = new_runstate;
gdunlap@0 337 v->state_start_time = now;
gdunlap@0 338
gdunlap@0 339 return;
gdunlap@0 340
gdunlap@0 341 unexpected_transition:
gdunlap@0 342 fprintf(stderr, "Unexpected transition for vm %d: %d->%d\n",
gdunlap@0 343 v->vid,
gdunlap@0 344 v->runstate,
gdunlap@0 345 new_runstate);
gdunlap@0 346 exit(1);
gdunlap@0 347 }
gdunlap@0 348
gdunlap@0 349 /*
gdunlap@0 350 * Main loop
gdunlap@0 351 */
gdunlap@0 352 void simulate(void)
gdunlap@0 353 {
gdunlap@0 354 while ( sim.now < opt.time_limit )
gdunlap@0 355 {
gdunlap@0 356 /* Take next event off list */
gdunlap@0 357 struct event evt;
gdunlap@0 358
gdunlap@0 359 evt = sim_next_event();
gdunlap@0 360
gdunlap@0 361 sim.now = evt.time;
gdunlap@0 362
gdunlap@0 363 switch(evt.type)
gdunlap@0 364 {
gdunlap@0 365 case EVT_WAKE:
gdunlap@0 366 {
gdunlap@0 367 struct vm *v = vm_from_vid(evt.param);
gdunlap@0 368 ASSERT(v->processor == -1);
gdunlap@0 369 sim_runstate_change(sim.now, v, RUNSTATE_RUNNABLE);
gdunlap@2 370 sim.sched_ops->wake(sim.now, v->vid);
gdunlap@0 371 }
gdunlap@0 372 break;
gdunlap@0 373 case EVT_BLOCK:
gdunlap@0 374 {
gdunlap@0 375 struct vm *v = vm_from_vid(evt.param);
gdunlap@0 376
gdunlap@0 377 ASSERT(v->processor != -1);
gdunlap@0 378 ASSERT(v->processor <= P.count);
gdunlap@0 379
gdunlap@0 380 sim_runstate_change(sim.now, v, RUNSTATE_BLOCKED);
gdunlap@0 381
gdunlap@0 382 evt.param = v->processor; /* FIXME */
gdunlap@0 383 }
gdunlap@0 384 /* FALL-THRU */
gdunlap@0 385 case EVT_TIMER:
gdunlap@0 386 {
gdunlap@0 387 struct vm *prev, *next;
gdunlap@0 388 int pid = evt.param;
gdunlap@0 389
gdunlap@0 390 ASSERT(pid < P.count);
gdunlap@0 391
gdunlap@0 392 prev = P.pcpus[pid].current;
gdunlap@0 393
gdunlap@0 394 next = sim.sched_ops->schedule(sim.now, pid);
gdunlap@0 395
gdunlap@0 396 if ( prev && prev != next )
gdunlap@0 397 {
gdunlap@0 398 prev->processor = -1;
gdunlap@0 399 if( prev->runstate != RUNSTATE_BLOCKED )
gdunlap@0 400 sim_runstate_change(sim.now, prev, RUNSTATE_RUNNABLE);
gdunlap@0 401 }
gdunlap@0 402
gdunlap@1 403
gdunlap@0 404 P.pcpus[pid].current = next;
gdunlap@1 405 if ( next )
gdunlap@1 406 {
gdunlap@1 407 if ( next != prev )
gdunlap@1 408 {
gdunlap@1 409 sim_runstate_change(sim.now, next, RUNSTATE_RUNNING);
gdunlap@1 410 next->processor = pid;
gdunlap@1 411 }
gdunlap@1 412 }
gdunlap@3 413 else
gdunlap@1 414 {
gdunlap@3 415 if ( P.pcpus[pid].idle )
gdunlap@3 416 {
gdunlap@3 417 fprintf(stderr, "Strange, pid %d already idle!\n",
gdunlap@3 418 pid);
gdunlap@3 419 abort();
gdunlap@3 420 }
gdunlap@3 421
gdunlap@3 422 /* If the pcpu is going idle, clear all timers from it */
gdunlap@3 423 sim_remove_event(EVT_TIMER, pid);
gdunlap@3 424
gdunlap@1 425 P.pcpus[pid].idle = 1;
gdunlap@1 426 P.idle++;
gdunlap@1 427 }
gdunlap@0 428 }
gdunlap@0 429 break;
gdunlap@0 430 default:
gdunlap@0 431 fprintf(stderr, "Unexpected event type: %d\n", evt.type);
gdunlap@0 432 exit(1);
gdunlap@0 433 break;
gdunlap@0 434 }
gdunlap@0 435 }
gdunlap@0 436 }
gdunlap@0 437
gdunlap@0 438 void init(void)
gdunlap@0 439 {
gdunlap@0 440 int vid, i;
gdunlap@0 441 const struct workload *w;
gdunlap@0 442
gdunlap@0 443 /* Initialize simulation variables */
gdunlap@0 444 sim.now=0;
gdunlap@0 445 sim.timer=NULL;
gdunlap@0 446 INIT_LIST_HEAD(&sim.events);
gdunlap@0 447 sim.sched_ops = &opt.scheduler->ops;
gdunlap@0 448
gdunlap@0 449 /* Initialize pcpus */
gdunlap@0 450 P.count = opt.pcpu_count;
gdunlap@1 451 P.idle = 0;
gdunlap@0 452 for ( i=0; i<P.count; i++ )
gdunlap@0 453 {
gdunlap@0 454 P.pcpus[i].pid = i;
gdunlap@1 455 P.pcpus[i].idle = 1;
gdunlap@1 456 P.idle++;
gdunlap@0 457 P.pcpus[i].current = NULL;
gdunlap@0 458 }
gdunlap@0 459
gdunlap@0 460 /* Initialize scheduler */
gdunlap@0 461 sim.sched_ops->sched_init();
gdunlap@0 462
gdunlap@0 463 /* Initialize vms */
gdunlap@0 464 w=opt.workload;
gdunlap@1 465 V.count = 0;
gdunlap@0 466 for ( vid=0; vid<w->vm_count; vid++)
gdunlap@0 467 {
gdunlap@0 468 struct vm *v = V.vms+vid;
gdunlap@0 469
gdunlap@0 470 v->vid = vid;
gdunlap@0 471 v->runstate = RUNSTATE_BLOCKED;
gdunlap@0 472 v->processor = -1;
gdunlap@0 473 v->private = NULL;
gdunlap@0 474
gdunlap@0 475 v->state_start_time = 0;
gdunlap@0 476 v->time_this_phase = 0;
gdunlap@0 477
gdunlap@0 478
gdunlap@0 479 v->phase_index = -1;
gdunlap@0 480 v->e = NULL;
gdunlap@0 481 v->workload = w->vm_workloads+vid;
gdunlap@0 482
gdunlap@0 483 V.count++;
gdunlap@0 484
gdunlap@0 485 sim.sched_ops->vm_init(vid);
gdunlap@0 486 }
gdunlap@0 487
gdunlap@0 488 /* Set VM starting conditions */
gdunlap@0 489 for ( vid=0; vid<V.count; vid++)
gdunlap@0 490 {
gdunlap@0 491 struct vm *v = V.vms+vid;
gdunlap@0 492
gdunlap@0 493 switch(v->workload->list[0].type)
gdunlap@0 494 {
gdunlap@0 495 case PHASE_RUN:
gdunlap@0 496 v->phase_index = v->workload->phase_count - 1;
gdunlap@0 497 v->e = v->workload->list + v->phase_index;
gdunlap@0 498
gdunlap@0 499 sim_insert_event(sim.now, EVT_WAKE, v->vid, 0);
gdunlap@0 500 v->state_start_time = sim.now;
gdunlap@0 501 v->time_this_phase = 0;
gdunlap@0 502 break;
gdunlap@0 503 case PHASE_BLOCK:
gdunlap@0 504 v->phase_index = 0;
gdunlap@0 505 v->e = v->workload->list;
gdunlap@0 506
gdunlap@0 507 sim_insert_event(sim.now + v->e->time, EVT_WAKE, v->vid, 0);
gdunlap@0 508 v->state_start_time = sim.now;
gdunlap@0 509 v->time_this_phase = 0;
gdunlap@0 510 break;
gdunlap@0 511 }
gdunlap@0 512 }
gdunlap@0 513 }
gdunlap@0 514
gdunlap@0 515 void report(void)
gdunlap@0 516 {
gdunlap@0 517 int i, j;
gdunlap@0 518
gdunlap@0 519 for ( i=0; i<V.count; i++ )
gdunlap@0 520 {
gdunlap@0 521 struct vm *v = V.vms + i;
gdunlap@0 522
gdunlap@0 523 printf("VM %d\n", i);
gdunlap@0 524 for ( j = 0; j < STATE_MAX ; j++ )
gdunlap@0 525 {
gdunlap@0 526 char s[128];
gdunlap@0 527 snprintf(s, 128, " %s", state_name[j]);
gdunlap@0 528 print_cycle_summary(&v->stats.state[j], sim.now, s);
gdunlap@0 529 }
gdunlap@0 530 }
gdunlap@0 531 }
gdunlap@0 532
gdunlap@0 533 int main(int argc, char * argv[])
gdunlap@0 534 {
gdunlap@0 535 warn = stdout;
gdunlap@0 536
gdunlap@2 537 parse_options(argc, argv);
gdunlap@0 538
gdunlap@0 539 /* Setup simulation */
gdunlap@0 540 init();
gdunlap@0 541
gdunlap@0 542 /* Run simulation */
gdunlap@0 543 simulate();
gdunlap@0 544 /* Report statistics */
gdunlap@0 545 report();
gdunlap@0 546 }