Customizable warnings with a GCC plugin

Pierre Vittet

Abstract

GCC allows plugins and this give new opportunities. This allows to add rules which are specific to a project while the heart of GCC is made to be generic. The idea of this project is to write a plugin, which can be used to add more warnings when compiling, helping to improve the quality of the code. For this, I would like to use MELT, which is a plugin simplifying the writing of others plugins. This is still very simple checks but it can helps to respect commonly used rules.

Additional Information

2011 Google summer of code proposal: customizable warnings with a GCC plugin

Pierre Vittet

I recommend you the additional documentation that has a more elegant layout.

Summary

GCC allows plugins and this give a lot of new opportunities. This allows to add rules which are specific to a project while the heart of GCC is made to be generic. A lot of projects have their own coding styles and conventions. The idea of this project is to write a plugin, which can be used to add more warnings when compiling, helping to improve the quality of the code. For this, I would like to use MELT, which is a plugin simplifying the writing of others plugins. This is still very simple checks but we will see how it helps to respect commonly used rules, for example when using the C stdlib.

Why using MELT?

The plugin could be written in C however I guess the functionalities of MELT will help me to achieve my task in time. This is high level language, in particular it has pretty powerful directives allowing to iterate on GIMPLE and to make pattern matching on the GCC trees. Moreover I have already used it and I have a good idea of what can be done (or not) with it. The last point is that it might show to other people how it can be used, and it is the opportunity to write some documentation about it. MELT should be soon available as a plugin, facilitating its use for end user.

Example of already written plugin

I have already had the opportunity to write a MELT plugin. This plugin parse the code and when it detects a call to the fopen function, it checks that result of the call is tested in order to avoid the use of a NULL pointer later (fopen return NULL if it can't open the file).

For the moment the plugin warns on every code that doesn't contain a test of this type:

FILE * ptr= fopen(file, option); if(ptr==NULL){/*some code*/}

or of this type:

FILE * ptr= fopen(file, option); if(ptr!=NULL){/*some code*/}

So the test sends wrong warnings when we have something like:

  • case 1:

    FILE * ptr= fopen(file, option); function_that_do_the_test(ptr);

    The problem is that we delegate the test in another function and that the plugin is too dummy to handle this case. In reality, it is not that easy, because we have to verify from a function to another.

  • case 2:

    FILE * myOpen(){     FILE * ptr= fopen(file, option);     return ptr; } ... another_function(){     ptr=myOpen();     //make a test here }

    The problem comes from the fact that we use an intermediate function (myOpen) and that the test is not done after the call to fopen but after the call to this intermediate function.

This plugin has been tested on small-sized programs with success. I tried it also on the readline library (it appears that every fopen is correctly handle, if I change one or if I create a new one in order to get the warning, it works). I tried to compile GCC too, however it looks like there is a problem when a C++ check comes.

This has also helped to improve MELT by highlighting some bugs in MELT.

I have created a git repository with this plugin (https://github.com/Piervit/GMWarn), the idea would be to have a library of plugins that do different checks like this one.

Project

The goal of the project is to give a simple test tool for GCC users. The idea is to make a new plugin which could make some generic testings, useful for different projects. The plugin will use the GIMPLE representation, as the middle end is the most adapted for this type of analyse.

This plugin will do the following way:

  1. We can give parameters to the plugin when starting GCC, so we could make a plugin which checks that there is a test on what the user want. User might want to monitor that the foo and the bar functions are tested to return something not NULL, so he will only have to use the plugin passing the function names as arguments:

    $ GCC-melt -O2 -fmelt-mode=return_test_mod -fmelt-init=@@:return_test -fmelt-module-path=. -fmelt-source-path=. -fmelt-arg="foo, bar" -c cfile.c -o cfile.o

    fmelt-mode, fmelt-init, fmelt-module-path fmelt-source-path are usual MELT arguments which respectively allow to enable a mod (a pass plugin), from the given .so file, module and source being located at the given path. fmelt-arg is another MELT argument with a plugin dependant meaning.

    And the test will be done on foo and bar instead of fopen.

    It would be a solution to the case 2 problem because the user will no more make the test on the fopen function but on the myOpen function instead, as it makes sense in his project.

  2. Extending the plugin to other tests cases: some functions use a negative value or only -1 to return errors. We could do this by extending the plugin for those cases: we need to change the options in a list of list as in the following example:

    fmelt-arg="{NULL, foo}, {neg, bar}, {zero, foobar} , {followed_by, foo2,bar2}, {in_same_function, foo3, bar3}"

    With the following meaning:

    • "{NULL, foo}": A test must check that the result of foo is (not) NULL, otherwise, a warning is issued.

    • "{neg, bar}": A test must check that the result of bar is (not) negative, otherwise, a warning is issued.

    • "{zero, foobar}": A test must check that the result of foobar is (not) zero, otherwise, a warning is issued.

    • "{followed_by, foo2, bar2}": Check that a call to foo2 is immediately followed by a call to the bar2 function, otherwise, a warning is issued

    • "{in_same_function, foo2, bar2}": Check that if foo2 is called, bar2 is called in the same function, otherwise, a warning is issued.

    It would be a solution to the case 1 problem because the user will no more try to check that a test is made on fopen but that the fopen is followed by the correct function.

  3. The two last possibilities (followed_by and in_same_function) might lead to another problem. They check that two functions are present at a given position, however they are often linked by a variable. For example the second function could use in parameter the value returned by the first, or in some other cases, we would like to ensure that both have the same pointer as first argument.

    I propose the following solution: We could add an optional number in parenthesis after the function name in the plugin argument. 0 would mean that we will check the returned variable of the function with the variable of the other function.

    For example:

    fmelt-arg="{in_same_function, foo(0), bar(1)}"

    It means here that we check that foo is followed by bar in the same function and that the variable returned by foo is the same as the variable used as first argument of foo.

Examples

Some examples related to the standard and POSIX libraries show the kind of interest of the plugin:

  • malloc/realloc/calloc : When we allocate new memory we should check that it worked. It returns NULL if it was not the case, we could ensure that there is a test using the plugin.

  • system : execute a shell command, return -1 if it failed, for example if fork failed. If it worked but the command return an error or was not found, it returns a non zero integer depending of the shell. So we could check that there is a correct test.

  • execX* : Those functions (execl, execlp, execle, execv, execvp, execvpe) allow to replace the process image with a new one. They return only if there is an error. So placing code after an execX* is only useful in order to check that there is no errors after the call. The plugin will allow to check that there is a perror call after the call to execX.

  • pthread_mutex_lock and pthread_mutex_unlock : If in one function we use, pthread_mutex_lock on a mutex, we would expect to see a pthread_mutex_unlock on the same mutex. The plugin would be able to check that.

This is only few examples of what can be done, many other tests can be imagine, especially when being in the context of the particular project. So I guess it can benefit to the GCC users, as well as, it might highlight the interest of plugins. To give a last example inspired by the viewing of the GCC source code, it would be very easy to check that when in a function we call a timevar_push, we have a timevar_pop on the same timevar_id_t in the same function.

Success Criteria of the project

  1. Writing the described plugin.

  2. Test it on both test-written program and real program. If there are bugs which seems linked to MELT, I will report them. I hope I will also have the luck to catch some errors with my test and will proudly report them.

  3. Writing documentation, showing how to use the plugin, and how it has been written.

Work Organisation

I have already a good overview of MELT and GCC internal representation, so I might be able to start quickly the project. Here is an approximate planning of the project:

Before May 23

I will first try to get some feedback on what can be added/changed/removed in the project (before the coding date). I can present the project on the GCC list and the MELT list, however I guess that the real potential users will came from others projects.

Until end of June

First task will be to handle correctly what comes in arguments (cases and functions) for the plugin and to parse it.

I will have to prepare my data structures to receive those multiple arguments.

I will have to search if all must be done in one big pass able to handle the different checks, or in several pass, created for one specific treatment. I will also have to find after which pass it is better to add the plugin pass.

I will start coding the system in order to have it redirecting the different checks in different functions or even pass.

Until end of july

Implementing the different cases (NULL, neg , zero, followed_by, in_same_function).

First test to see how it works, check if it find bugs, Preparing mid-term evaluation. I will release a first incomplete version of the plugin.

Adding the third project point (optional argument to look at a given variable)

Until August 26

Testing the project, try it find bugs in real programs.

Writing documentation, searching improvements, communicating about the project. I will release a documentation in the GCC wiki, as well as the final version of the plugin.

About Me

I am a 23 years old French student at the Polytechnic College of the University of Tours (A French computer science enginering school). I have been interested for years in free softwares and have make some small contributions, for example I have been contributor in the gwt-cal project(https://code.google.com/p/gwt-cal/). I already had the opportunity to see how to use plugin with GCC, and to understand how to use the GENERIC and GIMPLE representations. I especially looked at MELT, learned his syntax and made small plugins like the one presented here.

Code samples