Johan Bezem ist Mitglied bei
Posts Tagged ‘Lint’
Using PC Lint in Eclipse – no plug-ins required
I’ve been tinkering with Eclipse to see if I can make PC Lint run from inside Eclipse. Of course, running Lint as an external tool is always an option, but I couldn’t get Eclipse to recognize the Lint warnings and display them in the ‘Problems’ tab. From my testing and what I could find in the Internet, it seems that the output of so-configured external tools is not run by the error parsers installed.
What I found in the Internet suggested that I have to use a special error parser, configure that, and then it would run. Now, I have nothing against using extra tools if that’s the way to achieve what I want, but if I can do without, I consider that a better solution, since every tool used is another tool to learn how to use. And delivering configurations to my customers, it’s not my business model to make myself indispensable, on the contrary.
So, after experimenting some more, I have found a way to use PC Lint with Eclipse (I used Ganymede for testing) without resorting to additional tools or plug-ins. Be aware, I’m not an Eclipse expert in any sense, so there may be many more and possibly better ways to achieve this. I’d be happy to get some comments with further tips.
The key is to add a new ‘build target’ for running PC Lint:
- On the properties dialog for the workspace (right-click and select ‘Properties’), in the ‘Builders’ section, check that the “CDT Builder” is selected.
- In the ‘C/C++ Build’ section, click on “Manage Configurations…” and add a configuration for running Lint.
- Select the configuration from the drop-down box.
- On the ‘Builder Settings’ tab, deselect the “Use default build command” and instead provide the path to the Lint executable, eg. “C:\Lint90\LINT-NT.EXE”. Specify the ‘Build location’, from which directory you want Lint to run, either absolute or using the buttons offered.
- On the ‘Behaviour’ tab, deselect “clean”, and provide the parameters to Lint in the (selected) ‘Build (Incremental build)’ text box.
- In the ‘Settings’ section of ‘C/C++ Build’, make sure that all CDT parsers (or at least the “CDT GNU C/C++ Error Parser”) are selected.
Be aware that these indications all assume some kind of a general build or make environment, so the names themselves do not indicate the possibility for running a single program. If you use a Makefile concept for running Lint, you may use that as well, and ‘clean’ might even make sense.
And that’s it.
OK; one final thing remains. We now need to coerce Lint into providing warnings in a format similar to the GNU C/C++ compiler. This can be achieved by some Lint options in your option file. I use:
// Output options: One line, file info always; Use full path names
-hF1
+ffn
// Normally my format is defined as follows:
//-"format=%(\q%f\q %l %C%) %t %n: %m"
// For eclipse-usage, the GCC error format is necessary,
// since we have only the default eclipse error parser available.
-"format=%(%f:%l:%C:%) %t %n: %m"
// And also for eclipse, the reference locations provided by
// Lint, put into square brackets “[Reference: File: ... Line: ...]”
// are not correctly handled, therefore we switch them off.
// Enable warning 831 if you are interested.
-frl
// Do not break lines
-width(0)
// And make sure no foreign includes change the format
+flm
If you want to know more than my comments are telling you, check the Lint manual for details.
Now, don’t get me wrong: I will not switch from my trusted SlickEdit to Eclipse. But, as a consultant, I cannot always pick and choose. And running Lint from Eclipse, having a way to jump from warning to warning, definitely beats manual navigation.
Happy Linting!
Why 32768 isn’t always the same as 0×8000
Contrary to intuition, the C constants ’32768′ and ’0×8000′ have an
identical representation (0×8000), but possibly different types in C.
If you consider a processor with a 16-bit int type, and a 32-bit long
type, 32768 is considered long, whereas 0×8000 (and the octal variant
0100000) is considered ‘unsigned int’.
If you feel the need, check the C standard at
http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf, page 55 at the
bottom, and the table at page 56. (look at Harbison & Steele, 5th edition, and look at section 2.7.1 page 24ff).
Normally, there is no problem when using such values, since the
representations are identical.
However, consider this small example:
#define C_DECIMAL 32768
#define C_HEXADECIMAL 0x8000
void main(int argc, char *argv[])
{
volatile long long_dec = ((long)~C_DECIMAL);
volatile long long_hex = ((long)~C_HEXADECIMAL);
return;
}
When C_DECIMAL is considered long, the negation will invert 32 bits,
resulting in a representation 0xFFFF7FFF with type ‘long’; the cast is
superfluous.
When C_HEXADECIMAL is considered ‘unsigned int’, the negation will invert
16 bits, resulting in a representation 0x7FFF with type ‘unsigned int’;
the cast will then zero-extend to a ‘long’ value of 0x00007FFF.
Checking with a 16-bit integer compiler (CW7.1 ColdFire using ‘-intsize 2′):
0x00000000 _main: ; main: 0x00000000 0x4E560000 link a6,#0 0x00000004 0x518F subq.l #8,a7 0x00000006 0x223CFFFF7FFF move.l #-32769,d1 0x0000000C 0x2D41FFF8 move.l d1,-8(a6) 0x00000010 0x223C00007FFF move.l #32767,d1 0x00000016 0x2D41FFFC move.l d1,-4(a6) 0x0000001A 0x4E5E unlk a6 0x0000001C 0x4E75 rts
For those of you who do not know how to read assembler code I have made the differing values italic. So the compiler confirms the difference in behavior, and this is not a compiler error.
Lucky you if you have Lint to warn you. (Yes, I know, other tools will too, if you let them…)
Happy coding!
Use of in-line assembler using ‘asm’ variants
Just a “small” post to expand on my most recent tweet. In-line assembler is quite a difficult topic, but unavoidable in most embedded environments. And the syntactic variants are more numerous than the bugs in your code.
For that reason I give a piece of advice: For each different assembler semantic, use a different macro. For an assembler function, use ASM_FN like this:
ASM_FN int MyAssemblerFunction(void)For an in-line assembler block, use
{
...assembler instructions...
}
ASM_BLOCK like this:And for single-line in-line assembler instructions use
...
a += 4;
ASM_BLOCK volatile {
...assembler instructions...
};
ASM_LINE like this:Now, all these uses will need to expand into the non-standard keyword
...
a += 4;
ASM_LINE movb ax,0b00000010;
...
asm for the compiler to process everything correctly. Many compilers accept different forms of the keyword, so you may use asm, __asm and __asm__ interchangeably. If you want to, you can take these three variants instead of the ASM_(FN|BLOCK|LINE) as I suggested.
The idea is to enable Lint to expand each of these three forms differently: The function containing only assembler instructions shall be ignored by Lint, but its prototype needs to be known. Therefore we need to enable the Lint keyword _ignore_init (the body of the function is seen as a form of “initialization”), and provide the options:The plus-sign in the second option prevents the definition in our code (to
+rw(_ignore_init)
+dASM_FN=_ignore_init
asm or one its variants) to override the Lint-specific definition. However, for ASM_BLOCK this replacement will not work, so we need a different replacement: And the third form again needs a different replacement, since in this case, no brackets need be present at all:
+rw(_up_to_brackets)
+dASM_BLOCK=_up_to_brackets
With some other form of in-line assembler definition you might even need
+rw(_to_semi)
+dASM_LINE=_to_semi
_to_eol, or one of the other gobblers. But make sure to use different macros for different syntactic usages of asm, so you have the chance to use different gobblers for all situations.
Happy Linting!
Twitterfeed on PC Lint
I’m going to experiment a little with Twitter. I created a Twitter account called LintTweets, where I intend to collect some Lint wisdom in 140 characters. In some cases I may expand on those quite limited 140 characters in my blog here. You’re welcome to read here and/or follow me on Twitter. The frequency of my tweets there will probably not exceed once a week, so don’t expect too much.
Happy Linting!
New version of “How To Wield PC-Lint”
I finally got to adapt my document for the most recent Lint version 9.0b. I have replaced the original document, but if you just want to see where the changes have been incorporated, a version with change bars is available.
Happy linting!
Gimpel announces a new version of PC Lint: 9.0!
I guess the word is out:The press release is online.
I’ve had the pleasure of being a beta-tester for this version, and even though I’m an experienced user of version 8.0, I must admit that I’m impressed: I would never have believed that such a functionality increase would have been possible. Lint 8.0 already is really good, it’s amazing that Lint 9.0 can offer so much more.
Recommended!
Happy linting!
Heads-up: Gimpel released patch version 8.00x
If you’re not a regular reader of the Gimpel forum on PC Lint, please be aware that Gimpel released the next patch version of PC Lint, 8.00x. Several smaller errors are fixed, some support files have been updated (don’t forget the read80.txt and the bugfix80.txt).
And finally, support for Visual Studio 2008 is added.
If you’re a user, have a look at the patches page.
Happy Linting!
Johan
Updated version of “How to wield PC Lint”
With some more experience from some different environments, I have felt the need to update my document on “How to wield PC Lint”. The main changes I made to the document:
- the Gimpel-provided compiler support files ‘co-*.lnt’ may be a fair starting point, but certainly not the final word;
- I changed the name for my output file to ‘lintout.out’, for one project the names ‘error’ and ‘errors’ were already in use, and using the extension ‘lnt’ doesn’t seem a good idea anymore;
- I now use quotes around the filename for Lint’s output messages, to accommodate environments using spaces in path names;
- I added a warning about suppressing errors numbered less than 400;
in addition to some small corrections.
Have fun!
Johan
PolySpace – pros and cons
I have some experience with a static (or quasi-dynamic) code-checker named PolySpace.
And since several requests have reached me to comment on the usefulness of PolySpace, sometimes in comparison with PC Lint, I decided to share some of my experiences with my (hopefully large) audience.
I used version 2.6 IIRC more than two years ago. However, the observations noted here were written down less than a year after that, and backed-up with a complete experience report written in parallel to my activities as a sort of diary. Memory lapses therefore unlikely.
I have made following observations on the deployment and use of PolySpace for embedded systems:
- PolySpace starts its operation with a GNU-compiler run locally on the users machine, and being far more picky than most compilers:
- Macro redefines (without#undef) are not allowed
- An unused macro parameter is not allowed
- Function call without parentheses is not allowed (this is a real error, resulting in a function pointer; however, some compilers just warn, and “do the right thing”, like the GreenHills compiler)
- a macroassertis not allowed, since this would redefine an ANSI macro/function
- Too many braces in struct-initializers are not allowed, i.e.int myArr[2] = { { 5, 1 } };
- Bit fieldunsigned long bf:32is not allowed; justunsigned int bf:32; for widths less than 32 (long size?), no such limitation seems present
- Include filenames with a trailing space are not allowed, i.e.#include "abc.h "
- All prototypes must be available, correct and visible on use
- Inlining can be tedious, especially wheninlineis implicitly taken to meanstaticas with some compilers possible. Be aware: PolySpace warnings/errors in these cases are in no way always to the point and useful. In several cases it has proven necessary to dig quite deep into PolySpace innards and/or to refer to PolySpace support in order to understand why processing wasn’t successful. - Several concepts are tedious to accommodate with PolySpace, and may lead to red errors (errors stopping the processing at that point)
- All variables must be initialized in a way recognizable for the GNU compiler
- Backwardgoto‘s must be instrumented with special PolySpace macros (changes in source code unavoidable!)
- Some forms of assembler need to be stubbed, since just disabling the assembly code may lead to a missing or empty function
-assert(false)orassert(0)is not allowed, not even in conditional code, and leads to red errors, stopping the processing of the rest of the function
- Functions known not to terminate must be conscientiously indicated
- All task entry points (in addition tomain) must be indicated, as well as the ISR entry points
- Only functions with the prototypevoid <function>(void)are allowed as entry points for PolySpace
- Assignment of enum values to bit fields (even if the value range basically fits) can only be performed masked to the width of the bit field, even if the bit field has been declared with the enum-type as base
- If specific input conditions are implicitly assumed, and violation of those could/would lead to red/abort errors in PolySpace, these conditions must be made explicit by using asserts - Aside from the long running times required (more than four days on ‘Software Safety Analysis level 2′ with ‘-O2′), the welcomed option “continue-with-red-errors”, which enables one to find multiple aborting errors in one PolySpace run, may cause spurious red errors also, and makes it tedious to find the source of a series of red errors
- Deployment: If the client version and the server version differ even in minor parts of the version number, finding the cause of inevitable failures is very difficult, since the only clear-text error message is deposited in a cryptic log file normally ignored as part of the PolySpace noise
- Deployment: PolySpace on Windows needs its own version of the Cygwin environment; one cannot install PolySpace without also installing Cygwin. Since it is problematic to have more than one Cygwin installation on one machine, other tools and environments cannot be used on the same machine. To my experience this includes the Standard Core development environment from BMW/3SOFT as well as the testing tool Tessy, among others. And since PolySpace will not run with a different version of Cygwin, this might limit its deployment options. Full runs on a (Linux?) server are possible only if and when the server has access to the original sources, through Samba, FTP, NFS, or whatever. Depending on the regular development environment, this may be tedious but possible.
- Most important in my opinion: Mutual exclusion primitives must always appear in pairs within the same scope, like e.g. DI/EI. However, with an operating system like OSEK or VxWorks (or similar), sometimes constructs exist that do not appear in pairs (like ‘osSaveDisableGlobal’ only uses ‘DI’, whereas ‘osRestoreEnableGlobal’ only uses ‘EI’), or one has three primitives, two for setting and one for clearing (like ‘setLocal’ and ‘setGlobal’ vs. ‘clearAll’). Such combinations cannot be accommodated in
PolySpace. The idea was to add extra (empty) macros for use as PolySpace guards. However, only code reviews could ascertain that such guards had only be placed at the appropriate code lines, and nowhere else. Recommendation even from PolySpace support (!!) was to concentrate on the code reviews, and refrain from implementing such primitives in the code to have PolySpace check the multitasking accesses. Thus, I have no experience with the problematic cases PolySpace would be able to find.
I have worked with PolySpace for some three months on a project with roughly estimated 50 kLOC C code (Counted semicolons outside of comments/strings, but fair enough). I cannot assume to have seen all potential road blocks using PolySpace, as I cannot assume that all described road blocks will prove relevant for your C code. Using PolySpace for C++ was outside my scope. Therefore, these observations are just that: observations.
And please be aware: This was version 2.6, PolySpace has not halted development, and it might very well be that some of the issues mentioned here have been resolved
already.
And on the other hand, the things that PolySpace finds can be amazing: If a function creates a divide-by-zero runtime error when called with a parameter value of, say, 32,
PolySpace will find that. It will find effectively unused code, even if the conditions are far from trivial, and many other things.
OK, Lint will find much for you. If your code is Lint-clean, and both PolySpace and Lint have been properly configured, PolySpace will find few (but probably serious) errors,
if any. So, if you have the money, resources and time to use PolySpace, use both: Lint on a daily basis, available to all developers, with a clear message that code shall be clean before being checked-in. And well in advance of the next release, do a full PolySpace check, and take the warnings seriously.
Happy debugging!
How to handle include paths in PC Lint
Include file processing can be a bit tricky. Here I present a few Q&A on processing include files, and specifying paths.
- My
INCLUDEenvironment variable is set for Microsoft compilers. If I Lint for <name your compiler here>, Microsoft includes are processed. What can I do?
A: Well, for one, you can delete the environment variable for the shell you use. You may redefine yourINCLUDEvariable to reflect your compiler. Both can be achieved in a batch file. - I cannot change the value of
INCLUDEon my system. Can I tell Lint to simply ignore it?
A: I haven’t found a way using options in Lint, so until someone tells me how it can be done, I’d say ‘no’. However, you can write a small batch file to emptyINCLUDEbefore calling Lint. That works even if your system’s include is forced to be set. - I defined my own include variable
MYINCLUDE, and used ‘-incvar‘ to activate it. Lint still usesINCLUDE, though. What can I do?
A: According to my tests, Lint gives priority to an existing and validINCLUDEvariable. If the files can be found usingINCLUDE, you are forced to delete or redefineINCLUDE. - I have written my own version of (example) ‘
stdio.h‘. Lint refuses to find that file. What can I do?
A: Lint processes ‘-i‘ options for specifying include paths in order. Chances are, that you compiler’s include path is specified before your application’s. Use the verbosity options ‘-voif‘ (large output!) to see what order is presented to Lint, and change it, or use ‘--i‘ (double dashes!) to indicate the compiler’s include path. See the ‘readme.txt‘ for the current patch level for details. - I used ‘
--i‘ to indicate my compiler’s include path, but now Lint is finding Microsoft’s include files again. What can I do?
A: According to my tests, a present and validINCLUDEvariable is taking precedence over ‘--i‘ paths. You’ll have to delete or redefine theINCLUDEvariable. - I need to see what files Lint is using exactly, which ‘
-i‘/’--i‘ options it is processing and in which order. What can I do?
A: It will produce an enormous amount of output, but ‘-voif‘ (in addition to your normal options, preferably as one of the first options on the command line) will provide the information you need.
For all options presented here, look in the manual, or see the ‘readme.txt‘ as presented on the support pages of Gimpel’s website.
Deutsch
English
Nederlands