home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / gccdist / gcc-src / vms / gcclib / profile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-16  |  21.3 KB  |  726 lines

  1. /*                PROFILE
  2.  *
  3.  *    A function and block profiler for GNU "CC" running under the VMS
  4.  *    operating system.  When a program is compiled
  5.  *    requesting profiling, there are externals in the object file that
  6.  *    will be satisfied by this module.  When initialization is called,
  7.  *    an exit handler is set up to dump the info to the screen at image
  8.  *    exit.
  9.  *    
  10.  *  Copyright (C) 1991 Free Software Foundation, Inc.
  11.  
  12.   This file is part of PROFILE, the GNU block and function profiler for GCC.
  13. PROFILE is free software; you can redistribute it and/or modify
  14. it under the terms of the GNU General Public License as published by
  15. the Free Software Foundation; either version 1, or (at your option)
  16. any later version.
  17.  
  18. PROFILE is distributed in the hope that it will be useful,
  19. but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21. GNU General Public License for more details.
  22.  
  23. You should have received a copy of the GNU General Public License
  24. along with PROFILE; see the file COPYING.  If not, write to
  25. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  26.  
  27. /* Written by Eric Youngdale */
  28.  
  29. /* Edit history:    V1.0    2/8/91    ERY     Initial Release    */
  30.  
  31. #include <stdio.h>
  32. #include <sys/file.h>
  33. #include <jpidef.h>
  34. /* we need this to parse the debugger symbol table */
  35. #include "objrecdef.h"
  36.  
  37. /* this macro reverses the elements in a linked list, assuming that each
  38.    struct has an element named next that points to the next element */
  39. #define REVERSE(TYPE,HEAD)                    \
  40.             {                    \
  41.             struct TYPE * tpnt1, *tpnt2, *tpnt0;    \
  42.             tpnt0 = HEAD;                \
  43.             tpnt2 = (struct TYPE *) NULL;        \
  44.             while(tpnt0){                \
  45.               tpnt1 = tpnt0->next;            \
  46.               tpnt0->next = tpnt2;            \
  47.               tpnt2 = tpnt0;              \
  48.               tpnt0 = tpnt1;              \
  49.             };                    \
  50.             HEAD = tpnt2;                \
  51.             }                    \
  52.  
  53. struct from_list{
  54.     struct from_list* next;
  55.     unsigned int count;
  56.     int cpu_time;
  57.     struct function_list* from_function;
  58. };
  59.  
  60. struct function_list{
  61.     struct function_list* next;
  62.     unsigned int count;
  63.     int cpu_time;
  64.     char* function_name;
  65.     unsigned int * start_addr;
  66.     unsigned int * end_addr;
  67.     struct from_list* where;
  68. };
  69.  
  70. struct block{
  71.     int init;
  72.     char* name;
  73.     int * count_table;
  74.     unsigned int counts;
  75.     int sun_compatible;
  76.     int * address_table;
  77. };    /* used for block profiler */
  78.     
  79.  
  80. struct block_list{
  81.     struct block_list* next;
  82.     struct block * block;
  83. };
  84.  
  85. struct traceback{
  86.     struct traceback* next;
  87.     unsigned int line_number;
  88.     unsigned int Prog_Cnt;
  89. };
  90.  
  91. struct source_files{
  92.     struct source_files * next;
  93.     unsigned short int file_number;
  94.     char* file_name;
  95. };
  96.  
  97. struct source_list{
  98.     struct source_list * next;
  99.     unsigned short int file_number;
  100.     unsigned int line_number;
  101.     unsigned int n_rec;
  102. };
  103.  
  104. struct module_list{
  105.     struct module_list* next;
  106.     struct traceback* traceback_table;
  107.     struct source_files * files;
  108.     struct source_list * records;
  109. };
  110.  
  111. static struct function_list * functs = 0;
  112. static struct block_list * chains =0;
  113. static struct module_list * module_chain = 0;
  114. static int dstflag = 0;    /*flag to see if we have read the debugger symbol table*/
  115. static int nfunc=0;    /* number of functions we have seen */
  116. static unsigned int overhead=0;    /* cpu time spent profiling */
  117. static struct function_list ** list_of_routines;
  118. static unsigned int nfunc_power_of_two =0;    /*number of functions rounded
  119.                 to largest power of two < nfunc */
  120.  
  121. static int start_pc_sort(struct function_list**, struct function_list**);
  122.  
  123. static struct ihd{
  124.     short int space1;
  125.     short int space2;
  126.     short int symblk;
  127.     short int space3;
  128.     int rest[126];
  129. }buffer;
  130.  
  131. static int time[2]={-100000,-1};    /* every 10 ms */
  132.  
  133. static int last_time;        /* the last CPU time reported */
  134.  
  135.  
  136. /* this routine searches for the routine which contains the specified Prog_Cnt */
  137.  
  138. static struct function_list * find_function(unsigned int * Prog_Cnt){
  139.     int indx = nfunc_power_of_two;
  140.     int delta = nfunc_power_of_two;
  141.     delta >>=1;
  142.     do{
  143.     if(list_of_routines[indx]->end_addr <= Prog_Cnt){
  144.         if(delta==0) return 0;    /* routine not found ??? */
  145.         indx += delta;
  146.         delta >>=1;    /* shift one bit */
  147.         if(indx >= nfunc) indx = nfunc-1;
  148.         continue;};
  149.     if(list_of_routines[indx]->start_addr > Prog_Cnt){
  150.         if(delta==0) return 0;    /* routine not found ??? */
  151.         indx -= delta;
  152.         delta >>=1;    /* shift one bit */
  153.         continue;};
  154.     break;    /* found the routine */
  155.     } while(1==1);
  156.     return list_of_routines[indx];
  157. }
  158.  
  159.  
  160. /* type cast this for the exit handler */
  161. void __bb_dump_profile();
  162. /* use this to declare exit handler.  Statistics are automatically dumped */
  163. static unsigned int exit_condition;
  164.  
  165. static struct exh{
  166.     long int forward;
  167.     void (*function);
  168.     long int arg_cnt;
  169.     unsigned int  * condition;
  170. } exh_block = { 0,__bb_dump_profile,0,&exit_condition};
  171.  
  172. /* routine called by the timer */
  173.  
  174. void ___time_tick();    /* this is the AST routine.  It calls ___ast_routine */
  175.  
  176. ___ast_routine(unsigned int * Prog_Cnt,unsigned int * from_Prog_Cnt)
  177. {
  178.     struct function_list* pnt;    
  179.     struct from_list * wpnt;
  180.     int new_time;
  181.     int check_time;
  182.     int status;
  183. /* If this was from system address space, do not even try to figure this out
  184.    We will wait until the next tick, and then try again */
  185.     if(Prog_Cnt < (unsigned int *) 0x80000000){
  186.     new_time = clock();    /* do now, so we collect all the info */
  187.     pnt = functs;
  188. /* search our list of routines and increment it */
  189.     pnt = find_function(Prog_Cnt);
  190.     if(pnt){
  191.         pnt->cpu_time += new_time - last_time;
  192.         wpnt = pnt->where;
  193.         while(wpnt && (from_Prog_Cnt < wpnt->from_function->start_addr 
  194.             || from_Prog_Cnt >= wpnt->from_function->end_addr)) 
  195.                     wpnt = wpnt->next;
  196.         if(wpnt) wpnt->cpu_time += new_time - last_time;
  197.         last_time = new_time;
  198.     };
  199. /* and schedule the next wake-up */
  200.     check_time = clock();
  201. /* see how much time we wasted here */
  202.     if(check_time != new_time){
  203.         overhead += check_time - new_time;
  204.         last_time += check_time - new_time;
  205.         };
  206.     };
  207.     status = sys$setimr(0,&time,___time_tick,0);
  208.     if(!status) lib$stop(status);
  209.     return;
  210. }
  211.  
  212. static int get_delta_pc(unsigned char * pnt,int * ipnt){
  213.     int rval =0;
  214.     if(pnt[0] > 0x7f)
  215.         return    0x100 - *((unsigned char *)&pnt[0]);
  216.     if(pnt[0] == DST$C_DELTA_PC_W){
  217.         rval = *((short *)&pnt[1]);
  218.         *ipnt += 2;
  219.         };
  220.     if(pnt[0] == DST$C_DELTA_PC_L){
  221.         rval = *((long *)&pnt[1]);
  222.         *ipnt += 4;
  223.         };
  224.     return rval;
  225. }
  226.  
  227. /* this function reads the debugger symbol table to find out the names of all
  228.    of the functions, and the PC range that each one covers. */
  229. static readdst(){
  230.   int imafile;
  231.   struct ihd buffer;
  232.   int dstloc;    /* location of debugger symbol table */
  233.   int dstlen;    /* number of blocks in dst */
  234.   unsigned char dbuffer[256];
  235.   char imagename[80];
  236.   short int imagelen;
  237.   int ipnt;
  238.   int abs_pc = 0;
  239.   int line_number = 0;
  240.   int line_increment = 1;
  241.   int stat;
  242.   int prcnum=0;
  243.   int file_number;
  244.   int rec_no;
  245.   int n_lines;
  246.   struct traceback* tpnt;
  247.   struct source_files * spnt;
  248.   struct source_list * lpnt;
  249.   struct module_list* curr_module;
  250.   short int i;
  251.   struct function_list* rpnt, ** s_pnt;
  252.   unsigned char blen, blen1;
  253.   struct jpilist{
  254.     short int buflen;
  255.     short int item_code;
  256.     char * buff_addr;
  257.     short int * ret_len_addr;
  258.     int next;
  259.       } getimaname = {sizeof(imagename),(short int) JPI$_IMAGNAME, 
  260.             imagename,&imagelen,(int) 0};
  261.     /* start the clock, so we can keep track of overhead */
  262.     last_time = clock();    /* and initialize the CPU tic counter */
  263.     /* first we ask VMS the name of the image we are running now */
  264.     stat = sys$getjpiw(0,&prcnum,0,&getimaname,0,0,0);
  265.     if(!stat)  lib$signal(stat);
  266.     imagename[imagelen+1]='\0';
  267.     imafile = open(&imagename, O_RDONLY, 0666 );   /* now open that file*/
  268.     i = read(imafile,&buffer,512);    /* read the header block */
  269.     i = buffer.symblk/4 -2;    
  270.     dstloc = buffer.rest[i];   /* find the block where the dst starts*/
  271.     dstlen = buffer.rest[i+2] * 512;
  272.     lseek(imafile, (dstloc -1)* 512, 0);    /* and go there */
  273.    /* get blocks in debugger symbol table, one by one, and interpret
  274.             them if needed */
  275.     while(1==1){
  276.       i = read(imafile,&blen,1);    /* get length of debugger record */
  277.       dstlen--;
  278.       if(blen == 0) break;
  279.       blen1 = blen;
  280.       do{
  281.         i = read(imafile,&dbuffer[blen-blen1],blen1);    /* and read record */
  282.         blen1 -= i;    /* find out how many characters are left to read */
  283.       }while(blen1 != 0);
  284.       dstlen -= blen;    /* keep track of how many bytes are left */
  285.       switch(dbuffer[0]){
  286.         case DST$C_MODBEG:
  287.             curr_module = (struct module_list*)
  288.                 malloc(sizeof(struct module_list));
  289.             curr_module->next = module_chain;
  290.             module_chain = curr_module;
  291.             curr_module->traceback_table = (struct traceback*) NULL;
  292.             curr_module->files = (struct source_files*) NULL;
  293.             curr_module->records = (struct source_list*) NULL;
  294.             break;
  295.         case DST$C_MODEND:
  296.             /* re-order the linked lists */
  297.             REVERSE(traceback,curr_module->traceback_table);
  298.             REVERSE(source_files,curr_module->files);
  299.             REVERSE(source_list,curr_module->records);
  300.             break;
  301.         case DST$C_RTNBEG:
  302.             dbuffer[dbuffer[6]+7] = 0;
  303.             nfunc++;
  304.             rpnt = (struct function_list*) malloc(sizeof(struct function_list));
  305.             rpnt->start_addr = *((unsigned int  **)&dbuffer[2]);
  306.             rpnt->next = functs;
  307.             rpnt->function_name = (char*) malloc(strlen(&dbuffer[7])+1);
  308.             strcpy(rpnt->function_name,&dbuffer[7]);
  309.             rpnt->count = 0;
  310.             rpnt->cpu_time = 0;
  311.             rpnt->where = 0;    /* no routines calling from yet */
  312.             functs = rpnt;    /* put this at top of list */
  313.             break;
  314.         case DST$C_RTNEND:
  315.             rpnt->end_addr = (unsigned int *)
  316.         ((char*) rpnt->start_addr + *((unsigned int  *)&dbuffer[2]));
  317.             break;
  318.         case DST$C_SOURCE:
  319.             ipnt = 1;
  320.             do{
  321.             switch(dbuffer[ipnt]){
  322.             case DST$C_SRC_FORMFEED:    /* ^L counts    */
  323.                 ipnt += 1;
  324.                 break;
  325.             case DST$C_SRC_DECLFILE:    /* Declare file    */
  326.                 spnt = (struct source_files*) 
  327.                     malloc(sizeof(struct source_files));
  328.                 spnt-> next = curr_module->files;
  329.                 curr_module->files = spnt;
  330.                 spnt->file_number = 
  331.                   *((unsigned short int *)&dbuffer[ipnt+3]);
  332.                 spnt->file_name = (char*) malloc(dbuffer[ipnt+20]);
  333.                 strncpy(spnt->file_name,&dbuffer[ipnt+21], 
  334.                     dbuffer[ipnt+20]);
  335.                 ipnt += 2 + dbuffer[ipnt+1];
  336.                 break;
  337.             case DST$C_SRC_SETFILE:    /* Set file    */
  338.                 file_number = 
  339.                   *((unsigned short int *)&dbuffer[ipnt+1]);
  340.                 ipnt += 3;
  341.                 break;
  342.             case DST$C_SRC_SETREC_L:    /* Set record    */
  343.                 rec_no = *((int  *)&dbuffer[ipnt+1]);
  344.                 ipnt += 5;
  345.                 break;
  346.             case DST$C_SRC_DEFLINES_W:    /* # of line    */
  347.                 lpnt = (struct source_list*) 
  348.                     malloc(sizeof(struct source_list));
  349.                 lpnt-> next = curr_module->records;
  350.                 curr_module->records = lpnt;
  351.                 lpnt->n_rec = 
  352.                   *((unsigned short int *)&dbuffer[ipnt+1]);
  353.                 lpnt->file_number = file_number;
  354.                 lpnt->line_number = rec_no;
  355.                 ipnt += 3;
  356.                 break;
  357.             default:
  358.                 printf(" Source file description\n");
  359.                 for(i=ipnt;i<blen;i++) printf(" %x",dbuffer[i]);
  360.                 printf("\n");
  361.                 ipnt += 255;
  362.                 break;
  363.                 };
  364.             }while(ipnt < blen);
  365.             break;
  366.         case DST$C_LINE_NUM:
  367.             ipnt = 1;
  368.             do{
  369.             switch(dbuffer[ipnt]){
  370.             case DST$C_INCR_LINUM:
  371.                 line_number += 1 +
  372.                   *((unsigned char *)&dbuffer[ipnt+1]);
  373.                 abs_pc += get_delta_pc(&dbuffer[ipnt+2],&ipnt);
  374.                 ipnt += 3;
  375.                 break;
  376.             case DST$C_INCR_LINUM_W:
  377.                 line_number += 1 +
  378.                   *((unsigned short *)&dbuffer[ipnt+1]);
  379.                 abs_pc += get_delta_pc(&dbuffer[ipnt+3],&ipnt);
  380.                 ipnt += 4;
  381.                 break;
  382.             case DST$C_SET_LINE_NUM:        /* Set Line #    */
  383.                 line_number = 1+
  384.                   *((unsigned short int *)&dbuffer[ipnt+1]);
  385.                 ipnt += 3;
  386.                 break;
  387.             case DST$C_TERM_L:
  388.                 ipnt += 5;
  389.                 break;
  390.             case DST$C_TERM_W:
  391.                 ipnt += 3;
  392.                 break;
  393.             case DST$C_TERM:
  394.                 ipnt += 2;
  395.                 break;
  396.             case DST$C_SET_ABS_PC:
  397.                 abs_pc = *((int  *)&dbuffer[ipnt+1]);
  398.                 ipnt += 5;
  399.                 break;
  400.             case DST$C_DELTA_PC_W:
  401.             case DST$C_DELTA_PC_L:
  402.                 abs_pc += get_delta_pc(&dbuffer[ipnt],&ipnt);
  403.                 line_number += line_increment;
  404.                 ipnt += 1;
  405.                 break;
  406.             default:
  407.                 if(dbuffer[ipnt] > 0x7f){
  408.                   abs_pc += get_delta_pc(&dbuffer[ipnt],&ipnt);
  409.                     line_number += line_increment;
  410.                     ipnt += 1;
  411.                     break;
  412.                     };
  413.                 if(dbuffer[ipnt] != 0){
  414.                   printf(" Line number type %d\n",dbuffer[ipnt]);
  415.                   ipnt += 255;
  416.                 } else {
  417.                   ipnt += 1;
  418.                 };
  419.                 break;
  420.             };
  421.             }while(ipnt < blen);
  422.             tpnt = (struct traceback*) malloc(sizeof(struct traceback));
  423.             tpnt-> next = curr_module->traceback_table;
  424.             curr_module->traceback_table = tpnt;
  425.             tpnt->line_number = line_number;
  426.             tpnt->Prog_Cnt = abs_pc;
  427.             break;
  428. /* the rest are assumed to be of no interest */
  429.         default:
  430.               break;
  431.         };
  432.     };
  433.     close(imafile);
  434.     dstflag = 1;    /* mark this as having been read */
  435. /* now declare the exit handler, which will write the report to stdout */
  436.     stat = sys$dclexh(&exh_block);
  437.     if(!stat)  lib$signal(stat);
  438. /* now create a list of pointers to the function structs, sorted by the
  439.   starting PC value.  This can be used to speed up the search for the routine
  440.   name */
  441.     list_of_routines = (struct function_list **) 
  442.                 malloc(nfunc * sizeof(struct function_list*));
  443.     s_pnt = list_of_routines;
  444.     rpnt = functs;
  445.     while(rpnt){
  446.         *s_pnt++ = rpnt;
  447.         rpnt = rpnt->next;
  448.         };
  449. /* now sort in order of ascending starting PC value */
  450.     qsort(list_of_routines,nfunc,sizeof(struct function_list*),start_pc_sort);
  451.     nfunc_power_of_two = 0x80000000;
  452.     while((nfunc_power_of_two & nfunc) == 0) nfunc_power_of_two >>=1;
  453.  
  454. /* We need to fix up the end addresses sometimes, becoase MACRO does not
  455.    generate a proper end-of-routine marker in the OBJ file. */
  456.     {unsigned int * last_start = 0;
  457.     for(i=nfunc-1;i>=0;i--){
  458.       rpnt = list_of_routines[i];    /* get pointer to function struct */
  459.       if(rpnt->end_addr == 0){
  460.         if(last_start) rpnt->end_addr = last_start;
  461.         else rpnt->end_addr = rpnt->start_addr;
  462.         };
  463.       last_start = rpnt->start_addr; /* in case end address is not correct */
  464.     };};
  465. /* and schedule the timer.  This will interrupt the program every 10ms and
  466.    record where the PC was */
  467.     stat = clock();    /* and initialize the CPU tic counter */
  468.     overhead += stat - last_time;
  469.     last_time = stat;    /* and record the excess time as overhead */
  470.     stat = sys$setimr(0,&time,___time_tick,0);
  471.     if(!stat) lib$stop(stat);
  472. }
  473.  
  474. /* this function is called each time we enter a new function.  We must
  475.   increment the count  */
  476. /* ipnt is the address of the longword that contains a pointer to the
  477. function_list structure created for this function.  Once we have allocated the
  478. structure, we do not need to search the list.  addr is the PC of the function
  479. that called the profiler - we use this to determine which routine we are in. */
  480.  
  481. void 
  482. _mcount(struct function_list ** ipnt, unsigned int * addr, unsigned int *caddr)
  483. {
  484.     struct from_list* wpnt;    
  485.     struct function_list* pnt;    
  486.     struct function_list* rpnt;
  487.     struct function_list* cpnt;
  488.     if( ! *ipnt ){
  489.         if(!dstflag) readdst();    /* read the debugger symbol table */
  490.         rpnt = find_function(addr);
  491.         if(!rpnt) return; /* this is really bad. */
  492.         *ipnt = rpnt;    /* save this for later quick reference */
  493.     };
  494.     ((*ipnt)->count)++;    /* increment the counter */
  495. /* now add the info about who called this routine */
  496.     wpnt = (*ipnt)->where;
  497.     while(wpnt && ((wpnt->from_function->start_addr > caddr) ||
  498.         (wpnt->from_function->end_addr < caddr))) wpnt = wpnt->next;
  499.     if(!wpnt){ wpnt = (struct from_list*) 
  500.         malloc(sizeof(struct from_list));
  501.         wpnt->from_function = find_function(caddr);
  502.         if(wpnt->from_function ==0){
  503.             free(wpnt);
  504.             return;};
  505.         wpnt->next = (*ipnt)->where;
  506.         (*ipnt)->where = wpnt;
  507.         wpnt->cpu_time = 0;
  508.         wpnt->count = 0;
  509.         };
  510.     if (wpnt) wpnt->count++;
  511.     return;
  512. }
  513.  
  514.  
  515. void  _bb_init_func(struct block * block){
  516.     struct block_list* pnt;    
  517.     if(!dstflag) readdst();    /* read the debugger symbol table if not done*/
  518.     pnt = (struct block_list*) 
  519.         malloc(sizeof(struct block_list));
  520.     pnt->next = chains;
  521.     chains = pnt;
  522.     pnt->block = block;
  523.     block->init = 1;
  524.     return;
  525. }
  526.  
  527. static struct module_list * current_module;
  528.  
  529. static void getmod(struct block * blk){
  530.     struct module_list * mpnt;
  531.     struct traceback * tpnt;
  532.     int armed;
  533.     int i;
  534.     int * Prog_Cnt;
  535.     Prog_Cnt = blk->address_table;
  536.     for(i=0;i < blk->counts ; i++){
  537.     for(mpnt=module_chain;mpnt;mpnt = mpnt->next){
  538.       armed = 0;
  539.       for(tpnt=mpnt->traceback_table;tpnt;tpnt = tpnt->next){
  540.       if(tpnt->Prog_Cnt < *Prog_Cnt) armed = 1;
  541.       if(tpnt->Prog_Cnt == *Prog_Cnt || (tpnt->Prog_Cnt > *Prog_Cnt && armed)) {
  542.         current_module = mpnt;
  543.         return;
  544.         };
  545.       };
  546.     };
  547.     Prog_Cnt++;    /* go to next element and try again */
  548.     };
  549.     current_module = 0;
  550. }
  551.  
  552. static unsigned int lnum(unsigned int Prog_Cnt){
  553.     struct traceback * tpnt;
  554.     struct traceback closest;
  555.     if(!current_module) return 0;
  556.     closest.Prog_Cnt = 0xFFFFFFFF;
  557.     for(tpnt=current_module->traceback_table;tpnt;tpnt = tpnt->next){
  558.       if(tpnt->Prog_Cnt == Prog_Cnt) return tpnt->line_number;
  559.       if(tpnt->Prog_Cnt > Prog_Cnt && tpnt->Prog_Cnt < closest.Prog_Cnt) {
  560.         closest.Prog_Cnt = tpnt->Prog_Cnt;    
  561.         closest.line_number = tpnt->line_number;
  562.         };
  563.     };
  564.     if(closest.Prog_Cnt == 0xFFFFFFFF) return 0;
  565.     return closest.line_number;    /* best line number for this PC */
  566. }
  567.  
  568. static unsigned int lookup(int * line_number){
  569.     struct source_list * lpnt;
  570.     struct traceback * tpnt;
  571.     if(!current_module) return 0;
  572.       for(lpnt=current_module->records;lpnt;lpnt = lpnt->next){
  573.        if(*line_number <= lpnt->n_rec) {  
  574.         *line_number += lpnt->line_number -1;    /* add offset */
  575.         return lpnt->file_number;
  576.         };
  577.        *line_number -= lpnt->n_rec;
  578.       };
  579.     return 0;
  580. }
  581.  
  582. static unsigned int dump(){
  583.     struct module_list * mpnt;
  584.     struct traceback * tpnt;
  585.     struct source_files * spnt;
  586.     struct source_list * lpnt;
  587.     for(mpnt=module_chain;mpnt;mpnt = mpnt->next){
  588.       for(spnt=mpnt->files;spnt;spnt = spnt->next){
  589.        printf("%d %s\n",spnt->file_number,spnt->file_name);
  590.       };
  591.       for(lpnt=mpnt->records;lpnt;lpnt = lpnt->next){
  592.        printf("%d %d %d\n",lpnt->file_number,lpnt->line_number,lpnt->n_rec);
  593.       };
  594.     };
  595. }
  596.  
  597. static unsigned int fdump(){
  598.     struct source_files * spnt;
  599.       if(!current_module) return 0;
  600.       if(!current_module->files) return 0;
  601.       for(spnt=current_module->files;spnt;spnt = spnt->next){
  602.        printf("%d %s\n",spnt->file_number,spnt->file_name);
  603.       };
  604.     printf("\n");
  605. }
  606.  
  607. /* this function is used by qsort to sort the functions in descending order of
  608.    CPU usage */
  609.  
  610. static int cpu_sort(struct function_list** f1, struct function_list** f2){
  611.     if ((*f1)->cpu_time > (*f2)->cpu_time) return -1;
  612.     if ((*f1)->cpu_time < (*f2)->cpu_time) return 1;
  613.     return 0;
  614. }
  615.  
  616. /* this function is used by qsort to sort the functions in descending order of
  617.    CPU usage */
  618.  
  619. static int start_pc_sort(struct function_list** f1, struct function_list** f2){
  620.     if ((*f1)->start_addr < (*f2)->start_addr) return -1;
  621.     if ((*f1)->start_addr > (*f2)->start_addr) return 1;
  622.     return 0;
  623. }
  624.  
  625. /* this routine dumps the block profile to the screen.  It resets all of the
  626.    counts and CPU times, so that if the user wants to see this stuff
  627.    incrementally s/he can call this before and after the critical piece of
  628.    code */
  629. void __bb_dump_profile(){
  630.     unsigned int new_time;
  631.     struct from_list* wpnt;    
  632.     struct function_list* pnt;    
  633.     struct block_list * bpnt;    
  634.     int *ipnt, *jpnt;
  635.     int raw_line_number, fnumb;
  636.     unsigned int cumsec, total_cpu;
  637.     struct function_list ** sort_array, ** s_pnt;
  638.     int i;
  639.     int status;
  640.     status = sys$cantim(0,0);
  641.     printf("\n******************************************\n");
  642. /* count up cpu time, and make array to sort */
  643.     total_cpu = 0;
  644.     sort_array = (struct function_list **) 
  645.                 malloc(nfunc * sizeof(struct function_list*));
  646.     s_pnt = sort_array;
  647.     pnt = functs;
  648.     while(pnt){
  649.         *s_pnt++ = pnt;
  650.         total_cpu += pnt->cpu_time;
  651.         pnt = pnt->next;
  652.         };
  653. /* now sort in order of descending CPU time */
  654.     qsort(sort_array,nfunc,sizeof(struct function_list*),cpu_sort);
  655. /* now print the flat function profile to the screen */
  656.     pnt = functs;
  657.     s_pnt = sort_array;
  658.     cumsec = 0;
  659.     if(pnt){
  660.       printf(" Flat profile:\n");
  661.       printf("%% time  seconds   cumsec   calls  function\n");
  662.     };
  663.     for(i=0;i<nfunc;i++){
  664.     pnt = *s_pnt++;    /* get pointer to function struct */
  665.         if((pnt->cpu_time != 0) || (pnt->count != 0)){
  666.           cumsec += pnt->cpu_time;
  667.           printf("%6.2f %8.2f %8.2f %7d  %x %s\n",
  668.             (total_cpu != 0 ? (pnt->cpu_time * 100.0)/total_cpu : 0),
  669.             pnt->cpu_time/100.0,
  670.             cumsec/100.0,
  671.             pnt->count,
  672.             pnt->start_addr,pnt->function_name);
  673.         };
  674.     };
  675.     free(sort_array);    /* and let go of the array */
  676.     cumsec += overhead;
  677.     printf("       %8.2f %8.2f          (profiling overhead)\n",
  678.             overhead/100.0,cumsec/100.0);
  679. /* now print the call function profile to the screen */
  680.     pnt = functs;
  681.     if(pnt){
  682.       printf(" Function profile:\n");
  683.       printf(" # entries   CPU sec   Function Name   Called From\n");
  684.       printf(" _________   _______   ________ ____   ______ ____\n");
  685.     };
  686.     while(pnt){
  687.         if((pnt->cpu_time != 0) || (pnt->count != 0)){
  688.           printf("%10d %9.2f   %s\n",pnt->count,
  689.             pnt->cpu_time/100.0,pnt->function_name);
  690.           pnt->cpu_time= 0;
  691.           pnt->count = 0;
  692.           wpnt = pnt->where;
  693.           while(wpnt){
  694.             printf("%10d %9.2f                   %s\n",wpnt->count,
  695.                 wpnt->cpu_time/100.0,
  696.                 wpnt->from_function->function_name);
  697.             wpnt->cpu_time= 0;
  698.             wpnt->count = 0;
  699.             wpnt = wpnt->next;
  700.             };
  701.         printf("------------------------------\n");
  702.         };
  703.         pnt = pnt->next;
  704.         };
  705. /* now print the block profile to the screen */
  706.     bpnt = chains;
  707.     while(bpnt){
  708.         getmod(bpnt->block);    /* find current module */
  709.         printf("\n Block profile for file %s, %d lines\n",
  710.             bpnt->block->name,bpnt->block->counts);
  711.         ipnt = bpnt->block->count_table;
  712.         jpnt = bpnt->block->address_table;
  713.         printf(" Source files for this module are:\n");
  714.         fdump();
  715.         printf(" F#  line#  rline#     PC     Count\n");
  716.         printf(" ___ ______ ______  ________  ______\n");
  717.         for(i=0;i<bpnt->block->counts;i++,ipnt++,jpnt++){
  718.             raw_line_number = lnum(*jpnt);
  719.             fnumb = lookup(&raw_line_number);
  720.             printf(" %3d %6d %6d (%8x):%d\n",fnumb,
  721.                 raw_line_number,lnum(*jpnt),*jpnt,*ipnt);
  722.         };        
  723.         bpnt = bpnt->next;
  724.         };
  725. }
  726.