Debug to Release causes different application behavior
Hi,
I'm trying to understand how memory is allocated in the Release configuration vs. Debug and how to debug memory corruption issues that only appear in Release.
I'm coding for the STM32F4 using CrossStudio 2.3.3
I've reviewed Paul Curtis' helpful hints at the following links:
- https://rowley.zendesk.com/entries/46174-Changing-build-configuration-from-Debug-to-Release-causes-different-application-behaviour
- https://rowley.zendesk.com/forums/51134/entries/46195
I have copious amount of memory allocated to stack: 2048 to Main; 1024 to Heap, and 512 to Process (in both Release and Debug)
If I make the stupid mistake of assigning a larger argument for string size to function snprintf than is allocated to the string, I run into memory corruption and undefined behavior. That much I would expect. But, I would like to know why the behavior only surfaces in Release and not in Debug.
An example would be the following:
<code>
static uint8_t Percent_Text[6] = {' '}
void Update_WastePercentage(uint32_t Percent, uint8_t UpdatePercentage)
{
if (RollOver == NO) {
if (Percent == 0) {
strncpy(Percent_Text, " 0", 10);
}
else if (Percent == 1000) {
strncpy(Percent_Text, " 100", 10);
}
else {
snprintf(Percent_Text, 10, "%2d.%d", Percent/10, Percent%10);
}
}
else {
strncpy(Percent_Text, " ?", 10);
}
LCD_DisplayChangedString(216, 95, 4, Percent_Text, Percent_Text_Prev, UpdatePercentage);
}
</code>
Now, this code executes as expected in Debug. In Release, however, the string in "Percent_Text", although correct numerically, is output in a different color on the LCD, but only where the strncpy function was used. That is to say, if the percent is "0" or "100" or has "?", it is not output to the LCD in the predestinated color (white), but in black. I'm using global variables g_InfoTextColor and g_InfoBackColor. In this situation, I assume memory allocated to the LCD color variable is being overwritten by this function, because although I have told strncpy to verify that no more than 10 characters be included in the string, the compiler has only allocated 6. And, the LCD color variable resides right next to it in memory is my guess (or has not been defined at this point).
Additionally, one will note that I don't need to use strncpy for the above examples, as the string length is clearly defined. However, this behavior does not always surface, I assume, because memory is allocated in different locations and not all are initialized the same way.
Now, if I change the size argument in the strncpy function to 6 in the above example or change strncpy to strcpy, the problem goes away. Again, why does this behavior only surface in Release and not in Debug.
What is the strncpy function and Release property doing that creates this behavior? Additionally, I can't debug this. How can it be debugged when it only appears in Release and all the Release options are changed to match Debug (or vise versa)?
kind regards,
MAJ
EDIT: Is there a code or block tag we can use?
-
You're correct that the difference in behaviour (a nod to the UK side of the pond ;-) is almost certainly a side effect of the changed memory layout in the debug as compared to the release configuration. One way to get at it would be to return to the dark ages before JTAG and add a small routine that sends dumps of suspect variables or memory locations out through a serial port*. Of course, adding such a routine alters the program image so it is necessarily not as clean as JTAG but sometimes it's all we have.
As regards strncpy(s1, s2, n): This doesn't verify that no more than n characters are written to s1. Rather, it always writes exactly n characters to s1. If s2 is shorter than n then after copying the terminal null character from s2 it will append additional null characters to s1 until a combined total of n characters have been written. With Percent_Text an array [6] of uint8_t, an n of 10 will always write past the declared end of the array. Gimpel lint will catch this. I suspect that CrossWorks can as well, perhaps with -Wall?
* Or SPI or GPIO, etc. I recall once debugging a PIC14 app by dumping floating point values from a single output pin and then reading them with an o'scope. It's a wonder that I'm still (mostly) sane.
Please sign in to leave a comment.
Comments
1 comment