Frage

I am a C developer for an embedded system. YouTube has recently started recommending "C++ for embedded systems" talks. Having watched some of them, they pique my interest, but none of them answer the question they leave me with.

These talks (especially Modern C++ in Embedded Systems by Michael Caisse) advocate for a development process whereby, instead of:

  1. writing and edit code
  2. debugging it to confirm it works (or, more likely, debugging it to see what's wrong and where to go from here)
  3. repeat until working

...one should avoid the debugger completely, trusting that the choice of language and good practice makes bugs less likely, which then eliminates the need for the debugger.

But as someone who writes firmware for a microcontroller that controls analogue circuitry, many of my problems are found when hardware shows unexpected behaviour and I find I can only investigate this behaviour (especially the timing of events) by throwing breakpoints all over my code and waiting to see events happen out of order, or not happen at all.

This will then either reveal a mis-configured register, or unexpected behaviour by one of the microcontroller's peripherals, which was not obvious from the device manual and necessitates a small code re-design. These talks have my attention, but I cannot see how these techniques that are supposed to help people like me, actually help me with hardware issues.

Can abstractions and good code practice (which I'm all for) eliminate the need for the debugger (something I see as necessary for addressing hardware bugs)?

War es hilfreich?

Lösung

I think you are misrepresenting the message of the "Modern C++ in Embedded Systems" video. The point is that there are people in the embedded world that write code and then test it by running the code in the debugger to verify that it does what they think it does. He argues that a better alternative is to use abstractions so that the compiler can verify that certain assumptions about the code hold.

This method still allows to use the debugger to find bugs, especially hardware problems. You should just not use the debugger to understand code, it should be understandable and correct by writing it that way.

The advantage of using higher abstractions to validate assumptions is that there are certain types of bugs, e.g. having a function f(int mode, int value) which is called as f(value, mode), that can be completely avoided. Michael Caisse argues that using the right tools, e.g. strong types in C++, alleviates this and should therefore be used.

Andere Tipps

No, not at all !

Abstractions and good practices can of course reduce the risks of errors. For example:

  • language abstractions let the compiler generate code, that you would have to write yourself otherwise. For example, the C++ object model ensures that object constructed are destroyed as they supposed to be, without extra care on your shoulders;

  • these abstractions allow to build safer constructs that you can use in your code, such as RAII, or smart pointers that considerably alleviate the tasks related to memory management;

  • a rich container library and a powerful algorithm library further avoid that you have to write a lot of error prone code yourself by using already tested and highly optimized implementations.

But all this will only reduce probability of bugs. It will never eliminate bugs completely. So you will continue to use the debugger and log files to chase them.

This question basically boils down to "can you write bug free code the first time every time?” The answer is always going to be no.

Yes, there are practices that can help, you can isolate modules. You can compile both for the embedded and desktop, then test and develop on the desktop. You can create hardware abstraction layers that help isolate those modules so you can test and debug them easier on PC.

There is certainly value in reducing the usage of debuggers on embedded platforms, as they are typically much slower than on PC and your REPL is therefore much slower.

But, eventually something will come up that requires a debugger of some sort. Sometimes that's a JTAG debugger, sometimes it's an oscilloscope, or a blinking LED.

There are two basic types of software bugs:

  1. The code doesn't do what you intended.
  2. What you intended was the wrong thing to do.

The choice of languages etc may (or may not) have an impact on the first type of bug, but it has no effect at all on the second. Note, by "what you intended" I mean the real-world observable behaviour of the software, not internal design decisions.

For a real-world embedded system, the likelihood that you fully understood everything that the real world can throw at your software is, realistically, zero. So expect to go bug-hunting!

If everything you do is perfect, you don't need a debugger. Nobody is perfect.

There is a major class of bugs that I have seen in my career which can be described as the author thought they knew what the code was doing, but it in fact did something else. When this happens, you need a tool which shows precisely what the computer did, rather than what you thought it did. That tool is the debugger (or a suite of related tools, like hyper-paranoid levels of logging).

A framework which literally prevents you from confusing the left motor and the right motor is likely to be too restrictive to do anything interesting in. If you customize a generalized framework enough to reach this point, you have a decent sized body of code which is going to need a debugger. Indeed I ran into a case like this recently which was solved with the simultaneous application of a debugger, good software documentation, and some Lego models. I would not have wanted to solve the problem with any one of those fundamental tools missing.

There have been programmers which do the no-debugger approach. Donald Knuth was famous for thinking a program through from start to finish, and only then begin writing the code. From what I understand, his code was remarkably bug free, often compiling and running the first time! But I am certain he would appreciate a debugger for when your firmware causes a PCI-e exception to get thrown due to a timeout!

Debuggers, while a useful tool for many things, are by definition primarily for... de-bugging. So your question comes down to whether good practice and reliance on third party code can ever completely eliminate bugs.

[...] trusting that the choice of language and good practice makes bugs less likely, which then eliminates the need for the debugger.

As you said, even if you trust that your languages/frameworks and good practices make bugs less likely, you haven't eliminated all bugs, but reduced the likelihood of their occurrence. Without a debugger (or some similar approach such as logging), how will you diagnose those bugs that still occur?

Further, if everyone trusts their languages and frameworks 100%, how will defects in the languages/libraries themselves be discovered? Open any mainstream project on GitHub and see how many issues are reported.

Good practice can certainly reduce software defects, but even the best practices and tools will never eliminate the utility of a debugger.

I think your answer is in your own comment:

[...] many of my problems are found when hardware shows unexpected behaviour

The problem with bugs is, we never see them coming!

Isolating two specific use cases, in the context of embedded development:

As a development tool the debugger is essential for testing code and execution states in a sterile and controlled environment.

As a diagnostic tool the debugger is essential for diagnosing and understanding the failure modes of an embedded firmware.

So no, it cannot be eliminated, at least not completely.

Embedded devices interact with real world conditions, so the final testing of an embedded firmware is done under real world and not debugger induced stresses (environmental and application stress testing). There are "software bugs" which are purely the development realm of the software engineer (SE), and "system bugs" which are bugs which occur when the firmware interacts with the real world conditions of its embedded application.


During embedded development the SE in collaboration with the electronic engineer (EE), and mechanical engineer (ME) and project manager (PM) will define the nominal operating conditions and expected function of your firmware feature. In this development activity the debugger and the ICE/SWD device is invaluable as to enable

  • System Monitoring
  • Code Error Detection
  • Controlled Test Condition

Compared to logging, which on an embedded system can have many side effects and complexities, this is a particularly unintrusive way to develop and test with confidence that the nominal conditions are close to real world and all the immediate, "software" bugs are eliminated.


After the firmware is nominally complete, a qualification and test cycle is required. Unlike pure software there is usually a physical real world component to an embedded device and the effectiveness of the associated firmware. The environment can affect thing like

  • Rates of inputs and interrupts
  • Quality of data from external sensors
  • Operating conditions and base error rates of various protocols and I/O interfaces
  • Other environment dependent conditions.

All of this will serve to stress your embedded firmware to a point where all of you side logic and error condition checks will be stress tested. You are in rough seas, so to speak, compared to your development environment...

So an embedded device test cycle will combine

  • Environmental stress (vibration, thermal, electrical)
  • I/O Stress (external protocols and interfaces)
  • Application stress (demanding performance)
  • Any other applicable stress

In order to stress test the system, and in turn, the embedded firmware.

In that context the debugger, in combination with the ICE/SWD is an invaluable diagnostic tool to

  • Understand the nature of a bug or glitch
  • Place blame on the EE for screwing up the hardware
  • Diagnose and monitor the system after a weakness has shown up

So, even here, no, the debugger is an invaluable tool.

Lizenziert unter: CC-BY-SA mit Zuschreibung
scroll top