This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

How to tell how much stack is being used?

Other Parts Discussed in Thread: MSP430F1232, MSP430F5438

Hi,

I am running code that I suspect is overflowing the stack.  I know how much space I have allocated for the stack, however, I'm not sure how to find out how much is demanded by my code.  I am using a sprintf function that I am sure is the culprit, I just need to find out how much stack space it needs...

Please let me know if you have any insight!

Thanks

  • Hi Dustin,

    One common approach is to initialize all of your allocated stack space with "F00D" or "DEAD" or some other fun word.

    Let your system run long enough to experience all possible conditions and permutations of external stimulus.  (OK, that isn't usually possible but you get the point.)

    Then stop and see the "high water mark" where your clever pattern is broken.

    Jeff

  • Dustin,

    What is the tool chain you are using IAR or CCS or...?

    IAR for example provides an option of viewing the stack. Once the project is in debug mode you can use View --> Stack to see a listing of how 'deep' the stack is in your application in real time.

    Also printf statements do tend to cause a stack overflow so I would recommend increasing the size of the stack manually in the project options.

    Regards,

    Priya

  • Dustin Lane said:
    I know how much space I have allocated for the stack

    Unlike some (few) other processors with dedicated stack segments, the MSP has only one stack size: all available ram.
    There is no way to limit how much ram the stack uses. If the code requires additional space on the stack, the stack will grow, overwriting everything on its path, until it grows into the void (read-only or vacant memory areas) and cases a system crash (if it hasn't crashed the system by overwriting all variables before).

    Any 'reserved stack space' setting has only the effect to generate a linker error if the program uses so much memory for static variables that there is less ram left than the 'reserved stack space' setting. This check does not include any dynamically allocated memory (with malloc). And it is only a check. It does not prevent the stack from growing and does not generatey any code that checks or limits the stack size.

    The only way to know how much (or whether too much) space the stack requires is the beforementioned algorithm with a guard value. If you init all free ram with this value, run your code, then check the ram for the guard value still there, you'll know how much space is still available between the peak stack size during the program run and the bottom of the heap (the space behind your static variables).
    And depending on the timings, stack usage in interrupt functions or even interrupt nesting etc, this size may even vary between two tests.

    If the test, however, reveals, that at the start of the heap the guard word has been overwritten, but later it appears again, chances are that your problem is not stack related but the code is writing to an array out-of-bounds.

    Dustin Lane said:
    I am using a sprintf function

    The sprintf function is really a stack space eater. But sometimes, compiler optimizations can make things even worse. On the msp430f1232 (256 bytes ram) with the mspgcc compiler I noticed that the compiler does not remove the parameters of subsequent sprintf calls from the stack. It optimized the code so the stack will be freed only at the end of a code block (loop, if, function return etc.), removing all parametzers of all sprintf calls from the stack alltogether with one single instruction. While this was reducing code size and increasing speed, it was also greatly increasing the stack usage. The solution was to put each sprintf call into a do{}while(0); block.
    Also, each parameter used for printf increases the stack usage while processing it. It might help to split the sprintf statements, passing the result of the previous call as a string parameter to the next. It increases code size and slighly slows down the code, but saves much stack space.

  • I have a project with 17 applications, each application has 8 build configurations in IAR and 8 build configuration in CCS. All applications are written in C, and a large set of the C code is the same for all of the applications. None of the applications use the heap. Is there any way I can write some C code to analyze the stack usage that can be used without modification for each application? What I am thinking is, if the compiler defines a variable I can use that marks the top of the stack (e.g. 0xFFFFF), and the first address after my static variables, then I can just write a loop to initialize that area with something like "F00D" or "DEAD" as Jeff mentioned.

  • Here's one way for IAR:

       uint16* fencePtr = (uint16*) (((int) __segment_end("DATA16_N") + 1) & 0xFFFE);

    You would need to substitute the "DATA16_N" with whatever you locate right before the stack (see the linker configuration file).  I would guess "DATA16_N" is probably right for you.  You could start writing the fill data at <fencePtr>.

    Similarly, you could get the top of the stack the same way (a little easier actually) using:

       uint16* stackTopPtr = (uint16*) __segment_end("CSTACK");

    When "filling" the stack area, be careful not to write too close to the top -- even in low_level_init( ) some of the stack is being used already.  Or you could do the "filling" in a custom reset handler before passing control to __program_start( ).

    As for CCS, I can guess it would similar code to get located addresses from the linker during the build.

    Jeff

    Good luck!

  • Thanks for the suggestion Jeff. I'm not using a link directive/link configuration file so I can't refer to section names from it. However, I looked at the IAR-generated map file and find this snippet (the target is an MSP430F5438):

    DATA16_Z                               1CB2 - 3647            1996   rel    1
    CSTACK                                 5750 - 5BFF             4B0   rel    1
    DATA16_C                               5C00 - 76CA            1ACB   rel    1

    So I wrote the following function:

    void InitStackBTPS_Init(void)
    {
       uint16_t *StackStartAddress;
       uint16_t *StackStopAddress;
       uint16_t *CurrentAddress;
       
       StackStartAddress = (Word_t *)((((unsigned int)(&StackStopAddress)) - 50) & 0xFFFE);
       StackStopAddress  = (Word_t *)(((unsigned int)__segment_end("DATA16_Z") + 1) & 0xFFFE);
       
       for(CurrentAddress = StackStartAddress; CurrentAddress >= StackStopAddress; CurrentAddress--)
          *CurrentAddress = 0xAA55;
    }

    How's my logic? Regardless though, I'm getting the following error:

    "Error[Pa053]: segment has not been declared using #pragma segment/section."

    Can this error be resolved without a linker configuration file?

  • I'm going to assume that you are using the default linker configuration file.  If you don't specify your own, IAR assumes you want to use the default.  You can find the default file here:

    C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.0\430\config\linker\

    by searching for the file named to match the specific MCU your IAR project configuration uses.

    Your logic seems reasonable.

    Yes you need to declare the segment with this:

    // declare segment DATA16_N, and use "__data16" explicitly in case project uses large data model
    #pragma segment = "DATA16_N" __data16 
    

    Instead of "DATA16_N" you want to pick the last segment you might use in the list of segments in the Z(DATA) lines in the linker config.  From your map file it appears DATA16_Z will work for you for now, but if you ever add "no-init" data (DATA16_N) or thread-local storage (TLS16_I) or RAM functions (CODE_I) those may link after DATA16_Z and your code would fail.  But you know your project and using "DATA16_Z" might be best.

    Jeff

  • This is what I’m always use in CCS;

    	extern unsigned int _stack;
    	extern unsigned int __STACK_END;
    	unsigned int* p;
    
    	p = &_stack;
    	while (p < (&__STACK_END -3))
    	{
    		*p = 0xA5A5;
    		p++;
    	}
    

  • TI Support said:

    Thanks for the suggestion Jeff. I'm not using a link directive/link configuration file so I can't refer to section names from it. However, I looked at the IAR-generated map file and find this snippet (the target is an MSP430F5438):

    "Error[Pa053]: segment has not been declared using #pragma segment/section."

    Can this error be resolved without a linker configuration file?

    When you are using the IAR tools you always have a linker configuration file, if you don't provide one the tools will do it for you. You can get rid of the error by using "#pragma segment", just like the error message says, see my code below.

    The code looks a little bit strange, taking the address of StackStopAddress seems a bit wrong...

    Anyway, I put together the following. It will fit the end of the stack with 0xAA55:s. If you want the area right after the stack (rather than the end of the stack), you can still use this code and simply increase the stack size while checking. That way you don't accidentally overwrite any other data available. Also, it includes a safeguard so that it doesn't overwrite it's own return address in case you have a really small stack (assuming you call it with a relatively shallow stack).

    #include <stdint.h>
    #pragma segment="CSTACK"
    
    // Max number of 16-bit entries to fill.
    #define ENTRIES_TO_FILL 50
    
    // In case we have a very small stack, ensure that at the top of the
    // stack isn't overwritten.
    #define SAFE_ZONE 10
    
    
    // Higher addresses
    //
    //          <- stack_end
    //  ------  <- SP
    //  |    |
    //  |    |
    //  | .. |  <- fill here
    //  ------
    //
    //  Lower addresses
    
    void InitStackBTPS_Init(void)
    {
      uint16_t * p = __segment_begin("CSTACK");
    
      uint16_t * fill_limit = p + ENTRIES_TO_FILL;
    
      uint16_t * safe_start = __segment_end("CSTACK");
      safe_start -= SAFE_ZONE;
    
      if (fill_limit > safe_start)
      {
        fill_limit = safe_start;
      }
    
      while (p != fill_limit)
      {
        *p++ = 0xAA55;
      }
    }
    
    
    

        -- Anders Lindgren, IAR Systems, Author of the IAR compiler for MSP430

  • Hi There,

    I just wanted to bring to your attention the following tool provided by TI which might be able to help you out in identifying stack size in different call graphs of your application. As far as I am aware this should work with most TI compilers and code gen toolchains.

    Its called cg_xml and you can find the latest release here:

    As for the instructions on how to get started, check out this slideshow: 

    http://processors.wiki.ti.com/images/c/cb/Cg_xml_overview.pdf

**Attention** This is a public forum