A nice article found through Reddit, "Design by Contract (DbC) for Embedded 
Software" by Miro Samek, 2009:
http://www.netrino.com/Embedded-Systems/How-To/Design-by-Contract-for-Embedded-Software

It says nothing new, but it says such old things in a nice way, and I like how 
it compares assertions to fuses and how it contrasts DbC with a different kind 
of defensive programming that is kind of the opposite.

A quotation from the article:

>In contrast, every successful test run of code peppered with assertions builds 
>much more confidence in the software. I don't know exactly what the critical 
>density of assertions must be, but at some point the tests stop producing 
>undefined behavior, segmentation faults, or system hangs--all bugs manifest 
>themselves as assertion failures. This effect of DbC is truly amazing. The 
>integrity checks embodied in assertions prevent the code from "wandering 
>around" and even broken builds don't crash-and-burn but rather end up hitting 
>an assertion.<

I have seen this with the DMD compiler itself, around half of its bugs are 
found by its assertions.


Another quotation:

>You can no longer design a system without accounting for testing overhead 
>right from the start. Assuming that all the CPU cycles, the RAM, and all the 
>ROM will be devoted strictly to the job at hand simply won't get the job done.<


A third quotation, this seem different from D strategy (here the author is 
talking about normal PCs):

>As an example, consider dynamic memory allocation. In any type of system, 
>memory allocation with malloc() (or the C++ new operator) can fail. In a 
>general-purpose computer, a failed malloc() merely indicates that, at this 
>instant the operating system cannot supply the requested memory. This can 
>happen easily in a highly dynamic, general-purpose computing environment. When 
>it happens, you have options to recover from the situation. One option might 
>be for the application to free up some memory that it allocated and then retry 
>the allocation. Another choice could be to prompt the user that the problem 
>exists and encourage them to exit other applications so that the current 
>application can gather more memory. Yet another option is to save data to the 
>disk and exit. Whatever the choice, handling this situation requires some 
>drastic actions, which are clearly off the mainstream behavior of your 
>application. Nevertheless, you should design and implement such actions 
>because in a !
 desktop environment, a failed malloc() must be considered an exceptional 
condition.<


Eventually (thanks to Sean too) D will have readable stack traces on all OSes, 
but to understand better what the program was doing when the assertion has 
fired you have to run the code in a debugger or to add lot of print statements 
(inside contracts!).

As alternative, if the program keeps at runtime information about the types 
that are present in the all the stack frames (this can be done with LLVM, there 
is support for precise stack scanning, for certain kinds of GCs), then when the 
stack trace gets written it can also optionally print the state of all the 
variables in all the stack frames. This logged text can later help debug the 
program even if the program was running on the production machine (with stack 
tracing activated and precise stack types activated).


This is a paper that shows why DbC can not enough in some situations, by Ken 
Garlington, 1998:
http://home.flash.net/~kennieg/ariane.html

In the situation like the one of the Ariane I think the good solution is the 
introduce a fuzzy control system that has a degradation of its effectiveness as 
conditions come out of its specs, but avoids a total failure. This is what 
biological designs too do. It's a kind of 'defensive programming'.

Bye,
bearophile

Reply via email to