Splint is offering to present a “special reward” to the “first person to produce a real program that produces no errors with strict checking.” Here’s a summary of a recent real-world automotive project attempting this.
TL;DR: We were not successful, but we learned a few things about MISRA compliance. Feel free to skip to the Observations section to learn more.
Introduction
Here’s the Splint entry for the Mode Selector Flags strict in Appendix B Flags.
Absurdly strict checking. All checking done by checks, plus modifications and global variables used in unspecified functions, strict standard library, and strict typing of C operators. A special reward will be presented to the first person to produce a real program that produces no errors with strict checking.
It is, of course, theoretically possible to create error-free software that doesn’t comply with best practices specified in modern functional safety standards such as IEC 61508 or ISO 26262.
It’s also possible to create software with safety-related functions that do not achieve the guidelines set out by the Herstellerinitiative Software (HIS) that I wrote about previously. I’m sure there are plenty of successful projects that exist in the real world that might be able to demonstrate that. I am also sure there are developers who, like me, want to—or are required to—measure their work and assess compliance with MISRA rules.
I’m going to give you some insight into what happened over a three-year period on a real-world automotive project. Keep in mind, I have no way of knowing whether you can expect the same results on your project, or if this project is even representative of others. Although I don’t have access to the data anymore, it does seem to be a familiar pattern across other projects I have worked on.
If you don’t like MISRA, or you don’t want to learn from the underlying principles the rules point to, or you don’t like measuring your code or audits, then this blog post is probably not for you.
About MISRA
Both IEC 61508 and ISO 26262 highly recommend specific programming languages and additional measures for languages typically used for safety-related code like C and C++ additional measures.
IEC 61508 Part 7 Annex C Table C.1
|
Programming language |
SIL 1 |
SIL 2 |
SIL 3 |
SIL 4 |
---|---|---|---|---|---|
9 |
C |
R |
-- |
NR |
NR |
10 |
C with subset and coding standard, and use of static analysis tools |
HR |
HR |
HR |
HR |
11 |
C++ (see Annex G for guidance on use of object-oriented facilities) |
R |
-- |
NR |
NR |
12 |
C++ with subset and coding standard, and use of static analysis |
HR |
HR |
HR |
HR |
ISO 26262 Part 6 Section 9.4 Table 7
|
Methods |
ASIL |
|
|||
---|---|---|---|---|---|---|
A |
B |
C |
D |
|
||
1h |
Static code analysisd |
++ |
++ |
++ |
++ |
|
d |
Static analyses are a collective term which includes analysis such as searching the source code text or the model for patterns matching known faults or compliance with modelling or coding guidelines. |
It seems that magically adding a coding standard, using a subset of the language and then conducting static analysis, unlocks a widely supported programming language for safety-related work. MISRA is a well-known language subset for which static analysis tools already exist.
MISRA Violations
The first manual use of the static analysis tool resulted in thousands of hits. The tool was unable to parse certain sections of code, and the warnings were almost unintelligible.
Msg(4:3453) |
A function could probably be used instead of this function-like macro. |
Msg(4:3660) |
Named bit-field consisting of a single bit declared with a signed type. |
Msg(4:3719) |
Implicit conversion: unsigned char to float. |
There were plenty of easy-to-fix warnings, including:
Msg(6:1011) |
[C99] Use of C++ com inclusionment '//'. |
Msg(4:0883) |
Include file code is not protected against repeated. |
You can see in the graph how quickly the easy changes were made. This was mostly just a brute-force activity to rework lines and files over the weekend, with the help of Perl and other utility scripts. It was rewarding to see the magnitude of the violations coming down quickly, but there were two nagging concerns:
- What about the remaining hits we don’t understand how to fix?
- How are future commits protected against the same errors?
The developer’s contributing code had not changed overnight by fixing the easy warnings from the static analysis tool. It was cumbersome to manually start the static analysis with every code change, and tedious to find the same old easy-to-fix faults.
We needed a way of quickly highlighting code that was not going to pass the static analysis and to ensure something easy to fix was not creeping back into the code.
Therefore we conducted the following actions:
- Switching on all the warnings and diagnostics for every tool
- Compiling with GCC as well as the production cross compiler
- Switching on all the warnings GCC offers
- Using splint-checks mode at commit time
- Automating the static analysis tool so it could be run periodically
The results in all these graphs come from the results of the periodic use of a static analysis tool which was used before every customer release (approximately every two months). The tool used reported nearly 80 individual measurements, including the list below.
Magnitudes
- Number of external variables
- Number of functions
- Number of statements
- Number of lines of code
Complexity
- Deepest level of nesting
- Essential cyclomatic complexity
- Cyclomatic complexity
Relatives
- Comment-to-code ratio
- Program difficulty
- Code portability as percentage
Magnitudes
In all graphs, the actual figures are normalized. Here, predictably, the magnitude type measurements show that lines of code, statements and functions all grew over time at a similar rate.
At some point either the project becomes complete and grows no further, or presumably the code outgrows the processing capability. Measuring spare processor capacity (not plotted) by running a Whestone on development builds shows how that changed over time and was correlated with team size and these magnitude measurements.
Complexity
Cyclomatic complexity seems to be a topic that everyone has an opinion on. Here, the Green line of essential cyclomatic complexity (ECC) is much lower than the suggested HIS maximum of 10.
Once the initial areas of concern were identified, it was easy to rework the code, making it much easier to test. Nesting took some review, monitoring and rework and was consistently around the upper limits recommended by HIS.
Relatives
The comment-to-code ratio steadily declined towards the lower limit recommended by HIS, shown as a Red limit. The static analysis reported that the program difficulty improved after the initial cleansing of the MISRA warnings was completed. Portability also got better over time as the changes recommended by MISRA were completed.
Maintenance Violations
The first analysis conducted at the start of the project showed 50% of the MISRA violations were from seven rules: 2.2, 6.4, 6.5, 18.4, 19.4, 19.7 and 20.2.
§6.2 Rule | MISRA-C Rule |
---|---|
2.2 | Source code shall only use /* … */ style comments |
6.4 | Bit fields shall only be defined to be of type unsigned int or signed int. |
6.5 | Bit fields of type signed int shall be at least 2 bits long. |
18.4 | Unions shall not be used. |
19.4 | C macros shall only expand to a braced initialiser, a constant, a parenthesised expression, a type qualifier, a storage class specifier, or a do-while-zero construct. |
19.7 | A function should be used in preference to a function-like macro. |
20.2 | The names of standard library macros, objects and functions shall not be reused. |
All easily fixed. The more difficult-to-fix MISRA included Rules numbered 10 in section 6.2 of MISRA-C, including:
The value of an expression of floating type shall not be implicitly converted to a different type if:
a) it is not a conversion to a wider floating type, or
b) the expression is complex, or
c) the expression is a function argument, or
d) the expression is a return expression.
The static analysis tool doesn’t give a great deal of help other than to point out the line and roughly where in the line the concern is. Understanding the system of underlying type proposed by MISRA-C takes some effort, and the guidelines explain clearly “that underlying type is an artificial concept.”
Initially, it wasn’t unusual to break down a section of code and spend time with the static analysis tool to find combinations of casting and promotion by trial and error to work out, which would still perform the desired function and pass static analysis.
Many times all that was required were extra parentheses or an additional cast possibly serving only to help the future maintenance programmer. This is a worthy goal, but there were cases where not understanding what was going on led to incorrect results in the calculations—not something you want with safety-related code.
After the initial quieting down of the tool, various GCC warnings switched on, many beginning with the prefix - and addressing all the splint checks starting with -weak. The MISRA violations reduced and the most common reported (and fixed) for each release were as follows:
§6.2 Rule |
MISRA-C Rule |
---|---|
5.6 |
No identifier in one name space should have the same spelling as an identifier in another name space, with the exception of structure and union member names. |
8.7 |
Objects shall be defined at block scope if they are only accessed from within a single function. |
14.1 |
There shall be no unreachable code. |
14.3 |
Before preprocessing, a null statement shall only occur on a line by itself; it may be followed by a comment provided that the first character following the null statement is a white-space character. |
Do you remember how many namespaces there are in C? (The top hit on Google for ANSI C Namespaces is still “Why doesn't ANSI C have namespaces?”.) Yet, your complier will let you have overlapping names for many identifiers as follows:
Identifier |
Notes |
Label names |
e.g. goto targets |
Tag names |
struct, enum, union |
Structure / union members |
They each get their own namespace |
Everything else |
Constants, enum, typedef, variables, functions |
Remember, union and goto are not recommended by MISRA.
Rules 8.7 and 14.1 are prescient for maintenance activities where deleting code can result in objects that can have their scope narrowed since they are not shared anymore, and code that can no longer be reached. It’s very helpful to have the tool identify these easy-to-make mistakes that are also very easy to correct. (It’s also helpful to have a manager that understands how valuable deleting code can be.)
On this project Rule 14.3 cropped up for unnecessary semi-colons, e.g.
foo();;
Observations
In my experience, engineers developing safety-related software initially find that the MISRA rules are intimidating and restrictive. However, these engineers all agree the result is worth the effort and results in much better code that is, of course, much easier to maintain and test. I’ve never met any mature programmers with real-world experience that don’t recommend using MISRA and working towards zero violations—which doesn’t mean adding annotations in the code to keep the static analysis tool quiet.
MISRA static analysis tools easily catch some very common (and some not-so-common programming) errors that allow compilers to misinterpret what developers intended.
The biggest challenge to get code to comply with MISRA is mostly the time it takes developers to understand the underlying principles. It takes much longer for a software developer to understand the underlying weakness in their code than it does to correct it. Many people engineering software (and I was one of them) do not understand what is going on behind the scenes that made the construct used in the code insecure.
Best Practices
Start your static analysis as early as you can in the project.
I’ve used tools from PRQA, Synopsys, LDRA and Mathworks. They all have strengths and do a fast and accurate job of MISRA C checking as well as many other useful analyses. Your first task might be to put them to work on the MISRA-C exemplar suite to find out which rules they do and don’t detect.
Automate everything.
As much as possible, aim to test object code automatically as soon as, or as close to the point in time, that a developer completes a commit.
Use code measurements sensibly to guide your design reviews and discussions about sections of code.
Conclusion
I appreciate your comments and feedback. Please comment below or send me an email.
If you have code that continually fails a static analysis check, you might consider sending it to the MISRA experts using their bulletin board. Please get in touch with me directly if you need help with any of the issues discussed in this blog post.
Tagged as: Jonathan Moore ISO 26262 Automotive