/*
 *            Debugger
 *
 *    Copyright 1990-2011 Green Hills Software, Inc.
 *
 *    This program is the property of Green Hills Software, Inc,
 *    its contents are proprietary information and no part of it
 *    is to be disclosed to anyone except employees of Green Hills
 *    Software, Inc., or as agreed in writing signed by the President
 *    of Green Hills Software, Inc.
 */
/* ind_bcnt.c: compiler internal functions for Block Coverage Profiling. */

#if defined(EMBEDDED) || defined(__OSE)

#include "indos.h"
#include "ind_io.h"
#include "ind_exit.h"
#include "ind_thrd.h"

struct blk { void (*addr)(void); unsigned int cnt; };
struct bhdr { int len; struct blk *info; int inserted; struct bhdr *next; };
static struct bhdr *blocklisthead;

/* Macro for printing errors */
#define PERROR(msg) write(2, msg, sizeof(msg)-1);

/* File System I/O Calls to write the file to the host */
#if defined(__INTEGRITY) || defined(__INTEGRITY_SHARED_LIBS)
#define HOSTIO_CREAT hostio_creat
#define HOSTIO_WRITE write
#define HOSTIO_CLOSE close
#else
#define HOSTIO_CREAT __ghs_hostio_creat
#define HOSTIO_WRITE __ghs_hostio_write
#define HOSTIO_CLOSE __ghs_hostio_close
#endif

/* Holds the atexit call to __ghs_prof_dump_coverage. This is checked by
   __ghs_prof_dump_coverage to determine if any data exists to dump. */
static struct __GHS_AT_EXIT atexit_dump_coverage;

/**
 * Clears the coverage information.  This can be used as a command line
 * procedure call from MULTI to make it possible to generate coverage
 * information only for a specific interval in a run of a program.
 * Stop the program, call __ghs_prof_clear_coverage(), run to another
 * point, dump.
 *
 * The functions remain in the linked list so that any future block
 * coverage data gathered within a function, such as a main loop,
 * that is currently executing but that is not called again,
 * will be output by __ghs_prof_dump_coverage.
 *
 */
void __ghs_prof_clear_coverage(void)
{
    int i;
    struct bhdr *p, *next;

    for (p = blocklisthead; p; p = next) {
	next = p->next;
	if (p->len > 0) {
	    /* Reset all counts to 0 except the last block's count. */
	    for (i = 0; i < p->len - 1; i++)
		p->info[i].cnt = 0;
	    /* The last block's count can be either a special -1 marker or a
	       normal run count. */
	    if (p->info[p->len - 1].cnt != (unsigned int)-1)
		p->info[p->len - 1].cnt = 0;
	}
    }
}

#define _0644	0x1a4	/* MISRA C does not allow octal constants */

/* This routine is global to allow it to be called from MULTI */
void __ghs_prof_dump_coverage(void)
{
    int fd;
    struct bhdr *test;

    /* If no blocks were ever counted, just return. */
    if (!atexit_dump_coverage.func) {
	return;
    }

    /*
     * Use hostio, since the file belongs on the host system, not
     * the target system.
     */
    fd = HOSTIO_CREAT("bmon.out", _0644);

    if (fd == -1) {
	PERROR("bcount: could not create bmon.out\n");
	return;
    }
    for (test = blocklisthead; test; test = test->next) {
	int contains_nonzero_count = 0;
	int i;
	int cnt;
	if (test->len > 0) {
	    for (i = 0; i < test->len - 1; i++) {
		if (test->info[i].cnt != 0) {
		    contains_nonzero_count = 1;
		    break;
		}
	    }
	    /* The last block can have either a special -1 marker count or a
	       normal run count. */
	    if (test->info[test->len - 1].cnt != 0 &&
		    test->info[test->len - 1].cnt != (unsigned int)-1)
		contains_nonzero_count = 1;
	}
	/* Don't write out any block count data if no block in this function
	   has executed.  This can happen if __ghs_prof_clear_coverage was run,
	   as from the MULTI command "profilemode clear". */
	if(contains_nonzero_count == 0)
	    continue;
#if __PTR_BIT <= 32
	cnt = sizeof(struct blk)*test->len;
	if (HOSTIO_WRITE(fd, test->info, cnt) != cnt) {
	    PERROR("bcount: write failed for bmon.out\n");
	    HOSTIO_CLOSE(fd);
	    return;
	}
#else /* __PTR_BIT */
	cnt = sizeof(void *) + sizeof(int);
	for( i=0; i<test->len; i++ ) {
	    /* only write the packed data */
	    if(HOSTIO_WRITE(fd, &(test->info[i]), cnt) != cnt ) {
		PERROR("bcount: write failed for bmon.out\n");
		HOSTIO_CLOSE(fd);
		return;
	    }
	}
#endif /* __PTR_BIT */
    }
    HOSTIO_CLOSE(fd);
}

void __ghs_bcount(struct bhdr *routinfo, void (*routaddr)(void))
{
    __ghs_ProfileLock();
    if (routinfo->inserted) {
	__ghs_ProfileUnlock();
	return;
    }
    if (!blocklisthead) {
	if (!atexit_dump_coverage.func) {
	    atexit_dump_coverage.func = __ghs_prof_dump_coverage;
	    __ghs_at_exit(&atexit_dump_coverage);
	}
    }

    routinfo->inserted = 1;
    routinfo->next = blocklisthead;
    blocklisthead = routinfo;
    __ghs_ProfileUnlock();
}

#endif /* defined(EMBEDDED) */
