/*
 * block profile support for gcc.
 *	++jrb	bammi@cadence.com
 */
#include <compiler.h>
#include <stdio.h>
#include <stdlib.h>

/* block count struct produced by gcc -tcov */
typedef struct _bb_count {
    long	initialized;	/* has __bb_init_func been called */
    char	*filename;	/* filename for .d file		  */
    long	*block_counts;  /* address of block count table   */
    long	ncount;		/* sizeof block count table       */
			        /* ie: # of basic blocks in file  */
    struct _bb_count *next;	/* in memory link to next struct  */
    char	***addr_table;  /* addr of basic block address table */
				/* size of addr_table == ncount+1    */
} BB_COUNT;

void __bb_init_func __PROTO((BB_COUNT *));
static void exit_func __PROTO((void));	  /* installed to be called at exit */
static void save_info __PROTO((BB_COUNT *));

/* vars */
static BB_COUNT *hdr = NULL;	/* list of all bb_count for which 
			           __bb_init_func has been called */
static char first_call = 1;	/* flags first call to __bb_init_func */
static char at_exit_failed = 0; /* flag to indicate that atexit() failed */

/*
 * called by gcc -tcov generated code on first entry into a function
 */
void __bb_init_func(bb_count)
BB_COUNT *bb_count;
{
    if(at_exit_failed)
	return;

    if(first_call)
    {
	if(atexit(exit_func))
	{
	    fprintf(stderr, "Failed to install exit function. No block \
profile information will be saved\n");
	    at_exit_failed = 1;
	    return;
        }
	first_call = 0;
    }

    if(bb_count->initialized == 0)
    {   /* link into list of bb_counts */
	bb_count->next = hdr;
	hdr = bb_count;
	bb_count->initialized = 1;
    }
}

/*
 * called on normal exit
 *   write out block profile files for each bb_count struct in list.
 */
static void exit_func()
{
    BB_COUNT *p;

    for(p = hdr; p; p = p->next)
   	save_info(p);
}

typedef struct {
    long	lineno;		/* start of block */
    long	count;          /* # executions (cumulative over runs)  */
} DINFO;

/*
 * given a bb_count struct, save info into .d file
 */
static void save_info(p)
BB_COUNT *p;
{
    FILE *fp;
    long i, *bcounts;
    DINFO *dinfo = malloc(p->ncount * sizeof(DINFO));

    if(!dinfo)
    {
	fprintf(stderr, "No memory to process %s. Skipped\n", p->filename);
        return;
    }

    if((fp = fopen(p->filename, "r")) == NULL)
    {
	fprintf(stderr,"Failed to open %s for read. Skipped\n", p->filename);
	free(dinfo);
	return;
    }
    /* read .d file & accumulate counts */
    for(i = 0, bcounts = p->block_counts;
	    fscanf(fp, "%ld%ld", &dinfo[i].lineno, &dinfo[i].count) == 2; i++)
    {
	if(i >= p->ncount)
        {
          fprintf(stderr, "Block counts in %s exceed expected %ld, rest skipped\n",
		    p->filename, p->ncount);
          break;
        }
	dinfo[i].count += bcounts[i];
    }
    fclose(fp);
    if(i < p->ncount)
    {
      fprintf(stderr, "Warning Block counts in %s less than expected %ld\n",
		    p->filename, p->ncount);
    }
    if((fp = fopen(p->filename, "w")) == NULL)
    {
	fprintf(stderr,"Failed to open %s for write. Skipped\n", p->filename);
	free(dinfo);
	return;
    }
    for(i = 0; i < p->ncount; i++)
    {
	if(fprintf(fp, "\t%ld\t%ld\n", dinfo[i].lineno, dinfo[i].count) == EOF)
	{
	   fprintf(stderr,"Write Failed to %s\n", p->filename);
	   free(dinfo);
	   fclose(fp);
	   return;
        }
    }
    fclose(fp);
    free(dinfo);
}
