Compiler/runtime modifications towards supporting precise garbage collection
[IRC.git] / Robust / src / Runtime / runtime.c
1 #include "runtime.h"
2 #include "structdefs.h"
3 #include <string.h>
4 #include <signal.h>
5 #include "mem.h"
6 #include<fcntl.h>
7 #include<sys/types.h>
8 #include<sys/mman.h>
9 #include<errno.h>
10 #include<signal.h>
11 #include<stdio.h>
12
13 extern int classsize[];
14 jmp_buf error_handler;
15 int instructioncount;
16
17 #ifdef TASK
18 #include "checkpoint.h"
19 #include "Queue.h"
20 #include "SimpleHash.h"
21 #include "GenericHashtable.h"
22 #include <sys/select.h>
23
24 #ifdef CONSCHECK
25 #include "instrument.h"
26 #endif
27
28 struct Queue * activetasks;
29 struct parameterwrapper * objectqueues[NUMCLASSES];
30 struct genhashtable * failedtasks;
31 /*struct genhashtable * failedobjects;*/
32 extern char ** environ;
33 char *options;
34 int injectfailures=0;
35 float failurechance=0;
36 int debugtask=0;
37 int injectinstructionfailures;
38 int failurecount;
39 float instfailurechance=0;
40 int numfailures;
41 int instaccum=0;
42
43 void processOptions() {
44   int i;
45   options=NULL;
46   for(i=0;environ[i]!=0;i++) {
47     if (strncmp(environ[i],"BRISTLECONE=",12)==0) {
48       options=environ[i]+12;
49       break;
50     }
51   }
52   
53   while(options!=NULL) {
54     if (strncmp(options,"-injectfailures",sizeof("-injectfailures")-1)==0) {
55       options=strchr(options,' ');
56       if (options!=NULL) options++;
57       if (options==NULL)
58         break;
59       sscanf(options, "%f", &failurechance);
60       injectfailures=1;
61       printf("Injecting errors with chance=%f\n",failurechance);
62       options=strchr(options,' ');
63       if (options!=NULL) options++;
64     } else if (strncmp(options,"-injectinstructionfailures",sizeof("-injectinstructionfailures")-1)==0) {
65       options=strchr(options,' ');
66       if (options!=NULL) options++;
67       if (options==NULL)
68         break;
69       sscanf(options, "%d", &failurecount);
70       options=strchr(options,' ');
71       if (options!=NULL) options++;
72       if (options==NULL)
73         break;
74
75       sscanf(options, "%f", &instfailurechance);
76       options=strchr(options,' ');
77       if (options!=NULL) options++;
78       if (options==NULL)
79         break;
80
81       sscanf(options, "%d", &numfailures);
82       options=strchr(options,' ');
83       if (options!=NULL) options++;
84
85       instaccum=failurecount;
86       instructioncount=failurecount;
87       injectinstructionfailures=1;
88       printf("Number of failures=%d\n",numfailures);
89       printf("Injecting errors with count=%d\n",failurecount);
90       printf("Injecting errors with chance=%f\n",instfailurechance);
91     } else if (strncmp(options, "-debugtask",sizeof("-debugtask")-1)==0) {
92       options=strchr(options,' ');
93       if (options!=NULL) options++;
94       debugtask=1;
95       printf("Debug task option on.\n");
96     } else if (strncmp(options, "-initializerandom", sizeof("-initializerandom")-1)==0) {
97       options=strchr(options,' ');
98       if (options!=NULL) options++;
99       printf("Initializing random number generator.\n");
100       srandomdev();
101     } else
102       break;
103   }
104 }
105
106 int main(int argc, char **argv) {
107   GC_init(); // Initialize the garbage collector
108 #ifdef CONSCHECK
109   initializemmap();
110 #endif
111   processOptions();
112
113   {
114   int i;
115   /* Allocate startup object */
116   struct ___StartupObject___ *startupobject=(struct ___StartupObject___*) allocate_new(STARTUPTYPE);
117   struct ArrayObject * stringarray=allocate_newarray(STRINGARRAYTYPE, argc-1); 
118   failedtasks=genallocatehashtable((unsigned int (*)(void *)) &hashCodetpd, 
119                                    (int (*)(void *,void *)) &comparetpd);
120   /*  failedobjects=genallocatehashtable(NULL,NULL);*/
121   
122   activetasks=createQueue();
123
124   /* Set flags */
125   processtasks();
126   flagorand(startupobject,1,0xFFFFFFFF);
127
128   /* Build array of strings */
129
130   startupobject->___parameters___=stringarray;
131
132   for(i=1;i<argc;i++) {
133     int length=strlen(argv[i]);
134     struct ___String___ *newstring=NewString(argv[i],length);
135     ((void **)(((char *)& stringarray->___length___)+sizeof(int)))[i-1]=newstring;
136   }
137   executetasks();
138   }
139 }
140
141 int hashCodetpd(struct taskparamdescriptor *ftd) {
142   int hash=(int)ftd->task;
143   int i;
144   for(i=0;i<ftd->numParameters;i++) {
145     hash^=(int)ftd->parameterArray[i];
146   }
147   return hash;
148 }
149
150 int comparetpd(struct taskparamdescriptor *ftd1, struct taskparamdescriptor *ftd2) {
151   int i;
152   if (ftd1->task!=ftd2->task)
153     return 0;
154   for(i=0;i<ftd1->numParameters;i++)
155     if (ftd1->parameterArray[i]!=ftd2->parameterArray[i])
156       return 0;
157   return 1;
158 }
159
160 void flagorand(void * ptr, int ormask, int andmask) {
161   int flag=((int *)ptr)[1];
162   struct RuntimeHash *flagptr=(struct RuntimeHash *)(((int*)ptr)[2]);
163   flag|=ormask;
164   flag&=andmask;
165   ((int*)ptr)[1]=flag;
166   /*Remove from all queues */
167   while(flagptr!=NULL) {
168     struct RuntimeHash *next;
169     RuntimeHashget(flagptr, (int) ptr, (int *) &next);
170     RuntimeHashremove(flagptr, (int)ptr, (int) next);
171     flagptr=next;
172   }
173   
174   {
175     struct QueueItem *tmpptr;
176     struct parameterwrapper * parameter=objectqueues[((int *)ptr)[0]];
177     int i;
178     struct RuntimeHash * prevptr=NULL;
179     while(parameter!=NULL) {
180       for(i=0;i<parameter->numberofterms;i++) {
181         int andmask=parameter->intarray[i*2];
182         int checkmask=parameter->intarray[i*2+1];
183         if ((flag&andmask)==checkmask) {
184           RuntimeHashadd(parameter->objectset, (int) ptr, (int) prevptr);
185           prevptr=parameter->objectset;
186           {
187             struct RuntimeIterator iteratorarray[MAXTASKPARAMS];
188             void * taskpointerarray[MAXTASKPARAMS];
189             int j;
190             int numparams=parameter->task->numParameters;
191             int done=1;
192             struct taskdescriptor * task=parameter->task;
193             int newindex=-1;
194             for(j=0;j<numparams;j++) {
195               struct parameterwrapper *pw=(struct parameterwrapper *)task->descriptorarray[j]->queue;
196               if (parameter==pw) {
197                 taskpointerarray[j]=ptr;
198                 newindex=j;
199               } else {
200                 RuntimeHashiterator(pw->objectset, &iteratorarray[j]);
201                 if (RunhasNext(&iteratorarray[j])) {
202                   taskpointerarray[j]=(void *) Runkey(&iteratorarray[j]);
203                   Runnext(&iteratorarray[j]);
204                 } else {
205                   done=0;
206                   break; /* No tasks to dispatch */
207                 }
208               }
209             }
210             /* Queue task items... */
211
212             while(done) {
213               struct taskparamdescriptor *tpd=RUNMALLOC(sizeof(struct taskparamdescriptor));
214               tpd->task=task;
215               tpd->numParameters=numparams;
216               tpd->parameterArray=RUNMALLOC(sizeof(void *)*numparams);
217               for(j=0;j<numparams;j++)
218                 tpd->parameterArray[j]=taskpointerarray[j];
219               /* Queue task */
220               if (!gencontains(failedtasks, tpd))
221                 addNewItem(activetasks, tpd);
222               
223               /* This loop iterates to the next parameter combination */
224               for(j=0;j<numparams;j++) {
225                 if (j==newindex) {
226                   if ((j+1)==numparams)
227                     done=0;
228                   continue;
229                 }
230                 if (RunhasNext(&iteratorarray[j])) {
231                   taskpointerarray[j]=(void *) Runkey(&iteratorarray[j]);
232                   Runnext(&iteratorarray[j]);
233                   break;
234                 } else if ((j+1)!=numparams) {
235                   RuntimeHashiterator(task->descriptorarray[j]->queue, &iteratorarray[j]);
236                 } else {
237                   done=0;
238                   break;
239                 }
240               }
241             }
242           }
243           break;
244         }
245       }
246       parameter=parameter->next;
247     }
248     ((struct RuntimeHash **)ptr)[2]=prevptr;
249   }
250 }
251
252 /* Handler for signals. The signals catch null pointer errors and
253    arithmatic errors. */
254 void myhandler(int sig, siginfo_t *info, void *uap) {
255 #ifdef DEBUG
256   printf("sig=%d\n",sig);
257   printf("signal\n");
258 #endif
259   longjmp(error_handler,1);
260 }
261
262 fd_set readfds;
263 int maxreadfd;
264 struct RuntimeHash *fdtoobject;
265
266 void addreadfd(int fd) {
267   if (fd>=maxreadfd)
268     maxreadfd=fd+1;
269   FD_SET(fd, &readfds);
270 }
271
272 void removereadfd(int fd) {
273   FD_CLR(fd, &readfds);
274   if (maxreadfd==(fd+1)) {
275     maxreadfd--;
276     while(maxreadfd>0&&!FD_ISSET(maxreadfd-1, &readfds))
277       maxreadfd--;
278   }
279 }
280
281 /*
282 void restoreObject(void * obj) {
283   if (gencontains(failedobjects, obj)) {
284     struct tpdlist *tpd=gengettable(failedobjects, obj);
285     genfreekey(failedobjects, obj);
286     while(tpd!=NULL) {
287       int i;
288       struct taskparamdescriptor *task=tpd->task;
289       genfreekey(failedtasks, task);
290       for(i=0;i<task->numParameters;i++) {
291         void *objother=task->parameterArray[i];
292         struct tpdlist *tpdother=gengettable(failedobjects, objother);
293         struct tpdlist *tmpptr;
294         genfreekey(failedobjects, objother);
295         struct tpdlist **tpdptr=&tpdother;
296         while((*tpdptr)->task!=task)
297           tpdptr=&((*tpdptr)->next);
298         tmpptr=*tpdptr;
299         (*tpdptr)=(*tpdptr)->next;
300         RUNFREE(tmpptr);
301         if (tpdother!=NULL)
302           genputtable(failedobjects, objother, tpdother);
303       }
304       RUNFREE(task);
305       {
306         struct tpdlist *old=tpd;
307         tpd=tpd->next;
308         RUNFREE(old);
309       }
310     }
311   }
312 }
313 */
314
315 #ifdef PRECISE_GC
316 #define OFFSET 2
317 #else
318 #define OFFSET 0
319 #endif
320
321 void executetasks() {
322   void * taskpointerarray[MAXTASKPARAMS+OFFSET];
323
324   /* Set up signal handlers */
325   struct sigaction sig;
326   sig.sa_sigaction=&myhandler;
327   sig.sa_flags=SA_SIGINFO;
328   sigemptyset(&sig.sa_mask);
329
330   /* Catch bus errors, segmentation faults, and floating point exceptions*/
331   sigaction(SIGBUS,&sig,0);
332   sigaction(SIGSEGV,&sig,0);
333   sigaction(SIGFPE,&sig,0);
334
335   /* Zero fd set */
336   FD_ZERO(&readfds);
337   maxreadfd=0;
338   fdtoobject=allocateRuntimeHash(100);
339
340   /* Map first block of memory to protected, anonymous page */
341   mmap(0, 0x1000, 0, MAP_SHARED|MAP_FIXED|MAP_ANON, -1, 0);
342
343   newtask:
344   while(!isEmpty(activetasks)||(maxreadfd>0)) {
345
346     /* Check if any filedescriptors have IO pending */
347     if (maxreadfd>0) {
348       int i;
349       struct timeval timeout={0,0};
350       fd_set tmpreadfds;
351       int numselect;
352       tmpreadfds=readfds;
353       numselect=select(maxreadfd, &tmpreadfds, NULL, NULL, &timeout);
354       if (numselect>0) {
355         /* Process ready fd's */
356         int fd;
357         for(fd=0;fd<maxreadfd;fd++) {
358           if (FD_ISSET(fd, &tmpreadfds)) {
359             /* Set ready flag on object */
360             void * objptr;
361             if (RuntimeHashget(fdtoobject, fd,(int *) &objptr)) {
362               flagorand(objptr,1,0xFFFFFFFF); /* Set the first flag to 1 */
363             }
364           }
365         }
366       }
367     }
368
369     /* See if there are any active tasks */
370     if (!isEmpty(activetasks)) {
371       int i;
372       struct QueueItem * qi=(struct QueueItem *) getTail(activetasks);
373       struct taskparamdescriptor *tpd=(struct taskparamdescriptor *) qi->objectptr;
374       removeItem(activetasks, qi);
375
376       /* Check if this task has failed */
377       if (gencontains(failedtasks, tpd))
378         goto newtask;
379       
380       /* Make sure that the parameters are still in the queues */
381       for(i=0;i<tpd->task->numParameters;i++) {
382         void * parameter=tpd->parameterArray[i];
383         struct parameterdescriptor * pd=tpd->task->descriptorarray[i];
384         struct parameterwrapper *pw=(struct parameterwrapper *) pd->queue;
385         if (!RuntimeHashcontainskey(pw->objectset, (int) parameter))
386           goto newtask;
387         taskpointerarray[i+OFFSET]=parameter;
388       }
389       {
390         /* Checkpoint the state */
391         struct RuntimeHash * forward=allocateRuntimeHash(100);
392         struct RuntimeHash * reverse=allocateRuntimeHash(100);
393         void ** checkpoint=makecheckpoint(tpd->task->numParameters, &taskpointerarray[OFFSET], forward, reverse);
394         int x;
395         if (x=setjmp(error_handler)) {
396           /* Recover */
397           int h;
398 #ifdef DEBUG
399           printf("Fatal Error=%d, Recovering!\n",x);
400 #endif
401           genputtable(failedtasks,tpd,tpd);
402        /* for(i=0;i<tpd->task->numParameters;i++) {
403             void *parameter=tpd->parameterArray[i];
404             {
405               // Create mapping from object -> failed tasks 
406               struct tpdlist * tpnew=RUNMALLOC(sizeof(struct tpdlist));
407               tpnew->task=tpd;
408               if (gencontains(failedobjects, parameter)) {
409                 struct tpdlist * tpdptr=gengettable(failedobjects, parameter);
410                 tpnew->next=tpdptr->next;
411                 tpdptr->next=tpnew;
412               } else {
413                 tpnew->next=NULL;
414                 genputtable(failedobjects, parameter, tpnew);
415               }
416             }
417           }  */
418           restorecheckpoint(tpd->task->numParameters, &taskpointerarray[OFFSET], checkpoint, forward, reverse);
419         } else {
420           if (injectfailures) {
421             if ((((double)random())/RAND_MAX)<failurechance) {
422               printf("\nINJECTING TASK FAILURE to %s\n", tpd->task->name);
423               longjmp(error_handler,10);
424             }
425           }
426           /* Actually call task */
427 #ifdef PRECISE_GC
428           ((int *)taskpointerarray)[0]=tpd->task->numParameters;
429           taskpointerarray[1]=NULL;
430 #endif
431
432           if (debugtask) {
433             printf("ENTER %s count=%d\n",tpd->task->name, (instaccum-instructioncount));
434             ((void (*) (void **)) tpd->task->taskptr)(taskpointerarray);
435             printf("EXIT %s count=%d\n",tpd->task->name, (instaccum-instructioncount));
436           } else
437             ((void (*) (void **)) tpd->task->taskptr)(taskpointerarray);
438         }
439       }
440     }
441   }
442 }
443
444 /* This function processes the task information to create queues for
445    each parameter type. */
446
447 void processtasks() {
448   int i;
449   for(i=0;i<numtasks;i++) {
450     struct taskdescriptor * task=taskarray[i];
451     int j;
452
453     for(j=0;j<task->numParameters;j++) {
454       struct parameterdescriptor *param=task->descriptorarray[j];
455       struct parameterwrapper * parameter=RUNMALLOC(sizeof(struct parameterwrapper));
456       struct parameterwrapper ** ptr=&objectqueues[param->type];
457
458       param->queue=parameter;
459       parameter->objectset=allocateRuntimeHash(10);
460       parameter->numberofterms=param->numberterms;
461       parameter->intarray=param->intarray;
462       parameter->task=task;
463       /* Link new queue in */
464       while((*ptr)!=NULL)
465         ptr=&((*ptr)->next);
466       (*ptr)=parameter;
467     }
468   }
469 }
470
471 #endif
472
473 /* This function inject failures */
474
475 void injectinstructionfailure() {
476 #ifdef TASK
477   if (injectinstructionfailures) {
478     if (numfailures==0)
479       return;
480     instructioncount=failurecount;    
481     instaccum+=failurecount;
482     if ((((double)random())/RAND_MAX)<instfailurechance) {
483       if (numfailures>0)
484         numfailures--;
485       printf("FAILURE!!!\n");
486       longjmp(error_handler,11);
487     }
488   }
489 #else
490 #endif
491 }
492
493 int CALL01(___Object______hashCode____, struct ___Object___ * ___this___) {
494   return (int) VAR(___this___);
495 }
496
497 int CALL01(___Object______getType____, struct ___Object___ * ___this___) {
498   return ((int *)VAR(___this___))[0];
499 }
500
501 void CALL01(___System______printString____L___String___,struct ___String___ * ___s___) {
502     struct ArrayObject * chararray=VAR(___s___)->___value___;
503     int i;
504     int offset=VAR(___s___)->___offset___;
505     for(i=0;i<VAR(___s___)->___count___;i++) {
506         short sc=((short *)(((char *)& chararray->___length___)+sizeof(int)))[i+offset];
507         putchar(sc);
508     }
509 }
510
511 void * allocate_new(int type) {
512   void * v=FREEMALLOC(classsize[type]);
513   *((int *)v)=type;
514   return v;
515 }
516
517 struct ArrayObject * allocate_newarray(int type, int length) {
518   struct ArrayObject * v=FREEMALLOC(sizeof(struct ArrayObject)+length*classsize[type]);
519   v->type=type;
520   v->___length___=length;
521   return v;
522 }
523
524 struct ___String___ * NewString(const char *str,int length) {
525   struct ArrayObject * chararray=allocate_newarray(CHARARRAYTYPE, length);
526   struct ___String___ * strobj=allocate_new(STRINGTYPE);
527   int i;
528   strobj->___value___=chararray;
529   strobj->___count___=length;
530   strobj->___offset___=0;
531
532   for(i=0;i<length;i++) {
533     ((short *)(((char *)& chararray->___length___)+sizeof(int)))[i]=(short)str[i];  }
534   return strobj;
535 }
536
537 /* Generated code calls this if we fail a bounds check */
538 void failedboundschk() {
539 #ifndef TASK
540   printf("Array out of bounds\n");
541   exit(-1);
542 #else
543   longjmp(error_handler,2);
544 #endif
545 }
546
547 void abort_task() {
548 #ifdef TASK
549   longjmp(error_handler,4);
550 #else
551   printf("Aborting\n");
552   exit(-1);
553 #endif
554 }