Recent News
- [August 7, 2009] Website published!
Aspicere is an aspect language for C with the following characteristics:
/*@some_tag(field1,field2)*/
)cflow
sequence
-like temporal pointcutsproceed
-callWe give two simple code examples, followed by a more complex one.
This first example aspect checks whether a critical SQL query (call to _iqcftch
) has been interrupted by a database error. If the particular error which popped up is recoverable, the aspect tries to fix the error up to a maximum number of attempts. If the error still persists, an error-specific message is prompted. This example works for Aspicere1 and Aspicere2, but with a slightly different syntax.
#include <stdio.h> #include "sql.h" int recover(int ErrorCode, int Iterations, char* ErrorString) around Jp: invocation(Jp,"_iqcftch",_) && critical_call(Jp) && sql_redo(ErrorCode,Iterations) && sql_code(ErrorCode,ErrorString) { int res=0; int i; for(i=-1;i<Iterations;i++){ res=proceed(); if(sql_code==ErrorCode){ if(i<Iterations-1) { if (i == -1) fprintf(stderr,"!! Error (%d): %s\n", ErrorCode, ErrorString); fprintf(stderr,"!! Retrying (attempt %d/%d)...\n",(i+2), Iterations); } else { fprintf(stderr,"Retry limit reached. Bailing out.\n"); } } else { if (i > -1) fprintf(stderr,"!! Retry %d was successful.\n", (i+1)); break; } } return res; }
Of course, the notion of "a critical SQL query" is subject to interpretation. Developers can annotate queries, or use Prolog facts and rules for this. Here, we use the latter approach:
:- load_library('KnowledgeLibrary').
sql_code(-666,'The statement cannot be executed, because a time limit was exceeded.').
sql_code(-910,'The object cannot be used, because an operation is pending.').
sql_code(-542,'Invalid SQL code.').
sql_code(-357,'The file server is currently not available.').
sql_redo(-666,2).
sql_redo(-357,3).
sql_redo(-542,0).
critical_method('monthlyPayment').
critical_call(Jp) :-
enclosingMethod(Jp,EncMethod),
execution(EncMethod, Name),
critical_method(Name),
print('CRITICAL call found!!!'),nl.
In this example, we optimize an SQL-query by modifying the argument of the call to _iqcprep
.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "sql.h" #define CRITICAL_FREQ 2 /* Usage patterns */ static int usage=0; static char* last_query=0; static void cleanup(void){ if(last_query) free(last_query); } static char* getLastQuery(void){ if(!last_query) atexit(cleanup); return last_query; } static void setLastQuery(char* new_query){ if(last_query) free(last_query); last_query=(char*)malloc((strlen(new_query)+1)*sizeof(char)); strcpy(last_query,new_query); } /* Optimize SQL queries based on usage patterns */ void optimise_query (char* Query) before Jp: invocation(Jp,"_iqcprep") && args(Jp,[Query|_]) { char* optimised_query=0; if(getLastQuery()&&!strcmp(getLastQuery(),Query)){ /* Same query */ usage++; if(usage<=CRITICAL_FREQ) { /* Suboptimal optimizing */ optimised_query=(char*)malloc((strlen(Query)+9+1)*sizeof(char)); strcpy(optimised_query,"OPTIMISED"); strcat(optimised_query,Query); }else{ /* Optimal optimizing */ optimised_query=(char*)malloc((strlen(Query)+14+1)*sizeof(char)); strcpy(optimised_query,"SUPEROPTIMISED"); strcat(optimised_query,Query); } printf("--Unoptimised query:\t%s\n",Query); Query=optimised_query; printf("++Optimised query:\t%s\n",Query); }else{ setLastQueryQ(Query); usage=1; printf("--Could not optimise query yet.\n"); } }
This more complex example illustrates the concept of delimited continuation join points and Aspicere2's annotation support.
First, we show the base code.
#include <stdio.h>
int g(void){
return 0;
}
void f(void){
printf("B");
/*@halting()*/
g();
printf("C");
}
int main(void){
printf("A");
f();
printf("D\n");
}
The /*@halting()*/-annotation means that we want to stop the execution of procedure f
if the call to g
returns zero (which is always true here for the sake of simplicity). In other words, printf("C")
must NOT be executed. For this, we need the following advice:
int handling (int* ReturnVal) around Jp:
halting_invocation(JpCall,ReturnVal)
&& delimited_continuation(Jp,JpCall) {
if(*ReturnVal==0)
return *ReturnVal;
else
return proceed();
}
This advice does the following: if the return value of a /*@halting()*/-annotated call equals zero, the delimited continuation (i.e. the remaining execution within procedure f after the call to g) is skipped (not proceeded). Otherwise, the remaining execution is resumed (proceeded). In other words, we get "ABD" as output.
How do we define the halting_invocation/2 predicate? For this, we need to attach a join point property to procedure f
in order to temporarily store the return value of /*@halting()*/-annotated calls:
int* return_var () on Jp:
execution(Jp,_);
void var_update(int* ReturnVal) after Jp returning (int* Result):
halting_invocation(Jp,ReturnVal){
*ReturnVal=*Result;
}
Finally, this is the actual definition of the halting_invocation/2 predicate:
halting_invocation(JpCall,ReturnVal):- invocation(JpCall,_), annotation(JpCall,halting,_) enclosingMethod(JpCall,JpEncl), property(JpEncl,return_var,ReturnVal) .
Basically, there is a check whether JpCall
is a call annotated with a /*@halting()*/ annotation. If so, the current value of the return_var property attached to the calling execution join point property is returned.
In this paper, we apply delimited continuation join points to extract an idiom-based exception handling idiom into aspects.
There are two implementations of Aspicere. Aspicere2 fully implements Aspicere, whereas Aspicere1 only provides a small subset.
Beta 1 is the aspect weaver we used in the Kava case study. This means that it is able to weave in K&R- as well as in ANSI C-code. Unfortunately, in the former case, there are times when method prototypes don't declare the types of their arguments. Aspicere1 infers these types from a procedure's call site, but does not support all possible C types. In those cases, our weaver merely "skips" the relevant join points without halting the full weaving process. The weaver shows statistics about the relative percentage of "skipped" join points.
Aspicere1 has the following limitations:
As a manual workaround for the GCC 4.x bug, one can do the following:
(char*)voidptr=charptr;
by voidptr=(void*)charptr;
Aspicere1 is covered under a Tri License scheme: MPL/LGLP/GPL. It depends on Yerna Lindale and Lillambi, but these are included in the download. Furthermore, you need a Java 1.4-compatible VM, a C compiler, Perl interpreter and Bash shell. The included INSTALL file contains detailed installation instructions for Aspicere1.
Aspicere2 is also covered under a Tri License scheme: MPL/LGLP/GPL. It has the following dependencies:
This is the most recent version of Aspicere2, used in multiple papers. The best documentation can be found here. The included INSTALL file contains installation instructions for all of these and for Aspicere2 itself.