Is there a way to deal with unsupported C compilers?

sonarcfamily
build-wrapper

(Sebastian) #1

Dear community,

we have a little research project on a TMS320F28379D microcontroller. The code is written in C99 - compliant C with some GCC-style extensions (__attribute__((always_inline)), __attribute__((ramfunc)) and so forth) and intrinsic functions like __nop(). I would like to analyze the code with the following SonarQube setup:

  • SonarQube 7.4 Developer Edition
  • SonarCFamily 5.1.1
  • SonarQube Scanner 3.2.0.1227
  • build-wrapper 5.1

The compiler is not officially supported by the SonarCFamily plugin, so it comes as no surprise that analysis results are unsatisfying. The CFamily Sensor says

INFO: Sensor CFamily [cpp]
INFO: Available processors: 8
INFO: Using 1 thread for analysis according to value of “sonar.cfamily.threads” property.
WARN: Metric ‘comment_lines_data’ is deprecated. Provided value is ignored.
INFO: Using build-wrapper output: /home/sneuser/repositories/F172_20_FW/bw_output/build-wrapper-dump.json
INFO: 0 compilation units analyzed
INFO: PCH: 0 + 0 + 0 , 0 - 0 - 0 - 0
INFO: FS: 0 lookups
INFO: PPH: 0 files, 0 bytes, 0 hits, 0 queries
INFO: Sensor CFamily [cpp] (done) | time=1242ms

and the only thing recognized are code duplications.

I tried to bypass the build-wrapper and manually predefine macros and intrinsics and actually got results:

INFO: Sensor CFamily [cpp]
INFO: Available processors: 8
INFO: Using 1 thread for analysis according to value of “sonar.cfamily.threads” property.
WARN: “sonar.cfamily.build-wrapper-output.bypass=true” property is deprecated and will be removed soon.
WARN: build-wrapper is not used to analyse this project. This may result in false-positives and false-negatives.
INFO: Parsing based on ‘c11’ standard.
WARN: Metric ‘comment_lines_data’ is deprecated. Provided value is ignored.
INFO: 46 compilation units analyzed
INFO: PCH: 0 + 0 + 0 , 41 - 0 - 0 - 0
INFO: FS: 75 lookups
INFO: PPH: 9 files, 42973 bytes, 18 hits, 27 queries
INFO: Sensor CFamily [cpp] (done) | time=2171ms

Now SonarQube computes metrics, and finds issues. Unfortunately there are – as the warning suggests – a ton of false positives, in this case mostly c:S819 Functions should be declared explicitly.
While I am sure I could tweak sonar-project.properties and the quality profile to get good results, the warning about bypassing the build-wrapper being removed soon is a showstopper.

So my question is:
What is the preferred way of dealing with projects like this one? Not at all? :wink:
IMHO that would be a real shame because after some tweaking of (AFAICT undocumented!) sonar.c.* / sonar.cfamily.* properties, results were actually quite good and SonarQube did indeed find some real issues.

Kind regards,

Sebastian


Help needed, SonarQube7.4 analyze embedded C project failed!
(Loïc Joly) #2

Hello Sebastian,

Bypassing the build-wrapper is indeed not the recommended solution, and in fact, in next version of SonarCFamily (already deployed on SonarCloud), this will no longer be possible.

If your code is C99-compliant, what I would try to do is see if it can be compiled with clang or gcc without too many errors (don’t spend time on linking, the compilation is the only thing that matters, and linking might require some libraries not available for gcc). Maybe one step to achieve reasonably good results would be to create a header that contains the declaration of those intrinsic functions.

Once this is done, you can run the build wrapper around this ad-hoc clang/gcc compilation, and use its output to perform an analysis.

Of course, none of this is supported, but it might work pretty well, depending on what compiler-specific code is contained in the source code.


(Sebastian) #3

Great idea! I will try that right away and report the outcome.


(Sebastian) #4

TL;DR:
Worked perfectly. :partying_face:

Here is what I did:

  1. Created a new header file util/analysis.h which
    • contains #defines for intrinsic functions, types and other special symbols,
    • sits outside the source directory, so it does not interfere with the normal build process.
  2. Created a new Makefile.sonar, which collects options for gcc:
    • include search paths
    • #defines that are set by CodeComposerStudio during the normal build process
    • important: -include util/analysis.h to make gcc happy
    • some tweaks to get rid of gcc warnings
    • a special rule for C files ending in .cla (a co-processor which can also be programmed in C)
  3. Added a rule to my existing Makefile which invokes the build-wrapper around make -f Makefile.sonar and Sonar Scanner.
  4. The only thing I had to change in the code:
    Add #ifndef / #endif around a macro that expands to __asm(" ESTOP0") which is of course an illegal instruction on x86-64. I guess this must be done for all macros that expand to inline assembly as soon as they are used in one of the compilation units.

If anyone is interested, I’d be happy to upload / otherwise share these hacks. :slight_smile:

Greetings,

Sebastian


(Christian) #5

@camco
I have exactly the same problem. I’m trying to get the build-wrapper working with CCS and the cl2000 and have no idea how to handle it…
If I’m using the sonar-scanner only, I get a lot of heavy parsing errors, which - to be honest - I don’t understand, since the scanner stumbles over simple C code…

@JolyLoic
Is there some kind of roadmap for supporting other compilers?


(Sebastian) #6

You should definitely use the build-wrapper. It looks over your build process’ shoulder and extracts compiler flags like -DFOO="bar", -I/usr/include/something or whatever litany of flags you have to specify. Without the scanner knowing what to include, which symbols and pre-processor magic spells are defined and so on I would assume it has practically no chance on earth to correctly parse your code.

Now here’s the problem:
build-wrapper expects those flags to be written in either gcc, clang or <MS_compiler_name> syntax. Unfortunately cl2000 uses its own:

  • gcc -I/usr/include/something becomes cl2000 -i/usr/include/something
  • gcc -DCPU1 becomes cl2000 --define=CPU1
  • and so on…

Because of that it is my understanding that your only choice is to

  1. Make your code compile with gcc.
    • You will probably have to introduce some hacks to work around cl2000 specialties.
    • You will probably have to create a special Makefile.
    • Please have a look at my post and/or contact me if you are interested in details about what I did for my project.
  2. Use build-wrapper around a plain gcc build.
  3. Feed build-wrapper's output to sonar-scanner.

(Christian) #7

How did you handle these specialities?
Keywords like “interrupt”, “cregister” are unknown by gcc:

../TI/inc/PeripheralHeaderIncludes.h:38:18: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘volatile’
 extern cregister volatile unsigned int IFR;
                  ^
../TI/inc/DSP2803x_PieVect.h:26:19: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘void’
 typedef interrupt void(*PINT)(void);
                   ^

Can you share your analysis.h?


(Sebastian) #8

Here is my analysis.h
analysis.h.txt (986 Bytes)
and a special Makefile.sonar
Makefile.sonar.txt (810 Bytes)
which is used in the target analysis of the main Makefile:

analysis:
       build-wrapper-linux-x86-64 --out-dir bw_output make -f Makefile.sonar && sonar-scanner

HTH
Sebastian


(Christian) #9

Thanks! that helps a lot!

But how did you handled the assembler messages like Error: no such instruction: 'eallow' caused by lines like
asm(" EALLOW");


(Sebastian) #10

Fortunately I did not use inline assembly in the first place. I’m using compiler intrinsics like __eallow(), __edis(), __max(), __eisqrtf32() and so forth. TI headers use macros EALLOW, EDIS and so on. All these are “defined away” by analysis.h.

You will have to refactor your code to not use any inline assembly directly and use existing macros instead or - worst case - define new ones you can override with your custom analysis.h.


(Christian) #11

I changed the code like this:

#ifdef STATIC_CODE_ANALYSIS
	asm("nop");
#else
	EDIS;
#endif

and it works. Thanks a lot!


(Sebastian) #12

Shouldn’t that be the other way around? :wink:

Edit:
Ah, got it. asm("nop"); works on x86-64 as well.