| Author |
Message |
|
|
Posted: Jul 22, 2007 - 12:34 AM |
|

Joined: Jul 22, 2007
Posts: 17
Location: London, UK
|
|
Hi folks,
This is my first ever posting here - I hope it is useful!
A common problem is wanting to know how much stack space a program uses. This can reveal how much valuable RAM is can be used for static variables, or whether the stack is growing too large and corrupting program data.
With AVR gcc, two symbols are defined by the linker that can make this easy. These are _end and __stack which define the first free byte of SRAM after program variables, and the starting address of the stack, respectively.
The stack starts at __stack, which is conventionally the highest byte of SRAM, and grows towards zero; _end will be somewhere between zero and __stack. If the stack ever falls below _end, it has almost certainly corrupted program data.
The following C declarations gain access to these linker symbols:
Code:
extern uint8_t _end;
extern uint8_t __stack;
Taking the address of these symbols (e.g. &_end) gives the memory addresses that bound the stack. The following function therefore 'paints' the stack with a known value:
Code:
void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));
void StackPaint(void)
{
#if 0
uint8_t *p = &_end;
while(p <= &__stack)
{
*p = STACK_CANARY;
p++;
}
#else
__asm volatile (" ldi r30,lo8(_end)\n"
" ldi r31,hi8(_end)\n"
" ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */
" ldi r25,hi8(__stack)\n"
" rjmp .cmp\n"
".loop:\n"
" st Z+,r24\n"
".cmp:\n"
" cpi r30,lo8(__stack)\n"
" cpc r31,r25\n"
" brlo .loop\n"
" breq .loop"::);
#endif
}
This is declared in such a way that AVR-libc will execute the assembly before the program has started running or configured the stack. It also runs at a point before some of the normal runtime setup, hence assembly should be used as C maynot be fully reliable (this is discussed in the AVR libc manual).
The function itself simply fills the stack with 0xc5, the idea being that stack usage will overwrite this with some other value, hence making stack usage detectable.
Finally the following function can be used to count how many bytes of stack have not been overwritten:
Code:
uint16_t StackCount(void)
{
const uint8_t *p = &_end;
uint16_t c = 0;
while(*p == STACK_CANARY && p <= &__stack)
{
p++;
c++;
}
return c;
}
This function can be called at any time to check how much stack space has never been over written. If it returns 0, you are probably in trouble as all the stack has been used, most likely destroying some program variables. |
|
|
| |
|
|
|
|
|
Posted: Jul 29, 2007 - 10:31 PM |
|

Joined: Apr 01, 2004
Posts: 3812
Location: New Mexico
|
|
|
|
|
|
|
Posted: Jul 31, 2007 - 06:50 PM |
|

Joined: Jul 22, 2007
Posts: 17
Location: London, UK
|
|
Thanks for the kind comment  |
|
|
| |
|
|
|
|
|
Posted: Sep 18, 2007 - 02:14 AM |
|

Joined: Sep 18, 2007
Posts: 1
|
|
Thanks for the great post.
I was interested in monitoring the stack size of a subset of functions in my application.
Is there anyway I can paint the stack only up to the current stack pointer?
So that I can paint it right before calling the function I want to monitor and then doing the count after calling the function. |
|
|
| |
|
|
|
|
|
Posted: Sep 19, 2007 - 03:56 AM |
|

Joined: Apr 24, 2006
Posts: 283
Location: Sydney, Australia
|
|
great idea, saved a copy in case I ever need it!
(hopefully you put it in the avrfreaks projects section too) |
|
|
| |
|
|
|
|
|
Posted: Oct 07, 2007 - 01:41 PM |
|


Joined: Jul 25, 2001
Posts: 322
Location: Few feet away from a grunting watercooled ATMEGA aware of imminant detonation.
|
|
The code may be really usefull as long no use is made from Malloc
pre malloc free stack: 3469 bytes
post malloc free stack: 0 bytes
Code:
printf_P(PSTR("pre malloc free stack: %u bytes\r\n"), StackCount());
//
// Allocate memory for the volume
//
Volume = malloc(sizeof(s_VOLUME));
if (Volume != NULL)
{
printf_P(PSTR("post malloc free stack: %u bytes\r\n"), StackCount());
|
_________________ MY MICROCONTROLLER CAN BEAT THE HELL OUT OF YOUR MICROCONTROLLER /ATMEL
|
| |
|
|
|
|
|
Posted: Nov 28, 2007 - 05:33 PM |
|

Joined: Sep 10, 2003
Posts: 183
Location: 51,25° / Germany
|
|
Quite interesting!
But is it correct, that _end and __stack are uint8_t?
The bigger controllers have more than 256 bytes internal RAM.
Michael |
|
|
| |
|
|
|
|
|
Posted: Nov 28, 2007 - 06:08 PM |
|


Joined: Jul 18, 2005
Posts: 62246
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
| But he's not using what they contain (as they don't "contain" anything) but the addresses where they are pointing. |
_________________
|
| |
|
|
|
|
|
Posted: Nov 29, 2007 - 11:03 PM |
|


Joined: Nov 08, 2007
Posts: 103
Location: Eureka, CA
|
|
| Sweet! I will definately use this when trying to optimize. Every little bit helps. I mean I guess I could program asm style and be super efficient, but this could at least show me any gross C memory mismanagement. Thanks bro! |
|
|
| |
|
|
|
|
|
Posted: Dec 10, 2007 - 08:42 PM |
|


Joined: May 07, 2007
Posts: 291
Location: Seattle
|
|
| Thanks for the post. Very useful! |
_________________ -Brad
|
| |
|
|
|
|
|
Posted: Jun 14, 2008 - 09:03 PM |
|

Joined: Jul 22, 2007
Posts: 17
Location: London, UK
|
|
|
MaxK wrote:
The code may be really usefull as long no use is made from Malloc
pre malloc free stack: 3469 bytes
post malloc free stack: 0 bytes
Yup - that is a short coming of the code. As illustrated in the AVR libc manual, the malloc arena starts at __bss_end: http://www.nongnu.org/avr-libc/user-manual/malloc.html
If you really need malloc, you could use the malloc tunables to setup the stack painting to avoid the malloc arena - probably not too difficult to do.
Regards,
Mike |
|
|
| |
|
|
|
|
|
Posted: Oct 23, 2010 - 06:59 AM |
|

Joined: Oct 09, 2010
Posts: 74
|
|
Could you please tell me what's going wrong?My stack usage is returning 5 bytes. I am using atmega169. I am filling my ram (0x100 to 0x4ff) with 0xaa value. Then I calculate my stack usuage. I have to find 3 consecutive occurrence of 0xaa values, so I can confirm that I reached the end of the stack. Is there any logical problem in finding 3 consecutive occurrence of 0xaa values in my implementation??Any help??
My asm:
#define MAX_MEM_ADDR 0x4FF
#define RAM_PATTERN ((unsigned char)0xAA)
/* The minimum number of times this pattern must occur consecutively in memory */
#define RAM_MIN_PATTERN 3
#define RAMSTART 0x0100
Code:
LDI R27, 0X04 ;upper ram address high byte
ldI R26,0xff ;upper ram address low byte
ldi R25,0X01 ;lower ram address high byte
ldi R24,0X00 ;lower ram address low byte
LDI R16,0xAA ; VALUE TO BE COPIED
loop:
st x,r16 ; STORE 0XAA VALUE IN RAM LOCATION
sbiw r26,1 ;DECREMENT THE RAM ADDRESS
cp r26,r24 ;COMPARE THE LOW BYTES OF THE RAM END AND STARTING ADRDRESS
cpc r27,r25 ;COMPARE THE HIGH BYTES OF THE RAM END AND STARTING ADRDRESS THROUGH THE CARRY
brne loop ; loop continues untill it becomes equal
int GetStackByteCount(void)
{
int StackCount = 0;
unsigned char consecutive = 0;
unsigned char *p = NULL;
unsigned char *temp = NULL;
for ((p = (unsigned char *)MAX_MEM_ADDR); (p >= (unsigned char *)RAMSTART); --p )
{
if( ((*p) == RAM_PATTERN) )
{
break;
}
}
while( ( (p >= (unsigned char *)RAMSTART) && (p <= (unsigned char *)MAX_MEM_ADDR) ) )
{
temp = p;
if (((*temp) == RAM_PATTERN) )
{
StackCount++;
consecutive++;
if( ((*(temp + 1)) == RAM_PATTERN) )
{
StackCount++;
consecutive++;
if( ((*(temp -1)) == RAM_PATTERN) )
{
StackCount++;
consecutive++ ;
}
else
{
--p;
consecutive = 0;
}
}
else
{
--p;
consecutive = 0;
}
}
else
{
--p;
consecutive = 0;
}
if(consecutive == RAM_MIN_PATTERN)
{
break;
}
}
return StackCount;
}
MichaelMcTernan wrote:
Hi folks,
This is my first ever posting here - I hope it is useful!
A common problem is wanting to know how much stack space a program uses. This can reveal how much valuable RAM is can be used for static variables, or whether the stack is growing too large and corrupting program data.
With AVR gcc, two symbols are defined by the linker that can make this easy. These are _end and __stack which define the first free byte of SRAM after program variables, and the starting address of the stack, respectively.
The stack starts at __stack, which is conventionally the highest byte of SRAM, and grows towards zero; _end will be somewhere between zero and __stack. If the stack ever falls below _end, it has almost certainly corrupted program data.
The following C declarations gain access to these linker symbols:
Code:
extern uint8_t _end;
extern uint8_t __stack;
Taking the address of these symbols (e.g. &_end) gives the memory addresses that bound the stack. The following function therefore 'paints' the stack with a known value:
Code:
void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));
void StackPaint(void)
{
#if 0
uint8_t *p = &_end;
while(p <= &__stack)
{
*p = STACK_CANARY;
p++;
}
#else
__asm volatile (" ldi r30,lo8(_end)\n"
" ldi r31,hi8(_end)\n"
" ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */
" ldi r25,hi8(__stack)\n"
" rjmp .cmp\n"
".loop:\n"
" st Z+,r24\n"
".cmp:\n"
" cpi r30,lo8(__stack)\n"
" cpc r31,r25\n"
" brlo .loop\n"
" breq .loop"::);
#endif
}
This is declared in such a way that AVR-libc will execute the assembly before the program has started running or configured the stack. It also runs at a point before some of the normal runtime setup, hence assembly should be used as C maynot be fully reliable (this is discussed in the AVR libc manual).
The function itself simply fills the stack with 0xc5, the idea being that stack usage will overwrite this with some other value, hence making stack usage detectable.
Finally the following function can be used to count how many bytes of stack have not been overwritten:
Code:
uint16_t StackCount(void)
{
const uint8_t *p = &_end;
uint16_t c = 0;
while(*p == STACK_CANARY && p <= &__stack)
{
p++;
c++;
}
return c;
}
This function can be called at any time to check how much stack space has never been over written. If it returns 0, you are probably in trouble as all the stack has been used, most likely destroying some program variables.
Code:
|
|
|
| |
|
|
|
|
|
Posted: Oct 23, 2010 - 08:51 AM |
|

Joined: Nov 29, 2007
Posts: 3219
|
|
|
|
|
|
|
Posted: Jan 01, 2011 - 03:10 PM |
|

Joined: Jul 23, 2004
Posts: 64
|
|
Thanks for this tutorial! Can this be used as it is for xmegas, or if not what needs to change? This is going to be very useful in my app as I need as much free SRAM as I can get for other buffers.
Mark. |
|
|
| |
|
|
|
|
|
Posted: Jan 01, 2011 - 05:17 PM |
|


Joined: Jul 18, 2005
Posts: 62246
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
| The stack works the same in xmega as mega and tiny so the same principles apply but this is only a technique for monitoring usage, it won't magically free up SRAM - you do that by moving data to flash and EEPROM and reusing RAM when you can. |
_________________
|
| |
|
|
|
|
|
Posted: Jan 01, 2011 - 10:03 PM |
|

Joined: Jul 23, 2004
Posts: 64
|
|
I understand the principles are the same, but does the code posted work unaltered for xmega parts? If not, what needs to change in the code?
I will use this to monitor and measure stack usage, add a little contigency just to be sure and set the stack accordingly. Of course I'll use the standard techniques to reduce SRAM usage. |
|
|
| |
|
|
|
|
|
Posted: Jan 02, 2011 - 09:57 AM |
|


Joined: Jul 18, 2005
Posts: 62246
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
| What do you mean by "set the stack"? GCC does that for you setting it to RAMEND the only requirement on your part is to ensure it never descends as low as the end of .bss ( or .noinit if you use that) |
_________________
|
| |
|
|
|
|
|
Posted: Jan 02, 2011 - 10:47 AM |
|

Joined: Jul 23, 2004
Posts: 64
|
|
|
clawson wrote:
What do you mean by "set the stack"? GCC does that for you setting it to RAMEND the only requirement on your part is to ensure it never descends as low as the end of .bss ( or .noinit if you use that)
Yes, my bad. This is actually an LED display project and the more frame buffers I can allocate the better the system performance, so by 'set the stack' I actually meant make sure I don't allocate too many buffers such that the stack would over-write a buffer. The stack pointer would move down from the top of memory and my buffers would be allocated from the bottom up. I don't use the heap or malloc().
Mark. |
|
|
| |
|
|
|
|
|
Posted: Dec 10, 2011 - 03:22 PM |
|

Joined: Feb 03, 2003
Posts: 5
Location: Poland
|
|
Nice work, I've pasted it into my project and it worked without a glitch. I'm trying to find out if my stack overflows due to nested interrupts. Hope this will help.
Kind regards. |
|
|
| |
|
|
|
|
|
Posted: Oct 03, 2012 - 10:59 AM |
|

Joined: Jan 24, 2008
Posts: 515
|
|
_end should be __bss_end, according to http://www.nongnu.org/avr-libc/user-manual/malloc.html. I found that in my code it only produced meaningful results if I used __bss_end. I am using an XMEGA if it makes any difference, I have not tested it with MEGA/TINY. I don't use any memory allocation functions.
Thanks for this really useful bit of code. |
|
|
| |
|
|
|
|
|