Saturday, July 30, 2011

PC Lint

PC Lint (or more commonly just Lint) is pretty well known in the embedded community.  If you haven't bumped into it yet, Lint is a static analysis tool that you run against your code to help find code problems such as "not checking the return value of a function", and "casting a pointer to another pointer".

The affect of using Lint on your code is similar to doing a rudimentary code review.  It forces you to look at parts of your code that you probably wrote and then never thought about again.  Do you care about checking the return value of that function?  Can you mark that function argument as constant... or pointer to a constant?  The answer to both of those is yes.  The value is that Lint will make sure you at least consider these bits of code in a specific it one more time after you initially write the code.

The common question I hear when talking about Lint is... "Isn't that exactly what the compiler does?".  Kind of.  The Compiler's job is to ensure it can create valid code.  It doesn't care if the code is "correct" or could have unwanted side affects.  Most compilers simply check for valid syntax, and maybe a few other items such as "variable X isn't used"... and that's it.  Lint goes way beyond just syntax and gives messages for code that could produce un-wanted side affects.  Its like your compilers warnings and errors being turned up to eleven.  In short,  I haven't run into anyone who has used Lint that has described it as a waste of time.  It is a generally great tool to allow you to create better C and C++ code.  I highly recommend giving it a try.

I started by adding a lint directory to the source directory of my repository:

Hardac
- branches
- tags
- trunk
  - lint
  - ...

This lint directory contains all of the project specific lint configuration files.  I also put the lint executable and the lint batch file as well.  I'm not sold on whether this is a good idea or not, but it sure makes all of the script paths to be nice and tidy.

PC Lint can plug directly into most development environments, but I opted to just utilize a command line setup.  The command line setup will eventually be used to support an automated build process.

I started out by using the default batch file from the command line and passing it the name of individual files to process:

>lin.bat -u main.c

The first time you start linting files you usually get a bunch of "can't find include file whatever.h".  these are usually resovelved by adding an appropriate include line in the std.lin file or the options.lin file for your project.  The include files that are part of your project are pretty straight forward:

-i"Common\Whatever"


A little bit more of a sticky point is dealing with the environment includes.  In this case, my first linting complained that it could not find msp430x20x3.h.  The PC Lint installation and documentation gives guidance lint configuration files specific for different development environments.  Pointing to the environments include directories is straight forward.  However Lint evaluates code similar to the compiler.  If it can't find a pre-processor definition, it assumes it isn't defined.  I received the following error from lint:


#error msp430x20x3.h file for use with ICC430/A430 only


It turns out that the environment defines the chip that is being used, probably at the project level or in a compiler switch.  Taking a looking into the header file itself reveals that the error message is wrapped in a #if protected block:



#if (((__TID__ >> 8) & 0x7F) != 0x2b)
#error msp430x20x3.h file for use with ICC430/A430 only
#endif



This is resolved by defining __TID__ so when shifted left, the lower seven bits evaluate to 0x2b.  I did this by adding the following line to my Lint options file:


-d__TID__=0x2b00


After this, linting my files was producing useful messages as expected... but suspiciously few errors and warnings.  One of my code files contained an if statement with two separate conditions:


if (value >= MIN && value <= MAX)


From my previous experience working with Lint, I expected it to give me a warning about reliance on C operator precedence.  But no such Lint message...


It turns out that my previous work experience utilized Misra C lint configuration files.  I added these lines to my Lint configuration file:

// Include MISRA RULES
lint\au-sm123.lnt
lint\au-ds.lnt
lint\au-misra2.lnt
lint\co-kcarm.lnt

The first file I Linted displayed over 50 messages.  Seems like everything is as it should be now.  Within a few hours I had all of my files linted.  I didn't find a whole lot of interesting code items with the initial linting.  There were a couple of cases where I was not checking return values of functions and added some more robust error checking.

The biggest challenge now is to stay in the habit of only committing code that has been linted to the repository...