home *** CD-ROM | disk | FTP | other *** search
/ The CDPD Public Domain Collection for CDTV 3 / CDPDIII.bin / bbs / ff870.lha / StackCheck / StackCheck.c < prev    next >
C/C++ Source or Header  |  1993-05-02  |  16KB  |  544 lines

  1. /* StackCheck V1.0 written by Günther Röhrich */
  2. /* This program is Public Domain              */
  3.  
  4. /* To compile with Aztec C 5.2a:                                */
  5. /*   cc -so stackcheck.c                                        */
  6. /*   ln stackcheck -lc                                          */
  7. /* To compile with GNU C 2.3.3:                                 */
  8. /*   gcc -O2 stackcheck.c -lamy -o stackcheck                   */
  9.  
  10. /* If you want to adjust to another compiler you have to turn   */
  11. /* off the compiler's CTRL-C handler                            */
  12. /* NOTE: when using Aztec C or GNU C a short inline assembler   */
  13. /*       routine is used to get maximum speed                   */
  14.  
  15. #include <exec/execbase.h>
  16. #include <dos/dosextens.h>
  17. #include <string.h>
  18. #include <stdio.h>
  19. #include <clib/exec_protos.h>
  20. #include <clib/dos_protos.h>
  21. #ifdef AZTEC_C
  22. #include <pragmas/exec_lib.h>
  23. #include <pragmas/dos_lib.h>
  24. #endif
  25.  
  26. #ifdef __GNUC__
  27. #include <signal.h>
  28. #define MYSTRCMP strcasecmp
  29. #define MYSTRNCMP strncasecmp
  30. #else
  31. #define MYSTRCMP strcmp
  32. #define MYSTRNCMP strncmp
  33. #endif
  34.  
  35. #define WHITE  "\x1B[32m"
  36. #define NORMAL "\x1B[39m"
  37. #define CTRL_C (SetSignal(0L,0L)&SIGBREAKF_CTRL_C)
  38. #define MAXSTRSIZE 80         /* maximum string size */
  39. #define FILLED_UNKNOWN 0
  40. #define FILLED_PATTERN 1
  41. #define FILLED_ZERO    2
  42. #define STATUS_OK              0
  43. #define STATUS_OTHER_STACK     1
  44. #define STATUS_STACK_OVERFLOW  2
  45.  
  46.  
  47. #ifdef DEBUG
  48. BOOL PrintFlag = TRUE;
  49. #endif
  50.  
  51. char        *ver = "\0$VER: StackCheck 1.0 (2.5.93)";
  52. UBYTE        OSVersion;
  53.  
  54. /* NOTE: all 680x0 processors keep A7 word-aligned                  */
  55. /* WARNING: the last word on the stack has the address SPUpper - 2  */ 
  56. APTR         SPReg;           /* register A7 of the task we check */
  57. USHORT      *SPUpper;         /* upper bound of the stack we are checking */
  58. USHORT      *SPUpperFirst;
  59. USHORT      *GlobalSPLower;   /* a copy of the local SPLower */
  60. struct Task *Task = NULL;     /* the structure of the task we check */      
  61. ULONG        UsedMax;         /* the maximum stack used so far */
  62. USHORT       StackBase[8];    /* the last 8 words on the stack */
  63. ULONG        LongStackBase[4];
  64.  
  65. /* default values */
  66. ULONG        CheckDelay = 2L;
  67. LONG         Pri = 1L;
  68. LONG         Size=0L;         /* default for SIZE option */
  69. LONG         Num = 0L;        /* number of CLI           */
  70. SHORT        Filled = FILLED_UNKNOWN;
  71. USHORT       StackStatus = STATUS_OK;
  72. USHORT       AbortFlag = 0;   /* indicates what to do at CTRL-C */
  73.  
  74. #ifdef AZTEC_C
  75. extern int Enable_Abort;     /* indicates if CTRL-C handling allowed */
  76. #endif
  77. extern struct ExecBase *SysBase;
  78.  
  79. /* forward declarations */
  80.                               
  81. void         AbortUsage(void);    
  82. void         PrintFilled(void);
  83. struct Task *FindTaskEnhanced(UBYTE *name); 
  84. void         BConvert(BSTR BString); 
  85. void         Abort(void);
  86.  
  87. UBYTE ConvertedString[MAXSTRSIZE]; /* needed by BConvert */
  88.  
  89. void main(argc, argv)
  90. int argc;
  91. char *argv[];
  92. {
  93.  /* local variables to get maximum speed                        */
  94.  /* this is needed to increase performance because Aztec C does */
  95.  /* not put global variables and constants into registers       */
  96.  /* NOTE: GNU C does it when using -O2 option                   */
  97.  ULONG   i=0L;        
  98.  ULONG   Free;     /* amount of free stack (in words) */
  99.  USHORT *SPLower;  /* lower bound of the stack we are checking */
  100.  USHORT  ConstFill = 0xBEEF; /* value to fill the unused stack area */
  101.  
  102.  OSVersion = SysBase->LibNode.lib_Version;
  103.  
  104.  #ifdef AZTEC_C             /* Disable Aztec C CTRL-C handling */
  105.  Enable_Abort = 0;         
  106.  #endif
  107.  #ifdef __GNUC__            /* Disable GNU C CTRL-C handling */
  108.  signal(SIGINT, SIG_IGN);
  109.  #endif
  110.  
  111.  printf("StackCheck V1.0 by Günther Röhrich\n"
  112.         "This program is Public Domain. Press CTRL-C to abort.\n\n");
  113.  
  114.  if(argc < 2) AbortUsage();
  115.  
  116.  for(i = 2; i < argc; i++) 
  117.  {
  118.    #ifndef __GNUC__
  119.    strupr(argv[i]);
  120.    #endif
  121.    if(!MYSTRNCMP(argv[i], "DELAY=", 6))
  122.    {
  123.      CheckDelay = strtoul(&argv[i][6], NULL, 0);
  124.      if(CheckDelay == 0 || CheckDelay > 3000)
  125.      {
  126.        printf("DELAY must be between 1 and 3000\n");
  127.        exit(10);
  128.      }
  129.    }
  130.    else if(!MYSTRNCMP(argv[i], "PRI=", 4))
  131.    {
  132.      Pri = strtol(&argv[i][4], NULL, 0);
  133.      if(Pri > 5 || Pri < 0)
  134.      {
  135.        printf("PRI must be between 0 and 5\n");
  136.        exit(10);
  137.      }
  138.    }
  139.    else if(!MYSTRNCMP(argv[i], "STACK=", 6))
  140.    {
  141.      Size = strtol(&argv[i][6], NULL, 0);
  142.      /* make Size divisible by 4 */
  143.      /* the OS does the same     */ 
  144.      Size = Size & 0xFFFFFFFC;
  145.    }
  146.    else if(!MYSTRNCMP(argv[i], "NUM=", 4))
  147.    {
  148.      Num = strtol(&argv[i][4], NULL, 0);
  149.    }
  150.    else AbortUsage();
  151.  }
  152.  
  153.  
  154.  #ifndef DEBUG
  155.  SetTaskPri(FindTask(NULL), Pri); /* set our task to the desired priority */
  156.  #endif
  157.  
  158.  while(Task == NULL)  /* waits until the task is found */
  159.  {
  160.    Forbid();
  161.    Task = FindTaskEnhanced((UBYTE *)argv[1]);
  162.    #ifdef DEBUG
  163.    PrintFlag = FALSE;
  164.    #endif
  165.    Permit(); 
  166.    if(Task) break;   /* if task is found exit the loop */
  167.    if(CTRL_C) Abort();   /* handle CTRL-C                  */
  168.    Delay(CheckDelay); 
  169.  }
  170.  
  171.  Forbid(); 
  172.  SPUpperFirst = SPUpper;
  173.  
  174.  /* ensure that the task is still there */
  175.  Task = FindTaskEnhanced((UBYTE *)argv[1]);  
  176.  if(Task)
  177.  {
  178.    /* ensure we still have the same stack area */
  179.    if(SPUpper == SPUpperFirst)
  180.    {
  181.      SPReg = Task->tc_SPReg;
  182.      SPLower = GlobalSPLower;
  183.      if(SPReg < SPLower || SPReg > SPUpper)
  184.      {
  185.         Permit();
  186.         printf("Could not fill unused stack area.\n");
  187.         printf("Task uses alternative stack or stack already overflowed.\n");
  188.         exit(5);
  189.      }
  190.      Free = ((ULONG)SPReg - (ULONG)SPLower)/2L;
  191.  
  192.      for(i=0; i < 8; i++) StackBase[i] = SPLower[i];
  193.      /* Let's fill up the unused stack area (if it's not already done) */
  194.      /* we assume that the unused area contains 0 bytes                */
  195.      if((SPLower[0] == 0) && (SPLower[1] == 0) && (Free > 2))
  196.      {
  197.        i=0;
  198.        Filled = FILLED_ZERO;
  199.        while((SPLower[i] == 0) && (i < Free)) SPLower[i++] = ConstFill;
  200.      }
  201.  
  202.      /* if the unused area is filled with an unknown pattern,      */
  203.      /* let's fill it with our own pattern                         */
  204.      /* WARNING: by doing that UsedMax becomes inaccurate          */
  205.      else if((SPLower[0] != ConstFill) || (SPLower[1] != ConstFill))
  206.      {
  207.        for(i = 0; i < Free; i++) SPLower[i] = ConstFill;
  208.      }
  209.      else Filled = FILLED_PATTERN;
  210.    }
  211.  }
  212.  
  213.  AbortFlag = 1; /* Use Break1() in case of CTRL-C */
  214.  
  215.  while(Task) /* this loop ends if the task finishes or CTRL-C is pressed */
  216.  {
  217.    /* ensure that the task is still there */
  218.    Task = FindTaskEnhanced((UBYTE *)argv[1]); 
  219.    if(Task == NULL)
  220.    {
  221.      Permit();              
  222.      break;
  223.    }
  224.    /* ensure we still have the same stack area */   
  225.    if(SPUpper == SPUpperFirst)
  226.    {
  227.      SPReg = Task->tc_SPReg;
  228.      SPLower = GlobalSPLower;
  229.      Free = ((ULONG)SPUpper - (ULONG)SPLower) / 2;
  230.  
  231.      for(i=0; i < 4; i++) LongStackBase[i] = *(ULONG *)&SPLower[i*2];
  232.      /* count the undamaged area from SPLower to SPUpper   */
  233.      /* written in assembler to get maximum speed          */
  234.      /* (This is of course not the fastest solution, I am  */
  235.      /* a C programmer, not an assembler freak. Some minor */
  236.      /* enhancements are still possible.)                  */
  237.      #if defined AZTEC_C
  238.      /* a0 and d0 are scratch registers when Aztec C is used, */
  239.      /* there is no need to preserve them                     */
  240.      #asm
  241.                 move.l   _GlobalSPLower,a0
  242.                 move.l   #$BEEFBEEF,d0     
  243.         asm1:   cmp.l    (a0)+,d0          
  244.                 beq      asm1              
  245.                 lea      -4(a0),a0         ;decrement a0 by 4
  246.                 cmp.w    #$BEEF,(a0)       
  247.                 beq      asm2
  248.                 lea      -2(a0),a0         ;decrement a0 by 2
  249.         asm2:   move.l   _SPUpper,d0
  250.                 sub.l    a0,d0
  251.                 subq.l   #2,d0
  252.                 move.l   d0,_UsedMax
  253.      #endasm
  254.      #elif defined __GNUC__
  255.      /* let the compiler determine what registers to use          */
  256.      /* NOTE: GNU C does not have scratch registers like Aztec C  */
  257.      /* NOTE: we have to use a different syntax                   */
  258.      asm ("
  259.                 movel    #0xBEEFBEEF,%0;
  260.         asm1:   cmpl     %1@+,%0;
  261.                 jeq      asm1;
  262.                 lea      %1@(-4),%1;
  263.                 cmpw     #0xBEEF,%1@;       
  264.                 jeq      asm2;
  265.                 lea      %1@(-2),%1;   
  266.         asm2:   movel    %2,%0;
  267.                 subl     %1,%0;
  268.                 subql    #2,%0;"
  269.      /* tell the compiler about register usage */
  270.            : "=d" (UsedMax) : "a" (GlobalSPLower) , "a" (SPUpper)
  271.      /* tell the compiler that we modified the condition codes */
  272.      /* and register %1                                        */
  273.            : "cc", "%1");                 
  274.      #else
  275.      /* for those who don't have an inline assembler: */
  276.      /* this piece of code does the same job          */
  277.      /* (but is a lot slower)                         */    
  278.      for(i = 0; i < Free; i++) if(SPLower[i] != ConstFill) break;
  279.      UsedMax = (ULONG)SPUpper - (ULONG)SPLower - i * 2;
  280.      #endif
  281.      
  282.      if((SPReg < SPLower || SPReg > SPUpper) && StackStatus == STATUS_OK)
  283.         StackStatus = STATUS_OTHER_STACK;
  284.      if(UsedMax == Free * 2) StackStatus = STATUS_STACK_OVERFLOW;
  285.    }
  286.    Permit();
  287.    Delay(CheckDelay);
  288.    if(CTRL_C) Abort();  /* handle CTRL-C */
  289.    Forbid();
  290.  }
  291.  
  292.  #ifndef DEBUG
  293.  SetTaskPri(FindTask(NULL), 0L); /* set back to normal priority */
  294.  #endif
  295.  
  296.  PrintFilled();
  297.  #ifdef DEBUG
  298.  printf("SPReg:   %lX\n", SPReg);
  299.  printf("SPUpper: %lX\n", SPUpper);
  300.  printf("SPLower: %lX\n", SPLower);
  301.  #endif
  302.  printf("Stacksize: %lu\n", (ULONG)SPUpper - (ULONG)SPLower);
  303.  if(StackStatus != STATUS_STACK_OVERFLOW) printf("Used max:  %lu\n", UsedMax);
  304.  exit(0L);
  305. }
  306.  
  307.  
  308. void AbortUsage(void)
  309. {
  310.  printf("Usage: StackCheck Name|* [DELAY=n][PRI=n][STACK=n][NUM=n]\n");
  311.  exit(10L);
  312. }
  313.  
  314. /* We dont't know anything about the task and its stack usage */ 
  315. void Break0(void)
  316. {
  317.  printf("StackCheck aborted, task not found.\n");
  318.  exit(0L);
  319. }
  320.  
  321. /* We know a little more */
  322. void Break1(void)
  323. {
  324.  printf("StackCheck aborted, results so far:\n");
  325.  PrintFilled();
  326.  #ifdef DEBUG 
  327.  printf("SPReg:   %lX\n", SPReg);
  328.  printf("SPUpper: %lX\n", SPUpper);
  329.  printf("SPLower: %lX\n", GlobalSPLower);
  330.  #endif
  331.  printf("Stacksize: %lu,", (ULONG)SPUpper - (ULONG)GlobalSPLower);
  332.  printf(" Used now: %lu\n", (ULONG)SPUpper - (ULONG)SPReg);
  333.  if(StackStatus != STATUS_STACK_OVERFLOW) printf("Used max:  %lu\n", UsedMax);
  334.  exit(0L);
  335. }
  336.  
  337. /* This function is called when CTRL-C is pressed   */
  338. /* it overrides the default CTRL-C handler          */
  339. /* AbortFlag indicates what function we should call */
  340. void Abort(void)
  341. {
  342.  #ifndef DEBUG
  343.  SetTaskPri(FindTask(NULL), 0L);
  344.  #endif
  345.  if(AbortFlag == 0) Break0();
  346.  if(AbortFlag == 1) Break1();
  347.  exit(0L);
  348. }
  349.  
  350. BOOL CheckTask(struct Process *TaskFound, UBYTE *name)
  351. {
  352.  BOOL Found=FALSE;
  353.  struct CommandLineInterface *CLIinfo;
  354.  UBYTE TaskState;
  355.  ULONG StackSize;
  356.  
  357.  TaskState = ((struct Process *)TaskFound)->pr_Task.tc_State;
  358.  /* ensure Task is in a legal state */
  359.  if(TaskState == TS_RUN || TaskState == TS_READY || TaskState == TS_WAIT)
  360.  {
  361.    StackSize = (ULONG)TaskFound->pr_Task.tc_SPUpper - 
  362.                (ULONG)TaskFound->pr_Task.tc_SPLower;
  363.    #ifdef DEBUG
  364.    if(PrintFlag)
  365.    {
  366.      printf("Taskname: %s\n", TaskFound->pr_Task.tc_Node.ln_Name);
  367.      printf("TaskStackSize: %ld\n", StackSize);
  368.      printf("SPUpper: %lX\n", (ULONG)TaskFound->pr_Task.tc_SPUpper);
  369.      printf("SPReg:   %lX\n", TaskFound->pr_Task.tc_SPReg);
  370.      if(TaskFound->pr_Task.tc_Node.ln_Type == NT_PROCESS)
  371.      {
  372.        printf("ProcessStackSize: %ld\n", TaskFound->pr_StackSize);
  373.        printf("TaskNum: %ld\n", TaskFound->pr_TaskNum);
  374.        if(TaskFound->pr_TaskNum != 0L)
  375.        {
  376.          /* WARNING: pr_CLI is a BPTR */
  377.          CLIinfo = (struct CommandLineInterface *)BADDR(TaskFound->pr_CLI);
  378.          /* WARNING: cli_CommandName is a BSTR */
  379.          BConvert(CLIinfo->cli_CommandName);
  380.          printf("CommandName: %s\n", ConvertedString);
  381.          printf("DefaultStack: %ld\n", CLIinfo->cli_DefaultStack * 4L);
  382.        }
  383.      }
  384.      printf("\n");
  385.    }
  386.    #endif
  387.  
  388.    /* check if it is a process */
  389.    if(TaskFound->pr_Task.tc_Node.ln_Type == NT_PROCESS)
  390.    { 
  391.      /* check if it is a CLI/Shell process */
  392.      if(TaskFound->pr_TaskNum != 0L)
  393.      {
  394.        CLIinfo = (struct CommandLineInterface *)BADDR(TaskFound->pr_CLI);
  395.        if(Num == 0L || Num == TaskFound->pr_TaskNum)
  396.        {
  397.          if((OSVersion >= 36) && (Size == 0L || Size == TaskFound->pr_StackSize)) 
  398.          {
  399.            /* WARNING: cli_CommandName is a BSTR */
  400.            BConvert(CLIinfo->cli_CommandName);
  401.            if(!strcmp((char *)name, (char *)ConvertedString) || name[0] == '*')
  402.              if(StackSize == (ULONG)CLIinfo->cli_DefaultStack * 4L) Found = TRUE;
  403.          }
  404.        }
  405.      }
  406.      else
  407.      {
  408.        if((Num == 0) && (Size == 0L || Size == TaskFound->pr_StackSize) &&
  409.           (strcmp((char *)name, TaskFound->pr_Task.tc_Node.ln_Name) == 0 ||
  410.            name[0] == '*'))
  411.          Found = TRUE;
  412.      }
  413.    }
  414.    else
  415.    {
  416.      if(Size == 0L || Size == StackSize)
  417.      {
  418.        if((Num == 0) &&
  419.           (strcmp((char *)name, TaskFound->pr_Task.tc_Node.ln_Name) == 0 ||
  420.            name[0] == '*'))
  421.          Found = TRUE;
  422.      }
  423.    }
  424.  }
  425.  if(Found)
  426.  {
  427.    SPUpper = TaskFound->pr_Task.tc_SPUpper;
  428.    GlobalSPLower = TaskFound->pr_Task.tc_SPLower;
  429.  }
  430.  return(Found);
  431. }
  432.  
  433. struct Task *FindTaskEnhanced(UBYTE *name)
  434. {
  435.  struct Node *TaskFound;
  436.  BOOL Found=FALSE; 
  437.  
  438.  Forbid();    /* nested Forbid() calls are allowed */
  439.  
  440.  if(SysBase->TaskReady.lh_TailPred != (struct Node *)&SysBase->TaskReady)
  441.  {
  442.    for(TaskFound = SysBase->TaskReady.lh_Head; TaskFound->ln_Succ;
  443.        TaskFound = TaskFound->ln_Succ)
  444.    {
  445.      Found = CheckTask((struct Process *)TaskFound, name);
  446.      if(Found) break;
  447.    }
  448.  }
  449.  
  450.  if(SysBase->TaskWait.lh_TailPred != (struct Node *)&SysBase->TaskWait &&
  451.     Found == FALSE)
  452.  {
  453.    for(TaskFound = SysBase->TaskWait.lh_Head; TaskFound->ln_Succ;
  454.        TaskFound = TaskFound->ln_Succ)
  455.    {
  456.      Found = CheckTask((struct Process *)TaskFound, name);
  457.      if(Found) break;
  458.    }
  459.  }
  460.  
  461.  if(Found == FALSE)
  462.  {
  463.    TaskFound = (struct Node *)SysBase->ThisTask;
  464.    Found = CheckTask((struct Process *)TaskFound, name);
  465.  }
  466.  
  467.  
  468.  if(Found)
  469.  {
  470.    Permit();
  471.    return((struct Task *)TaskFound);
  472.  }
  473.  else
  474.  {
  475.    Permit();
  476.    return(NULL);
  477.  }
  478. }
  479.  
  480.  
  481. /* convert a BCPL-string to a C-string */
  482. void BConvert(BSTR BString)
  483. {
  484.  UBYTE *String;
  485.  UBYTE i=0;
  486.  
  487.  String = (UBYTE *)BADDR(BString);
  488.  while(i < String[0] && i < MAXSTRSIZE - 1)
  489.  {
  490.    ConvertedString[i] = String[i+1];
  491.    i++;
  492.  }
  493.  ConvertedString[i] = 0;
  494. }
  495.  
  496. void PrintPattern(void)
  497. {
  498.  USHORT i;
  499.  printf("%08lX: ", GlobalSPLower);
  500.  for(i=0; i < 8; i = i + 2)
  501.  printf("%04hX%04hX ", StackBase[i], StackBase[i+1]);
  502.  printf("\n");
  503. }   
  504.  
  505. void PrintDamaged(void)
  506. {
  507.  USHORT i;
  508.  printf("%08lX: ", GlobalSPLower);
  509.  for(i=0; i < 4; i++)
  510.  printf("%08lX ", LongStackBase[i]);
  511.  printf("\n");
  512. }
  513.  
  514. void PrintFilled(void)
  515. {
  516.  switch(Filled)
  517.  {
  518.    case FILLED_UNKNOWN: printf("Free stack area contained unknown pattern:\n");
  519.                         PrintPattern();                               
  520.                         break;
  521.    case FILLED_PATTERN: printf("Free stack area contained known pattern:\n");
  522.                         PrintPattern();
  523.                         break;
  524.    case FILLED_ZERO   : printf("Free stack area was cleared:\n");
  525.                         PrintPattern();
  526.  }
  527.  switch(StackStatus)
  528.  {
  529.    case STATUS_OK            :
  530.                                printf("Stack OK:\n");
  531.                                PrintDamaged();
  532.                                break;
  533.  
  534.    case STATUS_OTHER_STACK   : printf(WHITE
  535.                                  "Task used alternative stack!\n" NORMAL);
  536.                                printf("Status of normal stack area:\n");
  537.                                PrintDamaged();
  538.  
  539.                                break;
  540.    case STATUS_STACK_OVERFLOW: printf(WHITE  "Task stack overflowed:\n" NORMAL);
  541.                                PrintDamaged();                          
  542.  }
  543. }
  544.