Download The Joint Strike Fighter Coding Standard - Bill Emshoff - CppCon 2014.pptx...
JSF++ Using C++ on Mission and Safety Critical Platforms
B il illl E ms ho hoff ff S eni enior or S ta taff ff E mbedded S /W E ng i neer L ock ockhee heed d Martin Martin A erona eronaut utic icss
[email protected]
Overview
•
•
Background and Philosophy behind the JSF++ standard Coding Issues Distinct to JSF++ (vs. those for General-Purpose Software)
•
Automated JSF++ compliance testing
•
Lessons Learned
Overview
•
•
Background and Philosophy behind the JSF++ standard Coding Issues Distinct to JSF++ (vs. those for General-Purpose Software)
•
Automated JSF++ compliance testing
•
Lessons Learned
History (1/2)
•
In the late 1990’s, Lockheed Martin selected C++ as the programming language to be used for Joint Strike Fighter embedded software softw are development –
–
–
A da tool c hains hai ns were wer e i n decline decli ne C ++ tools ools we werr e impr impr oving ovi ng C ++ was was attr ac ti ve to to pr pr os pec pec ti ve eng eng i neer neer s
History (2/2)
•
In the Fall of 2003, Bjarne Stroustrup provided assistance to JSF engineers in the development of coding guidelines – “JSF++” –
Many s ources of C and C++ prog ramming g uidelines were available, but no s ing le s et of g uidelines were found suitable for the cons traints of our environment
Pre-Existing Specifications •
MISRA C 1998 –
–
–
Us ed as the bas is for J S F++, but: Limited to C lang uag e cons tructs C++ obviates the need for many C -s pecific rules –
Stronger type-checking
–
Templates can replace macros
–
–
•
Container classes are safer alternatives to C arrays Smart Pointers are safer alternatives to C pointers
Embedded C++ –
Overly limited subs et of C++ omitting features deemed ess ential to larg e projects •
No namespaces, templates, or multiple inheritance
JSF S/W Environmental Constraints •
Hard Real Time –
•
Limited Memory –
•
Failure can cause loss of life or aircraft
Portability –
•
Memory is fixed to that physically available - no memory paging
Safety and/or Mission Critical Applications –
•
Failure to meet schedule can be a fatal error
Code must run on multiple operating systems to support development and simulation
Maintainability –
Code will likely need to be maintained for decades
General JSF++ Goals C makes it eas y to shoot yours elf in the foot; C ++ makes it harder, but when you do it blows your whole leg off. ― Bjarne Stroustrup •
•
Define a common style for all developers that is easy to comprehend and maintain Avoid undefined, unspecified, and implementation-defined behavior –
•
Provide safe alternatives to unsafe or error-prone features –
•
Code s hould have predictable and portable s emantics and performance Often in the form of library components
Automatic verification of guidelines is an important goal –
Promote s tatic over dynamic error detection whenever pos s ible
Coding Standard topics relevant to JSF++
•
Resource Management in JSF++
•
Arrays
•
Multiple Inheritance
•
Fault Handling without exceptions
•
Life without RTTI (Run-time type information)
•
Templates
•
Summary Comparison of JSF++ and MISRA C++ 2008
Dynamic Memory Allocation
•
Allocation from the free store (global new/delete) is prohibited except during initialization (JSF Rule 206) –
•
Fragmentation of the heap due to repeated allocation/deallocation leads to non-determinis tic performance
The JSF container library provides STL-style, class-specific allocators as alternatives to eliminate the possibility of fragmentation –
–
–
C++ STL vector’s interface allows class-specific allocators as an option; for JSF’s container library, they are essentially required By defining fixed-size memory pools at initialization time, constant-time new/delete can be guaranteed No need to pay overhead costs for thread-safe allocators for objects only accessed in a single thread
Problems with Pointers
•
•
Replacing global new/delete with custom allocators resolves some performance issues but doesn’t guarantee safe operation C pointers and raw resource handles can lead to a host of potential problems: T* p = new T; if (p->fail1) return 0;
// leak!
if (p->fail2) delete p;
// p is now a dangling pointer!
return p;
// does caller know he now “owns” p?
R eturn values s hould not obs cure res ource owners hip
RAII to the Rescue
•
The Resource Acquisition Is Initialization (RAII) idiom solves many pointer problems: shared_ptr p ( new T); if (p->fail1) return 0; // No leak! if (p->fail2) delete p; // Does not even compile! return p;
// caller gets ownership, // but has no need to explicitly release p
Other RAII Benefits •
•
•
With self-managed (RAII) class attributes, the implementer is relieved of a lot of work in class implementation The compiler-generated copy constructor, assignment operator, and destructor should just “do the right thing”, so there is no need for the developer to write one (JSF rule 80) RAII is not limited to pointers – mutexes are likewise wrapped in RAII classes (e.g. C++11 std::lock_guard)
Arrays •
The use of arrays in interfaces is prohibited in JSF++ –
•
The problems of pointer decay have a long , notorious history
foo( i nt*p ) // a function taking a pointer to an integer // really no different… foo( i nt[2] ) JSF’s container library provides Array-like container classes that avoid these problems
F oo( A r ray& ) –
•
// much s afer
Containers know their own size and don’t participate in
implicit pointer convers ions For the s pecial cas e of dealing with external / leg acy interfaces implemented with arrays , template wrapper functions can help: template void foo(T (&p)[N]) …
Multiple Inheritance (MI) •
MI is allowed in a restricted form to allow for clean, maintainable designs (JSF rule 88): –
Unlimited public interface inheritance
–
Unlimited private implementation inheritance
–
Plus at most 1 protected, inherited implementation
–
•
MI is specifically allowed for “policy-based design” [1], with each base class representing independent facets of the derived class
For the purposes of this JSF++ rule, an interface may contain : –
non-virtual, protected methods
–
small data items if they function as part of the interface
[1] Modern C++ Des ig n , by Andrei Alexandrescu
Fault Handling without exceptions (1/3) •
Exceptions are prohibited in JSF++ –
–
•
A t the time J S F++ was defined, various C++ compiler implementations res ulted in widely varying overheads B etter tool s upport was deemed neces s ary prior to allowing C ++ exceptions in critical code •
Will all exceptions be caught?
•
Is all affected code exception-safe?
•
Are all possible control-paths covered?
Disabling C++ exceptions doesn't eliminate the need for fault handling though…
Fault Handling without exceptions (2/3) •
In general, functions should return error information, and calling functions must test it (JSF rule 115) –
Problem for automated verification: how is “error information” defined and returned? S implis tic tool ass umption: if a function returns a value, it must be used or cast to void
Fault Handling without exceptions (3/3) •
What about constructors, overloaded operators, or other functions that don't return a status value? –
–
Consider moving potentially-failing constructor code to separate initialization / factory methods Use a class attribute as a status indication, e.g., Matrix m = a + b; // what to do if a + b fails ? if (m.is_bad()) { /* handle error case */ } Compilers and lint-tools handily complain about unused return values, but relying on users to check error codes set as side-effects is inherently error-prone (a reason why JSF++ disparages use of errno).
–
Some errors are patently unrecoverable For never-should-happen errors (failed assertions, watchdog timeouts, hardware failures) – log the fault data and try a reboot
Life without RTTI (Run-time type information) •
For somewhat similar reasons that JSF++ banned C++ exceptions, RTTI is also banned (JSF rule 178) Worst-case overhead not easily predictable Often, a perceived need for RTTI is an indication of design problems better solved with abstract methods In lieu of dynamic_cast, the Visitor Pattern can be used – this requires no typecasts at all; users can derive from a class such as this: –
•
•
class DefaultVisitor : Visitor { virtual defaultImpl(Base&) {} virtual visit(Derived1& d) { defaultImpl(d); } virtual visit(Derived2& d) { defaultImpl(d); } ... };
Templates
•
JSF++ allows templates as a means to achieve type-safe, high-performance code; however, C++ template usage introduces additional challenges for development, analysis, and testing –
–
Template code cannot be completely analyzed until instantiated Each unique template instantiation requires its own test and review if in critical code
Summary Comparison of JSF++ and MISRA C++ 2008 •
•
•
•
MISRA C++ builds on JSF++ as JSF++ built on MISRA C With an additional ~five years of compiler and developer community maturity, MISRA 2008 allows, with restrictions, –
C++ exceptions
–
dynamic_cast (RTTI)
In keeping with original MISRA C rules, MISRA 2008 disallows all use of the free store (heap), even for initialization JSF++ forbids use of NULL macro due to C++ type checking; MISRA requires it (as being more expressive than “0”) –
•
Should consider C++11’s nullptr for future coding standards
JSF++ includes style guidelines; MISRA C++ recognizes style is subjective, and given its more general target audience, only suggests that an in-house style guide should exist
Automated JSF++ compliance testing •
Commercial static analysis tools providing automated compliance checking are available from multiple vendors –
–
Rules that are checked automatically are rules that developers don’t need to waste review time on, and can instead concentrate on the tough problems Automatic rule checkers catch issues commonly missed by human eyes, sometimes even testing, e.g., •
•
•
use of “=” vs. “==” inadvertent semi-colon following a conditional expression ensuring all attributes of a class are initialized in a constructor
Automated compliance challenges –
–
Static analysis is essential (and relatively cheap) but no substitute for code review Issues of style or that require human interpretation are especially problematic for automated checks, e.g., •
JSF rule 45 - words in an ID will be separated by ‘_’. –
•
How should a tool determine what is a “word”
JSF rule 88 – multiple public interface inheritance R ule text includes intent in the definition of interface. –
•
JSF rule 113 - multiple return statements are banned
unless s uch a s tructure would obscure or otherwis e s ig nificantly complicate… –
How s hould a tool implement this rule?
Automated JSF++ compliance testing –
Automated rules produce tradeoffs •
•
Tools erring towards catching more true positives at the expense of more false positives result in increased developer time to review and filter results and possibly “fix” or annotate code to meet tool expectations Tools erring towards generating fewer false positives tend to do so at the expense of missing some true positives Often, code that triggers a “false” positive is code that
may be too clever and could stand a bit of critical review anyway
Lessons Learned (1/3) •
A consistent, easy-to-read coding style for naming, braces, and indention is more important than the particular style itself –
•
•
•
Developers modifying legacy code modules should be encouraged to follow the existing style in those modules rather than resort to either leaving a module in JSF++ / K&R mixed style or refactoring existing working code
Even in the absence of exceptions, the “resource acquisition is initialization” (RAII) idiom generally, and smart pointers specifically, should be explicitly required for resource handling Rules with subtle or exceptional cases can be difficult to analyze mechanically. JSF++ includes guidelines on good template design, but failed to specifically identify portability issues related to templates, notably –
–
Some compilers didn't require typename at all; some allowed it in contexts prohibited by others (and by the C++ standard) Dependent base class name lookup implementations varied widely
Lessons Learned (2/3) •
A rationale for each rule educates developers and gets their buy-in –
•
Interpretation of rules would be enhanced by more pass/fail examples –
•
Compliance comes easier when developers understand rationales
not just novices, but even experienced implementers of rulechecker software have sometimes missed the full intent of rules
Rules should be written with automated compliance in mind, leaving little to ambiguity Consider rule 142, “All variables shall be initialized before use”. This could be enforced for local variables by requiring initialization at declaration, but the standard is largely silent on how this rule is to be verified. By comparison, the related MISRA rule 8-5-1 specifically does not require initialization at declaration, but does require class constructors to initialize all non-static members.
Lessons Learned (3/3) •
Don’t over-specify –
•
rather than prescribe use of a particular “Array” class in lieu of an array, simply specify that library container classes be used
Provide more clear guidance on how to deal with non-compliant automatically-generated code, such as from UML tools –
Where possible, common tool configuration settings should be supplied to teams to achieve consistent code generation, as consistent with the coding standard as practical
The Future
•
With a more mature C++ developer community, and better tool support, C++ exception handling should be considered for future safety-critical applications –
–
•
A llows error handling log ic to be cons olidated at a hig her level than deeply-nested functions E liminates need for code to explicitly check and pas s error s tatus for truly exceptional events
The coding standards will need to be revised for future applications using C++11 (C++14?), which in many ways “feels like a new language” to quote Bjarne Stroustrup
Mission Success –
After 15 years and more than 8 million lines of software code [1], JSF++ has been a decided success for the JSF program •
•
•
•
•
C ++ is now in widespread us e throug hout indus try with excellent tool chains broadly available Lig ht-weig ht, express ive abstraction mechanis ms provide J S F eng ineers with crucial s upport for manag ing complexity without incurring performance penalties When aug mented with reas onable g uidelines and s uitable libraries , C++ has proven its elf for s afetycritical application 100+ F-35’s in the field today
[1] https://www.f35.com/about/life-cycle/software
Questions?