Who needs a debugger?
Although embedded software developers would like to think that they spend the bulk of their time designing software and coding it, that is not the case. Almost everyone actually spends more time getting the code to function correctly - a process that we call "debugging". Different people have different ideas about what debugging actually entails and, with the advent of multicore designs, new challenges are presented. This article reviews debug approaches and technology and considers what is likely in the future.
What do you do?
If you met someone in a bar, say, and you got talking, they are likely to ask what you do. You will probably reply that you are an embedded software developer. (Actually, in my experience, this can be something of a conversation killer. You might be better saying you are an airline pilot or a brain surgeon.) If they carry on talking with you, they might ask you what that job entails and you would probably answer with something about designing software and writing C/C++ code. Of course, that is not really the case. Most embedded software developers spend the bulk of their time debugging.
This does beg the question: what does debugging actually mean? The answer is less simple than you might expect.
Debugging by hand
Many years ago, when computers were big and expensive, there were no debugging tools that we would recognize today. Frankly, programmers made fewer errors. One reason for this was that time on the computer was precious, so the programmer would write the code down on paper first, thinking it through very thoroughly. They would very carefully proof read to avoid syntax errors, as just one slip could waste a computer run. Then they would think through the operation of the code, dry running it on paper to check the logic. Eventually, this carefully scrutinized code would be submitted to the computer. After the run, the results would be studied and, if an error was detected, the code would be modified to include extra printf() (or equivalent) lines to show how values were changing. By using conditional compilation, "debug" and "production" versions of code could be compiled.
So, why not take this approach when writing embedded software today? The first part - writing the code on paper and thinking a lot - might be a very good practice. However, the ready access to computers that cost almost nothing has moved us away from such an approach. The practice of adding printf() calls to log values is still amazingly common, despite it being quite problematic for embedded applications. There are five key issues:
- Most implementations of printf() are quite large. The function needs to address a very wide range of formatting situations and that takes a lot of code. This does not matter in a desktop software context, but memory is rarely in abundance in an embedded system.
- There is the question of where the output of printf() actually goes. Many embedded systems have nothing that looks like a "console" and directing output back to a host computer can be challenging.
- In a multi-threading context - i.e. when using an RTOS - reentrancy is a concern. If two threads try to use printf() at the same time, confusion can result.
- Every time you want to change the variables you are looking at, a complete rebuild and download of the code is required.
- Lastly, of course, formatting and sending the output (somewhere) takes time. If it is a real time application, this overhead can introduce problems.