GeistHaus
log in · sign up

https://feeds.feedburner.com/BetterEmbeddedSystemSw

atom
25 posts
Polling state
Status active
Last polled May 19, 2026 04:17 UTC
Next poll May 20, 2026 00:42 UTC
Poll interval 86400s
Last-Modified Mon, 18 May 2026 20:19:18 GMT

Posts

Static Analysis Ranked Defect List
compilersstatic analysis
Show full content

 Crazy idea of the day: Static Analysis Ranked Defect List.


Here is a software analysis tool feature request/product idea: So many times we see the problem that a static analysis tool or other way of automatically finding bugs inundates developers with so many possible bugs they turn it off in frustration. Or maybe they have a requirement to ship only "clean" code so they don't run the tool because then there is nothing to clean up. Put your favorite head-in-the-sand organizational dysfunction here.


I'd love to be able to recommend a static analysis or other tool that has a feature that reports that top 10 defects currently in the code base, ranked by likely risk. Go ahead and use machine learning for the ranking; fine with me. Regardless of methodology, fixing only some is a guess after all.


Ultimately, all the static analysis warnings should be cleaned up. Having a "top K warnings" feature would allow a team to make progress over time instead of simply sweeping the entire mess under the carpet and ignoring what is often very valuable information that predicts defect escapes to deployment.


For tools with many warnings an approximation can be to gradually turn on a series of warning flags over time and/or just run the warnings on a subset of modules. But a global "these are the 10 scariest warnings" would be a nice feature.


Thoughts? Does some tool already do this?

tag:blogger.com,1999:blog-4172950626830217643.post-4239847805405700039
Extensions
Job and Career Advice
career
Show full content

I sometimes get requests from LinkedIn contacts about help deciding between job offers. I can't provide personalize advice, but here are my thoughts in general.




You must accept personal ownership for choosing what you want to do with at least the next few years of your life. Nobody can do this for you. Some luck is always involved, but fortune favors the prepared. It is up to you to set your own course.

Years of your working career are a scarce, non-renewable resource, so spend care in deciding. On the other hand, it is difficult to make a choice because, as they say, it is difficult to make predictions -- especially if they're about the future. It's even harder to foresee consequences, especially on your first couple jobs when you are learning how everything works. And there are always surprises, even for the most experienced of us.  But if you end up making a choice that works out poorly, then figure out the lesson to learn and switch to something else.

Take a look at how the job fulfills or supports your needs on Maslow's Hierarchy (https://en.wikipedia.org/wiki/Maslow%27s_hierarchy_of_needs).  Some of us live to work; some work to live.  There is no one right answer, but at least this gives you a simple checklist of what you want to be provided by the job -- and what you don't.  If you're signing up for a 12 hour x 7 day type job, it had better go a long way to filling up that pyramid in an acceptable, non-destructive way while you're at work, because you'll always be at work. (And that might be OK for some people in some phases of their career. But not for others.)
https://en.wikipedia.org/wiki/Maslow%27s_hierarchy_of_needsMaslow's Hierarchy of Needs [https://en.wikipedia.org/wiki/Maslow%27s_hierarchy_of_needs]
All organizations have dysfunction. Figure out if this organization's dysfunctions are going to be irritating to you or get in the way of satisfying your hierarchy of needs.  Most people can stand an irritating environment less well than they think they can over long stretches of time.

When you're young, there is a lot to be said for working in a structured, mature organization. You can learn a lot by watching how experienced folks work rather than by making rookie mistakes (if you are paying attention). Later on you might want less structure.  Skipping right to an unstructured job at an immature company will teach you a lot of bad habits that it can take a lifetime to unlearn, and leave many holes in your practical education. (Some jump right to a startup company with no "graybeards." Some skip college. Some get rich by winning the lottery. Some don't. I can only tell you how to stack the odds in your favor.) Consider the availability of mentors in your new position. Even the smallest, newest of startups can work for this if the mentorship is there.

Ask if the level of responsibility & authority is a fit both in terms of scope and structure.  My experience has ranged from military officer (highly structured) to consultant (freedom but few safety nets unless you've already built up a big cushion toward retirement).  Where you want to be will likely change as your career progresses.

Read the general job hunting advice books/web sites for things such as the realities of accepting a low paying first job and trying to get raises later.  The classic book is "what color is your parachute," but no doubt there are others, keeping in mind that highly skilled workers are a bit different than the general work force.  It helps to have a realistic understanding of what you are worth, and to get some objective advice from someone you trust on whether you're getting taken advantage of in a job offer.
2023 update: the flood of cheap venture money is drying up along with rising interest rates. It is easier to justify sky-high salaries when investor emphasis is on subsidized growth rather than profit margin.  Don't be surprised if there is downward pressure on salaries. Many will find themselves losing to inflation even if they maintain their salary. Junior folks and those with demands easier to fill with new grads will probably be hurt the most here. It's hard to predict how this will play out, but expect a fundamental change from the days of near-0% interest rates driving leveraged speculative investments.

After you've considered the above, IMHO only then should you worry about the more common philosophical areas you see mentioned on this topic.  (And really, most of them end up on the upper levels of Maslow's Hierarchy.)  My personal preferences are:
  • Surround yourself with the smartest, most capable people you can. But stop short of jerks. Note that any company with a "no jerks" policy is making a relative statement to their existing staff, rather than an absolute measurement. Pay attention during the interview to this.
  • Work for good leaders that support and empower those who work with them.
  • Take advantage of any opportunity you can get to improve your communication skills and soft skills.
  • If you're taking a job purely for the money, go into that situation with an exit plan and target exit date.  Make sure that is really how you want to spend a part of your life -- but in some circumstances this can be the right move.
  • If you're stressed out, it's time to find a new job.  (Or re-invent your job from within.)
  • If you're stressed out about your career, it's time to reinvent yourself and find a new career.
  • I personally think the whole LeetCode interview process is nothing more than ritualized hazing.  More than a two-round interview process is another caution sign unless you are signing up for an extremely senior position. Anyone with a strong GRE score (or top-tier SAT score) has already sacrificed at the altar of preparing for a required hoop sufficiently, and that probably taught them more generalized skills. But apparently others disagree on this point.
  • If you strive to be the absolute best at what you do, opportunities will find you. (Being visible helps: speak at conferences, write something that people might see somewhere.)
  • If most days you wake up and are eager to get to work, reflect on how fortunate you are to have that.
Don't forget the part that you must own your choice. Your preferences will probably differ.
Please do not contact me with questions about your particular individual situation. The hours in my day are already too few to accomplish what I'd like for my personal goals.  I took some time to write this to help as many people as I can (one of my goals), but I lack the time to provide individual responses.  So if I don't respond to a personal query, please understand (and better yet, send the personal query to a trusted friend instead).

I hope this is helpful, and wish you the best of luck in your job choices and your career!
tag:blogger.com,1999:blog-4172950626830217643.post-7364888485410821471
Extensions
What compiler warnings should you enable?
compilersstatic analysis
Show full content

 Here is a brief piece I wrote that Jack Ganssle just ran in The Embedded Muse 460.

The context was a previous discussion about enabling compiler warnings.

List of compiler warnings

John Carter's suggestion to use compiler warnings as a first step toward coding standards is an important one.  I tend to split these up into "coding style for people" and "coding styles for the compiler." Often we talk about indenting curly braces and tabs vs. spaces (for people) -- but making sure the compiler isn't confused about what you mean is also important. For those who've never contemplated the difference you might find video lectures #4 and #5 here of interest: http://course.ece.cmu.edu/~ece642/

However, there is a common misconception that someone might take away from a recommendation of a gcc warning default of "-W -Wall -Werror" which is that "-Wall" is not actually "all." Apparently the warning list for "-Wall" got frozen at some point, and there are a whole bunch more useful baseline warnings included if you add "-Wextra"

So for starting I'd recommend:
-Wall -Wextra -Werror

When I teach better software engineering skills (including clean code) at Carnegie Mellon University, I require all projects to use these warnings to produce some interesting learning experiences:

-Werror -Wextra -Wall -Wfloat-equal -Wconversion -Wparentheses -pedantic -Wunused-parameter -Wunused-variable -Wreturn-type -Wunused-function -Wredundant-decls -Wreturn-type -Wunused-value -Wswitch-default -Wuninitialized -Winit-self

No doubt there will be those who find some warnings controversial, but any warning that helps find a bug provides value, and I'd rather spend a bit more time structuring my code to enable passing automated compiler analysis than chase down bugs in production.

UPDATE 1/23/2023: thanks to a social media commenter for pointing out that some of the listed warnings are redundant.  My revised recommendation that implements the same compiler behavior is:

-Werror -Wextra -Wall -Wfloat-equal -Wconversion -Wredundant-decls  -Wswitch-default  -pedantic

Can be omitted because already in -Wall:

-Wparentheses -Wunused-variable -Wreturn-type -Wunused-function -Wreturn-type  -Wunused-value  -Winit-self -Wuninitialized

Can be omitted because already in -Wextra:
-Wuninitialized -Wunused-parameter 

You should also consider enforcing C standard support to avoid gcc-specific features that will be less portable. You can do this by for example adding the flag "-std=c11" or  "-std=c++11" as appropriate.
If you have a compiler other than GCC, then you will need to consider those proprietary warning flags to achieve a similar outcome.




For those paying really close attention, you'll note -Wreturn-type appears twice. That does no harm but it is a nice bit of fun to have it appear after -Wredundant-decls  :)
tag:blogger.com,1999:blog-4172950626830217643.post-792700559324985051
Extensions
Better Embedded System Software e-Book & Paperback
book
Show full content

There are only a handful of hardcover books left of the first edition, so I spend some time converting things over to an eBook & Paperback edition.

Amazon Kindle: https://amazon.com/gp/product/B08TZ9LYXC

Smashwords (epub): https://www.smashwords.com/books/view/1264918

Barnes & Noble (ebook): https://www.barnesandnoble.com/s/philip%20koopman

This is not a 2nd edition, but more like version 1.1.  The changes are:

  • Some minor rewording and cleanup.
  • A few small sections rewritten to reflect lessons I've learned about how to better explain things from teaching courses.  However, scope remains the same and the hardcover book is still serviceable if you already have that.
  • A new summary list of high-level takeaways in the conclusions chapter.
  • Publication support for everywhere KDP reaches, with local distribution in all supported markets.
The paperback is probably what you'd expect given the above, and is Print-On-Demand with production handled directly by Amazon.  There is no index due to publication platform issues. However, the table of contents is pretty well structured and in most cases that will get you where you need to go.  The price is significantly lower than the hardcover, and non-US readers can get it printed and shipped from someplace much closer to home since KDP has local POD for markets in Europe and Asia.
Amazon indicates they will print-on-demand for the following markets: US, DE, ES, FR, IT, UK, JP and CA.  Some readers report availability in other markets as well (for example, AU). So try your usual Amazon marketplace first, then one of the others close to you to minimize shipping cost.
The eBook is reflowable text for the body text (authored in EPUB format, but Amazon changes formats I believe).  Bitmaps are used for figures and equations, so it should look fine on most viewing devices without symbol font issues.  The price is significantly lower than the paperback since production and distribution costs are much lower.
Amazon has worldwide distribution rights for the eBook, but availability varies based on which country your device is set up for.  If your kindle is set up as being in the US market then you should have no troubles purchasing.  Many other markets (especially in Europe) should be fine as well.  Amazon promises e-boook availability in these specific markets: US (.com), IN, UK, DE, FR, ES, IT, NL, JP, BR, CA, MZ, AU.
The book is published via KDP, but Digital Rights Management (DRM) is OFF.  That should help folks with non-Amazon viewers (but I'm not able to provide support for how to side-load onto whatever platform).  If you have Kindle or a machine that runs the Kindle App then it should be seamless as with any other Kindle book.
I really appreciate the support of the thousands of readers of the hardcover edition over the past years. I hope that this makes the material more broadly available!
Link to free preview (from the hardcover edition)

tag:blogger.com,1999:blog-4172950626830217643.post-5066178970973617279
Extensions
The Y2038 Problem. Sooner than you think.
time keeping
Show full content

In the coming years, there will be other time rollovers beyond Y2K. The next big one isn't all that far away.

Contrary to what you might have heard, the reason more computers didn't break on Jan 1st 2000 wasn't because it was a false alarm. It was because massive resources were poured into avoiding many of the problems.  And many things did in fact break, but backup plans were in place.  (I recall not getting financial reports for most of 2000 for my spending accounts at work.  So I had to keep my own books and hope I didn't overspend -- because the old accounting system expired at the end of 1999 and the new one wasn't on-line until Fall 2000.)


In January 2021 we saw some aftershocks when a 2-year time digit window hack ran out of steam from Y2K patches.  But the world didn't come to an end.

The next potentially huge time problem will be January 2038 when the 32-bit signed Unix time in seconds rolls over.  

Plenty of embedded systems last 20+ years (already we are closer than that to 2038).  Plenty of embedded systems are using 32-bit Unix, since 64-bit CPUs just cost too much for the proverbial toaster oven.  An increasing number of systems are updatable, but many require manual intervention.   Updating your DVD player (if we still have them in 2038) won't be so bad.  Updating a natural gas pipeline valve in the middle of nowhere -- not as fun.   Updating all your smart light bulbs will range from tedious to buying all new lightbulbs. And so on.

This is a good time for embedded system designers to decide what their game plan is for Y2038.  As your expected product life starts overlapping with that (as I write this, it's only 17 years away), you're accumulating technical debt that will come due in a big chunk that year.  Better to have a plan now than a panic later.  Later has a way of sneaking up on you when you're not looking.

For a more detailed list of timer rollover issues, see:

http://www.lieberbiber.de/2017/03/14/a-look-at-the-year-20362038-problems-and-time-proofness-in-various-systems/


tag:blogger.com,1999:blog-4172950626830217643.post-2404801494667556268
Extensions
62 Software Experience Lessons by Karl Weigers
process improvementproject managementqualityrequirements
Show full content

Karl Weigers has an essay about lessons he's learned from a long career in software development. You should benefit from his experience. The essay covers requirements, project management, quality, process improvement, and other insights.

https://medium.com/swlh/62-lessons-from-50-years-of-software-experience-2db0f400f706

A good example from the article is:

"You don’t have time to make every mistake that every software practitioner before you has already made. Read and respect the literature. Learn from your colleagues. Share your knowledge freely with others." 



tag:blogger.com,1999:blog-4172950626830217643.post-6995633459996256486
Extensions
LINT does not do peer reviews
coding stylepeer reviewstools
Show full content
https://pixabay.com/vectors/code-programming-head-computer-2858768/
Once in a while I run into developers who think that peer review can be completely automated by using a good static analysis (generically "lint" or compiler warnings).  In other words, run PC-LINT (or whatever), and when you have no warnings peer review is done.

Nope.

But the reality has some nuance, so here's how I see it.

There are two critical aspects to style:
  (1) coding style for compilers  (will the compiler generate the code you're expecting)
  (2) coding style for humans   (can a human read the code)

A good static analysis tool is good at #1.  Should you run a static analysis tool?  Absolutely.  Pick a good tool.  Or at least do better than -Wall for Gcc (hint, "all" doesn't mean what you think it means (*see note below)).  When your code compiles clean with all relevant warnings turned on, only then is it time for a human peer review.

For #2, capabilities vary widely, and no automated tool can evaluate many aspects of good human-centric coding style.  (Can they use heuristics to help with #1?  Sure.  Can they replace a human?  Not anytime soon.)

My peer review checklist template has a number of items that fall into the #1 bin. The reason is that it is common for embedded software teams to not use static analysis at all, or to use inadequate settings. So the basics are there.  As they become more sophisticated at static analysis, they should delete the automated checks (subsuming them into item #0 -- has static analysis been done?).  Then they should add additional items they've found from experience are relevant to them to re-fill the list to a couple dozen total items.

Summarizing: static analysis tools don't automate peer reviews. They automate a useful piece of them if you are warning-free, but they are no substitute for human judgement about whether your code is understandable and likely to meet its requirements.

Pointers:
* Note: in teaching I require these gcc flags for student projects:
-Werror -Wextra -Wall -Wfloat-equal -Wconversion -Wparentheses -pedantic -Wunused-parameter -Wunused-variable -Wreturn-type -Wunused-function -Wredundant-decls -Wreturn-type -Wunused-value -Wswitch-default -Wuninitialized -Winit-self





tag:blogger.com,1999:blog-4172950626830217643.post-3894512002966601296
Extensions
Counter Rollover Brings Down Rail Service
time keeping
Show full content
In October 2018 Hong Kong had "six hours of turmoil" in their rail service due to as signalling outage. The culprit has now been identified as counter roll-over. https://www.scmp.com/news/hong-kong/transport/article/2178723/unknown-signalling-system-incompatibility-caused-october
South China Morning Post
https://www.scmp.com/news/hong-kong/transport/article/2178723/unknown-signalling-system-incompatibility-caused-october
Summary version: a system synchronization counter had been counting away since 1996 and required a system reset when it saturated.  (At least it didn't just roll over without anything noticing.)  But over the years two different systems with slightly different counter roll-over procedures were installed.  When rollover time came, they disagreed with each other on count value, paralyzing the system during the window until the second system shut down due to counter saturation.  Details below quoted from the official report. (https://www.mtr.com.hk/archive/corporate/en/press_release/PR-18-108-E.pdf)
The Detailed version:
"5.1.3. Data transmission between sector computers is always synchronized through an internal software counter in each sector computer. If any individual sector computer is individually rebooted, its counter will be re-initialized and will immediately synchronize to the higher counter figure for the whole synchronized network. Therefore, when the Siemens sector computers were commissioned and put into service in 2001/2002, the relevant counters were synchronized to those of the Alstom sector computers which were installed in 1996. If the counter reaches its ceiling figure, the associated sector computer will halt and need to be re-initialized. However the counter re-initialization arrangements for the two suppliers’ sector computers are different. The Alstom sector computers will be re-initialized automatically once their counters reach an inbuilt re-initialization triggering point approximately 5 hours before reaching the ceiling figure. However, this internal software function was not made known to the operators and maintainers. The Siemens sector computers do not have an automatic reinitialization function and therefore need to be manually reinitialized through rebooting in SER by maintenance staff.  
5.1.4 At around 05:26 hours on the incident day, the Alstom software counters reached the triggering point for automatic re- initialization while the Siemens sector computers continued counting up, creating an inconsistent re-initialization situation between the two interconnected sector computers at KWT (Alstom) and LAT (Siemens). This resulted in repeated execution of re-initialization followed by re-synchronization with the higher counter figure from LAT, in the KWT sector computer in an endless loop causing corresponding instability in all 25 Alstom sector computers in the system.  
5.1.5 When all the Siemens software counters reached the ceiling figure at around 10:22 hours, some 5 hours after the Alstom sector computers had passed their automatic re-initialization triggering point, the 8 Siemens sector computers halted as designed. Moreover, trains on the TKL had already encountered trainborne signalling failure earlier at 10:02 hours due to the around 20 minutes counter look ahead validity requirements. 
5.1.6 After the interconnections between the signalling systems of the relevant lines and the Alstom and Siemens sector computers between KWT and LAT were isolated, all sector computers were effectively rebooted to complete the entire re-initialization process and the signalling system for the four incident lines resumed normal. "
With credit for calling my attention to the report to:
Date: Sun, 30 Dec 2018 15:39:37 +0800
From: Richard Stein 
Subject: Re: MTR East Rail disruption caused by failure of both primary 
 and backup (Stein, RISKS-30.89)

tag:blogger.com,1999:blog-4172950626830217643.post-8107534340348184014
Extensions
Sometimes Bug Severity Isn't the Most Important Thing
automotive softwaredefectsrisk managementSQA
Show full content
Generally you need to take into account both the consequence of a software defect as well as how often it occurs when doing bug triage.  (See: Using a Risk Analysis Table to Categorize Bug Priority)

But an important special case is one in which the consequence is a business consequence such as brand tarnish rather than a spectacular software crash.   I used to use a hypothetical example of the audience's company name being misspelled on the system display to illustrate the point.  Well, it's not hypothetical any more!

Lamborghini sells a quarter-million dollar SUV with numerous software defects, including spelling the company name as "Lanborghini"   Guess which defect gets the press?

https://carbuzz.com/news/lamborghini-urus-experiencing-major-software-bugs
And it turns out that a software update not only didn't solve the typo, but also broke a bunch more functionality.  
tag:blogger.com,1999:blog-4172950626830217643.post-2655085667364548788
Extensions
Cost of highly safety critical software
economicssafety
Show full content
It's always interesting to see data on industry software costs. I recently came across a report on software costs for the aviation industry. The context was flight-critical radio communications, but the safety standards discussed were DO-178B and DO-254, which apply to flight controls as well.

Here's the most interesting picture from the report for my purposes:


(Source: Page 28 https://www.eurocontrol.int/sites/default/files/content/documents/communications/29012009-certification-cost-estimation-for-fci-platform.pdf.pdf )

Translating from DO-178B terminology, this means:

  • DAL A  (failure would be "catastrophic"):  3 - 12 SLOC/day
  • DAL B  (failure would be "hazardous"): 8 - 20 SLOC/day
  • DAL C (failure would be "major"): 15 - 40 SLOC/day
  • DAL D (failure would be "minor"): 25 - 64 SLOC/day
Worth noting is that, in my experience, really solid mission critical but NOT life-critical embedded software can be done at up to 16 SLOC per day for well-run experienced teams, so it tends to line up with DAL B costs.

For interpretation, "DAL" expresses a criticality level (a "Development Assurance Level"), with more critical software requiring more rigorous processes.  The document has quite a lot to say about how the engineering process works, and is worth a read if you want to see how the aviation folks do business.  (I'm aware that DO-178C is out, but this paper talks about the older "B" version.)    Note that there are other cost models in the paper that are less pessimistic in that report, but this is the one that says "industry experience."

Have you found other cost of software data for embedded or mission critical systems?
tag:blogger.com,1999:blog-4172950626830217643.post-6304103097433655906
Extensions
Different types of risk analysis: ALARP, GAMAB, MEMS and more
risk managementsafety
Show full content
When we talk about how much risk is enough, it is common to do things like compare the risk to current systems, or argue about whether something is more (or less) likely than events such as being killed by lightning. There are established ways to think about this topic, each with tradeoffs.

Tightrope Walker

The next time you need to think about how much risk is appropriate in a safety-critical system, try these existing approaches on for size instead of making up something on your own:

ALARP: "As Low As Reasonably Practicable"  Some risks are acceptable. Some are unacceptable. Some are worth taking in exchange for benefit, but if that is done the risk must be reduced to be ALARP.

GAMAB: "Globalement Au Moins Aussi Bon"  Offer a level of risk at least as good as the risk offered by an equivalent existing system. (i.e., no more dangerous than what we have already for a similar function)

MEM: "Minimum Endogenous Mortality"  The technical system must not create a significant risk compared to globally existing risks. For example, this should cause a minimal increase in overall death rates compared to the existing population death rates.

MGS: "Mindestens Gleiche Sicherheit"   (At least the same level of safety) Deviations from accepted practices must be supported by an explicit safety argument showing at least the same level of safety. This is more about waivers than whole-system evaluation.

NMAU: "Nicht Mehr Als Unvermeidbar"  (Not more than unavoidable)  Assuming there is a public benefit to the operation of the system, hazards should be avoided by reasonable safety measures implemented with reasonable cost.

Each of these approaches has pros and cons.  The above terms were paraphrased from this nice discussion:
Kron, On the evaluation of risk acceptance principles,
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.455.4506&rep=rep1&type=pdf

There is an interesting set of slides that covers similar ground here, and works some examples. In particular the graphs involving whether risks are taken voluntarily for different scenarios is thought provoking:
http://agse3.informatik.uni-kl.de/teaching/suze/ws2014/material/folien/SRES_03_Risk_Acceptance.pdf

In general, if you want to dig deeper into this area, a search on
    gamab mem alarp 
will bring up a number of hits

Also note that legal and other types of considerations exist, especially regarding product liability.
tag:blogger.com,1999:blog-4172950626830217643.post-6292009512196311417
Extensions
Activity on my Safe Autonomy Blog
safetyself-driving cars
Show full content
For those who might have missed it, most of my recent blogging has been on my Safe Autonomy Blog: https://safeautonomy.blogspot.com

Recent post topics include slides and paper preprints such as:



tag:blogger.com,1999:blog-4172950626830217643.post-7459074389823261464
Extensions
Embedded Code Quality and Best Practices Training Videos full length
coding styleembedded Cpeer reviews
Show full content
I've posted the full series of my available embedded system code quality and related best practices videos on YouTube.  These are full-length narrated slides of the core set of safety topics from my new course.  They concentrate on getting the big picture about code quality and good programming practices.
Each of the videos is posted to YouTube as a playlist, with each video covering a slide or two. The full lecture consists of playing the entire play list, with most lectures being 5-7 videos in sequence. (The slide download has been updated for my CMU grad course, so in general has a little more material than the original video. They'll get synchronized eventually, but for now this is what I have.)

Obviously there is more to code quality and safety than just these topics. Additional topics are available slides-only.  You can see the full set of course slides including for those lectures and others here:
  https://users.ece.cmu.edu/~koopman/lectures/index.html#642
tag:blogger.com,1999:blog-4172950626830217643.post-3240675292202343318
Extensions
New Blog on Self-Driving Car Safety
self-driving cars
Show full content
I'm doing a lot more work on self-driving car (autonomous vehicle) safety, so I've decided to split my blogging for that activity.  I'll still post more general embedded system topics here, perhaps with reduced frequency.

You can see my new blog on self-driving car safety here:
    https://safeautonomy.blogspot.com

Just to keep perspective, self-driving cars are still very complex embedded systems. You need to get the basics right (this blog) if you want them to be safe!
tag:blogger.com,1999:blog-4172950626830217643.post-7912774578413152756
Extensions
Robustness Testing of Autonomy Software (ASTAA Paper Published)
exceptionsrobustnesstools
Show full content
I'm very pleased that our research team will present a paper on Robustness Testing of Autonomy Software at the ICSE Software Engineering in Practice session in a late May. You can see a preprint of the paper here:  https://goo.gl/Pkqxy6

The work summarizes what we've learned across several years of research stress testing many robots, including self-driving cars.

ABSTRACT As robotic and autonomy systems become progressively more present in industrial and human-interactive applications, it is increasingly critical for them to behave safely in the presence of unexpected inputs. While robustness testing for traditional software systems is long-studied, robustness testing for autonomy systems is relatively uncharted territory. In our role as engineers, testers, and researchers we have observed that autonomy systems are importantly different from traditional systems, requiring novel approaches to effectively test them. We present Automated Stress Testing for Autonomy Architectures (ASTAA), a system that effectively, automatically robustness tests autonomy systems by building on classic principles, with important innovations to support this new domain. Over five years, we have used ASTAA to test 17 real-world autonomy systems, robots, and robotics-oriented libraries, across commercial and academic applications, discovering hundreds of bugs. We outline the ASTAA approach and analyze more than 150 bugs we found in real systems. We discuss what we discovered about testing autonomy systems, specifically focusing on how doing so differs from and is similar to traditional software robustness testing and other high-level lessons.

Authors:
Casidhe Hutchison
Milda Zizyte
Patrick Lanigan
David Guttendorf
Mike Wagner
Claire Le Guoes
Philip Koopman


tag:blogger.com,1999:blog-4172950626830217643.post-999869583043851511
Extensions
New Peer Review Checklist for Embedded C Code
coding stylepeer reviews
Show full content
Here's a new peer review checklist to help improve the quality of your embedded C code.

To use the checklist, you should do a sit-down meeting with, ideally, three reviewers not including the code author. Divide the checklist up into three portions as indicated.  Be sure to run decent static analysis before the review to safe reviewer time -- let the tools find the easy stuff before spending human time on the review.

After an initial orientation to what the code is supposed to do and relevant background, the review process is:
  1. The review leader picks the next few lines of code to be reviewed and makes sure everyone is ONLY focused on those few lines.  Usually this is 5-10 lines encompassing a conditional structure, a basic block, or other generally unified small chunk within the code.
  2. Reviewers identify any code problems relevant to their part of the checklist.  It's OK if they notice others, but they should focus on individually considering each item in their part of the checklist and ask "do I see a violation of this item" in just the small chunk of code being considered.
  3. Reviewer comments should be recorded in the form: "Line X seems to violate Checklist Item Y for the following reason." Do NOT suggest a fix -- just record the issue.
  4. When all comments have been recorded, go back to step 1.  Continue to review up to a maximum of 2 hours. You should be covering about 100-200 lines of code per hour. Too fast and too slow are both a problem.
A text version of the checklist is below. You can also download an acrobat version here.  Additional pointers to support materials are after the checklist. If you have a static analysis tool that automates any of the checklist item, feel free to replace that item with something else that's important to you.
===============================================================
Peer Review Checklist: Embedded C Code
       
Before Review:
0    _____    Code compiles clean with extensive warning checks (e.g. MISRA C rules)
       
Reviewer #1:       
1    _____    Commenting:  top of file, start of function, code that needs an explanation
2    _____    Style is consistent and follows style guidelines
3    _____    Proper modularity, module size, use of .h files and #includes
4    _____    No orphans (redundant, dead, commented out, unused code & variables)
5    _____    Conditional expressions evaluate to a boolean value; no assignments
6    _____    Parentheses used to avoid operator precedence confusion
7    _____    All switch statements have a default clause; preferably an error trap
       
Reviewer #2:       
8    _____    Single point of exit from each function
9    _____    Loop entry and exit conditions correct; minimum continue/break complexity
10    _____    Conditionals should be minimally nested (generally only one or two deep)
11    _____    All functions can be unit tested; SCC or SF complexity less than 10 to 15
12    _____    Use const and inline instead of #define; minimize conditional compilation
13    _____    Avoid use of magic numbers (constant values embedded in code)
14    _____    Use strong typing (includes: sized types, structs for coupled data, const)
15    _____    Variables have well chosen names and are initialized at definition
       
Reviewer #3:       
16    _____    Minimum scope for all functions and variables; essentially no globals
17    _____    Concurrency issues? (locking, volatile keyword, minimize blocking time)
18    _____    Input parameter checking is done (style, completeness)
19    _____    Error handling for function returns is appropriate
20    _____    Null pointers, division by zero, null strings, boundary conditions handled
21    _____    Floating point use is OK (equality, NaN, INF, roundoff); use of fixed point
22    _____    Buffer overflow safety (bound checking, avoid unsafe string operations)
       
All Reviewers:     
23    _____    Does the code match the detailed design (correct functionality)?
24    _____    Is the code as simple, obvious, and easy to review as possible?
       
        For TWO Reviewers assign items:   Reviewer#1:  1-11; 23-24    Reviewer#2: 12-24
        Items that are covered with static analysis can be removed from checklist
        Template 1/28/2018:  Copyright CC BY 4.0, 2018, Philip Koopman
===============================================================

Additional material to help you with successful peer reviews:


tag:blogger.com,1999:blog-4172950626830217643.post-8730490644576700543
Extensions
Embedded Software Course Notes On-Line
coding styleembedded Csafetysecuritytutorial
Show full content
I'm just wrapping up my first semester teaching a new course on embedded system software. It covers code quality, safety, and security. Below is table of lecture handouts.

NOTE: there is an update here:
     https://users.ece.cmu.edu/~koopman/lectures/index.html#642
which includes newer course notes and quite a few YouTube videos of these lectures.
You should use that URL instead of this blog post, but I've left this post as-is for Fall 2017.

18-642 Embedded System Software Engineering
Prof. Philip Koopman, Carnegie Mellon University, Fall 2017


SlidesTopics 1Course IntroductionSoftware is eating the world; embedded applications and markets; bad code is a problem; coding is 0% of software; truths and management misconceptions 2Software Development ProcessesWaterfall; swiss cheese model; lessons learned in software; V model; design vs. code; agile methods; agile for embedded 3Global VariablesGlobal vs. static variables; avoiding and removing globals 4Spaghetti CodeMcCabe Cyclomatic Complexity (MCC); SCC; Spaghetti Factor (SF) 5Unit TestingBlack box testing; white box testing; unit testing strategies; MCDC coverage; unit testing frameworks (cunit) 6Modal Code/StatechartsStatechart elements; statechart example; statechart implementation 7Peer ReviewsEffective code quality practices, peer review efficiency and effectiveness; Fagan inspections; rules for peer review; review report; perspective-based reviews; review checklist; case study; economics of peer review 8Code Style/HumansMaking code easy to read; good code hygiene; avoiding premature optimization; coding style 9Code Style/LanguagePitfalls and problems with C; language use guidelines and analysis tools; using language wisely (strong typing); Mars Climate Orbiter; deviations & legacy code 10Testing QualitySmoke testing, exploratory testing; methodical test coverage; types of testing; testing philosophy; coverage; testing resources 11RequirementsAriane 5 flight 501; rules for good requirements; problematic requirements; extra-functional requirements; requirements approaches; ambiguity 12System-Level TestFirst bug story; effective test plans; testing won't find all bugs; F-22 Raptor date line bug; bug farms; risks of bad software 13SW ArchitectureHigh Level Design (HLD); boxes and arrows; sequence diagrams (SD); statechart to SD relationship; 2011 Health Plan chart 14Integration TestingIntegration test approaches; tracing integration tests to SDs; network message testing; using SDs to generate unit tests 15TraceabilityTraceability across the V; examples; best practices 16SQA isn't testingSQA elements; audits; SQA as coaching staff; cost of defect fixes over project cycle 17Lifecycle CMA400M crash; version control; configuration management; long lifecycles 18MaintenanceBug fix cycle; bug prioritization; maintenance as a large cost driver; technical debt 19Process Key MetricsTester to developer ratio; code productivity; peer review effectiveness 33Date Time ManagementKeeping time; time terminology; clock synchronization; time zones; DST; local time; sunrise/sunset; mobility and time; date line; GMT/UTC; leap years; leap seconds; time rollovers; Zune leap year bug; internationalization. 21Floating Point PitfallsFloating point formats; special values; NaN and robots; roundoff errors; Patriot Missile mishap 23Stack OverflowStack overflow mechanics; memory corruption; stack sentinels; static analysis; memory protection; avoid recursion 25Race ConditionsTherac 25; race condition example; disabling interrupts; mutex; blocking time; priority inversion; priority inheritance; Mars Pathfinder 27Data IntegritySources of faults; soft errors; Hamming distance; parity; mirroring; SECDED; checksum; CRC 20Safety+Security OverviewChallenges of embedded code; it only takes one line of bad code; problems with large scale production; your products live or die by their software; considering the worst case; designing for safety; security matters; industrial controls as targets; designing for security; testing isn't enough
Fiat Chrysler jeep hack; Ford Mytouch update; Toyota UA code quality; Heartbleed; Nest thermostats; Honda UA recall; Samsung keyboard bug; hospital infusion pumps; LIFX smart lightbulbs; German steel mill hack; Ukraine power hack; SCADA attack data; Shodan; traffic light control vulnerability; hydroelectric plant vulnerability; zero-day shopping list 22DependabilityDependability; availability; Windows 2000 server crash; reliability; serial and parallel reliability; example reliability calculation; other aspects of dependability 24Critical SystemsSafety critical vs. mission critical; worst case and safety; HVAC malfunction hazard; Safety Integrity Levels (SIL); Bhopal; IEC 61508; fleet exposure 26Safety PlanSafety plan elements; functional safety approaches; hazards & risks; safety goals & safety requirements; FMEA; FTA; safety case (GSN) 28Safety RequirementsIdentifying safety-related requirements; safety envelope; Doer/Checker pattern 29Single Points of FailureFault containment regions (FCR); Toyota UA single point failure; multi-channel pattern; monitor pattern; safety gate pattern; correlated & accumulated faults 30SIL IsolationIsolating different SILs, mixed-SIL interference sources; mitigating cross-SIL interference; isolation and security; CarShark hack 31Redundancy ManagementBellingham WA gasoline pipeline mishap; redundancy for availability; redundancy for fault detection; Ariane 5 Flight 501; fail operational; triplex modular redundancy (TMR) 2-of-3 pattern; dual 2-of-2 pattern; high-SIL Doer/Checker pattern; diagnostic effectiveness and proof tests 32Safety Architecture PatternsSupplemental lecture with more detail on patterns: low SIL; self-diagnosis; partitioning; fail operational; voting; fail silent; dual 2-of-2; Ariane 5 Flight 501; fail silent patterns (low, high, mixed SIL); high availability mixed SIL pattern 34Security PlanSecurity plan elements; Target Attack; security requirements; threats; vulnerabilities; mitigation; validation 35CryptographyConfusion & diffusion; Caesar cipher; frequency analysis; Enigma; Lorenz & Colossus; DES; AES; public key cryptography; secure hashing; digital signatures; certificates; PKI; encrypting vs. signing for firmware update 36Security ThreatsStuxnet; attack motivation; attacker threat levels; DirectTV piracy; operational environment; porous firewalls; Davis Besse incident; BlueSniper rifle; integrity; authentication; secrecy; privacy; LG Smart TV privacy; DoS/DDos; feature activation; St. Jude pacemaker recall 37Security VulnerabilitiesExploit vs. attack; Kettle spambot; weak passwords; master passwords; crypto key length; Mirai botnet attack; crypto mistakes; LIFX revisited; CarShark revisited; chip peels; hidden functionality; counterfeit systems; cloud connected devices; embedded-specific attacks 38Security Mitigation ValidationPassword strength; storing passwords & salt/pepper/key stretching; Adobe password hack; least privilege; Jeep firewall hack; secure update; secure boot; encryption vs. signing revisited; penetration testing; code analysis; other security approaches; rubber hose attack 39Security PitfallsKonami code; security via obscurity; hotel lock USB hack; Kerckhoff's principle; hospital WPA setup hack; DECSS; Lodz tram attack; proper use of cryptography; zero day exploits; security snake oil; realities of in-system firewalls; aircraft infotainment and firewalls; zombie road sign hack
Note that in Spring 2018 these are likely to be updated, so if want to see the latest also check the main course page:  https://www.ece.cmu.edu/~ece642/   For other lectures and copyright notes, please see my general lecture notes & video page: https://users.ece.cmu.edu/~koopman/lectures/index.html


tag:blogger.com,1999:blog-4172950626830217643.post-8516912079817849978
Extensions
Highly Autonomous Vehicle Validation
safetyself-driving cars
Show full content
Here are the slides from my TechAD talk today.


Highly Autonomous Vehicle Validation from Philip Koopman

Highly Autonomous Vehicle Validation: it's more than just road testing!
- Why a billion miles of testing might not be enough to ensure self-driving car safety.
- Why it's important to distinguish testing for requirements validation vs. testing for implementation validation.
- Why machine learning is the hard part of mapping autonomy validation to ISO 26262

tag:blogger.com,1999:blog-4172950626830217643.post-4281280479887506948
Extensions
Top Five Embedded Software Management Misconceptions
defectseconomicsmanagement
Show full content
Here are five common management-level misconceptions I run into when I do design reviews of embedded systems. How many of these have you seen recently?

(1) Getting to compiled code quickly indicates progress. (FALSE!)

Many projects are judged by "coding completed" to indicate progress.  Once the code has been written, compiles, and kind of runs for a few minutes without crashing, management figures that they are 90% there.  In reality, a variant of the 90/90 rule holds:  the first 90% of the project is in coding, and the second 90% is in debugging.

Measuring teams on code completion pressures them to skip design and peer reviews, ending up with buggy code. Take the time to do it right up front, and you'll more than make up for those "delays" with fewer problems later in the development cycle.  Rather than measure "code completed" do something more useful, like measure the fraction of modules with "peer review completed" (and defects found in peer review corrected).  There are many reasonable ways to manage, but waterfall-ish projects that treat "code completed" as the most critical milestone is not one of them.

(2) Smart developers can write production-quality code on a long weekend (FALSE!)

Alternate form: marketing sets both requirements and end date without engineering getting a chance to spend enough time on a preliminary design to figure out if it can actually be done.

The true bit is anyone can slap together some code that doesn't work.  Some folks can slap together code in a long weekend that almost works.  But even the best of us can only push so many lines of code in a short amount of time without making mistakes, much less producing something anyone else can understand.  Many of us remember putting together hundreds or thousands of lines on an all-nighter when we were students. That should not be mistaken for writing production embedded code.

Good embedded code tends to cost about an hour for every 1 or 2 lines of non-comment code all-in, including testing (on a really good day 3 lines/hr).  Some teams come from the Lake Wobegone school, where all the programmers are above average.  (Is that really true for your team?  Really?  Good for you!  But you still have to pay attention to the other four items on this list.)  And sure, you can game this metric if you try. Nonetheless, it is remarkable how often I see a number well above about 2 SLOC/hour of deeply embedded code corresponding to a project that is in trouble.

Regardless of the precise productivity number, if you want your system to really work, you need to treat software development as a core competency.  You need an appropriately methodical and rigorous engineering process. Slapping together code quickly gives the illusion of progress, but it doesn't produce reliable products for full-scale production.

(3) A “mostly working,” undisciplined prototype can be deployed.  (FALSE!)

Quick and dirty prototypes provide value by giving stakeholders an idea of what to expect and allowing iterations to converge on the right product. They are invaluable for solidifying nebulous requirements. However, such a prototype should not be mistaken for an actual product!   If you've hacked together a prototype, in my experience it's always more expensive to clean up the mess than it is to take a step back and start a project from scratch or a stable production code base.

What the prototype gives you is a solid sense of requirements and some insight into pitfalls in design.

A well executed incremental deployment strategy can be a compromise to iteratively add functionality if you don't know all your requirements up front. But an well-run Agile project is not what I'm talking about when I say "undisciplined prototype." A cool proof of concept can be very valuable.  It should not be mistaken for production code.

(4) Testing improves software quality (FALSE!)

If there are code quality problems (possibly caused by trying to bring an undisciplined prototype to market), the usual hammer that is brought to bear is more testing.  Nobody ever solved code quality problems by testing. All that testing does is make buggy code a little less buggy. If you've got spaghetti code that is full of bugs, testing can't possibly fix that. And testing will generally miss most subtle timing bugs and non-obvious edge cases.

If you're seeing lots of bugs in system test, your best bet is to use testing to find bug farms. The 90/10 rule applies: many times 90% of the bugs are in bug farms -- the worst 10% of the modules. That's only an approximate ratio, but regardless of the exact number, if you're seeing a lot of system test failures then there is a good chance some modules are especially bug-prone.  Generally the problem is not simply programming errors, but rather poor design of these bug-prone modules that makes bugs inevitable. When you identify a bug farm, throw the offending module away, redesign it clean, and write the code from scratch. It's tempting to think that each bug is the last one, but after you've found more than a handful of bugs in a module, who are you kidding? Especially if it's spaghetti code, bug farms will always be one bug away from being done, and you'll never get out of system test cleanly.

(5) Peer review is too expensive (FALSE!)

Many, many projects skip peer review to get to completed code (see item #1 above). They feel that they just don't have time to do peer reviews. However, good peer reviews are going to find 50-75% of your bugs before you ever get to testing, and do so for about 10% of your development budget.  How can you not afford peer reviews?   (Answer: you don't have time to do peer reviews because you're too busy writing bugs!)

Have you run into another management misconception on a par with these? Let me know what you think!

tag:blogger.com,1999:blog-4172950626830217643.post-7963852963507632328
Extensions
The Spaghetti Factor -- A Software Complexity Metric Proposal
coding stylemetrics
Show full content

I've had to review code that has spaghetti-level complexity in control flow (too high cyclomatic complexity).  And I've had to review code that has spaghetti-level complexity its data flow (too many global variables mixed together into a single computation).  And I've had to review procedures that just go on for page after page with no end in sight. But the stuff that will really make your brain hurt is code that has all of these problems.

There are many complexity metrics out there. But I haven't seen a one that directly tries to help balance three key points of complexity into a single intuitive number: code complexity, data complexity, and module size. So here is a proposal that could help drive improvement in a lot of the terrible embedded control code I've seen:



The Spaghetti Factor metric (SF):
SF = SCC + (Globals*5) + (SLOC/20)

SCC = Strict Cyclomatic Complexity
Globals = # of read/write global variables
SLOC = # source lines of non-comment code (e.g., C statements)

Scoring:
5-10 - This is the sweet spot for most code except simple helper functions
15 - Don't go above this for most modules
20 - Look closely; review to see if refactoring makes sense
30 - Refactor the design
50 - Untestable; throw the module away and fix the design
75 - Unmaintainable; throw the module away; throw the design away; start over
100 - Nightmare; probably you need to throw the whole subsystem away and re-architect it



Notation:

SCC is Strict Cyclomatic Complexity (sometimes called CC2).  This is a variant of McCabe Cyclomatic complexity (MCC). In general terms, MCC is based on the number of branches in the program. SCC additionally considers complexity based on the number of conditions within each branch. SCC is an approximation of how many test cases it takes to exercise all the paths through code including all the different ways there are to trigger each branch. In other words, it is an estimate of how much work it is to do unit testing. Think of it as an approximation to the effort required for MC/DC testing. But in practice it is also a measure of how hard it is to understand the code.  The idea is to keep SCC low enough that it is feasible to understand and test paths through the code.

Globals is the number of read/write global variables accessed by the module. This does not include "const" values, nor file static variables.  In an ideal world you have zero or near-zero global variables. If you have inherent global state, you should encapsulated that in a state object with appropriate access functions to enforce well-disciplined writes.  Referencing an unstructured pile of dozens or hundreds of global variables can make software difficult to test, and can make subsystem testing almost impossible. Partly that is due to the test scaffolding required, but partly that is simply due to the effort of chasing down all the globals and trying to figure out what they do both inbound and outbound. Moreover, too many globals can make it nearly impossible to chase down bugs or understand the effects of changing one part of the code on the rest of the code. An important goal of this part of the metric is to discourage use of many disjoint global variables to implicitly pass data around from routine to routine instead of passing parameters along with function calls.

SLOC is the number of non-comment "Source Lines of Code."  For C programs, this is the number of programming statements. Typical guidelines are a maximum 100-225 maximum lines of code for a module, with most modules being smaller than that.

As an example calculation, if you have 100 lines of code with an SCC of 9 and 1 global reference, your score will be  SF = 9 + (1*5) + (100/20) = 19.  A score of 19 is on the upper edge of being OK. If you have a distribution of complexity across modules, you'd want most of them to be a bit lower in complexity than this example calculation.

Discussion:

The guideline values are taken primarily from MCC, which typically has a guideline of 10 for most modules, 15 as a usual bound, and 30 as limit.  To account for globals and length, based on my experience, I've changed that to 15 for most modules, 20 as a soft limit and 30 as a hard limit.  You might wish to adjust the threshold and multipliers based on your system and experience. In particular it is easy to make a case that these limits aren't strict enough for life-critical software, and a case can be made for being a little more relaxed in throw-away GUI management code.  But I think this is a good starting point for most every-day embedded software that is written by a human (as opposed to auto-generated code).

The biggest exception is usually what to do about switch statements.  If you exempt them you can end up with multiple switches in one module, or multiple switch/if/switch/if layered nesting.  (Neither is a pretty sight.) I think it is justifiable to exempt modules that have ONLY a switch and conditional logic to do sanity checking on the switch value.  But, because 30 is a pretty generous limit, you're only going to see this rarely. Generally the only legitimate reason to have a switch bigger than that is for something like processing a message type for a communication protocol.  So I think you should not blanket exempt switch statements, but rather include them in an overall case-by-case sign-off by engineering management as to which few exceptions are justifiable.

Some might make the observation that this metric discourages extensive error checking.  That's a different topic, and certainly the intent is NOT to discourage error checking. But the simple answer is that error checking has to be tested and understood, so you can't simply ignore that part of the complexity. One way to handle that situation is to put error checking into a subroutine or wrapper function to get that complexity out of the way, then have that wrapper call the actual function that does the work.  Another way is to break your overall code down into smaller pieces so that each piece is simple enough for you to understand and test both the functionality and the error checking.

Finally, any metric can be gamed, and that is surely true of simple metrics like this one.  A good metric score doesn't necessarily mean your code is fantastic. Additionally, this metric does not consider everything that's important, such as the total number of globals across your code base. On the other hand, if you score poorly on this metric, most likely your code is in need of improvement.

What I recommend is that you use this metric as a way to identify code that is needlessly complex.  It is the rare piece of code indeed that unavoidably needs to have a high score on this complexity metric. And if all your code has a good score, that means it should be that much easier to do peer review and unit testing to ensure that other aspects of the code are in good shape.

References:

A NIST paper on applying metrics is here: http://www.mccabe.com/pdf/mccabe-nist235r.pdf including an interesting discussion of the pitfalls of handling switch statements within a complexity framework.

tag:blogger.com,1999:blog-4172950626830217643.post-4173207074055051733
Extensions
Don't use macros for MIN and MAX
coding style
Show full content


It is common to see small helper functions implemented as macros, especially in older C code. Everyone seems to do it.  But you should avoid macros, and instead use inline functions.

The motivation for using macros was originally that you needed to use a small function in many places but were worried about the overhead of doing a subroutine call. So instead, you used a macro, which expands into source code in the preprocessor phase.  That was a reasonable tradeoff 40 years ago. Not such a great idea now, because macros cause problems for no good reason.
For example, you might look on the Web and find these common macros     #define MAX(a,b) ((a) > (b) ? a : b)     #define MIN(a,b) ((a) < (b) ? a : b)
And you might find that it seems to work for a while.  You might get bitten by the missing "()" guards around the second copy of a and b in the above -- which version you get depends on which cut & paste code site you visit. 
But then you'll find that there are still weird situations where you get unexpected behavior. For example, what does this do?     c = MAX(a++, b); If a is greater than b executing the code will increment a twice, but if a is less than or equal to b it will only increment a once.  And if you start mixing types or putting complicated expressions into the macro things can get weird and buggy in a hurry.
Another related problem is that the macro will expand, increasing the cyclomatic complexity of your code. That's because a macro is equivalent to you having put the conditional branch into the source code. (Remember, macro expansion is done by the preprocessor, the so compiler itself acts as if you'd typed the conditional assignment expression every place you use the macro.) This complexity rating is justified, because there is no actual procedure that can be unit tested independently.
As it turns out, macros are evil. See the C++ FAQ: https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-if  which lists 4 different types of evil behavior.  There are fancy hacks to try to get any particular macros such as MIN and MAX to be better behaved, but no matter how hard you try you're really just making a deal with the devil. 
What's the fix?
The fix is: don't use macros. Instead use inline procedure calls.
You should already have access to built-in functions for floating point such as fmin() and fmax().  If it's there, use the stuff from your compiler vendor instead of writing it yourself!
If your compiler doesn't have integer min and max, or you are worried about breaking existing macro code, convert the macros into inline functions with minimal changes to your code base:
inline int32_t MAX(int32_t a, int32_t b) { return((a) > (b) ? a : b); } inline int32_t MIN(int32_t a, int32_t b) { return((a) < (b) ? a : b); }
If you have other types to deal with you might need different variants depending on the types, but often a piece of code uses predominantly one data type for its calculations, so in practice this is usually not a big deal. And don't forget, if your build environment has a built in min or max you can just set up the macro to call that directly.
What about performance?
The motivation for using macros back in the bad old days was efficiency. A subroutine call involved a lot of overhead. But the inline keyword tells the compiler to expand the code in-place while retaining all the advantages of a subroutine call.  Compilers are pretty good at optimization these days. So there is no overhead at run-time.  I've also seen advice to put the inline function in a header file so it will be visible to any procedure needing it, and the macro was already there anyway.
Strictly speaking, "inline" is a suggestion to the compiler. However, if you have a decent compiler it will follow the suggestion unless the inline function is so big the call overhead just doesn't matter. Some compilers have a warning flag that will let you know when the inline didn't happen.  For example, use -Winline for gcc.  If your compiler ignores "inline" for something as straightforward as MIN or MAX, get a different compiler.
What about multiple types?
A perceived advantage of the macro approach is that you can play fast and loose with types.  But playing fast and loose with types is a BAD IDEA because you'll get bugs.  
If you really hate having to match the function name to the data types then what you need is to switch to a language that can handle this by automatically picking the right function based on the operator types. In other words, switch from a to a language that is 45 years old (C) to one that is only about 35 years old (C++).  There's a paper from 1995 that explains this in the context of min and max implemented with templates:  http://www.aristeia.com/Papers/C++ReportColumns/jan95.pdf As it turns out the rabbit hole goes a lot deeper than you might think for a generic solution.
But you don't have to go down the rabbit hole.  For most code the best answer is simply to use inline functions and pick the function name that matches your data types. You shouldn't lose any performance at all, and you'll be likely to save a lot of time chasing obscure bugs.
tag:blogger.com,1999:blog-4172950626830217643.post-5477212599450959731
Extensions
#define vs. const
coding style
Show full content
Is your code full of "#define" statements?  If so, you should consider switching to the const keyword.

Old school C:     #define MYVAL 7
Better approach:    const uint32_t myVal = 7;
Here are some reasons you should use const instead of #define:
  • #define has global scope, so you're creating (read-only) global values every time you use #define. Global scope is evil, so don't do that.  (Read-only global scope for constant values is a bit less evil than global variables per se, especially if you can't use the namespace features of C++. But gratuitous global scope is always a bad idea.) A const alternative can obey scoping rules, including being purely local if defined inside a procedure, or more commonly file static with the "static" keyword.
  • Const lets you do more aggressive type checking (depending upon your compiler and static analysis tools, especially if you use a typedef more specific than built-in C data types). While C is a bit weak as a language in this area compared to other languages, a classical example is a const lets you identify a number as being in feet or meters, while the #define approach is just as if you'd typed the number 7 in with no units. The #define approach can bite you if you use the wrong value in the wrong place. Type checking is an effective way to find bugs, and using #define gives up an opportunity to let static analysis tools help you with that.
  • Const lets you use the value as if it were a variable when you need to (e.g., passing an address to the variable) without having to change how the variable is defined.
  • #define in general is so bug-prone that you should minimize its use just to avoid having to spend time asking "is this one OK?" in a peer review. Most #define uses tend to be const variables in old-school code, so getting rid of them can dramatically reduce the peer review burden of sifting through hundreds of #define statements to look for problems.
Here are some common myths about this tradeoff. (Note that on some systems these statements might be true, especially if you have and old and lame compiler.  But they don't necessarily have to be true and they often are false, especially on newer chips with newer compilers.)
  • "Const wastes memory."  False if you have a compiler that is smart enough to do the right thing. Sure, if you want to pass a pointer to the const it will actually have to live in memory somewhere, but you can't even pass a pointer to a #define at all. One of the points of "const" is to give the compiler a hint that lets it optimize memory footprint.
  • "Const won't work for X." Generally false if you have a newer compiler, and especially if you are using a mostly-C subset of the capability of a C++ compiler, as is increasingly common. And honestly, most of the time #define is just being used as a plain old integer const to get rid of magic numbers. const will work fine.  (If you have magic numbers instead of #define, then you have bigger problems than this even.) Use const for the no-brainer cases. Something is probably wrong if everything about your code is so special you need #define everywhere.
  • "Const hassles me about type conversions."  That's a feature to prevent you from being sloppy!  So strictly speaking the compiler doing this is not a myth. The myth is that this is a bad thing.
There are plenty of discussions on this topic.  You'll also see that some folks advocate using enums for some situations, which we'll get to another time. For now, if you change as many #defines as you can to consts then that is likely to improve your code quality, and perhaps flush out a few bugs you didn't realize you had.
Be careful when reading discussion group postings on this topic.  There is a lot of dis-information out there about performance and other potential tradeoff factors, usually based on statements about 20 year old versions of the C language or experiences with compilers that have poor optimization capability.  In general, you should always use const by default unless your particular compiler/system/usage presents a compelling case not to.
See also the Barr Group C coding standard rule 1.8.b which says to use const, and has a number of other very useful rules.

tag:blogger.com,1999:blog-4172950626830217643.post-6466482683367975647
Extensions
Optimize for V&V, not for writing code
coding styleeconomicsmanagementoptimizationtesting
Show full content


Geralt / CC0 PD/noattrib.
Writing code should be made more difficult so that Verification &Validation can be made easier.

I first heard this notion years ago at a workshop in which several folks from industry who build high assurance software (think flight controls) stood up and said that V&V is what matters. You might expect that from flight control folks, but their reasoning applies to pretty much every embedded project. That's because it is a matter of economics. 
Multiple speakers at that workshop said that aviation software can require 4 or 5 hours of V&V for every 1 hour of creating software. It makes no economic sense to make life easy for the 1 hour side of the ratio at the expense of making life painful for the 5 hour side of the ratio.
Good, but non-life-critical, embedded software requires about 2 hours of V&V for every 1 hour of code creation. So the economic argument still holds, with a still-compelling multiplier of 2:1.  I don't care if you're Vee,  Agile, hybrid model or whatever. You're spending time on V&V, including at least some activities such as peer review, unit test, created automated tests, performing testing, chasing down bugs, and so on. For embedded products that aren't flaky, probably you spend more time on V&V than you do on creating the code. If you're doing TDD you're taking an approach that has the idea of starting with a testing viewpoint built in already, by starting from testing and working outward from there. But that's not the only way to benefit from this observation.
The good news is that making code writing "difficult" does not involve gratuitous pain. Rather, it involves being smart and a bit disciplined so that the code you produce is easier for others to perform V&V on. A bit of up front thought and organization can save a lot on downstream effort. Some examples include:
  • Writing concise but helpful code comments so that reviewers can understand what you meant.
  • Writing code to be obvious rather than clever, again to help reviewers.
  • Follow a style guide to make your code consistent, and thus easier to understand.
  • Writing code that compiles clean for static analysis, avoiding time wasted finding defects in test that a tool could have found, and avoiding a person having to puzzle out which warnings matter, and which don't.
  • Spending some time to make your unit interfaces easier to test, even if it requires a bit more work designing and coding the unit.
  • Spending time making it easy to trace between your design and the code. For example, if you have a statechart, make sure the statechart uses names that map directly to enum names rather than using arbitrary state variables such as "magic number" integers between 1 and 7. This makes it easier to ensure that the code and design match. (For that matter, just using statecharts to provide a guide to what the code does also helps.)
  • Spending time up front documenting module interaction so that integration testers don't have to puzzle out how things are supposed to work together. Sequence diagrams can help a lot.
  • Making the requirements both testable and easy to trace. Make every requirement idea a stand-alone sentence or paragraph and give it a number so it's easy to trace to a specific test primarily designed to test that particular requirement. Avoid having requirements in huge paragraphs of free-form text that mix lots of different concepts together.
Sure, these sound like a good idea, but many developers skip or skimp on them because they don't think they can afford the time. They don't have time to make their code clean because they're too busy writing bugs to meet a deadline. Then they, and everyone else, pay for this during the test cycle. (I'm not saying the programmers are necessarily the main culprits here, especially if they didn't get a vote on their deadline. But that doesn't change the outcome.)
I'm here to say you can't afford not to follow these basic code quality practices. That's because every hour you're saving by cutting corners up front is probably costing you double (or more) downstream by making V&V more painful than it should be. It's always hard to invest in downstream benefits when the pressure is on, but doing so is costing you dearly when you skimp on code quality.
Do you have any tricks to make code easier to understand that I missed?
tag:blogger.com,1999:blog-4172950626830217643.post-3073407004848695123
Extensions