library/sas_profile.c
/*
* $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();
}