
static char rcsid[] = 
	"$Id: trcmess.c,v 4.2 1997/04/02 19:47:43 pvmsrc Exp $";

/*
 *         Tracer version 1.0:  A Trace File Generator for PVM
 *           Oak Ridge National Laboratory, Oak Ridge TN.
 *           Authors:  James Arthur Kohl and G. A. Geist
 *                   (C) 1994 All Rights Reserved
 *
 *                              NOTICE
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted
 * provided that the above copyright notice appear in all copies and
 * that both the copyright notice and this permission notice appear
 * in supporting documentation.
 *
 * Neither the Institution, Oak Ridge National Laboratory, nor the
 * Authors make any representations about the suitability of this
 * software for any purpose.  This software is provided ``as is''
 * without express or implied warranty.
 *
 * Tracer was funded by the U.S. Department of Energy.
 */


/* Tracer Library Header */

#include "trclib.h"


/* Routines */

int
trc_recv_messages( ID, max_count, status )
TRC_ID ID;
int max_count;
int *status;
{
	static int *tids = (int *) NULL;
	static int ntids = 0;

	TRC_HOST H;

	TRC_ID DEAD_ID_last;
	TRC_ID DEAD_ID_next;
	TRC_ID DEAD_ID;

	int msgtag;
	int others;
	int ecnt;
	int cnt;
	int len;
	int mid;
	int num;
	int tid;
	int src;
	int tmp;
	int cc;
	int i;

	/* Verify Message Codes Set */

	if ( TRC_HOST_ADD_NOTIFY_CODE == -1
		|| TRC_HOST_DEL_NOTIFY_CODE == -1
#ifndef USE_PVM_33
		|| ID->event_ctx == -1 || ID->output_ctx == -1
#endif
		|| ID->event_tag == -1 || ID->output_tag == -1 )
	{
		printf( "Error: Message Codes Not Set - %d %d %d %d %d %d\n",
			TRC_HOST_ADD_NOTIFY_CODE, TRC_HOST_DEL_NOTIFY_CODE,
			ID->event_ctx, ID->output_ctx,
			ID->event_tag, ID->output_tag );
		
		return( -1 );
	}

	/* Clear Out Flags */

	if ( status != NULL )
		*status = TRC_MSG_STATUS_NONE;

	/* Init Event Counter */

	ecnt = 0;

	/* Check for Host Sync Updates */

	ecnt += trc_check_host_sync( ID );

	/* Receive Stuff */

	cnt = 0;

	while ( cnt < max_count
		&& (mid = trc_get_msg( ID, &len, &msgtag, &src, &others )) > 0 )
	{
		/* Set Activity Flag */

		if ( status != NULL )
			*status |= TRC_MSG_STATUS_ACTIVE;

		/* Host Added */

		if ( msgtag == TRC_HOST_ADD_NOTIFY_CODE && src == 0x80000000 )
		{
			cc = pvm_upkint( &num, 1, 1 );

			if ( cc < 0 )
				pvm_perror( "Error Unpacking Host Add Notify" );

			else
			{
				if ( num > ntids )
				{
					ntids = num * 2;

					if ( tids )
						free( tids );

					tids = (int *) malloc( (unsigned) ntids
						* sizeof(int) );
					trc_memcheck( tids, "Host Add Notify Tids Array" );
				}

				cc = pvm_upkint( tids, num, 1 );

				if ( cc < 0 )
					pvm_perror( "Error Unpacking Host Add TIDs" );

				else
				{
					pvm_notify( PvmHostDelete, 100, num, tids );

					ecnt += trc_handle_host_add_notify( ID, tids, num );
				}
			}
		}

		/* Host Deleted */

		else if ( msgtag == TRC_HOST_DEL_NOTIFY_CODE
			&& src == 0x80000000 )
		{
			cc = pvm_upkint( &tid, 1, 1 );

			if ( cc < 0 )
				pvm_perror( "Error Unpacking Host Delete Notify" );

			else
				ecnt += trc_handle_host_del_notify( ID, tid );
		}

		/* PVM Trace Event */

		else if ( msgtag == ID->event_tag )
		{
			ecnt += trc_handle_trace_message( ID, src );

			if ( status != NULL )
			{
				if ( ecnt )
					*status |= TRC_MSG_STATUS_OVERWRITE;

				else
					*status |= TRC_MSG_STATUS_CLEANUP;
			}
		}

		/* PVM Output Event */

		else if ( msgtag == ID->output_tag )
		{
			ecnt += trc_handle_output_message( ID );

			if ( status != NULL )
			{
				if ( ecnt )
					*status |= TRC_MSG_STATUS_OVERWRITE;

				else
					*status |= TRC_MSG_STATUS_CLEANUP;
			}
		}

		/* Unknown Message */

		else
		{
			fprintf( stderr,
				"Tracer:  msg from 0x%x code %d len %d ?\n",
				src, msgtag, len );
		}

		if ( pvm_freebuf( mid ) < 0 )
			pvm_perror( "Error Freeing Message Buffer" );

		cnt++;
	}

	if ( mid < 0 )
	{
		pvm_perror( "PVM System Failure" );

		printf( "Tracer exiting...\n" );

		return( -1 );
	}

	if ( status != NULL )
	{
		if ( cnt == max_count )
			*status |= TRC_MSG_STATUS_MORE;
	}

	/* Try Flushing Dead IDs */

	if ( others )
	{
		DEAD_ID = TRC_DEAD_ID_LIST;

		DEAD_ID_last = (TRC_ID ) NULL;

		while ( DEAD_ID != NULL )
		{
			trc_recv_messages( DEAD_ID, 1, &tmp );

			if ( status != NULL )
				*status |= tmp;

			if ( trc_tevtasks_dead( DEAD_ID ) )
			{
				DEAD_ID_next = DEAD_ID->next;

				if ( DEAD_ID_last != NULL )
					DEAD_ID_last->next = DEAD_ID_next;

				else
					TRC_DEAD_ID_LIST = DEAD_ID_next;

				trc_free_tracer_id( &DEAD_ID );

				DEAD_ID = DEAD_ID_next;
			}

			else
			{
				DEAD_ID_last = DEAD_ID;

				DEAD_ID = DEAD_ID->next;
			}
		}
	}

	return( ecnt );
}


int
trc_get_msg( ID, len, msgtag, src, others )
TRC_ID ID;
int *len;
int *msgtag;
int *src;
int *others;
{
	int mid;
	int tag;

	/* Reset "Others" Flag */

	if ( others != NULL )
		*others = TRC_FALSE;

	/* Check for Host Add */

	if ( (mid = pvm_nrecv( -1, TRC_HOST_ADD_NOTIFY_CODE )) > 0 )
	{
		if ( pvm_bufinfo( mid, len, msgtag, src ) < 0 )
		{
			pvm_perror( "Error Checking Message Buffer" );
		
			return( -1 );
		}

		return( mid );
	}

	else if ( mid < 0 )
	{
		pvm_perror( "Error Checking Host Add Notify Message" );
			
		return( -1 );
	}

	/* Check for Host Delete */

	if ( (mid = pvm_nrecv( -1, TRC_HOST_DEL_NOTIFY_CODE )) > 0 )
	{
		if ( pvm_bufinfo( mid, len, msgtag, src ) < 0 )
		{
			pvm_perror( "Error Checking Message Buffer" );
		
			return( -1 );
		}

		return( mid );
	}

	else if ( mid < 0 )
	{
		pvm_perror( "Error Checking Host Delete Notify Message" );
			
		return( -1 );
	}

	/* Any message at all? */

	if ( (mid = pvm_probe( -1, -1 )) > 0 )
	{
		if ( pvm_bufinfo( mid, len, &tag, src ) < 0 )
		{
			pvm_perror( "Error Checking Message Buffer" );
		
			return( -1 );
		}

		if ( msgtag != NULL )
			*msgtag = tag;

		/* The next one's for us... */

		if ( tag == ID->event_tag || tag == ID->output_tag )
		{
			pvm_setrbuf( mid );

			return( mid );
		}

		/* Or not... */

		else
		{
			if ( others != NULL )
				*others = TRC_TRUE;

			return( 0 );
		}
	}

	/* System Error */

	else if ( mid < 0 )
	{
		pvm_perror( "Error Probing for Trace / Output Messages" );
		
		return( -1 );
	}

	/* No Messages */

	else
		return( 0 );
}


int
trc_handle_trace_message( ID, src )
TRC_ID ID;
int src;
{
	TRC_DATADESC DD;

	TRC_TEVDESC TD;

	TRC_TEVTASK TT;

	TRC_HOST H;

	TRC_DID dptr;

	char diddesc[1024];
	char tmp[1024];
	char did[8];

	int desc_done;
	int did_done;
	int done;
	int omit;

	int entry_exit;
	int dump_event;
	int pvmd_tid;
	int did_int;
	int tusec;
	int tsec;
	int ecnt;
	int eid;
	int tid;
	int dt;

	int marker;
	int nest;

	ecnt = 0;

	TRC_PVMCKERR( pvm_upkint( &marker, 1, 1 ),
		"Unpacking Trace Message", return( ecnt ) );

	/* Old PVM 3.3 Trace Format */

	if ( marker > 0 )
	{
		tsec = marker;

		TRC_PVMCKERR( pvm_upkint( &tusec, 1, 1 ),
			"Event Parse Error", return( ecnt ) );

		TRC_PVMCKERR( pvm_upkint( &tid, 1, 1 ),
			"Event Parse Error", return( ecnt ) );

		TRC_PVMCKERR( pvm_upkint( &eid, 1, 1 ),
			"Event Parse Error", return( ecnt ) );

		ecnt += trc_store_old_trace_event( ID, tsec, tusec, tid, eid );

		return( ecnt );
	}

	/* New PVM 3.4 Format */

	/* Get Task & Host Info */

	TT = trc_get_tevtask_tid( ID, src );

	pvmd_tid = pvm_tidtohost( src );

	if ( !pvmd_tid )
		pvmd_tid = pvm_tidtohost( TRC_TID );

	H = trc_get_host_tid( pvmd_tid );

	/* Set Leftover Event Omit Flag */

	omit = ( ID->trace_out == NULL
		|| ( TT != NULL && TRC_TASK_OMIT( TT ) ) )
			? TRC_TRUE : TRC_FALSE;

	/* Process Event Message */

	nest = 0;

	done = 0;

	do
	{
		switch ( marker )
		{
			case TEV_MARK_EVENT_BUFFER:
			{
				nest++;

				break;
			}

			case TEV_MARK_EVENT_BUFFER_END:
			{
				if ( !(--nest) )
					done++;

				break;
			}

			case TEV_MARK_DATA_ID:
			{
				did_done = 0;

				do
				{
					TRC_PVMCKERR( pvm_upkint( &did_int, 1, 1 ),
						"Data ID Message", return( ecnt ) );

					if ( did_int != TEV_MARK_DATA_ID_END )
					{
						TRC_PVMCKERR( pvm_upkstr( did ),
							"Data ID Name", return( ecnt ) );

						TRC_PVMCKERR( pvm_upkstr( diddesc ),
							"Data ID Description", return( ecnt ) );

						dptr = (TRC_DID) trc_lookup_trie( TRC_DID_TRIE,
							did );
						
						if ( dptr == NULL
							|| dptr->id != did_int
							|| strcmp( dptr->desc, diddesc ) )
						{
							dptr = trc_create_did();

							dptr->id = did_int;

							dptr->name = trc_copy_str( did );

							dptr->desc = trc_copy_str( diddesc );

							dptr->next = TRC_DID_LIST;

							TRC_DID_LIST = dptr;

							trc_add_to_trie( TRC_DID_TRIE,
								dptr->name, (void *) dptr );
						}

						trc_set_didlist( H, dptr );
					}

					else
						did_done++;
				}
				while ( !did_done );

				if ( !nest )
					done++;

				break;
			}

			case TEV_MARK_EVENT_DESC:
			{
				TD = trc_create_tevdesc();

				TD->refcount = 1;

				TRC_PVMCKERR( pvm_upkint( &eid, 1, 1 ),
					"Event Descriptor ID", return( ecnt ) );

				TD->eid = eid & ~( TEV_EVENT_ENTRY | TEV_EVENT_EXIT );

				if ( eid & TEV_EVENT_ENTRY )
					TD->entry_exit = TRC_ENTRY_TEV;

				else if ( eid & TEV_EVENT_EXIT )
					TD->entry_exit = TRC_EXIT_TEV;

				else
					TD->entry_exit = TRC_IGNORE_TEV;

				TRC_PVMCKERR( pvm_upkstr( tmp ),
					"Event Descriptor Name", return( ecnt ) );

				TD->name = trc_copy_str( tmp );
		
				desc_done = 0;

				do
				{
					TRC_PVMCKERR( pvm_upkint( &did_int, 1, 1 ),
						"Semantic Data ID", return( ecnt ) );

					if ( did_int != TEV_MARK_EVENT_DESC_END )
					{
						if ( TD->ddesc == NULL )
							DD = TD->ddesc = trc_create_datadesc();

						else
							DD = DD->next = trc_create_datadesc();

						if ( H->didlist == NULL )
						{
							printf(
							"Warning: Missing DID List For Host %s\n",
								H->name );
						}

						DD->did = trc_get_did( H, did_int );

						if ( DD->did == NULL )
						{
							printf( "Error: DID=%d Not Found\n",
								did_int );

							trc_free_tevdesc( &TD );

							return( ecnt );
						}

						TRC_PVMCKERR( pvm_upkint( &dt, 1, 1 ),
							"Data Type", return( ecnt ) );

						DD->dt = dt &
							~( TEV_DATA_SCALAR | TEV_DATA_ARRAY );

						DD->array = dt &
							( TEV_DATA_SCALAR | TEV_DATA_ARRAY );

						if ( !trc_unpack_data_value( DD ) )
						{
							trc_free_tevdesc( &TD );

							return( ecnt );
						}
					}

					else
						desc_done++;
				}
				while ( !desc_done );

				dump_event = trc_add_tevdesc( ID, &TD, src, omit );

				trc_set_tevlist( TT, H, TD );

				if ( TD->dump )
					trc_dump_tevdesc( ID, TD, src );

				if ( dump_event )
					trc_store_data_values( ID, TD, src, omit );

				if ( !omit )
					ecnt++;

				if ( !nest )
					done++;

				break;
			}

			case TEV_MARK_USER_EVENT_RECORD:
			{
				TD = trc_create_tevdesc();

				TD->refcount = 1;

				TRC_PVMCKERR( pvm_upkint( &eid, 1, 1 ),
					"Event Descriptor ID", return( ecnt ) );

				TD->eid = eid & ~( TEV_EVENT_ENTRY | TEV_EVENT_EXIT );

				if ( eid & TEV_EVENT_ENTRY )
					TD->entry_exit = TRC_ENTRY_TEV;

				else if ( eid & TEV_EVENT_EXIT )
					TD->entry_exit = TRC_EXIT_TEV;

				else
					TD->entry_exit = TRC_IGNORE_TEV;

				TRC_PVMCKERR( pvm_upkstr( tmp ),
					"Event Descriptor Name", return( ecnt ) );

				TD->name = trc_copy_str( tmp );
		
				/* Yank Out Hardwired Timestamp */

				DD = TD->ddesc = trc_create_datadesc();

				DD->did = trc_get_did( (TRC_HOST) NULL, TEV_DID_TS );

				DD->dt = TEV_DATA_INT;
				DD->array = TEV_DATA_SCALAR;

				if ( !trc_unpack_data_value( DD ) )
				{
					trc_free_tevdesc( &TD );

					return( ecnt );
				}

				DD = DD->next = trc_create_datadesc();

				DD->did = trc_get_did( (TRC_HOST) NULL, TEV_DID_TU );

				DD->dt = TEV_DATA_INT;
				DD->array = TEV_DATA_SCALAR;

				if ( !trc_unpack_data_value( DD ) )
				{
					trc_free_tevdesc( &TD );

					return( ecnt );
				}

				desc_done = 0;

				do
				{
					TRC_PVMCKERR( pvm_upkint( &dt, 1, 1 ),
						"Data Type", return( ecnt ) );

					if ( dt != TEV_MARK_USER_EVENT_RECORD_END )
					{
						DD = DD->next = trc_create_datadesc();

						DD->dt = dt &
							~( TEV_DATA_SCALAR | TEV_DATA_ARRAY );

						DD->array = dt &
							( TEV_DATA_SCALAR | TEV_DATA_ARRAY );

						if ( !trc_unpack_data_value( DD ) )
						{
							trc_free_tevdesc( &TD );

							return( ecnt );
						}
					}

					else
						desc_done++;
				}
				while ( !desc_done );

				dump_event = trc_add_tevdesc( ID, &TD, src, omit );

				if ( TD->dump )
					trc_dump_tevdesc( ID, TD, src );

				if ( dump_event )
					trc_store_data_values( ID, TD, src, omit );

				if ( !omit )
					ecnt++;

				if ( !nest )
					done++;

				break;
			}

			case TEV_MARK_EVENT_RECORD:
			{
				TRC_PVMCKERR( pvm_upkint( &eid, 1, 1 ),
					"Event ID", return( ecnt ) );

				if ( eid & TEV_EVENT_ENTRY )
					entry_exit = TRC_ENTRY_TEV;

				else if ( eid & TEV_EVENT_EXIT )
					entry_exit = TRC_EXIT_TEV;

				else
					entry_exit = TRC_IGNORE_TEV;

				eid &= ~( TEV_EVENT_ENTRY | TEV_EVENT_EXIT );

				TD = trc_get_tevdesc( TT, H, eid, entry_exit );

				if ( TD != NULL )
				{
					if ( trc_store_trace_event( ID, TD, src, omit ) )
						ecnt++;
				}

				else
					printf( "Error: Descriptor Not Found\n\n" );

				if ( !nest )
					done++;

				break;
			}
		}

		if ( !done )
		{
			TRC_PVMCKERR( pvm_upkint( &marker, 1, 1 ),
				"Unpacking Trace Message", return( ecnt ) );
		}
	}
	while ( !done );

	return( ecnt );
}


int
trc_handle_output_message( ID )
TRC_ID ID;
{
	TRC_TEVTASK TT;

	char tmp[255];
	char msg[255];

	char *output;

	int ecnt;
	int omit;
	int len;
	int tid;

	ecnt = 0;

	TRC_PVMCKERR( pvm_upkint( &tid, 1, 1 ),
		"Error Unpacking Output Message", return( ecnt ) );
	
	TRC_PVMCKERR( pvm_upkint( &len, 1, 1 ),
		"Error Unpacking Output Length", return( ecnt ) );

	/* Set Leftover Event Omit Flag */

	TT = trc_get_tevtask_tid( ID, tid );

	omit = ( ID->trace_out == NULL
		|| ( TT != NULL && TRC_TASK_OMIT( TT ) ) )
			? TRC_TRUE : TRC_FALSE;

	/* Unpack Output */

	if ( len > 0 )
	{
		output = (char *) malloc( (unsigned) ( len + 1 )
			* sizeof(char) );
		trc_memcheck( output, "PVM Output String" );

		TRC_PVMCKERR( pvm_upkbyte( output, len, 1 ),
			"Error Unpacking Task Output",
			free(output); return( ecnt ) );

		output[ len ] = '\0';

		if ( !omit )
			ecnt = trc_store_output_event( ID, tid, output, len );

		free( output );
	}

	else
	{
		switch ( len )
		{
			case 0:
			{
				if ( !omit )
				{
					sprintf( tmp, "GOTEOF\n" );

					ecnt = trc_store_output_event( ID,
						tid, tmp, strlen(tmp) );
				}

				if ( TT != NULL )
				{
					if ( TT->tevstatus == TRC_TASK_ALIVE )
					{
						TT->outstatus = TRC_TASK_EOF;

						trc_check_for_dead_host( ID, TT );

						if ( !trc_tevtasks_alive( ID ) )
							trc_end_trace( ID );
					}

					else
						TT->outstatus = TRC_TASK_EOF;
				}

				else if ( !omit )
				{
					printf(
						"\nWarning: GOTEOF Unknown Task TID=0x%x.\n\n",
						tid );
				}

				break;
			}

			case -1:
			{
				if ( omit )
					break;

				sprintf( tmp, "GOTSPAWN\n" );

				ecnt = trc_store_output_event( ID,
					tid, tmp, strlen(tmp) );

				if ( TT == NULL )
				{
					sprintf( msg, "Task TID=0x%x Connected to %s",
						tid, TRC_NAME );

					trc_status_msg( ID, msg );

					TT = trc_create_tevtask();

					TT->tid = tid;

					TT->outstatus = TRC_TASK_OUT;
					TT->tevstatus = TRC_TASK_NOLIFE;

					TT->next = ID->tevtask_list;

					ID->tevtask_list = TT;
				}

				else
					TT->outstatus = TRC_TASK_OUT;

				break;
			}

			case -2:
			{
				if ( omit )
					break;

				sprintf( tmp, "CREATION\n" );

				ecnt = trc_store_output_event( ID,
					tid, tmp, strlen(tmp) );

				if ( TT == NULL )
				{
					sprintf( msg, "Task TID=0x%x Connected to %s",
						tid, TRC_NAME );

					trc_status_msg( ID, msg );

					TT = trc_create_tevtask();

					TT->tid = tid;

					TT->outstatus = TRC_TASK_OUT;
					TT->tevstatus = TRC_TASK_NOLIFE;

					TT->next = ID->tevtask_list;

					ID->tevtask_list = TT;
				}

				else
					TT->outstatus = TRC_TASK_OUT;

				break;
			}

			default:
			{
				if ( !omit )
				{
					fprintf( stderr,
						"Console: unknown output msg 0x%x?\n", tid );
				}
			}
		}
	}

	return( ecnt );
}

