Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
MichaelMcTernan
PostPosted: Jul 22, 2007 - 12:34 AM
Newbie


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.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
kmr
PostPosted: Jul 29, 2007 - 10:31 PM
Raving lunatic


Joined: Apr 01, 2004
Posts: 3812
Location: New Mexico

Nice idea and clean implementation. Thanks for posting.

_________________
Kevin Rosenberg
http://b9.com
http://kevin.hypershots.com
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
MichaelMcTernan
PostPosted: Jul 31, 2007 - 06:50 PM
Newbie


Joined: Jul 22, 2007
Posts: 17
Location: London, UK

Thanks for the kind comment Smile
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
akhi3030
PostPosted: Sep 18, 2007 - 02:14 AM
Newbie


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.
 
 View user's profile Send private message  
Reply with quote Back to top
Geoff
PostPosted: Sep 19, 2007 - 03:56 AM
Hangaround


Joined: Apr 24, 2006
Posts: 285
Location: Sydney, Australia

great idea, saved a copy in case I ever need it! Smile

(hopefully you put it in the avrfreaks projects section too)
 
 View user's profile Send private message  
Reply with quote Back to top
MaxK
PostPosted: Oct 07, 2007 - 01:41 PM
Hangaround


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
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
skotti
PostPosted: Nov 28, 2007 - 05:33 PM
Hangaround


Joined: Sep 10, 2003
Posts: 334
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
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Nov 28, 2007 - 06:08 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71149
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.

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
rubixcubix
PostPosted: Nov 29, 2007 - 11:03 PM
Hangaround


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!
 
 View user's profile Send private message  
Reply with quote Back to top
schickb
PostPosted: Dec 10, 2007 - 08:42 PM
Hangaround


Joined: May 07, 2007
Posts: 291
Location: Seattle

Thanks for the post. Very useful!

_________________
-Brad
 
 View user's profile Send private message  
Reply with quote Back to top
MichaelMcTernan
PostPosted: Jun 14, 2008 - 09:03 PM
Newbie


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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
dolphin@gmx.com
PostPosted: Oct 23, 2010 - 06:59 AM
Wannabe


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:
 
 View user's profile Send private message  
Reply with quote Back to top
ArnoldB
PostPosted: Oct 23, 2010 - 08:51 AM
Raving lunatic


Joined: Nov 29, 2007
Posts: 3219


Duplicate posting http://www.avrfreaks.net/index.php?name ... 158#757158

_________________
Stealing Proteus doesn't make you an engineer.
 
 View user's profile Send private message  
Reply with quote Back to top
mapelec
PostPosted: Jan 01, 2011 - 03:10 PM
Wannabe


Joined: Jul 23, 2004
Posts: 71


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.
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Jan 01, 2011 - 05:17 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71149
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.

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
mapelec
PostPosted: Jan 01, 2011 - 10:03 PM
Wannabe


Joined: Jul 23, 2004
Posts: 71


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.
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Jan 02, 2011 - 09:57 AM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71149
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)

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
mapelec
PostPosted: Jan 02, 2011 - 10:47 AM
Wannabe


Joined: Jul 23, 2004
Posts: 71


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.
 
 View user's profile Send private message  
Reply with quote Back to top
lutecki
PostPosted: Dec 10, 2011 - 03:22 PM
Newbie


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.
 
 View user's profile Send private message  
Reply with quote Back to top
mojo-chan
PostPosted: Oct 03, 2012 - 10:59 AM
Resident


Joined: Jan 24, 2008
Posts: 975


_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.
 
 View user's profile Send private message  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits