OC Systems

Home | Contact | Advanced Search 

Technology

Can I track stack usage with Aprobe?

/* Windows stack usage probe:

This probe will report the stack size and usage for each thread in the application under test. The probe works by hooking the entry of each thread and writing a known value to the unused portion of the stack. On the exit of each thread the stack is examined to find the first byte from the top that is not the known value.

Notes:

  1. This probe has the effect of finding many uses of uninitialized local variables (as the stack is now dirty instead of zeroed). In the unlikely event that the program uses the value defined for DIRTYWORD at its highest stack usage the wrong value for the stack usage will be reported. If you suspect this, change the DIRTYWORD value.

  2. If thread 0 (the main thread) exits before another thread the stack results for the other thread will not be reported. This is the result of the main thread calling exit and the still-running threads never getting a chance to execute. To fix this problem a more elaborate bookkeeping scheme would be necessary. This problem will be fixed in Aprobe versions 3.2 and above.

  3. Aprobe adds a small (currently 2k bytes) buffer at the beginning of each thread for exception handling. This amount is subtracted from each usage size.

  4. This probe will have the effect of slowing thread creation times.  The thread stack must be faulted in and written with the DIRTYWORD value.

  5. Remember on Windows-Intel the stack grows from high addresses to low addresses.

*/

#include <stdio.h>

#define DIRTYWORD 0xDEADBEEF

probe thread
{
  on_entry
  {
    int *StkHi, *StkLow, *StkCur, *i;
    

    /* go get this thread's stack values from 
       the windows thread local data area.
    */
    

    __asm {
       mov EAX,FS:[8]
       mov StkHi,EAX
       mov EAX,FS:[4]
       mov StkLow,EAX
       mov StkCur,ESP
    }
    

    printf("Thread [%d] Stack Top: 0x%x, "
           "Stack Bottom: 0x%x, "
           "Size: 0x%x bytes\n",
           ap_ThreadIdToInteger
              (ap_ThreadId()),
               StkHi, StkLow,
               ((int)StkLow - (int)StkHi));
    

    /* Dirty up the unused portion of the stack: */
    for (i = StkHi; i < StkCur; i++)
    {
      *i = DIRTYWORD;
    }
  }
  

  on_exit
  {
    int *StkHi, *StkLow, *StkCur, *i;
    int AprobeThreadAdjust;
    

    /* Get this thread's stack values again: */
    __asm {
       mov EAX,FS:[8]
       mov StkHi,EAX
       mov EAX,FS:[4]
       mov StkLow,EAX
       mov StkCur,ESP
    }
    

    /* Scan from the top of the stack until we 
       find a word that isn't the DIRTYWORD.
    */
    

    if (ap_ThreadIdToInteger (ap_ThreadId()) == 0)
      AprobeThreadAdjust = 
        MAIN_THREAD_EXCEPTION_BUFFER_POOL_SIZE;
    else
      AprobeThreadAdjust = 
        NEW_THREAD_EXCEPTION_BUFFER_POOL_SIZE;
    

    for (i = StkHi; i < StkCur; i++)
    {
      if (*i != DIRTYWORD)
      {
        printf(
          "Thread [%d] last unused address: "
          "0x%x, used: 0x%x bytes\n",
          ap_ThreadIdToInteger
             (ap_ThreadId()),
          i,
          ((int)StkLow - (int)i - 
           AprobeThreadAdjust));
        break;
      }
    }
  }
}

Aprobe Technology Overview

Schedule a web demo

Contact us