home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Programming / C / SASC6571.LZX / extras / sprof / sprof.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-24  |  11.8 KB  |  476 lines

  1. /*-------------------------------------------------------------------*/
  2. /* Copyright (c) 1993 by SAS Institute Inc., Cary NC                 */
  3. /* All Rights Reserved                                               */
  4. /*                                                                   */
  5. /* SUPPORT:    walker - Doug Walker                                  */
  6. /*-------------------------------------------------------------------*/
  7. #define __USE_SYSBASE 1
  8.  
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <stdlib.h>
  12. #include <time.h>
  13. #include <exec/ports.h>
  14. #include <dos/dosextens.h>
  15. #include <dos/dostags.h>
  16. #include <exec/execbase.h>
  17. #include <proto/exec.h>
  18. #include <proto/dos.h>
  19. #include <dos.h>
  20. #include "sprofpriv.h"
  21.  
  22. #define V37 (SysBase->LibNode.lib_Version > 36) 
  23.  
  24. char nullid[] = "";
  25.  
  26. void __regargs __chkabort(void){}
  27.  
  28. static const char __ver[] = "$VER: SPROF 6.50 (26.8.93)";
  29.  
  30. char *process_status = "Initializing";
  31.  
  32. struct MsgPort *port;
  33.  
  34. struct SPDAT *spdat;
  35. int spcur, spmax;
  36.  
  37. struct GPInfo **GPInfo;
  38. int GPCur;
  39. int GPMax;
  40. int verbose;
  41.  
  42. char __stdiowin[] = "CON:0/0/639/199/";
  43.  
  44. unsigned long process;
  45. static void spdoexit(SPM msg);
  46.  
  47. void _STDcleanup(void)
  48. {
  49.    if(port)
  50.    {
  51.       DeletePort(port);
  52.       port = NULL;
  53.    }
  54. }
  55.  
  56. int main(int argc, char *argv[])
  57. {
  58.    SPM msg;
  59.    int i, len;
  60.    int keepon = 1;
  61.    char *modname = NULL;
  62.    char *cmd;
  63.    int wait = 0;
  64.    int reporttime = 0;
  65.    int doreport = 0;
  66.    int forcereport = 0;
  67.    int process_stopped = 0;
  68.    long waitbits, timerbit, res;
  69.    sptime now = 0;
  70.    struct ExecBase *SysBase = *(struct ExecBase **)4;
  71.  
  72.    if(!V37)
  73.    {
  74.       fprintf(stderr, "This program requires AmigaDOS 2.0 or higher\n");
  75.       /* If we were run from the 1.3 WorkBench, our stderr window will */
  76.       /* close when we exit, so delay for 4 seconds.                   */
  77.       if(argv == NULL) Delay(200);
  78.       exit(20);
  79.    }
  80.  
  81.    /* Parse workbench arguments */
  82.    if(argc == 0)
  83.    {
  84.       /* See <dos.h> */
  85.       argc = _WBArgc;
  86.       argv = _WBArgv;
  87.    }
  88.  
  89.    timerbit = waitbits = OpenTimer();
  90.  
  91.    if(waitbits == 0)
  92.    {
  93.       fprintf(stderr, "ERROR: Can't open timer.device\n");
  94.       exit(20);
  95.    }
  96.  
  97.    waitbits |= (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_F);
  98.  
  99.    /* We can't use ReadArgs because our parameters are positional. */
  100.    /* We want arguments specified after the program name to be     */
  101.    /* program arguments, and ReadArgs doesn't support this.        */
  102.    if(argc)
  103.    {
  104.       while(argv[1])
  105.       {
  106.          if(!stricmp(argv[1], "WAIT")) wait = 1;
  107.          else if(!strnicmp(argv[1], "REPORT", 6))
  108.          {
  109.             if(argv[1][6])
  110.             {
  111.                if(argv[1][6] != '=')
  112.                {
  113.                   /* It was a mistake, just LOOKED like the REPORT keyword */
  114.                   modname = argv[1];
  115.                }
  116.                else
  117.                {
  118.                   if(stcd_i(argv[1]+7, &reporttime) <= 0)
  119.                      goto usage;
  120.                }
  121.             }
  122.             else
  123.             {
  124.                if(argv[2] == NULL ||
  125.                   stcd_i(argv[2], &reporttime) <= 0)
  126.                {
  127.                   usage:
  128.                   fprintf(stderr, "USAGE: sprof [WAIT] [VERBOSE] [REPORT <n>] [FORCE] [<program> [<arguments>...]]\n");
  129.                   exit(20);
  130.                }
  131.                argv++;
  132.             }
  133.             reporttime *= 1000; // Needs to be in milliseconds
  134.          }
  135.          else if(!stricmp(argv[1], "VERBOSE"))
  136.          {
  137.             verbose = 1;
  138.          }
  139.          else if(!stricmp(argv[1], "FORCE"))
  140.          {
  141.             /* Produce reports even at the expense of the program */
  142.             /* being profiled.  If FORCE is not set, reports will */
  143.             /* only be produced when the program is suspended.    */
  144.             forcereport = 1;
  145.          }
  146. #if DODEBUG
  147.          else if(!stricmp(argv[1], "DEBUG"))
  148.          {
  149.             dodebug=1;
  150.          }
  151. #endif
  152.          else
  153.          {
  154.             modname = argv[1];
  155.             argv++;
  156.             break; /* Args after this belong to program */
  157.          }
  158.  
  159.          argv++;
  160.       }
  161.    }
  162.  
  163.    /* Check to make sure there isn't already another SPROF process */
  164.    /* running.  If there isn't, go ahead and allocate our MsgPort. */
  165.    /* Since it's possible that one would start up after the call to*/
  166.    /* FindPort() but before the call to CreatePort(), we do the    */
  167.    /* FindPort()/CreatePort() pair under Forbid().                 */
  168.    Forbid();
  169.    if(FindPort(SPROFPORT))
  170.    {
  171.       Permit();
  172.       fprintf(stderr, "Message port \"" SPROFPORT "\" already open\n");
  173.       exit(99);
  174.    }
  175.  
  176.    port = CreatePort(SPROFPORT, 0L);
  177.    Permit();
  178.  
  179.    if(port == NULL)
  180.    {
  181.       fprintf(stderr, "Can't allocate message port \"" SPROFPORT "\"\n");
  182.       exit(99);
  183.    }
  184.  
  185.    waitbits |= (1<<port->mp_SigBit);
  186.  
  187.    if(!modname || wait)
  188.       printf("SPROF: Please run the program to be profiled now.\n");
  189.    else
  190.    {
  191.       BPTR fh1, fh2;
  192.  
  193.       for(i=1, len=strlen(modname) + 2; argv[i]; i++)
  194.          len += strlen(argv[i]) + 1;
  195.  
  196.       if(!(cmd = malloc(len)))
  197.       {
  198.          fprintf(stderr, "Can't allocate %d bytes!\n", len);
  199.          exit(20);
  200.       }
  201.  
  202.       cmd[0] = 0;
  203.  
  204.       strcat(cmd, modname);
  205.       for(i=1, len=strlen(cmd); argv[i]; i++)
  206.       {
  207.          cmd[len] = ' ';
  208.          strcpy(cmd+len+1, argv[i]);
  209.          len += strlen(argv[i]) + 1;
  210.       }
  211.  
  212.       fh1 = Open("*", MODE_OLDFILE);
  213.       fh2 = Open("*", MODE_OLDFILE);
  214.  
  215.       printf("SPROF: Executing command \"%s\"\n", cmd);
  216.  
  217.       if(SystemTags(cmd,  SYS_Input, fh1,
  218.                           SYS_Output, fh2, 
  219.                           SYS_Asynch, -1,
  220.                           SYS_UserShell, -1,
  221.                           TAG_DONE))
  222.       {
  223.          invoke_error:
  224.          fprintf(stderr, "Can't invoke command \"%s\"!\n", cmd);
  225.          exit(20);
  226.       }
  227.       free(cmd);
  228.    }
  229.    printf("SPROF: Waiting for process...\n");
  230.  
  231.    while(keepon)
  232.    {
  233.       res = SetSignal(0,SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_F);
  234.       if(res & SIGBREAKF_CTRL_C)
  235.       {
  236.          doctrlc:
  237.          if(process)
  238.          {
  239.             printf("SPROF: Sending CTRL-C to process...\n");
  240.             Signal((struct Task *)process, SIGBREAKF_CTRL_C);
  241.          }
  242.          else
  243.             break;
  244.       }
  245.       if(res & SIGBREAKF_CTRL_F)
  246.       {
  247.          doctrlf:
  248.          BUG(("CTRL-F received, setting up for report\n"))
  249.          if(forcereport)
  250.             Report(now);
  251.          else
  252.             doreport = 1;
  253.       }
  254.  
  255.       if(msg=(SPM)GetMsg(port))
  256.       {
  257.          process_stopped = 1;
  258.          switch(msg->flags)
  259.          {
  260.             case SPROF_INIT:
  261.                if(process == NULL) 
  262.                {
  263.                   BUG(("Accepting SPROF_INIT message, process 0x%08lx\n", msg->process))
  264.                   printf("SPROF: Contacted process 0x%08lx\n", msg->process);
  265.                   process = msg->process;
  266.                   PostTimerReq(reporttime);
  267.                   process_status = "Running";
  268.                }
  269.                else
  270.                {
  271.                   BUG(("Rejecting SPROF_INIT message, process 0x%08lx\n", msg->process))
  272.                   msg->flags = SPROF_DENIED;
  273.                }
  274.                break;
  275.  
  276.             case SPROF_ENTRY:
  277.                if(verbose)
  278.                   printf("SPROF: ENTRY timestamp %ld \"%s\"\n", msg->clk, 
  279.                          msg->id ? msg->id : "PROFILE_OFF");
  280.                if(spcur>=spmax)
  281.                {
  282.                   spmax += SPINCR;
  283.                   spdat = realloc(spdat, spmax*SIZSPDAT);
  284.                   if(!spdat)
  285.                   {
  286.                      keepon = 0;
  287.                      break;
  288.                   }
  289.                }
  290.                spdat[spcur].subrs = spdat[spcur].off = 0;
  291.                spdat[spcur].clk   = now = msg->clk;
  292.                spdat[spcur].id    = msg->id ? msg->id : nullid;
  293.                spcur++;
  294.                break;
  295.  
  296.             case SPROF_EXIT:
  297.                if(verbose)
  298.                   printf("SPROF: EXIT timestamp %ld \"%s\"\n", 
  299.                           msg->clk, msg->id ? msg->id : "PROFILE_ON");
  300.                if(msg->id == NULL) msg->id = nullid;
  301.                spdoexit(msg);
  302.                now = msg->clk;
  303.                break;
  304.             
  305.             case SPROF_TERM:
  306.             case SPROF_ABORT:
  307.                BUG(("SPROF_TERM/SPROF_ABORT\n"))
  308.                keepon = 0;
  309.                doreport = 1;
  310.                process_status = "Complete";
  311.                break;
  312.  
  313.             default:
  314.                BUG(("Unknown message flags %d(0x%08lx\n", msg->flags, msg->flags))
  315.                msg->flags = SPROF_ABORT;
  316.                break;
  317.          }
  318.  
  319.          if(process_stopped && doreport)
  320.          {
  321.             Report(now);
  322.             doreport = 0;
  323.          }
  324.  
  325.          process_stopped = 0;
  326.          ReplyMsg(msg);
  327.       }
  328.       else
  329.       {
  330.          if(!process && verbose) printf("Waiting for process...\n");
  331.          res = Wait(waitbits);
  332.          if(res & SIGBREAKF_CTRL_C) goto doctrlc;
  333.          if(res & SIGBREAKF_CTRL_F) goto doctrlf;
  334.          if(res & timerbit)
  335.          {
  336.             BUG(("Timeout, setting up for report\n"))
  337.             if(forcereport)
  338.                Report(now);
  339.             else
  340.                doreport = 1;
  341.             GetTimerPkt(reporttime, 0);
  342.          }
  343.       }
  344.    }
  345.  
  346.    Forbid();
  347.    while(msg=(SPM)GetMsg(port))
  348.    {
  349.       msg->flags = SPROF_TERM;
  350.       ReplyMsg(msg);
  351.    }
  352.    DeletePort(port);
  353.    Permit();
  354.    port = NULL;
  355.  
  356.    return(0);
  357. }
  358.  
  359. static struct GPInfo *NewGPI(char *id)
  360. {
  361.    struct GPInfo *gpi;
  362.    
  363.    gpi = malloc(sizeof(struct GPInfo) + strlen(id) + 1);
  364.    if(!gpi)
  365.    {
  366.       fprintf(stderr, "Out of memory!\n");
  367.       exit(20);
  368.    }
  369.  
  370.    memset(gpi, 0, sizeof(*gpi));
  371.  
  372.    gpi->id = id;  // For sorting purposes
  373.    gpi->name = (char *)(gpi+1);
  374.    if(id) strcpy(gpi->name, id);
  375.  
  376.    return gpi;
  377. }
  378.  
  379.  
  380. struct GPInfo *FindGPI(struct GPInfo ***GPInfo_p, char *id,
  381.                        int *cur, int *tot)
  382. {
  383.    int i;
  384.    struct GPInfo **gpi;
  385.    struct GPInfo **GPInfo = *GPInfo_p;
  386.  
  387.    for(i=0; i<*cur && GPInfo[i]->id < id ; i++);
  388.  
  389.    if(i<*cur && GPInfo[i]->id == id)
  390.       return GPInfo[i];
  391.  
  392.    if(!tot) return NULL;
  393.  
  394.    /* Need to insert a new one right here */
  395.    if(*cur >= *tot)
  396.    {
  397.       *tot += 100;
  398.       if(!(gpi = malloc(*tot*sizeof(struct GPInfo *))))
  399.       {
  400.          fprintf(stderr, "Out of memory!\n");
  401.          exit(20);
  402.       }
  403.       if(i) memcpy(gpi, GPInfo, i*sizeof(struct GPInfo *));
  404.       gpi[i] = NewGPI(id);
  405.       if(i<*cur) memcpy(gpi+i+1, GPInfo+i, (*cur-i)*sizeof(struct GPInfo *));
  406.       if(GPInfo) free(GPInfo);
  407.       GPInfo = *GPInfo_p = gpi;
  408.       (*cur)++;
  409.    }
  410.    else
  411.    {
  412.       if(i < *cur)
  413.          memmove(GPInfo+i+1, GPInfo+i, (*cur-i)*sizeof(struct GPInfo *));
  414.       GPInfo[i] = NewGPI(id);
  415.       (*cur)++;
  416.    }
  417.    return GPInfo[i];
  418. }
  419.  
  420.  
  421. static void spdoexit(SPM msg)
  422. {
  423.    sptime elapsed;
  424.    struct GPInfo *gpi;
  425.  
  426.    if(spcur <= 0)
  427.    {
  428.       /* Should never happen */
  429.       if(verbose) printf("SPROF: Call stack underflow! \"%s\"\n", msg->id);
  430.       return;
  431.    }
  432.  
  433.    /* Determine net elapsed time since function entry */
  434.    /* NOT COUNTING any time spent in PROFILE_OFF      */
  435.    spcur--;
  436.    elapsed = msg->clk - spdat[spcur].clk;
  437.  
  438.    if(msg->id == nullid)
  439.    {
  440.       /* PROFILE_ON() seen */
  441.       /* Adjust our parent's "off" time */
  442.       spdat[spcur-1].off += elapsed;
  443.       return;
  444.    }
  445.  
  446.    /* Adjust our parent's "subroutine elapsed" field */
  447.    spdat[spcur-1].subrs += elapsed - spdat[spcur].off;
  448.  
  449.    /* Adjust our parent's "off" field */
  450.    spdat[spcur-1].off += spdat[spcur].off;
  451.  
  452.    /* Now we need to associate the elapsed time with the function */
  453.    /* that is returning                                           */
  454.    gpi = FindGPI(&GPInfo, msg->id, &GPCur, &GPMax);
  455.  
  456.    gpi->count++;
  457.    gpi->time += elapsed - spdat[spcur].subrs - spdat[spcur].off;
  458.    gpi->tottime += elapsed - spdat[spcur].off;
  459.  
  460.    return;
  461. }
  462.  
  463. #if DODEBUG
  464. void bug(char *fmt, ...)
  465. {
  466.    va_list arg;
  467.    char buf[512];
  468.  
  469.    va_start(arg,fmt);
  470.    vsprintf(buf, fmt, arg);
  471.    va_end(arg);
  472.  
  473.    Write(Output(), buf, strlen(buf));
  474. }
  475. #endif
  476.