adtools/clib2

View on GitHub
library/sas_profile.c

Summary

Maintainability
Test Coverage
/*
 * $Id: sas_profile.c,v 1.7 2006-04-05 06:43:56 obarthel Exp $
 *
 * :ts=4
 *
 * Adapted from SAS/C runtime library code.
 *
 *
 * Portable ISO 'C' (1994) runtime library for the Amiga computer
 * Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Neither the name of Olaf Barthel nor the names of contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef EXEC_MEMORY_H
#include <exec/memory.h>
#endif /* EXEC_MEMORY_H */

#ifndef EXEC_PORTS_H
#include <exec/ports.h>
#endif /* EXEC_PORTS_H */

#ifndef EXEC_DEVICES_H
#include <exec/devices.h>
#endif /* EXEC_DEVICES_H */

#ifndef DEVICES_TIMER_H
#include <devices/timer.h>
#endif /* DEVICES_TIMER_H */

/****************************************************************************/

#define __NOLIBBASE__
#define __NOGLOBALIFACE__

#ifndef PROTO_EXEC_H
#include <proto/exec.h>
#endif /* PROTO_EXEC_H */

#ifndef PROTO_TIMER_H
#include <proto/timer.h>
#endif /* PROTO_TIMER_H */

/****************************************************************************/

extern struct Library * SysBase;

/****************************************************************************/

#ifndef _STDLIB_CONSTRUCTOR_H
#include "stdlib_constructor.h"
#endif /* _STDLIB_CONSTRUCTOR_H */

/****************************************************************************/

#include "macros.h"
#include "debug.h"

/****************************************************************************/

/* A quick workaround for the timeval/timerequest->TimeVal/TimeRequest
   change in the recent OS4 header files. */

#if defined(__NEW_TIMEVAL_DEFINITION_USED__)

#define timerequest    TimeRequest
#define tr_node        Request
#define tr_time        Time

#endif /* __NEW_TIMEVAL_DEFINITION_USED__ */

/****************************************************************************/

struct SPROFMSG
{
    struct Message        message;

    struct Process *    process;
    ULONG                clock_value;
    char *                id;
    ULONG                stack_pointer;
    ULONG                flags;
};

/****************************************************************************/

/* Values for the 'flags' field of SPROFMSG */
#define SPROF_INIT        0x00000001    /* Initialize connection */
#define SPROF_ENTRY        0x00000002    /* Function entry */
#define SPROF_EXIT        0x00000004    /* Function exit */
#define SPROF_TERM        0x00000008    /* Terminate connection, program continues */
#define SPROF_ABORT        0x00000010    /* Abort program */
#define SPROF_DENIED    0x00000020    /* Connection refused */

/****************************************************************************/

static struct Library * TimerBase;

/****************************************************************************/

static struct MsgPort *        profiler_port;
static struct MsgPort *        reply_port;

static struct MsgPort *        time_port;
static struct timerequest *    time_request;

static struct Process *        this_process;

static struct EClockVal        start_time;
static ULONG                eclock_frequency;
static struct EClockVal        last_time;
static ULONG                overhead;

static ULONG                num_messages_allocated;

static BOOL                    initialized;

/****************************************************************************/

STATIC int send_profiler_message(ULONG clock_value, char *id, ULONG flags);
STATIC ULONG get_current_time(void);
STATIC void update_overhead(void);
STATIC void __profile_init(void);
STATIC void __profile_exit(void);

/****************************************************************************/

void ASM _PROLOG(REG(a0, char *id));
void ASM _EPILOG(REG(a0, char *id));

/****************************************************************************/

STATIC int
send_profiler_message(ULONG clock_value,char * id,ULONG flags)
{
    extern long __builtin_getreg(int);

    struct SPROFMSG * spm;
    int result = ERROR;

    spm = (struct SPROFMSG *)GetMsg(reply_port);
    if(spm != NULL)
    {
        if(flags != SPROF_TERM && spm->flags == SPROF_TERM)
        {
            PutMsg(reply_port,(struct Message *)spm);

            __profile_exit();
            goto out;
        }
    }
    else
    {
        spm = AllocMem(sizeof(*spm),MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR);
        if(spm == NULL)
        {
            __profile_exit();
            goto out;
        }

        spm->message.mn_Length        = sizeof(*spm);
        spm->message.mn_ReplyPort    = reply_port;
        spm->process                = this_process;

        num_messages_allocated++;
    }

    spm->clock_value    = clock_value;
    spm->id                = id;
    spm->stack_pointer    = __builtin_getreg(15); /* getreg(REG_A7) */
    spm->flags            = flags;

    PutMsg(profiler_port,(struct Message *)spm);
    WaitPort(reply_port);

    result = 0;

 out:

    return(result);
}

/****************************************************************************/

STATIC ULONG
get_current_time(void)
{
    ULONG result;

    ReadEClock(&last_time);

    result = (last_time.ev_lo - start_time.ev_lo - overhead) / eclock_frequency;

    return(result);
}

/****************************************************************************/

STATIC void
update_overhead(void)
{
    struct EClockVal ev;

    ReadEClock(&ev);

    overhead += (ev.ev_lo - last_time.ev_lo);
}

/****************************************************************************/

void ASM
_PROLOG(REG(a0,char * id))
{
    if(initialized)
    {
        if(send_profiler_message(get_current_time(),id,SPROF_ENTRY) == OK)
            update_overhead();
    }
}

/****************************************************************************/

void ASM
_EPILOG(REG(a0,char * id))
{
    if(initialized)
    {
        if(send_profiler_message(get_current_time(),id,SPROF_EXIT) == OK)
            update_overhead();
    }
}

/****************************************************************************/

STATIC VOID
__profile_init(void)
{
    struct SPROFMSG * spm;
    BOOL ready = FALSE;

    this_process = (struct Process *)FindTask(NULL);

    time_port = CreateMsgPort();
    if(time_port == NULL)
        goto out;

    time_request = (struct timerequest *)CreateIORequest(time_port,sizeof(*time_request));
    if(time_request == NULL)
        goto out;

    if(OpenDevice(TIMERNAME,UNIT_ECLOCK,(struct IORequest *)time_request,0) != OK)
        goto out;

    TimerBase = (struct Library *)time_request->tr_node.io_Device;

    reply_port = CreateMsgPort();
    if(reply_port == NULL)
        goto out;

    Forbid();

    profiler_port = FindPort("SPROF_Profiler");
    if(profiler_port == NULL)
    {
        Permit();

        goto out;
    }

    if(send_profiler_message(0,NULL,SPROF_INIT) != OK)
    {
        Permit();

        goto out;
    }

    initialized = TRUE;

    Permit();

    while((spm = (struct SPROFMSG *)GetMsg(reply_port)) == NULL)
        WaitPort(reply_port),

    PutMsg(reply_port,(struct Message *)spm);

    if(spm->flags == SPROF_DENIED || spm->flags == SPROF_TERM)
        goto out;

    eclock_frequency = ReadEClock(&start_time) / 1000;

    ready = TRUE;

 out:

    if(NOT ready)
        __profile_exit();
}

/****************************************************************************/

STATIC VOID
__profile_exit(void)
{
    if(initialized)
    {
        send_profiler_message(0,NULL,SPROF_TERM);

        initialized = FALSE;
    }

    if(reply_port != NULL)
    {
        struct SPROFMSG * spm;

        while(num_messages_allocated > 0)
        {
            while((spm = (struct SPROFMSG *)GetMsg(reply_port)) == NULL)
                WaitPort(reply_port),

            FreeMem(spm,sizeof(*spm));

            num_messages_allocated--;
        }

        DeleteMsgPort(reply_port);
        reply_port = NULL;
    }

    if(time_request != NULL)
    {
        if(time_request->tr_node.io_Device != NULL)
            CloseDevice((struct IORequest *)time_request);

        DeleteIORequest((struct IORequest *)time_request);
        time_request = NULL;
    }

    if(time_port != NULL)
    {
        DeleteMsgPort(time_port);
        time_port = NULL;
    }
}

/****************************************************************************/

PROFILE_CONSTRUCTOR(profile_init)
{
    ENTER();

    __profile_init();

    LEAVE();

    CONSTRUCTOR_SUCCEED();
}

/****************************************************************************/

PROFILE_DESTRUCTOR(profile_exit)
{
    ENTER();

    __profile_exit();

    LEAVE();
}