AspeCt-oriented C Examples

Index Description
[1] HelloWorld program
[2] around() advice and proceed()
[3] Composite pointcut
[4] Context exposure by args() and result()
[5] Modify function argument value by around(), proceed(), and args()
[6] Named pointcut
[7] Wildcard character matching
[8] Dynamic crosscutting with cflow()
[9] Reflective information at join points with this variable
[10] Multiple around() advice execution with proceed()
[11] Static crosscutting with intype() and introduce()
[12] Capture function calls through function pointer deferencing with callp() pointcut
[13] Expose argument value in cflow()
[14] preturn() in advice, using acc and tacc
[15] Exception handling by try() pointcut, catch() advice, and throw() statement
[16] Variable set() pointcut
[17] Variable get() pointcut
[18] A generic tracing aspect which outputs parameter type and values, and return type
Tutorial ACC Tutorial with slight bias on systems development.
[Flash Demo 1] Build and test ACC
[Flash Demo 2] Debug a program compiled by ACC

Note Case study

Example 1: HelloWorld program

1. Assuming the normal C file, "world.mc", is:


int main() {
	printf("world");
	
}

2. Assuming the aspect file, "hello.acc", is:

before(): execution(int main()) {
	printf("Hello ");
}

after(): execution(int main()) {
	printf(" from ACC ! \n");
}

3. Compile both files with acc, the AspeCt-oriented C compiler:

>acc hello.acc world.mc		<--- compilation will generate the hello.c and world.c files
>

4. Compile the generated files with gcc and run the executable file:

>gcc hello.c world.c

>./a.out

Hello world from ACC !

[Top]

Example 2: around() advice + proceed()

1. Assuming the normal C file, "foo.mc", is:

void foo(char * a) {
	printf("inside foo, a = %s\n", a);
}

int main() {
	foo("abcde");
}

2. Assuming the aspect file, "fooac.acc", is:

void around(): call(void foo(char *)) {
	printf("in around advice: start\n");
	proceed();
	printf("in around advice: end\n");
}

3. Compile both files with the ACC compiler:

>acc foo.mc fooac.acc		<--- compilation will generate foo.c and fooac.c
>

4. Compile the generated files by gcc and run the executable file:

>gcc foo.c fooac.c

>./a.out

in around advice: start
inside foo, a = abcde
in around advice: end

[Top]

Example 3: Composite pointcut



1. Assuming the normal C file, "foo.mc", is:
void foo(char * a) {
	printf("inside foo, a = %s\n", a);
}

void foo2() {
	printf("in foo2, call foo\n");
	foo("ABCDE");
}

int main() {
	foo("abcde");
	foo2();
	return 0;
}

2. Assuming the aspect file, "fooac.acc", is:

before(): call(void foo(char *)) && infunc(main) {		
	printf("aspect 1: call foo in main \n");
}

before(): call(void foo(char *)) && infunc(foo2) {		
	printf("aspect 2: call foo in foo2\n");
}

3. Compile both files with the ACC compiler:

>acc foo.mc fooac.acc		<--- compilation will generate foo.c and fooac.c
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac.c

>./a.out

aspect 1: call foo in main
inside foo, a = abcde
in foo2, call foo

aspect 2: call foo in foo2
inside foo, a = ABCDE

[Top]

Example 4: Context exposure with args() or result()



1. Assuming the normal C file, "foo.mc", is:

int foo(char * a) {
	printf("inside foo, a = %s\n", a);
	return 99;
}

int main() {
	foo("abcde");
}

2. Assuming the aspect file, "fooac.acc", is:

after(int value, char * argu): call(int foo(char *)) && args(argu) && result(value) {
	printf("aspect: after call foo, param = %s , return value = %d\n", argu, value);
}

3. Compile both files with the ACC compiler:

>acc foo.mc fooac.acc		<---  compilation will generate foo.c and fooac.c
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac.c

>./a.out

inside foo, a = abcde
aspect: after call foo, param = abcde , return value = 99

[Top]

Example 5: Modify function argument value by around(), proceed(), and args()



1. Assuming the normal C file, "foo.mc", is:

void foo(int a, int b, int c) {
	printf("inside foo, argu = %d, %d, %d\n", a, b, c);
}

int main() {
	int n = 3;
	printf("call foo in main, argu = %d,%d,%d\n", 1, n, 5);
	foo(1,n,5);
}

2. Assuming the aspect file, "fooac.acc", is:

void around(int * i): call(void foo(int,int,int)) && args(int,*i, int) { //use *i to pick the address of an argument
	printf("in around advice, 2nd argument = %d.\n", *i);
	printf("double its value in advice\n");
	*i = (*i) * 2;	//change argument value
	proceed();
}

3. Compile both files with the ACC compiler:

>acc foo.mc fooac.acc		<--- compilation will generate foo.c and fooac.c
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac.c

>./a.out

call foo in main, argu = 1,3,5
in around advice, 2nd argument = 3
double its value in advice
inside foo, argu = 1, 6, 5

[Top]

Example 6: Named pointcut



1. Assuming the normal C file, "foo.mc", is:

int foo(char * a) {
	printf("inside foo, a = %s\n", a);
	return 99;
}

void foo2() {
	foo("call foo in foo2\n");
}

int main() {
	foo("call foo in main");
	foo2();
}

2. Assuming the aspect file, "fooac.acc", is:

pointcut callfoo(): call(int foo(char *)) ;		<-- declare a named pointcut

pointcut callfooinfoo2(): callfoo() && infunc(foo2);	<-- declare a new named pointcut

pointcut callfooinmain(): callfoo() && infunc(main);	<-- declare a new named pointcut

before() : callfooinfoo2() {				<-- advice 1: match a "foo" call inside function foo2
	printf("aspect: before call foo in foo2\n");
}

before() : callfooinmain() {				<-- advice 2: match a "foo" call inside function main
	printf("aspect: before call foo in main\n");
}

3. Compile both files with the ACC compiler:

>acc foo.mc fooac.acc		<---  compilation will generate foo.c and fooac.c
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac.c

>./a.out

aspect: before call foo in main
inside foo, a = call foo in main
aspect: before call foo in foo2
inside foo, a = call foo in foo2

[Top]

Example 7: Wildcard character matching



1. Assuming the normal C file, "foo.mc", is:

int foo1(char * a, char * b, int c) {
	printf("inside foo1, b = %s\n", b);
	return 99;
}

int foo2(int a, int b, int c, char * d, char e) {
	printf("inside foo2, d = %s\n", d);
}

int main() {
	foo1("abc", "call foo1", 3);
	foo2(1,2,3, "call foo2", 'c');
	foo1("abc", 0, 4);			
	foo2(1,2,3, 0, 'd');
}

2. Assuming the aspect file, "fooac.acc", is:

int around(char * x) : call($ foo$(..., char *, $)) && args(...,x,$) { 	<-- advice : use "x" to expose the second last parameter
	printf("aspect: call function\n");
	if(x != 0) {
		proceed();
	}else {
		printf("aspect, the second last parameter is 0, skip the call\n");
	}
}

3. Compile both files with the ACC compiler:

>acc foo.mc fooac.acc		<---  compilation will generate foo.c and fooac.c
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac.c

>./a.out

aspect: call function
inside foo1, b = call foo1
aspect: call function
inside foo2, d = call foo2
aspect: call function
aspect, the second last parameter is 0, skip the call
aspect: call function
aspect, the second last parameter is 0, skip the call

[Top]

Example 8: Dynamic crosscutting with cflow()



1. Assuming the normal C file, "foo.mc", is:

void foo1() {
	printf("inside foo1\n");
}

void foo2() {
	printf("inside foo2\n");
	foo1();
}

void foo3() {
	printf("inside foo3\n");
	foo2();
}
	
int main() {
	printf("call foo1 in main\n");
	foo1();
	printf("-----------------\n\n");
	printf("call foo2 in main\n");
	foo2();
	printf("-----------------\n\n");
	printf("call foo3 in main\n");
	foo3();
	return 0;
}

2. Assuming the aspect file, "fooac.acc", is:

before(): call(void foo1()) && cflow(execution(void foo3())) {	<-- advice : capture call foo1() in the control flow of execution of foo3()
	printf("aspect: call foo1 in cflow of foo3 \n");
}

3. Compile both files with the ACC compiler:

>acc foo.mc fooac.acc		<---  compilation will generate foo.c and fooac.c
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac.c

>./a.out

call foo1 in main
inside foo1



call foo2 in main
inside foo2
inside foo1



call foo3 in main
inside foo3
inside foo2

aspect: call foo1 in cflow of foo3
inside foo1

[Top]

Example 9: Reflective information at join points with "this" variable



1. Assuming the normal C file, "foo.mc", is the same as the one in Example 8.

2. Assuming the aspect file, "fooac.acc", is:

before(): call(void $()) && cflow(execution(void foo3())) {	<-- advice : capture all function calls in the control flow of the execution of foo3()
	printf("aspect: call %s in cflow of foo3. \n", this->funcName);
}

3. Compile both files with the ACC compiler:

>acc foo.mc fooac.acc		<--- compilation will generate foo.c and fooac.c
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac.c

>./a.out

call foo1 in main
inside foo1



call foo2 in main
inside foo2
inside foo1



call foo3 in main
inside foo3

aspect: call foo2 in cflow of foo3.
inside foo2
aspect: call foo1 in cflow of foo3.
inside foo1

[Top]

Example 10: Multiple around() advice execution with proceed()



1. Assuming the normal C file, "foo.mc", is:

void foo1() {
	printf("inside foo1\n");
}
	
int main() {
	printf("call foo1 in main\n");
	foo1();
	return 0;
}

2. Assuming the first aspect file, "fooac1.acc", is:

void around(): call(void foo1()) {	<-- advice : print a message before call foo1()
	printf("aspect 1 \n");
	proceed();
}

Assuming the second aspect file, "fooac2.acc", is:

void around(): call(void foo1()) {	<-- advice : print a message before call foo1()
	printf("aspect 2 \n");
	proceed();
}

Assuming the third aspect file, "fooac3.acc", is:

void around(): call(void foo1()) {	<-- advice : print a message before call foo1()
	printf("aspect 3 \n");
	proceed();
}

3. Compile 4 files with the ACC compiler:

>acc foo.mc fooac1.acc fooac2.acc fooac3.acc	<---  compilation will generate foo.c, fooac1.c fooac2.c and fooac3.c
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac1.c fooac2.c fooac3.c

>./a.out

call foo1 in main
aspect 1
aspect 2
aspect 3

inside foo1

[Top]

Example 11: Static crosscutting with intype() and introduce()



1. Assuming the normal C file, "foo.mc", is:

struct X {
	char a;
	int b;
};

typedef struct X MYX1;
typedef MYX1 MYX2;

int main() {
	printf("size of struct X = %d\n", sizeof(struct X));
	return 0;
}

2. Assuming the aspect file, "fooac.acc", is:

introduce(): intype(struct X) {		<-- advice : add a member
	char * c;
}

introduce(): intype(MYX1) {		<-- advice : add another member
	char * d;
}

introduce(): intype(MYX2) {		<-- advice : add another member
	char * e;
}

3. Compile 2 files with the ACC compiler:

>acc foo.mc fooac.acc
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac.c
>./a.out
size of struct X = 20 <-- struct X has 5 members.

[Top]

Example 12: callp() pointcut: capture function call through function pointer deferencing



1. Assuming the normal C file, "foo.mc", is:

void foo1(char * a) {
	printf("inside foo1, a = %s\n", a);
}

void foo2(char * a) {
	printf("inside foo2, a = %s\n", a);
}

int foo(char * a, void (*p)(char *)) {
	printf("inside foo \n");
	(*p)(a);
	return 0;
};

int main() {
	foo("call foo1", foo1);
	
	foo("call foo2", foo2);
	
	return 0;
}

2. Assuming the aspect file, "fooac.acc", is:


before(): callp(void foo1(char *)) {	<-- advice : print a message before call foo1() through deferencing a pointer
	printf("aspect: call foo1 through a function pointer\n");
}

3. Compile 2 files with the ACC compiler:

>acc foo.mc fooac.acc
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac.c
>./a.out
inside foo aspect: call foo1 through a function pointer inside foo1, a = call foo1 inside foo inside foo2, a = call foo2

[Top]

Example 13: Expose argument value in cflow()



1. Assuming the normal C file, "foo.mc", is:

void foo1(char * a) {
	printf("inside foo1, a = %s\n", a);
}

void foo2(char * a) {
	printf("inside foo2, a = %s\n", a);
	foo1("call foo1 in foo2");
}

int foo(char * a) {
	printf("inside foo, a = %s\n", a);
	foo2("call foo2 in foo");
	return 0;
};

int main() {
	
	foo1("call foo1 in main");
	printf("\n");
		
	foo("call foo in main");
	
	
	return 0;
}

2. Assuming the aspect file, "fooac.acc", is:


before(char * a, char * b) : cflow(call(int foo(char*)) && args(a)) 
                                              && call(void foo1(char *))  && args(b) {	

          // "a" matches the argument of foo , "b" matches argument of foo1
             printf("aspect: call foo1 in cflow of foo\n");
             printf("foo argument = %s \n", a);
             printf("foo1 argument = %s \n", b);
}

3. Compile 2 files with the ACC compiler:

>acc foo.mc fooac.acc
>

4. Compile the generated files with gcc and run the executable file:

>gcc foo.c fooac.c
>./a.out
inside foo1, a = call foo1 in main inside foo, a = call foo in main inside foo2, a = call foo2 in foo aspect: call foo1 in cflow of foo foo argument = call foo in main foo1 argument = call foo1 in foo2 inside foo1, a = call foo1 in foo2

[Top]

Example 14: preturn() in advice


method 1: use "acc"


1. Assuming the normal C file, "foo.mc", is:

int foo2(void) {
	printf("in foo2\n");
	return 0;
}

int foo1(void) {
	printf("in foo1\n");
	foo2();
	printf("end of foo1\n");
	return 0;
}

int main(void) {
	printf("return value of foo1 = %d \n", foo1());
	return 0;
}

2. Assuming the aspect file, "fooac.acc", is:

before(): call(int foo2()) {
	printf("before advice \n");
	preturn(99);
}

3. Compile 2 files with the ACC compiler:

>acc foo.mc fooac.acc
>

4. Compile the generated files with gcc and run the executable file:
Note: The supports of preturn() and exception handling requires linking with a library file, libacc.a, provided by ACC. The libacc.a resides under the "lib" directory.

>gcc foo.c fooac.c -L../lib -lacc
>./a.out
in foo1 before advice return value of foo1 = 99


method 2: use "tacc"


1. Assuming the above normal C file is "foo.c" and aspect file is "fooac.acc", compile 2 files with "tacc" :
>tacc foo.c fooac.acc
>

2. Run the executable file:

>./a.out
in foo1 before advice return value of foo1 = 99

[Top]

Example 15: Exception handling by try() / catch() / throw()



1. Assuming the normal C file, "foo.c", is:

int foo2(int i) {
        printf("\t\t\t in foo2\n");
        return i;
}

int foo1(int i) {
        printf("\t\t in foo1\n");
        foo2(i);
        printf("\t\t end of foo1\n");
        return i;
}

int foo(int i) {
	printf("\t in foo\n");
	foo1(i);
	printf("\t end of foo\n");
	return i;
}


int main() {
	printf("in main, call foo\n");
	foo(1);
	printf("end of main\n");
	return 0;
}

2. Assuming the aspect file, "fooac.acc", is:

before() : call(int foo2(int)) {
	printf("aspect, throw exception 3 before calling foo2\n");
	throw(3);
	printf("end of aspect\n");
}

catch(int e): try(call(int foo(int))) {
	printf("catch exception = %d when calling foo\n", e);
}

3. Compile 2 files with the "tacc"

>tacc foo.c fooac.acc
>

4. Run the executable file:

>./a.out
in main, call foo in foo in foo1 aspect, throw exception 3 before calling foo2 catch exception = 3 when calling foo end of main

[Top]

Example 16: Variable set() pointcut



1. Assuming the normal C file, "foo.c", is:

int a;

int main() {
	printf("in main\n");
	a = 99;
	return 0;
}

2. Assuming the aspect file, "fooac.acc", is:

before() : set(int a) {
	printf("aspect, before set variable a \n");
}

3. Compile 2 files with the "tacc"

>tacc foo.c fooac.acc
>

4. Run the executable file:

>./a.out
in main aspect, before set variable a

[Top]

Example 17: Variable get() pointcut



1. Assuming the normal C file, "foo.c", is:

int a;

int main() {
	printf("in main\n");
	a = 99;
	printf("a = %d\n", a);
	return 0;
}

2. Assuming the aspect file, "fooac.acc", is:

before() : get(int a) {
	printf("aspect, before get variable a \n");
}

3. Compile 2 files with the "tacc"

>tacc foo.c fooac.acc
>

4. Run the executable file:

>./a.out
in main aspect, before get variable a a=99

[Top]

Example 18A generic tracing aspect which outputs parameter type and values, and return type



1. Assuming the normal C file, "foo.c", is:

char * foo(int a) {
   return "just a test ";
}

void foo2(int a, double b) {
   foo(3);
}

void foo3() {
   foo2(5, 2.2);
}

int main() {
   foo3();
}

2. Assuming the aspect file, "fooac.acc", is:

before(): call($ $(...)) {
   printf("%s \" %s \" in function %s \n", this->kind, this->targetName, this->funcName);
   
   if ( this->argsCount == 0 ) printf("no parameter \n");
   else {
         for(int i = 1 ; i <= this->argsCount; i++) {
                  printf("arg[%d] = %s  ", i, this->argType(i));
                  
                  if(strcmp(this->argType(i), "int") == 0) {
                         printf(", value = %d ", *(int *)(this->arg(i)));
                  } else if(strcmp(this->argType(i), "double") == 0) {
                         printf(", value = %.2f ", *(double *)(this->arg(i)));
                  }
                  
                  printf("\n");
          }
    }

    printf("return type = %s \n \n", this->retType);
}

3. Compile 2 files with the "tacc"

>tacc foo.c fooac.acc
>

4. Run the executable file:

>./a.out
call "foo3" in function main "foo3" parameter type: no parameter return type = void call "foo2" in function foo3 "foo2" parameter type: arg[1] = int , value = 5 arg[2] = double , value = 2.20 return type = void call "foo" in function foo2 "foo" parameter type: arg[1] = int , value = 3 return type = char*

[Top]

Note

As a proof of concept, the AspeCt-oriented C compiler was used successfully in refactoring functions belonging to the logging concern of ORBit2. ORBit2 is an object request broker used extensively as middleware in Red Hat's GNOME project.

The aspect weaving process was successfully integrated into ORBit2's build system and the generated files were used to build ORBit2. A set of test cases provided by ORBit2 were successfully exercised as well.

We are actively using AspeCt-oriented C in the refactoring and the development of other systems.

We are successfully using AspeCt-oriented C in a 3rd year introductory-level, undergraduate Operating Systems course.

[Top]

Topic revision: r13 - 2008-01-05 - 14:38:12 - MichaelGong
 
Copyright © Middleware Systems Research Group. Send feedback