The devil is in the details, but this may work for you:
First get Frama-C . If you are using Unix, your distribution may have a package. The package will not be the latest version, but it can be good enough, and it will save you some time if you install it this way.
Say your example is as below, only so large that it is unclear what is wrong:
int add(int x, int y) { static int state; int result = x + y + state; // I tested it once and it worked. state++; return result; }
Enter the command:
frama-c -lib-entry -main add -deps ugly.c
The -lib-entry -main add mean "look at the add function". The -deps calculates functional dependencies. You will find these "functional dependencies" in the journal:
[from] Function add: state FROM state; (and default:false) \result FROM x; y; state; (and default:false)
The actual inputs that add results depend on and the actual outputs computed from these inputs, including static variables read and changed, are listed here. A static variable that was correctly initialized before its use will usually not be displayed as an input if the analyzer could not determine that it was always initialized before reading.
The log displays state as a dependency on \result . If you expected that the returned result would depend only on the arguments (which means that two calls with the same arguments give the same result), this may mean that there might be something wrong with the state variable.
Another hint shown in the lines above is that the function modifies state .
It can help or not. The -lib-entry option means that the analyzer does not assume that any non-constant static variable retains its value during a function call, which may be too inaccurate for your code. There are ways around this, but then it’s up to you to decide whether you want to play the time spent learning these methods.
EDIT: here is a more complex example:
void initialize_1(int *p) { *p = 0; } void initialize_2(int *p) { *p; // I made a mistake here. } int add(int x, int y) { static int state1; static int state2; initialize_1(&state1); initialize_2(&state2); // This is safe because I have initialized state1 and state2: int result = x + y + state1 + state2; state1++; state2++; return result; }
In this example, the same command gives the results:
[from] Function initialize_1: state1 FROM p [from] Function initialize_2: [from] Function add: state1 FROM \nothing state2 FROM state2 \result FROM x; y; state2
What you see for initialize_2 is an empty list of dependencies, that is, the function does not assign anything. I will make this case clearer by showing an explicit message, not just an empty list. If you know what any of the functions initialize_1 , initialize_2 or add should perform, you can compare this a priori knowledge with the results of the analysis and see that something is wrong for initialize_2 and add .
SECOND EDIT: now my example shows something strange for initialize_1 , so maybe I should explain this. The variable state1 depends on p in the sense that p used to write to state1 , and if p was different, the final value of state1 would be different. Here is the last example:
int t[10]; void initialize_index(int i) { t[i] = 1; } int main(int argc, char **argv) { initialize_index(argv[1][0]-'0'); }
Using the frama-c -deps tc dependencies calculated for initialize_index are:
[from] Function initialize_index: t[0..9] FROM i (and SELF)
This means that each of the cells depends on i (it can be changed if i is the index of this particular cell). Each cell can also retain its value (if i indicates a different cell): this is indicated with a mention (and SELF) in the latest version, and in previous versions the more obscure (and default:true) was indicated.