#48 Assertions and Design by Contract, Part-2
HTML-код
- Опубликовано: 19 мар 2023
- In this second lesson about assertions and Design by Contract (DBC), you'll see how to practically apply them in embedded systems.
Contents with Timestamps:
-------------------------
0:45 Agenda for the previous Part-1 and today's Part-2
1:00 Embedding assertions (the qassert.h header file)
1:45 qassert.h code review (using Doxygen documentation)
2:35 Q_ASSERT_ID() macro implementation
3:53 Q_ASSERT_ID() macro use
4:34 Q_DEFINE_THIS_MODULE() macro
4:52 no-return semantics of the Q_onAssert() handler
5:37 DBC elements: Preconditions, Postconditions, Invariants
6:57 Errors and the use of Q_ERROR_ID() macro
7:05 The Q_onAssert() assertion handler
8:12 Resetting the target in the assertion handler
8:37 Use of fault-injection for testing assertion failures
8:59 Assertions & Throwing Exceptions
9:48 Hardware assertions
10:28 Avoiding "denial of service"
11:06 Impact of Assertions
11:32 NASA JPL guidelines about assertion density
11:48 Microsoft Research study of assertions and code quality
12:25 Transformative impact of assertions on development
13:15 Demoralizing effects of "defensive programming"
13:32 Disabling assertions in the final product
14:45 Disabling assertions in the assertions-as-fuzes analogy
14:52 Disabling assertions in the assertions-as-guardrails analogy
15:08 Disabling assertions in the assertions-as-insurance analogy
End Notes:
----------
Companion web page to this video course
www.state-machine.com/video-c...
Project download for this lesson:
www.state-machine.com/course/...
GitHub repository for projects for this video course:
github.com/QuantumLeaps/moder...
Transcript of this lesson:
www.state-machine.com/course/...
References resources:
---------------------
Key Concepts: Design by Contract
www.state-machine.com/dbc
Bertrand Meyer, “Applying Design by Contract”, IEEE Computer, 1992
www.state-machine.com/doc/May...
Miro Samek, “An Exception or a Bug?”, C/C++ Users Journal, June 2003
www.state-machine.com/doc/Sam...
Quantum Leaps, Design By Contract (DBC) for Embedded C and C++, GitHub
github.com/QuantumLeaps/DBC-f...
Music credits:
--------------
The background music comes from:
www.bensound.com/royalty-free... Наука
Great video. But would you recommend leaving asserts ON even(in release builds) for safety critical systems? Wouldnt defensive programming be better in such systems as they dont crash the system(and possibly let the safety monitors take actions like limp home mode when error manifest down the line)
There is a lot to unpack in this question. But first, I recommend leaving assertions ON *especially* in safety-critical systems. However, that does NOT mean "crashing the system." On the contrary: leaving assertions ON provides the last line of defense against "dangerous modes of failure" (a term from IEC 61508) to put the system in Fail-Safe mode (whatever that means in the particular case). You lose this chance when you disable assertions. Second, there seems to be a misunderstanding of what "defensive programming" means. Behavior like "limp mode" requires very *deliberate* design as part of handling this exceptional condition. Such deliberate design is the exact opposite of "defensive programming," which just hides problems (like returning a bogus value instead of dividing by zero). It is somewhat naive to believe that any coherent behavior (like the "limp" mode) can somehow emerge from such "defensive programming." --MMS
Man, you can stick to one thing for 10+ years, this is also another valuable lesson for me personally. Waiting for the next!
Embedded programming is a *deep* subject, and you got to stick with it for longer to get good at it. It takes 10,000 hours to become good at anything. --MMS
Eh, really appreciate the videos, but they didn't come out over 10 years because of perseverance or something..
@@damiengates7581 Actually, the "Modern Embedded Programming" video course is a result of frustration. In the embedded programming community almost every term means completely different things to different people. For example, you say "state machine", and everyone means something else. You say "event-driven", and again, there is no common understanding. Same goes for "assertion-based programming", "concurrency hazards", "encapsulation", etc.
Most of my coworkers think that asserts and tracing/logging is not that important and a debugger is the only thing they need and they only use the debugger for step-by-step debugging. For a long time I was of the same mind, but now I think that longer term good assertions and tracing save a lot of time. Sometimes it's even not possible to reach the final, good design without them.
Yes, exactly. Assertions seem to be one of the most misunderstood, unappreciated, and underutilized techniques in programming (not just embedded). That's precisely why I made these videos. I hope that at the very least developers will become *aware* that such a technique even exists. --MMS
I feel like this is the one video as an intermediate I needed to finish. It defines a certain style and practice for writing secure, and scalable code safely. I also am glad you used C examples, mostly because I am here for game development and smaller SOCs like piboards. Everytime someone I work with asks about software with these devices in mind, will probably link this video series.
Small aside, but you can model your contracts based on hardware expectations as well such as for x86 and also ARM with different assertions and conditions, right? I also think this because the differences in assembly possibly needed for things like registers, buses, memory management etc etc. Just want to cover all my bases. Personally am only developing on x86 at the moment, but eventually will get an SBC/SOC board.
Finally, I am using Nim/Swift, so I am curious how well these modern languages will work in embedded or in my use case smaller computer boards; but I guess the only way to find out is to try, and see if it is a pass or fail.
Edit: Also, I am sure Piboards and ARM SOCs are not quite 'embedded', don't want to go away from this with that in mind. I considered embedded a range from smaller chips found in controller for example to something at most like a SBC board with a full motherboard of features.
Assertions (as part of Design by Contract philosophy) are applicable to any software, not just embedded and not just "small" systems. However, the effective use of assertions requires two critically important considerations. First, assertions must be used only for errors (bugs) and never for "exceptional conditions". Second, assertion failure handler must be carefully designed and *tested* to bring the system to a "safe state" (whatever that means for a given system).
Also, assertions *are* the recommended practice, and indeed a cornerstone, of safety-critical systems (e.g., IEC 61508 standard recommends "Failure Assertion Programming" practice). Safe software must either operate correctly, or fail *safely*. The worst possible (and unacceptable) scenario is when the software keeps operating unsafely. Unfortunately, this is precisely what happens when assertions are turned off in the production release.
Finally, regarding Nim/Swift, there is a discussion of the QP frameworks on the Nim forum: forum.nim-lang.org/t/5137 .
--MMS
Disabling asserts in release builds is like learning to drive and using seat belt and then stop using it once you get driving license :)
This reminds me of the quote from Tony Hoare (one of the founding fathers of computer science), who used to say: "Disabling assertions in production is like using a lifebelt during exercise but not bothering with it for the real thing".
Sometimes in initial phase of development it's difficult to
to judge if a condition is exceptional, or should be treated
as an error. I frequently see junior developer code
with return codes totally ignored etc. I would argue that
in those cases, even if the conditions finally turn out to be just exceptions,
putting in assertions is also beneficial.
Sure, do whatever helps you during development. But in the *production* version you release to customers, assertions become as important as the rest of the code. (This assumes that you leave assertions enabled in the production release.) In my programming practice, I actually spend more time thinking about assertions and *testing* that they actually catch errors than the regular code. In my mind, treating assertions as second-class, throw-away code misses a huge opportunity. I hope that these comments make sense to you. --MMD
@@StateMachineCOM yes, this makes a lot of sense. Thank you.
Amazing Video as always, maybe they choose to disable assertions to prevent "unnecessary” bloated code. but I totally agree with you and if they think that these simple assert conditions bloat the code, they should have considered them in the original system design in the first place.
Yes, I agree that many developers believe that assertions add a lot of overhead to the code. But in practice, the overhead is quite small and often pays for itself because (as mentioned in the video) assertions *eliminate* a lot of "defensive code". --MMS
In my experience, the asserts are of two types. Debug asserts and prod_asserts. As name suggests the debug asserts are used during development till it system gets maturity and are disabled from final production. The disabling of the debug assert will lead to change in the code execution time and some defects may pop out. That is the reason, the debug asserts are disabled few months earlier to release so that, it gets tested enough before release. My two cents. -:)
As I said in the other comments, do whatever helps you in debugging. But from my experience, introducing "debug assertions" alongside "production assertions" is a slippery slope. (The next level is adding a "severity level" in assertions and then disabling assertions only up to a given level.) The problem with this approach is that assertions stop being taken seriously but rather as some temporary and throw-away code. With this attitude, people lose confidence in assertions and don't trust them to really perform drastic actions. Also, developers are confused as to which assertions are really "worthy" leaving in the production code and which to turn off. I hope you see my point. --MMS
Again Great video, one point, I am not sure to follow: the _ID suffixe, OK lane can change but not once compiled. Is there any added value to use _ID for others tools in the futur ? I have trouble seeing in a real case the benefit of adding _ID, am I missing something ? You are the best !
Good point that needs to be explained. So, if the assertions can bring down the whole system, they are apparently important. Such code needs to be TESTED. To do so, you need to make the assertion fail (e.g., by "failure injection") and then you must verify that the *right* assertion has failed. Now, how do you identify the *right* assertion in your test? If you identify it by the line number, you'd have to change the test every time the lines shift in your code. Such tests would be then very unstable. The use of the assertion-IDs helps in writing *stable* tests because these IDs don't shift. I hope this makes more sense now. --MMS
Thank you for all the videos ! Would it be possible to make one to compare some of the different RTOSes available on the market and explain the difference with Linux Embedded RTOS (ex VxWorks) ? Thank you !!!
A comparison like this would be tricky and necessarily biased. But such comparisons have been already done. Here, I would recommend a blog post "Are all RTOSes the Same?" by Michael Barr: embeddedgurus.com/barr-code/2008/01/are-all-rtoses-the-same . Having said that, I do plan some future lessons in this course to cover various types of schedulers, but these would not be the traditional RTOSes or embedded Linux offerings you're requesting. (Traditional RTOSes have been discussed in lessons 22-28, see ruclips.net/p/PLPW8O6W-1chyrd_Msnn4LD6LBs2slJITs ). --MMS
I absolutely agree for not disabling the assert in the final firmware. But in my experience, I implemented two levels of asserts. 1. assert_debug 2. assert_release. The debug asserts are disabled(tapered slowly) few months before release. The release asserts are critical ones and acts like guard rails and debug asserts are like training wheels.
Yes, I've seen the distinct "debug-assertions" and "release-assertions". I've also seen a "severity-level" parameter added to assertions and then an option to disable assertions only up to a certain level. All of this is a bit slippery slope leading to low-quality assertions that people don't take seriously. Could you give me an example of a "debug-assertion" that would NOT be good enough as a "release-assertion"? In my opinion, assertions are an inherent part of the code (the design contracts). They should be developed and *tested* along with the code they protect. Then they should be released, exactly as they've been tested. --MMS
@@StateMachineCOM - Here are some examples of debug asserts. 1. a routine checking the 32byte alignment of address that is returned by another routine.
2. checking the command/packet size. 3. Checking the execution time of a routine. The data point is, during the product qualification.
The main reason for removing debug assert from release code is - the asserts add cpu overhead and data shows that system never hit these asserts during hours of qualification and some of them are purely debug purpose.
Great video!
what do you plan to cover next?
In the next segment of lessons, I plan to go back to the fundamentals and real-time architectures. Specifically, I'd like to explain the various real-time kernels that can be used to run event-driven systems, because the traditional RTOS is not the only option here. It turns out that the non-blocking, run-to-completion characteristics of event-driven systems allow us to use *simpler* and more efficient real-time kernels than the traditional RTOS. Stay tuned! --MMS
@@StateMachineCOM Many thanks for your reply.
That sounds interesting.
If I may, I would recommend to also revisit the Event driven design pattern and QP framework
with more relatable projects rather than the Time bomb we had.
Although I am keen on adapting that pattern at my team, I really still struggle to start, even with projects I already made
I have the flat State Machines for them, the modules, everything.
but I don't know how to start
creating the Active Objs
what is difference between signals and events
and so
Huge part is because I didn't read the Docs yet.
but I am sure this part needs revisit on the channel.
and if you can spare advise here on comments
it would be appreciated
How can it be so difficult?
@@damiengates7581 did you try it?
If you did successfully
I would like to hear your experience
Hi Miro - Is it possible to have a video that describe different terms firmware, embedded software, and real time system? I know it is very very basic question, but there is confusion among the engineers.
The concept of "real-time" has been explained in lesson#26 "What is real-time?" ruclips.net/video/kLxxXNCrY60/видео.html . But regarding the distinction between "firmware" and "embedded software", I don't quite see the difference, so I can't explain this aspect. --MMS
what is your opinion in using exceptions on embedded systems?
Good question. First of all, I see the mechanism of throwing and catching exceptions (if this is what you mean) as part of handling *exceptional conditions* with the intent to continue execution. As I explained in the video at 8:50, in my view throwing exceptions should *NOT* be used to "handle" errors/bugs because it's overkill and additional risk for that. Now, throwing exceptions in exceptional conditions has obviously value when you have deep function call trees, so unwinding the call stack gives you a chance to reasonably "handle" an exception. This has some merit, perhaps, in sequentially written code (e.g., with traditional RTOS). But in *event-driven* code, you don't have much stack to deal with because the stack is constantly discarded after processing each event. In other words, there is no much *context* on the stack and instead the context is in the *state* of the state machine. For that, throwing exceptions is not that helpful. instead, an exceptional condition should be handled as a separate *state* of the state machine. Also, please google for the article "Exception Handling: A False Sense of Security" by Tom Cargill. --MMS
@@StateMachineCOM thank you. i will read that referenced article
@@StateMachineCOM yeah, and the bubbling up of unhandled events in hierarchical SM is somewhat similar to throwing exceptions.
Download Link doesnt work.
Thanks for reporting. I corrected the download link in the video description. The links on the companion page ( www.state-machine.com/video-course ) seem to be correct. --MMS
This series started 10 years ago. Is IAR Embedded Workbench for ARM still available? On their website it is not available anymore which is understandable after so many years.
I plan to add the ARM/KEIL uVision projects to all downloads, including the older lessons released originally with IAR. This will allow anybody interested to use ARM/KEIL toolset instead of IAR. This work will be completed soon. The downloads will be available in the usual locations:
- www.state-machine.com/video-course
- github.com/QuantumLeaps/modern-embedded-programming-course