sharc stack measurement hack?

I have read that monitoring the stack during runtime (without the IDE) for the 21489 sharc is not possible, especially using CCES which I am using. Would the following hack be possible?

  • Modify the stack memory section to ZERO_INIT instead of NO_INIT in the .ldf file.
  • Run some low-priority process that crawls the stack and compares the ratio of zeroed to non-zeroed memory locations

The intention is to capture the maximum stack usage - I don't care about instantaneous stack usage. It seems possible, but I saw in the CCES compiler manual (3-96) that the stack is implemented as a circular buffer. Does that mean sometimes the bottom of the stack can get popped off and the stack moves around in memory, eventually clobbering all the zero-initialized memory?



Clarified my intentions.
[edited by: BGraham352 at 3:36 PM (GMT 0) on 30 May 2019]
Parents
  • I found a crude but effective-enough solution which ended up a little different than expected. First, addressing some of my questions and assumptions:

    • Modifying the stack's memory section to ZERO_INIT didn't seem to zero-initialize it. Either that or it all got clobbered early on.
    • CCES 2.8.0 Compiler Manual 2-234 says the stack is implemented as a circular buffer so a circular-buffer interrupt will occur upon stack overflow. I'm assuming besides that, the stack won't shuffle around in memory.
    • Still not sure if a popped stack address's contents would be set to 0, but since the rest of the memory didn't seem to be zeroed out it didn't matter
    • The stack starts at the highest address and grows downwards. Some casual reading suggested this is typical stack behavior.

    What ended up working was to run a program which looks for changes in values stored in the stack. The lowest address which contains a changed value is stored and used to estimate maximum stack usage. I ended up using a diff detector which looks at a single address, comparing the value stored therein to the value it contained last time. Next time it looks at the next address, wrapping around when needed. I had more elaborate code in there at one point but this version seemed plenty effective.

    There are some flaws in the design, and it's going to be hard to capture the true maximum stack usage with this method (that's why you'll see a fudge factor added in there). Still, it gets close enough to help me know if my stack size should be 512 or 4096 a little more elegantly than waiting for the processor to crash. I found that it gets a rough estimate very quickly but may take a couple minutes to settle on its best guess.

    This is a pared-down version of the code I ran on a sharc EzKit with CCES version 2.8.3
    ADSP21489EXKIT.cpp:
    extern "asm" void* ldf_heap_length; //These are defined in the .ldf file
    extern "asm" void* ldf_stack_space;
    extern "asm" void* ldf_stack_end;
    void main(int argc, char *argv[])
    {
      codeThatInitializesStuff();

      //Stack monitoring stuff
    #ifdef MEMORY_MONITORING
      int stackLength = int((unsigned long)&ldf_stack_end - (unsigned long)&ldf_stack_space);
      int prevStackValue = *((int*)&ldf_stack_end); //Start at the last address (bottom of the stack)
      int stackAddressOffset = 0;
      int highestActiveStackOffset = 0;
      float heapPercent = 0.f;
      float stackPercent = 0.f;
    #endif

      while (true)
      {
        waitUntilAudioInterruptIsServiced(); //Equates to waiting 333 us with my sample rate settings, etc.

    #ifdef MEMORY_MONITORING
        //Stack Crawler. Starting at the highest address, this periodically steps through each address of the stack, checking to see if the value therein  changes.
        //It doesn't bother to search areas where there have already been changes (it only seeks to increase the reported max stack usage)
        //You can enable stack overflow detection via Project > Properties > C/C++ Build > Settings > Tool Settings > Compiler > Run-time Checks >      Generate code to catch a Stack Overflow or -rtcheck-stack
        int newStackValue = *((int*)(&ldf_stack_end - stackAddressOffset));
        if (newStackValue != prevStackValue)
        {
          highestActiveStackOffset = stackAddressOffset;
          stackPercent = ((float)stackAddressOffset + 100.f) / (float)stackLength * 100.f; //The +100 is to get a little closer to actual stack usage (crashing near 100%)
          //printf("Stack Change Detected. Highest Stack Offset: %d\n", highestActiveStackOffset);
        }
        if (++stackAddressOffset >= stackLength) //Move on to the next address. Check if we've crawled the whole stack
        {
          stackAddressOffset = highestActiveStackOffset + 1; //Start over looking right above the previous high-water mark
          assert(stackAddressOffset < stackLength);
          //printf("I crawled the stack! Highest Active Stack Offset %d\n", stackAddressOffset);
        }
        prevStackValue = *((int*)(&ldf_stack_end - stackAddressOffset)); //Set prevStackValue to the current value at the next address we'll be inspecting

        //every 2 seconds I report the stack and heap usage. Implement your own counter logic here
        heapPercent = 100.f - 100.f*((float)heap_space_unused(0) / (unsigned long)&ldf_heap_length); //Default heap usage
        printf("Heap: %2.2f%%, Stack: %2.2f%%\n", heapPercent, stackPercent);
    #endif
      }
    }

Reply
  • I found a crude but effective-enough solution which ended up a little different than expected. First, addressing some of my questions and assumptions:

    • Modifying the stack's memory section to ZERO_INIT didn't seem to zero-initialize it. Either that or it all got clobbered early on.
    • CCES 2.8.0 Compiler Manual 2-234 says the stack is implemented as a circular buffer so a circular-buffer interrupt will occur upon stack overflow. I'm assuming besides that, the stack won't shuffle around in memory.
    • Still not sure if a popped stack address's contents would be set to 0, but since the rest of the memory didn't seem to be zeroed out it didn't matter
    • The stack starts at the highest address and grows downwards. Some casual reading suggested this is typical stack behavior.

    What ended up working was to run a program which looks for changes in values stored in the stack. The lowest address which contains a changed value is stored and used to estimate maximum stack usage. I ended up using a diff detector which looks at a single address, comparing the value stored therein to the value it contained last time. Next time it looks at the next address, wrapping around when needed. I had more elaborate code in there at one point but this version seemed plenty effective.

    There are some flaws in the design, and it's going to be hard to capture the true maximum stack usage with this method (that's why you'll see a fudge factor added in there). Still, it gets close enough to help me know if my stack size should be 512 or 4096 a little more elegantly than waiting for the processor to crash. I found that it gets a rough estimate very quickly but may take a couple minutes to settle on its best guess.

    This is a pared-down version of the code I ran on a sharc EzKit with CCES version 2.8.3
    ADSP21489EXKIT.cpp:
    extern "asm" void* ldf_heap_length; //These are defined in the .ldf file
    extern "asm" void* ldf_stack_space;
    extern "asm" void* ldf_stack_end;
    void main(int argc, char *argv[])
    {
      codeThatInitializesStuff();

      //Stack monitoring stuff
    #ifdef MEMORY_MONITORING
      int stackLength = int((unsigned long)&ldf_stack_end - (unsigned long)&ldf_stack_space);
      int prevStackValue = *((int*)&ldf_stack_end); //Start at the last address (bottom of the stack)
      int stackAddressOffset = 0;
      int highestActiveStackOffset = 0;
      float heapPercent = 0.f;
      float stackPercent = 0.f;
    #endif

      while (true)
      {
        waitUntilAudioInterruptIsServiced(); //Equates to waiting 333 us with my sample rate settings, etc.

    #ifdef MEMORY_MONITORING
        //Stack Crawler. Starting at the highest address, this periodically steps through each address of the stack, checking to see if the value therein  changes.
        //It doesn't bother to search areas where there have already been changes (it only seeks to increase the reported max stack usage)
        //You can enable stack overflow detection via Project > Properties > C/C++ Build > Settings > Tool Settings > Compiler > Run-time Checks >      Generate code to catch a Stack Overflow or -rtcheck-stack
        int newStackValue = *((int*)(&ldf_stack_end - stackAddressOffset));
        if (newStackValue != prevStackValue)
        {
          highestActiveStackOffset = stackAddressOffset;
          stackPercent = ((float)stackAddressOffset + 100.f) / (float)stackLength * 100.f; //The +100 is to get a little closer to actual stack usage (crashing near 100%)
          //printf("Stack Change Detected. Highest Stack Offset: %d\n", highestActiveStackOffset);
        }
        if (++stackAddressOffset >= stackLength) //Move on to the next address. Check if we've crawled the whole stack
        {
          stackAddressOffset = highestActiveStackOffset + 1; //Start over looking right above the previous high-water mark
          assert(stackAddressOffset < stackLength);
          //printf("I crawled the stack! Highest Active Stack Offset %d\n", stackAddressOffset);
        }
        prevStackValue = *((int*)(&ldf_stack_end - stackAddressOffset)); //Set prevStackValue to the current value at the next address we'll be inspecting

        //every 2 seconds I report the stack and heap usage. Implement your own counter logic here
        heapPercent = 100.f - 100.f*((float)heap_space_unused(0) / (unsigned long)&ldf_heap_length); //Default heap usage
        printf("Heap: %2.2f%%, Stack: %2.2f%%\n", heapPercent, stackPercent);
    #endif
      }
    }

Children
No Data