CMP EMBEDDED.COM

Login | Register     Welcome Guest ESC Boston  esc india  Call for Abstracts
 


The State of the Art of Debuggers

by Jack Ganssle

Download the Debuggers Special Report PDF

Learn to love your debugger— you’re going to spend a lot of time with it.

At the 1998 Embedded Systems Conference, Larry Constantine described the only known way to write totally bug-free programs: find the smartest programmer around, and stick him in an auditorium with a computer and projection screen. Fill the other seats with a hundred or so of the second-best programmers you can find and let them kibitz about the guru’s work.

As long as this option remains unavailable, we’ll continue to write defect-laden code demanding long, painful sessions with our debugging tools. The fact is that, on average, about 5% of the firmware will be just plain wrong, yielding about 50 bugs per 1,000 lines of code. Clearly, we’ve got to ensure our debuggers are powerful assistants and not clunky half-measures.

Most modern debuggers are PC- or Unix-hosted software GUIs divorced from the target-end debugging resources. That is, the debugger may drive an in-circuit emulator, BDM, or ROM monitor. The target-end resource provides basic debug functionality like breakpoints, single-step, memory access, perhaps trace, and the like. The debugger itself, directing all actions from the PC, correlates low-level actions to source code, both when you issue a command (“break on C line 110”) and when something happens in the target (“stopped at 0x1214,” which the debugger will translate to a graphical view of the corresponding source code).

Once, each emulator and ROM monitor vendor provided a custom debugger tuned to the particular needs and capabilities of their target-end device. No longer. Now the debugger tends to be a part of the compiler/linker toolchain; usually it supports a wide range of debug hardware resources. For example, virtually every vendor of target-end tools for the PowerPC and 68K supports SDS’s SingleStep, so you can find emulators, BDMs, ROM monitors, and the like that all have the same user interface.

Having one common front end for a wide range of debug tools is a real benefit to the user. With time-to-market pressures driving development at a frenetic pace, we’re often forced to start debugging code long before the hardware is ready. This means we need full-cycle debug support, from pre-hardware to bringing the prototype boards up to final integration and test. Before there’s a lick of hardware, a simulator—driven by a software debugger—might be the natural development platform. When bringing the boards to life there’s no tool like an ICE—again, driven by a software debugger. Final integration and test might use almost any target-end debugging resource, but given that the front end is always the same debugger, we’re not learning new tools at every phase of the project.

Learning issues pervade the computer field; most of us are fed up with reading manuals and mastering new software. For instance, who has time to master a new CPU’s on-chip peripherals? Some parts have literally hundreds of registers used to configure peripheral operation. Many debuggers now include a complete interactive CPU description that lets you examine and modify the contents of each I/O register using high-level commands, often accompanied with complete English descriptions of the meanings of each bit.

When selecting any development tool, particularly a debugger, first understand what the tool’s impact will be on your target system. Will it require ROM and RAM resources? Many now do. Are other resources needed, like communications ports, extra stack space, and the like?

Real-time trace was once considered an essential feature of any embedded development environment, and, in fact, served to differentiate many embedded tools from the ones used for, say, PC-application development. We’re perhaps in the nadir of development at the moment, with CPUs so complex (due to cache, prefetchers, systems-on-chip, and the like) that trace is now often an impossible luxury.

Over the next few years we’ll see this start to change, as various CPU vendors find ways of bringing limited trace information from the target to the debugger. The good old days of 100+ bit-wide trace buffers are ending, though; most of the solutions now offered or proposed involve sending only partial address information to the PC. The address might come only when a branch takes place or partial address information appears.

Regardless, it’s up to the debugger to take this limited and sometimes incomplete bit of trace data and reconstruct the processor’s execution path, shifting the burden of execution path analysis to the debugger. Today, most debuggers are not up to the task of making sense of the data. This surely will change.

Another trace option is to modify the code itself (for example, Applied Microsystems’ CodeTEST products). Run the source though a pre-processor that seeds a bit of instrumentation into it, and an external hardware unit with associated debugger can capture and reconstruct program flow. Again, this requires very close coupling between the target-end unit and the debugger itself.

Probably the most significant debugger trend in recent years is the addition of RTOS-awareness to most higher-end products. As 32-bit processors come down in price, more complex applications are common, usually requiring an RTOS. The idea is simple: an RTOS is a complex beast, sequencing many different activities concurrently. The debugger, if RTOS-aware, can tell the user what happens when, and allows debugging from the same high level of abstraction used to write the code. Any time we add layers of abstraction to the coding process, we invariably see corresponding needs in the debugging tools. For instance, write in C++ and you expect the tools to include a class browser. No doubt someday we’ll see TCP/IP packet sniffers as a standard part of most debuggers as well (in fact, Hitex optionally includes such network sniffers for their CAN-bus tools).

RTOS-awareness comprises two basic sets of resources: RTOS state knowledge, and tracking of code execution on a thread, rather than on a procedural basis.

The state of the RTOS is represented in its data structures, and includes simple information like the task control block (TCB), which shows the current status of each task—ready, running, suspended, waiting, and so on. Equally important are the contents of the message queues and mailboxes used to pass information between tasks. The debugger, knowing the internal operation of the RTOS, displays these data structures in a high-level format to the user, giving a quick snapshot of the system’s operation.

Thread-specific information is just as important, but is somewhat less intuitive. Remember that a task is a distinct chunk of code that does something concurrently with other tasks. A thread is an instance of a task. That is, the CPU could be running through one task three or four times, all more or less simultaneously. Each instance, of course, has its own data area to keep one from affecting another. A simple example of a multi-threaded task is one that performs the same Fourier transform on many sets of data—it may make little sense to write multiple copies of the math code when a single task, using separate data areas, can handle all of the math requirements.

Conventional debuggers fail miserably in multi-threaded environments. There’s no way to differentiate threads, so the breakpoint you set on one affects all instances of the code. Worse, single-stepping through one thread results in a crazy patchwork quilt of responses as the code stops in seemingly random instances of the task (with data pointers aimed at a different instance each time).

An RTOS-aware debugger lets you specify the thread of interest. Set the breakpoint or single step and you stay locked into that thread, with the displayed data always corresponding to that instance of the code.

Often this capability requires more target-end debugging resources, usually with the addition of a bit of code linked into your own. ROM monitor users have long accepted this requirement, one that usually has little impact on your development except for a slightly increased demand for ROM and RAM.

RTOS-awareness provides a tremendous amount of insight into the structure of the RTOS; as such, debuggers support only commercial operating systems, not homemade ones. No single debugger supports the more than 80 commercial OSes available, so you’ve got to ensure the debugger vendor explicitly supports the RTOS you’re using.

Probably one of the more frustrating parts of choosing a debugger today is finding one that matches the entire selected tool chain. Does the debugger handle your compiler? Your CPU? The specific RTOS you’re using? The target-end debugging tool? Is it compatible with your version control system? If you’re really fond of a particular editor, can you incorporate that into the debugger’s IDE?

A debugger is like that old overstuffed armchair at home. Get one you’re comfortable with, as you’ll spend a lot of time—more than you figure—one-on-one with the tool.

Jack G. Ganssle is a lecturer and consultant on embedded development issues. He conducts seminars on embedded systems and helps companies with their embedded challenges. He has founded two companies specializing in embedded systems. Contact him at jack@ganssle.com.

Download the Debuggers Special Report PDF

Table 1: Debugger Checklist
Language/CPU
Does the debugger support the language and CPUyou’re using? Be sure that it understands your language subset, like EC++, if any
The connection
Does the debugger support the full line of target-end tools you’ll use, like a ROMemulator, ROMmonitor, ICE, BDM, JTAG, and so forth
System impact
Does the debugger require system resources like RAM, ROM, interrupts, or I/O? If so, are such resources available?
RTOS-awareness
If you’re using an RTOS, you owe it to yourself to get a debugger that understands your RTOSimplicitly. Make sure the debugger is aware of the specific RTOSyou’ve selected
Trace
If you need trace, be sure the target-end hardware tool provides such data, and that the debugger understands how to reconstruct corresponding program flow

Embedded.com Career Center
Ready to take that job and shove it?
SEARCH JOBS

Browse all jobs

SPONSOR
RECENT JOB POSTINGS




 :