Showing posts with label static analysis. Show all posts
Showing posts with label static analysis. Show all posts

Tuesday, April 14, 2015

Configuring PC-Lint to use with Atmel Studio and GCC Toolchain.

Generation of macro definition headers, --i options and size options for Lint when using Atmel GCC toolchain.

In theory this is done by using co-gcc.mak file provided with PC-Lint intallation (see C:/lint/lnt folder). I guess this works with standalone GCC installation. But when GCC is provided as part of Atmel Studio, invoking GNU make from C:\Program Files (x86)\Atmel\Atmel Studio 6.2\shellUtils folder doesn't work.
Instead, I just did manually all that makefile does.
Here's step-by-step instructions.

1) Makefile must provide a lot of options to Make utility to get correct output. These options are:
- GCC_BIN='name of gcc binary'
- GXX_BIN='name of g++ binary'
- CFLAGS='[usual C compile switches here]'
- CXXFLAGS='[usual C++ compile switches here]'
- CPPFLAGS='[usual common preprocessor switches here]'
- COMMON_FLAGS='[usual C & C++ compile switches here]'
In case of only C program we don't need all these C++ options, so we'll get only what we need.

2) Get COMMON_FLAGS options from Atmel Studio.
Open Project Properties (press Alt+F7), then get your options from Toolchain->ARM/GNU Common tab:

In our case it's -mthumb -D__SAMD21J17A__.

3) Get CFLAGS from Toolchain->ARM/GNU C Compiler tab. It's a huge list of options:
Here's full list: 
-x c -mthumb -D__SAMD21J17A__ -DDEBUG -DBOARD=SAMD21_XPLAINED_PRO -D__SAMD21J18A__ -DARM_MATH_CM0=true -DSPI_CALLBACK_MODE=false -DEXTINT_CALLBACK_MODE=true -DEVENTS_INTERRUPT_HOOKS_MODE=true  -I"../sam0/drivers/sercom/spi/quick_start_master/samd21_xplained_pro" -I"../src/ASF/sam0/utils/header_files" -I"../src/ASF/sam0/drivers/system/power/power_sam_d_r" -I"../src/ASF/common/utils" -I"../src/ASF/sam0/drivers/system/pinmux" -I"../src/ASF/sam0/drivers/system/power" -I"../src/ASF/sam0/drivers/system/reset/reset_sam_d_r" -I"../src/ASF/common/boards" -I"../src/ASF/sam0/drivers/sercom/spi" -I"../src/ASF/sam0/drivers/port" -I"../src/ASF/sam0/drivers/system/clock/clock_samd21_r21" -I"../src/ASF/sam0/boards" -I"../src/ASF/sam0/utils" -I"../src/ASF/thirdparty/CMSIS/Include" -I"../src/config" -I"../src/ASF/thirdparty/CMSIS/Lib/GCC" -I"../src/ASF/sam0/drivers/system/reset" -I"../src/ASF/sam0/drivers/system/interrupt/system_interrupt_samd21" -I"../src/ASF/sam0/boards/samd21_xplained_pro" -I"../src" -I"../src/ASF/sam0/drivers/sercom" -I"../src/ASF/sam0/utils/preprocessor" -I"../src/ASF/sam0/utils/cmsis/samd21/include" -I"../src/ASF/sam0/drivers/system" -I"../src/ASF/sam0/utils/cmsis/samd21/source" -I"../src/ASF/sam0/drivers/system/clock" -I"../src/ASF/sam0/drivers/system/interrupt" -I"../inc" -I"../src/ASF/common2/services/delay" -I"../src/ASF/common2/services/delay/sam0" -I"../src/ASF/sam0/drivers/extint" -I"../src/ASF/sam0/drivers/extint/extint_sam_d_r" -I"../src/ASF/common/services/ioport" -I"../src/ASF/sam0/drivers/events/events_sam_d_r" -I"../src/ASF/sam0/drivers/events"  -O3 -fdata-sections -ffunction-sections -g3 -Wall -mcpu=cortex-m0plus -c -pipe -fno-strict-aliasing -Wall -Wstrict-prototypes -Wmissing-prototypes -Werror-implicit-function-declaration -Wpointer-arith -std=gnu99 -ffunction-sections -fdata-sections -Wchar-subscripts -Wcomment -Wformat=2 -Wimplicit-int -Wmain -Wparentheses -Wsequence-point -Wreturn-type -Wswitch -Wtrigraphs -Wunused -Wuninitialized -Wunknown-pragmas -Wfloat-equal -Wundef -Wshadow -Wbad-function-cast -Wwrite-strings -Wsign-compare -Waggregate-return  -Wmissing-declarations -Wformat -Wmissing-format-attribute -Wno-deprecated-declarations -Wpacked -Wredundant-decls -Wnested-externs -Wunreachable-code -Wcast-align --param max-inline-insns-single=500  -Wno-long-long -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)"

Notice, that CFLAGS does contain both COMMON_FLAGS options. There's no need to use them twice, so we can drop COMMON_FLAGS and stick with CFLAGS only.

4) This is optional, but it lets to run GCC executable by only printing it's name without the full (and very long) path to it every time.
Add path to GCC binaries to PATH system variable. The path is "C:\Program Files (x86)\Atmel\Atmel Toolchain\ARM GCC\Native\4.8.1429\arm-gnu-toolchain\bin".

5) Create empty C file, for example D:\empty.c.
Get  macro definitions and redirect them to file by running GCC command with all that CFLAGS as arguments plus "-E -dM empty.c >lint_cmac.h" assuming you run command line from drive D:.

To have fully prepared command at hand, I created file D:\gcc_get_lint_cmac.txt with such contents:
arm-none-eabi-gcc -x c -mthumb -D__SAMD21J17A__ -DDEBUG -DBOARD=SAMD21_XPLAINED_PRO -D__SAMD21J18A__ -DARM_MATH_CM0=true -DSPI_CALLBACK_MODE=false -DEXTINT_CALLBACK_MODE=true -DEVENTS_INTERRUPT_HOOKS_MODE=true  -I"../sam0/drivers/sercom/spi/quick_start_master/samd21_xplained_pro" -I"../src/ASF/sam0/utils/header_files" -I"../src/ASF/sam0/drivers/system/power/power_sam_d_r" -I"../src/ASF/common/utils" -I"../src/ASF/sam0/drivers/system/pinmux" -I"../src/ASF/sam0/drivers/system/power" -I"../src/ASF/sam0/drivers/system/reset/reset_sam_d_r" -I"../src/ASF/common/boards" -I"../src/ASF/sam0/drivers/sercom/spi" -I"../src/ASF/sam0/drivers/port" -I"../src/ASF/sam0/drivers/system/clock/clock_samd21_r21" -I"../src/ASF/sam0/boards" -I"../src/ASF/sam0/utils" -I"../src/ASF/thirdparty/CMSIS/Include" -I"../src/config" -I"../src/ASF/thirdparty/CMSIS/Lib/GCC" -I"../src/ASF/sam0/drivers/system/reset" -I"../src/ASF/sam0/drivers/system/interrupt/system_interrupt_samd21" -I"../src/ASF/sam0/boards/samd21_xplained_pro" -I"../src" -I"../src/ASF/sam0/drivers/sercom" -I"../src/ASF/sam0/utils/preprocessor" -I"../src/ASF/sam0/utils/cmsis/samd21/include" -I"../src/ASF/sam0/drivers/system" -I"../src/ASF/sam0/utils/cmsis/samd21/source" -I"../src/ASF/sam0/drivers/system/clock" -I"../src/ASF/sam0/drivers/system/interrupt" -I"../inc" -I"../src/ASF/common2/services/delay" -I"../src/ASF/common2/services/delay/sam0" -I"../src/ASF/sam0/drivers/extint" -I"../src/ASF/sam0/drivers/extint/extint_sam_d_r" -I"../src/ASF/common/services/ioport" -I"../src/ASF/sam0/drivers/events/events_sam_d_r" -I"../src/ASF/sam0/drivers/events"  -O3 -fdata-sections -ffunction-sections -g3 -Wall -mcpu=cortex-m0plus -c -pipe -fno-strict-aliasing -Wall -Wstrict-prototypes -Wmissing-prototypes -Werror-implicit-function-declaration -Wpointer-arith -std=gnu99 -ffunction-sections -fdata-sections -Wchar-subscripts -Wcomment -Wformat=2 -Wimplicit-int -Wmain -Wparentheses -Wsequence-point -Wreturn-type -Wswitch -Wtrigraphs -Wunused -Wuninitialized -Wunknown-pragmas -Wfloat-equal -Wundef -Wshadow -Wbad-function-cast -Wwrite-strings -Wsign-compare -Waggregate-return  -Wmissing-declarations -Wformat -Wmissing-format-attribute -Wno-deprecated-declarations -Wpacked -Wredundant-decls -Wnested-externs -Wunreachable-code -Wcast-align --param max-inline-insns-single=500  -Wno-long-long -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)" -E -dM empty.c >lint_cmac.h

If you followed stage 4 and added path to system variable, you can copy it in command line nad run it as it is. If not, instead of just arm-none-eabi-gcc provide full path to .exe
As a result of running this command we now have file D:\lint_cmac.h with all the macros from compiler set to our project build options. The list is pretty long, so here's only part of it, so you could understand how does it look like:
#define __DBL_MIN_EXP__ (-1021)
#define __HQ_FBIT__ 15
#define __UINT_LEAST16_MAX__ 65535
#define __ARM_SIZEOF_WCHAR_T 32
#define __ATOMIC_ACQUIRE 2
#define __SFRACT_IBIT__ 0
#define __FLT_MIN__ 1.1754943508222875e-38F
#define BOARD SAMD21_XPLAINED_PRO
......................................

6) Now we need to specify the list of directories with system headers, so Lint would know where to get them.
Run arm-none-eabi-gcc -c -v empty.c. You get a lot of output. Find the section which starts with "#include "..." search starts here" and ends with "End of search list". Copy that paths to a file named gcc-include-path.lnt (create that file first, of course).
There's more simple way to do this. Just go to Project options in Atmel Studio and copy these paths from ARM/GNU Common->General->Default Include Paths.
gcc-include-path.lnt file provides this paths to PC-Lint as parameters, so it must include paths in form of --i parameter as follows:
--i"c:\program files (x86)\atmel\atmel toolchain\arm gcc\native\4.8.1429\arm-gnu-toolchain\bin\../lib/gcc/arm-none-eabi/4.8.3/include"
Each path should be on a separate line.

7) Copy files lint_cmac.h and gcc-include-path.lnt to PC-Lint folder C:\lint.

Running PC-Lint for GCC compiled projects.

Use provided with install co-gcc.lnt as your Lint configuration file or as a template for your own such file.
You will invoke Lint in form of
lint std.lnt source-files-to-be-linted
That std.lnt file could include that macro and directories files we got earlier. It also can include a list of source files, so you'll be able to lint your project with just 
lint std.lnt

I use PC-Lint with Visual Lint embedded in Atmel Studio and Visual Lint invokes Lint with custom std.lnt file. This file includes following:

// Author recommendations and checks recommended by industry bodies
au-barr10.lnt                   //
au-misra3.lnt                   //

// Compiler configuration
co-gcc.lnt                   // AVR/ARM GCC compiler

// PC-lint warning level (0 through 4). Recommended levels are -w1 (Basic), -w2 (Thorough), -w1 -w3 (Aggressive), or -w3 (Very Aggressive)
-w1 -w3                             // Aggressive

Here we turn on Michael Barr's 10 rules check and MISRA-2012 checks, set warning level to Aggressive and use co-gcc.lnt as compiler configuration.

co-gcc.lnt is used as it was provided with PC-Lint installation and it contains the calls to our gcc-include-path.lnt and lint_cmac.h files (see the contents of your co-gcc.lnt and how it calls this files and other options useful for GCC).

So now any time we run PC-Lint it takes in consideration all the include files paths and all macros that are actually used in our GCC project compilation.


Monday, April 13, 2015

Static inline functions in embedded C.

To declare and define inline static function:

1) Do not declare it in header file! Any other source files must be unable to know about this function's existence.
2) Instead, declare it in function prototypes section in source file after variables section and before functions section.
Example: static inline void dmx_flush_idx(void);
3) Place definition of a function in the source file where you define all your functions.
Example:
static inline void dmx_flush_idx(void)
{
    g_rx_cnt = 0;
    g_rx_idx = 0;
    g_tx_cnt = 0;
    g_tx_idx = 0;  
}

When static inline function is declared and defined as described, it passes MISRA 2012 checking in PC-Lint and Atmel Studio's Clang-based Naggy isn't naggy about it ;)

Thursday, April 9, 2015

Integrating Cppcheck into Atmel Studio 6.2.

Introduction.

Static analysis tools are very useful. And it's a good idea to use them frequently.
Since I am mostly using Atmel Stuio as my IDE, it seems natural to try and integrate all possible static analysis tools right into it than use them in standalone manner.

Tools I've tried so far are Cppcheck and PC-Lint. The former is freeware and has GUI application where you can test the whole project folder.
The latter is not free, but it is a nightmare to use with GCC compiler - there's no any nice instructions on how to set it up for GCC. I did all the steps from readme files of PC-Lint, but in my case there were a lot of  errors after invoking GNU Make and I didn't really want to dig into it to make it work.

Instead, I downloaded Visual Lint, which has integration of PC-Lint into Atmel Studio 6.1 and 6.2, although even this way needs some manual Windows registry editing to make things work. More to say, Visual Lint still uses all PC-Lint configuration and option files, so even if it seems to work now, I'm still not sure it works how it should work. I'll take a more closer look on working with Visual Lint and post about it later. One more thing - Visual Lint is not freeware, it has Trial version which works for 30 days. Seems enough to decide whether you need it or not.

Now, let's get to integrating free Cppcheck into Atmel Studio.

Integration of Cppcheck into Atmel Studio.

This article is where I got the method and description.

First, get Cppcheck from their site and install it.

Then, you may see that there're integration plugins for popular IDEs, including Visual Studio.
And since Atmel Studio is based on VS, it seems appropriate to use VS plugin.
Unfortunately, it didn't work for me.

I also found somewhere VSIX plugin for Atmel Studio 6.0, but that didn't work either.

The solution is to use Cppcheck as External Tool. The procedure of integrating is usual for Visual Studio and the same with Atmel Studio.

In Atmel Studio, open Tools-->External Tools.
Click Add and fill in forms as seen on this screenshot:


You can change arguments as you wish to get different level of testing and reporting.

After that, Cppcheck appears in External Tools menu:


Using Cppcheck under Atmel Studio.

It can be used in following way. When source file opened in Atmel Studio, just click Cppcheck in External Tools and it will perform static analisys of this file. Any messages will be shown in Output window.
Here's an example of Error message from Cppcheck:

The example of arguments shown previously works for testing only current source file. To test all files in project folder, just change $(ItemPath) to $(ItemDir).

Then, you can experiment with different flags. Try and find what combination works better for you: maybe it's preferred to check only for critical errors in code, or maybe it's nice to have warnings about style or performance issues.

This is flags I found useful for me:
--force - forces checking of all possible #IFDEFs; could be slow, but is a must for big projects
--includes-file=$(EXTRA_INCLUDE_PATHS_ARM)" - in my case EXTRA_INCLUDE_PATHS_ARM is a system path variable showing path for text file with paths of all header files from GCC-ARM toolchain.
-q or --quiet - quiet mode, suppresses service messages from Cppcheck
-j 4 - to use all four cores of CPU for faster testing
--enable=warning,style,performance - enable more tests
--inconclusive - shows warnings even if Cppcheck is not sure if it's really and error. There may be false positives, each such warning should be closely investigated whether it really needs any correction.

Full list of checks performed by Cppcheck is here.