profile.c 7.04 KB

/**************************************************************************
 *                                    *
 *       Copyright (C) 1994, Silicon Graphics, Inc.       *
 *                                    *
 *  These coded instructions, statements, and computer programs  contain  *
 *  unpublished  proprietary  information of Silicon Graphics, Inc., and  *
 *  are protected by Federal copyright law.  They  may  not be disclosed  *
 *  to  third  parties  or copied or duplicated in any form, in whole or  *
 *  in part, without the prior written consent of Silicon Graphics, Inc.  *
 *                                    *
 **************************************************************************/


#ifndef _FINALROM

#include <rdb.h>
#include <os_internal.h>
#include "osint.h"

#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif 


#define OSPROFIO_STACKSIZE      300

/*
 *  Profile Timer
 */
OSTimer             __osProfTimer;
OSMesg              __osProfTimerMsg;

OSMesgQueue      __osProfFlushMQ;
OSMesg           __osProfFlushMesg;
OSMesgQueue      __osProfAckMQ;
OSMesg           __osProfAckMesg;

u32 __osProfTimerPeriod;  /* Profile sample period in microseconds */
u32 __osProfNumSections;  /* number of sections to be profiled */
/*
 *  Flag to indicate profiling is active
 */
static u32  __osProfileActive = 0;
static u32  __osProfileIOActive = 0;

/*
 *  Profiler IO
 */
u64                 __osProfileIOStack[OSPROFIO_STACKSIZE];
static OSThread     __osProfileIOThread;

void osProfSendWord(u32 word);


/*
 *  Thread which writes profile histogram data back to
 *  host when requested.
 */
void
__osProfileIO( void *arg )
{
    s32     totalBytes;
    u32     bytesThisBlock, ct;
    u8      *sendPtr;
    OSProf  *t;

    
    while (1) 
    {
        osRecvMesg(&__osProfFlushMQ, 0, OS_MESG_BLOCK);

        osProfSendWord(__osProfNumSections);
        osProfSendWord(__osProfTimerPeriod);
        osProfSendWord(__osProfileOverflowBin);

        /*  write data for each segment */
        for (t = __osProfileList; t < __osProfileListEnd; t++) 
        {
            /* write start and histo size */
            osProfSendWord((u32)t->text_start);
            osProfSendWord((u32)t->histo_size);

            osRecvMesg( &__osProfAckMQ,  0, OS_MESG_BLOCK );

            /* write histo data */
            totalBytes = (s32)t->histo_size * 2; /* 2 bytes per 16-bit counter */   
            sendPtr = (u8*)t->histo_base;          

            while (totalBytes > 0) 
            {
                bytesThisBlock = MIN((u32)totalBytes,PROF_BLOCK_SIZE);

                ct = 0;
                while(ct < bytesThisBlock)
                    ct += __osRdbSend(&sendPtr[ct], bytesThisBlock - ct,
                                      RDB_TYPE_GtoH_PROF_DATA);

                sendPtr += bytesThisBlock;
                totalBytes -= (s32)bytesThisBlock;

                osRecvMesg( &__osProfAckMQ,  0, OS_MESG_BLOCK );
    
            } 

        } /* end once for each profiled segment */

    } /* forever */
}

void osProfSendWord(u32 word)
{
    u32  ct = 0;
    u8   *sendPtr = (u8*)&word;

    while(ct < sizeof(u32))
        ct+= __osRdbSend(&sendPtr[ct], sizeof(u32) - ct, RDB_TYPE_GtoH_PROF_DATA);
}


/*
 *  Initiate transfer of profile data programatically.
 */
void
osProfileFlush( void )
{
    osSendMesg(&__osProfFlushMQ, NULL, OS_MESG_BLOCK);
}



/*
 * This routine saves a pointer to the segments to be profiled
 * and performs error checking.  It also starts a thread which
 * listens for requests to dump profile data back to the host.
 * 
 */
void
osProfileInit(OSProf *profp, u32 profcnt)
{
    u32 i;
    OSProf  *t;

#ifdef _DEBUG
    if (__osProfileActive) 
    {
        __osError(ERR_OSPROFILEINIT_STR, 0);
        return;
    }

    if (profcnt == 0) 
    {
        __osError(ERR_OSPROFILEINIT_CNT, 1, profcnt);
        return;
    }
#endif /* _DEBUG */

    /*
     *  Check each profile for errors
     *
     *  Should check for out of 0-4MB range?
     *  Should check for overlapping text segments?
     */

    for (t = profp; t < (profp + profcnt); t++) 
    {

#ifdef _DEBUG
        if ((int)t->histo_base & 1) {
            __osError(ERR_OSPROFILEINIT_ALN, 1, t->histo_base);
            return;
        }

        if (t->text_start >= t->text_end) {
            __osError(ERR_OSPROFILEINIT_ORD, 2, 
                t->text_start, t->text_end);
            return;
        }

        if (((u32)(t->text_end - t->text_start) >> 2) > t->histo_size){
            __osError(ERR_OSPROFILEINIT_SIZ, 1, t->histo_size);
            return;
        }
#endif /* _DEBUG */

        /* clear counters */
        for (i = 0; i < t->histo_size; i++) {
            t->histo_base[i] = 0;
        }
    }
    
    /* Initialize global variables */

    __osProfileActive       = 0;
    __osProfileOverflowBin  = 0;
    __osProfileList         = profp;
    __osProfileListEnd      = profp + profcnt;
	__osProfNumSections     = profcnt;

    if (!__osProfileIOActive) 
    {
        /*
         * Create the message queues that we will use, and set to get PROF messages 
         */
        osCreateMesgQueue(&__osProfFlushMQ, &__osProfFlushMesg, 1);
        osSetEventMesg(OS_EVENT_RDB_FLUSH_PROF, &__osProfFlushMQ, NULL);
        osCreateMesgQueue(&__osProfAckMQ, &__osProfAckMesg, 1);
        osSetEventMesg(OS_EVENT_RDB_ACK_PROF, &__osProfAckMQ, NULL);
    
        /*
         *  Create thread that communicates profile data back to host
         */

        osCreateThread( &__osProfileIOThread, OS_TID_PROFILEIO, __osProfileIO, NULL, 
                        __osProfileIOStack + OSPROFIO_STACKSIZE, 129);
        osStartThread( &__osProfileIOThread ); 
        __osProfileIOActive = 1;
    }
}



/*
 * This routine sets up a retriggering timer with an interval
 * defined by the user in microseconds.  The profile timer's
 * message is never actually sent; instead, the timer interrupt 
 * routine intercepts the interrupt and profiling information 
 * is calculated.
 */
void
osProfileStart(u32 microseconds)
{

#ifdef _DEBUG
    if (microseconds < PROF_MIN_INTERVAL) {
        __osError(ERR_OSPROFILESTART_TIME, 1, microseconds);
        return;
    }

    if (__osProfileActive) {
        __osError(ERR_OSPROFILESTART_FLAG, 0);
        return;
    }
#endif /* _DEBUG */

    /*
     *  Add profile timer
     */

    osCreateMesgQueue(&__osProfTimerQ, &__osProfTimerMsg, 1);
    osSetTimer(&__osProfTimer, 0, OS_USEC_TO_CYCLES(microseconds), 
        &__osProfTimerQ, NULL);
    
    /* Turn on profiling */
    __osProfTimerPeriod = microseconds;
    __osProfileActive = 1; 
}



/*
 * This routine kills the profile timer and turns off the
 * the profiling flag.
 */
void
osProfileStop( void )
{

#ifdef _DEBUG
    if (!__osProfileActive) {
        __osError(ERR_OSPROFILESTOP_FLAG, 0);
        return;
    }

    /*
     *  Remove profile timer
     */

    if (osStopTimer(&__osProfTimer) < 0) {
        __osError(ERR_OSPROFILESTOP_TIMER, 0);
        return;
    }
#else
    osStopTimer(&__osProfTimer);

#endif /* _DEBUG */
    
    /* Turn off profiling flag */

    __osProfileActive = 0;

}

#endif /* #ifndef _FINALROM */