United Business Media EE Times


Search

HOMEMARKET INTELLIGENCE UNITFORUMSDESIGNNEW PRODUCTSCAREERSBLOGSCONTACTEVENTSSIGN UP!RSSMost Popular contentTrusted Sources

 


Simplicity key to taming memory bugs
Print this article Email this article Reprints RSS Digital Edition

EE Times


An effective way to combat nasty memory bugs is to keep things simple. But there's more than one way to pursue simplicity.

In the multitude of software bugs, memory bugs are often the worst. They manifest themselves as corrupted data or instructions due to the erroneous action of some bug in a piece of code. Such effects appear at seemingly random instances in time and place and are nearly impossible to catch with traditional direct debugging techniques. However, with some of the techniques described below, developers often can prevent them and more easily find and fix the ones that sneak through.

In embedded systems bugs can be categorized according to the type of memory affected, and generally fall into three categories: global memory bugs, stack memory bugs and bugs associated with dynamically allocated memory.

Let's assume that the run-time environment does not have any hardware memory protection facilities (memory-management unit, or MMU). If your target has such capabilities, they can certainly be put to good use-most commonly to protect the program's data and instructions from erroneous data writes. However, you might be able to utilize these facilities to help eliminate some of the bugs described here.

Whether or not your hardware offers such memory protection facilities, it still might be beneficial to follow the "Kiss"-keep it simple, stupid-principle. In terms of software development, this means avoiding overly complex approaches in favor of those that are easy to follow and debug. Some protection approaches, including full process-model virtual memory systems, add a large amount of execution overhead and code size to the application. They also tend to have a more complex programming model and are more difficult to use and understand. These burdens remain even after all the bugs have been found and the application is running correctly. It might be better to design in a more efficient operating system and to use a simpler programming model than to pay the usability, performance and cost penalties for so-called advanced features you may not really need.

The following debugging ideas can be employed in simple systems, without leaving a legacy of sluggish performance and bloated code size long after the bugs are gone. Global memory bugs are those that result in corruption of global memory data areas, which include global and static C data structure areas as well as other physical memory areas defined by the application.

Global bugs
With global memory bugs, the load map produced by your linker is very important-it's nice to have one load map sorted alphabetically by symbol name and another by physical address. Having these load maps in hand, we are now ready to examine several techniques for finding global bugs.

As a first step, see if the data structures preceding the corrupted area are valid or have grown past their allocated size. Global memory corruption is often caused by abnormal growth of preceding data structures. Be sure to also examine the bad data. This might very well be something recognizable and lead directly to the bug. For example, if you notice that some numeric data structure is corrupted by ASCII information, the bug might be something like the destination's not being large enough in a string copy.

Checkpoints involve checking the corrupted memory area at various points in the program. The goal is to find the bug in a binary search fashion. Examples of checkpoints might include traditional breakpoints, printfs or even dynamically collecting samples in some type of circular memory buffer during run-time. Data fences are basically just identifiable data patterns placed around select data structures in the global memory area.

The example code segment below contains two data fences. If any fence values change, some type of memory corruption has taken place. Any change in Top_Fence or Bottom_Fence indicates a memory corruption problem.

unsigned int Top_Fence = 0x1234;

/* Sample data structure ABC */

struct ABC

{

int a;

long b;

float c;

} Example_Structure;

unsigned int Bottom_Fence = 0x5678;

In some cases it might be helpful to initialize the entire global memory area with a recognizable data pattern (0xFEFE is often a good choice) before the program executes. After the bug hits, the global memory area can be examined for change. Any area that does not contain the preset data pattern was corrupted.

Stack bugs
Stack memory bugs are typically some of the hardest to eliminate and are also the most likely to cause the program execution to go completely crazy-and there's nothing crazier than corrupting a return address on the stack. This type of bug is especially tricky because the problems are often a function of external events and the current state of the stack, so the program might not die at exactly the same place or even the same way each time.

Each thread in the application should have enough stack area to account for maximum function-call nesting, local variable allocation and nested interrupt context saving. In processor architectures that switch to an interrupt stack on the first interrupt, space for only one interrupt context save is required. This also holds true for applications running under real-time kernels that switch stacks on the initial interrupt.

There is a relatively easy way to empirically determine the proper stack size of each application thread. Early in initialization, fill each thread's stack with some data pattern like 0xFEFE. After very thorough application testing, each thread's stack area may be examined to determine its maximum stack growth. The amount of stack used is calculated by finding the first intact data pattern from the beginning of the stack.

Typically, stack overflow results in corruption of memory adjacent to the stack (usually memory prior to the actual stack memory because most stacks grow to lower addresses). The previously mentioned method of filling the stack with a constant data pattern is useful in detecting this type of out-of-bounds stack growth. Alternatively, data pattern fences may be constructed around the stack area in an attempt to detect stack overflows or under flows.

If stack corruption is occurring in a stack of sufficient size, chances are the bug involves some type of pointer problem. Most likely a pointer to a local variable on the stack is going out-of-bounds or a pointer to a local variable is being used after that variable is out of scope.

Writing past the boundaries of an allocated memory block or using one that is no longer allocated are typical bugs. Dynamically allocated memory also suffers from a type of memory bug called a memory leak, which is defined as a gradual loss of available memory blocks over time. This is caused by the application's somehow not releasing memory blocks that are no longer needed.

Some of the bugs are vulnerable to checkpoints and code analysis, while others may require additional software debug code.

A simple technique to improve the dynamic memory debug process is to encapsulate all allocated memory blocks within a debug structure. This makes it possible to keep track of certain facts about the allocation of each memory block and its usage.

William Lamie is the chief executive officer and founder of Express Logic Inc. (San Diego).






  Free Subscription to EE Times
First Name Last Name
Company Name Title
Email address
  Click here for your Free Subscription to EETimes Europe
 
CAREER CENTER
Looking for a new job?
SEARCH JOBS
SPONSOR

RECENT JOB POSTINGS
CAREER NEWS
SRC Expands R&D Centers
The Semiconductor Research Corp has added a new center to its university R&D efforts.

For more great jobs, career related news, features and services, please visit EETimes' Career Center.


All White Papers »   

 
Education and
Learning


Learn Now:












Home | About | Editorial Calendar | Feedback | Subscriptions | Newsletter | Media Kit | Contact | Reprints|  RSS|   Digital|  Mobile
Network Websites
International
Network Features




All materials on this site Copyright © 2009 TechInsights, a Division of United Business Media LLC All rights reserved.
Privacy Statement | Terms of Service | About