
static char rcsid[] =
	"$Id: trcsort.c,v 4.9 1997/06/17 22:00:41 pvmsrc Exp $";

/*
 *         Trcsort version 1.0:  A Trace File Sorter 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 Headers */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <pvm3.h>
#include <pvmtev.h>

#include "trcdef.h"


/* Constants */

#define HANDLE_HOST_SYNC    1

#define MAX_OPEN_FP    10


/* Macros */

#define TIME_COMPARE( _t1, _t2 ) \
( \
	( (_t1).tv_sec > (_t2).tv_sec \
			|| ( (_t1).tv_sec == (_t2).tv_sec \
				&& (_t1).tv_usec > (_t2).tv_usec ) ) ? 1 : \
		( ( (_t1).tv_sec == (_t2).tv_sec \
			&& (_t1).tv_usec == (_t2).tv_usec ) ? 0 : -1 ) \
) \
\


/* Data Structures */

struct fp_buffer_struct
{
	FILE *fp;
	char *rwstr;
	/* TMPFILE */ struct tmpfile_struct *tmpfile;
	/* FPBUF */ struct fp_buffer_struct *next;
};

typedef struct fp_buffer_struct *FPBUF;


struct tmpfile_struct
{
	FPBUF fpbuf;
	char *fname;
	long lastseek;
	TRC_TEVDESC savetd;
	TRC_TEVREC savetr;
	int tid;
	struct timeval nexttime;
	struct timeval lasttime;
	struct timeval delta;
	int valid;
	int eof;
	/* TMPFILE */ struct tmpfile_struct *next;
};

typedef struct tmpfile_struct *TMPFILE;


/* Handler Routines */

TRC_TEVREC save_tevrec();

TMPFILE create_tmpfile();
TMPFILE get_tmpfile();

FPBUF create_fpbuf();

void get_tevrec_tmpfile();
void handle_event_split();
void handle_event_join();
void handle_passthru();
void my_status_msg();
void program_init();
void free_fpbuf();
void read_args();
void sync_time();
void close_fp();
void trc_init();
void cleanup();
void usage();
void bail();

int verify_fp();


/* Globals */

TMPFILE CURRENTTF;
TMPFILE	LASTTF;
TMPFILE	TMPF;

FPBUF	FPS;
int		NFPS;

TRC_ID	ID;

char	DIRNAME[255];

char	*TRACE_OUTFILE;
char	*TRACE_INFILE;
char	*OUTPUT_FILE;

int		vflag;


/* MAIN */

int
main( argc, argv )
int argc;
char **argv;
{
	TMPFILE NEXTTF;
	TMPFILE HOSTTF;
	TMPFILE TF;

	static struct timeval tmpt = { -1, -1 };

	int host;
	int done;
	int eof;

	/* Read Command Line Args */

	read_args( argc, argv );

	/* Initialize Program Constants & Structs */

	program_init();

	/* Initialize Tracer Globals */

	trc_init();

	/* Parse Input Trace, Create Subfiles */

	while ( trc_read_trace_event( ID, &eof ) );

	fclose( ID->trace_in );

	/* Flush Temporary File Ptrs, Reset Subfiles for Reading */

	TF = TMPF;

	while ( TF != NULL )
	{
		close_fp( TF );

		TF->eof = TRC_FALSE;

		TF = TF->next;
	}

	/* Reset Pass Thru -> All Goes to Output Trace Now */

	LASTTF = (TMPFILE) NULL;

	/* Set Event Handling for Joining */

	ID->handle_event = handle_event_join;

	/* Read In First Event From Each Subfile (Prime Pump) */

	TF = TMPF;

	while ( TF != NULL )
	{
		/* Check Setting of Time Stamp Delta */

		host = pvm_tidtohost( TF->tid );

		if ( host != TF->tid )
		{
			HOSTTF = get_tmpfile( host, &tmpt, TRC_FALSE );

			if ( HOSTTF != NULL )
			{
				TF->delta.tv_sec = HOSTTF->delta.tv_sec;
				TF->delta.tv_usec = HOSTTF->delta.tv_usec;
			}
		}

		/* Get First Event (Record) */

		TF->valid = TRC_FALSE;

		do
		{
			get_tevrec_tmpfile( TF );
		}
		while( !(TF->valid) && !(TF->eof) );

		if ( TF->eof )
			close_fp( TF );

		TF = TF->next;
	}

	/* Read In Subfiles, Dump Sorted Trace */

	do
	{
		done = TRC_TRUE;

		/* Find "Next" Trace Event */

		NEXTTF = (TMPFILE) NULL;

		TF = TMPF;

		while ( TF != NULL )
		{
			if ( TF->valid )
			{
				if ( NEXTTF != NULL )
				{
					if ( TIME_COMPARE( TF->nexttime,
							NEXTTF->nexttime ) < 0
						|| ( TIME_COMPARE( TF->nexttime,
								NEXTTF->nexttime ) == 0
							&& TF->tid < NEXTTF->tid ) )
					{
						NEXTTF = TF;
					}
				}

				else
					NEXTTF = TF;

				done = TRC_FALSE;
			}

			TF = TF->next;
		}

		/* Dump It */

		if ( NEXTTF != NULL )
		{
			trc_store_tevrec( ID, NEXTTF->savetd, NEXTTF->savetr,
					NEXTTF->tid );

			/* Get Next Event for Task */

			NEXTTF->valid = TRC_FALSE;

			do
			{
				get_tevrec_tmpfile( NEXTTF );
			}
			while( !(NEXTTF->valid) && !(NEXTTF->eof) );

			if ( NEXTTF->eof )
				close_fp( NEXTTF );
		}
	}
	while ( !done );

	fclose( ID->trace_out );

	cleanup();

	exit( 0 );
}


void
handle_event_split( trcid, TD, TR )
TRC_ID trcid;
TRC_TEVDESC TD;
TRC_TEVREC TR;
{
	TMPFILE TFptr;
	TMPFILE TF;

	FILE *fpsave;

	struct timeval tmpt;

	int tid;

	if ( trcid != ID )
	{
		printf( "Error: Unknown Trace ID\n" );

		return;
	}

	/* Grab Task ID from Event */

	tid = TRC_GET_TEVREC_VALUE( TR, "TID", int );

	tmpt.tv_sec = TRC_GET_TEVREC_VALUE( TR, "TS", int );
	tmpt.tv_usec = TRC_GET_TEVREC_VALUE( TR, "TU", int );

	/* Key Off of TID & Time Stamp to Select Temp File */

	TF = get_tmpfile( tid, &tmpt, TRC_TRUE );

	if ( TF == NULL )
	{
		printf( "\nError Creating Temporary Trace File\n\n" );
		bail();
	}

	/* Set Time Stamp Marker */

	TF->lasttime.tv_sec = tmpt.tv_sec;
	TF->lasttime.tv_usec = tmpt.tv_usec;

	/* Check for Host Sync */

	if ( TD->hid == HANDLE_HOST_SYNC )
	{
		TF->delta.tv_sec = TRC_GET_TEVREC_VALUE( TR, "HDS", int );
		TF->delta.tv_usec = TRC_GET_TEVREC_VALUE( TR, "HDU", int );

		TFptr = TMPF;

		while ( TFptr != NULL )
		{
			if ( TFptr->tid == TF->tid && TFptr != TF )
			{
				TFptr->delta.tv_sec = TF->delta.tv_sec;
				TFptr->delta.tv_usec = TF->delta.tv_usec;
			}

			TFptr = TFptr->next;
		}
	}

	/* Check for Descriptor Dump */

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

	/* Write Event to Temp File */

	if ( !verify_fp( TF, "a" ) )
		bail();

	fpsave = ID->trace_out;

	ID->trace_out = TF->fpbuf->fp;

	trc_store_tevrec( ID, TD, TR, tid );

	ID->trace_out = fpsave;
	
	/* Save Last Temp File Ptr for Pass Thru */

	LASTTF = TF;
}


void
handle_event_join( trcid, TD, TR )
TRC_ID trcid;
TRC_TEVDESC TD;
TRC_TEVREC TR;
{
	if ( trcid != ID )
	{
		printf( "Error: Unknown Trace ID\n" );

		return;
	}

	if ( CURRENTTF == NULL )
	{
		printf( "Warning:  Current Temp File Not Set...\n" );

		return;
	}

	CURRENTTF->nexttime.tv_sec = TRC_GET_TEVREC_VALUE( TR, "TS", int );
	CURRENTTF->nexttime.tv_usec = TRC_GET_TEVREC_VALUE( TR, "TU", int );

	sync_time( CURRENTTF );

	CURRENTTF->savetd = TD;
	CURRENTTF->savetr = save_tevrec( TR );

	CURRENTTF->valid = TRC_TRUE;
}


void
handle_passthru( trcid, str )
TRC_ID trcid;
char *str;
{
	if ( trcid != ID )
	{
		printf( "Error: Unknown Trace ID\n" );

		return;
	}

	if ( LASTTF != NULL )
	{
		if ( !verify_fp( LASTTF, "a" ) )
			bail();

		fprintf( LASTTF->fpbuf->fp, "%s\n", str );
	}
	
	else
		fprintf( ID->trace_out, "%s\n", str );
}


void
read_args( argc, argv )
int argc;
char **argv;
{
	char tmp[1024];

	int i, j, k;
	int do_usage;
	int len;

	TRACE_OUTFILE = (char *) NULL;
	TRACE_INFILE = (char *) NULL;
	OUTPUT_FILE = (char *) NULL;

	vflag = 0;

	do_usage = 0;

	for ( i=1 ; i < argc ; i++ )
	{
		if ( argv[i][0] == '-' )
		{
			k = i + 1;

			len = strlen( argv[i] );

			for ( j=0 ; j < len ; j++ )
			{
				switch ( argv[i][j] )
				{
					case 'o':
					{
						if ( TRACE_OUTFILE != NULL )
							free( TRACE_OUTFILE );
			
						TRACE_OUTFILE = trc_copy_str( argv[ k++ ] );

						break;
					}

					case 'O':
					{
						if ( OUTPUT_FILE != NULL )
							free( OUTPUT_FILE );
			
						OUTPUT_FILE = trc_copy_str( argv[ k++ ] );

						break;
					}

					case 'H':
					case 'h':
						usage(); break;

					case 'v': vflag++; break;

					case '-': break;

					default:
					{
						printf( "Unknown Option -%c\n", argv[i][j] );

						do_usage++;

						break;
					}
				}
			}

			i = k - 1;
		}

		else
		{
			if ( TRACE_INFILE != NULL )
				free( TRACE_INFILE );
			
			TRACE_INFILE = trc_copy_str( argv[i] );
		}
	}

	if ( do_usage )
		usage();
}


void
usage()
{
	printf( "\nusage:  trcsort [ <trace_infile> ] " );
	printf( "[ -o <trace_outfile> ] " );
	printf( "[ -O <output_file> ] " );
	printf( "[ -v ] " );
	printf( "\n\n" );

	printf( "where:\n" );
	printf( "------\n" );

	printf( "<trace_infile>    =    " );
	printf( "trace file to be sorted (default is stdin)\n" );

	printf( "<trace_outfile>   =    " );
	printf( "file to store sorted trace (default is stdout)\n" );

	printf( "<output_file>     =    " );
	printf( "file to store separate copy of any task output\n" );

	printf( "-v                =    " );
	printf( "display dynamic open file usage\n" );

	printf( "\n" );

	exit( 0 );
}


void
program_init()
{
	/* Create Temp Directory for Subfiles */

	sprintf( DIRNAME, "tmpsort.%d", (int) getpid() );

	if ( mkdir( DIRNAME, S_IRWXU | S_IRWXG | S_IRWXO ) )
	{
		printf( "\nError Creating Temporary Directory \"%s\".\n\n",
				DIRNAME );

		exit( -1 );
	}

	/* Initialize Temporary File Ptrs */

	LASTTF = (TMPFILE) NULL;

	TMPF = (TMPFILE) NULL;

	FPS = (FPBUF) NULL;
	NFPS = 0;
}


void
trc_init()
{
	/* Initialize Tracer */

	trc_tracer_init();

	/* Get Tracer ID */

	ID = trc_get_tracer_id();

	/* Set Local Handler Routines */

	ID->handle_command = handle_passthru;
	ID->handle_comment = handle_passthru;
	ID->handle_event = handle_event_split;
	ID->status_msg = my_status_msg;

	/* Set Up Handle ID for Host Sync Events */

	trc_add_to_trie( TRC_HANDLE_TRIE,
		"host_sync",    (void *) HANDLE_HOST_SYNC );

	/* Create In & Out Trace File Ptrs */

	if ( TRACE_INFILE != NULL )
	{
		ID->trace_in = fopen( TRACE_INFILE, "r" );
		if ( !trc_filecheck( ID->trace_in, TRACE_INFILE ) )
			bail();
	}

	else
		ID->trace_in = stdin;

	if ( TRACE_OUTFILE != NULL )
	{
		ID->trace_out = fopen( TRACE_OUTFILE, "w" );
		if ( !trc_filecheck( ID->trace_out, TRACE_OUTFILE ) )
			bail();
	}

	else
		ID->trace_out = stdout;

	/* Open Additional Output File */

	if ( OUTPUT_FILE != NULL )
	{
		trc_set_output_file( ID, OUTPUT_FILE );

		trc_open_output_file( ID );
	}
}


/* Status Message Handler */

void my_status_msg( ID, msg )
TRC_ID ID;
char *msg;
{
	printf( "%s\n", msg );
}


/* TRC_TEVREC Routines */

TRC_TEVREC
save_tevrec( TR )	/* actually, more like "steal_tevrec" :-) */
TRC_TEVREC TR;
{
	TRC_TEVREC tmp;

	tmp = trc_create_tevrec();

	tmp->ddesc = TR->ddesc;
	TR->ddesc = (TRC_DATADESC) NULL;

	tmp->value = TR->value;
	TR->value = (TRC_VALUE) NULL;

	tmp->num = TR->num;
	TR->num = -1;

	tmp->next = TR->next;
	TR->next = (TRC_TEVREC) NULL;

	return( tmp );
}


/* TMPFILE Routines */

TMPFILE
create_tmpfile()
{
	TMPFILE tmp;

	tmp = (TMPFILE) malloc( sizeof( struct tmpfile_struct ) );
	trc_memcheck( tmp, "Temporary File Structure" );

	tmp->fpbuf = (FPBUF) NULL;
	tmp->fname = (char *) NULL;
	tmp->lastseek = (long) 0;

	tmp->savetr = (TRC_TEVREC) NULL;
	tmp->tid = -1;

	tmp->nexttime.tv_sec = -1;
	tmp->nexttime.tv_usec = -1;

	tmp->lasttime.tv_sec = -1;
	tmp->lasttime.tv_usec = -1;

	tmp->delta.tv_sec = 0;
	tmp->delta.tv_usec = 0;

	tmp->valid = -1;
	tmp->eof = -1;
	tmp->next = (TMPFILE) NULL;

	return( tmp );
}


TMPFILE
get_tmpfile( tid, tmptp, createflag )
int tid;
struct timeval *tmptp;
int createflag;
{
	TMPFILE TF;

	static struct timeval delta = { 0, 0 };

	char fname[255];

	int index;

	TF = TMPF;

	index = 1;

	while ( TF != NULL )
	{
		if ( TF->tid == tid )
		{
			if ( ( tmptp->tv_sec == -1 && tmptp->tv_usec == -1 )
					|| TIME_COMPARE( *tmptp, TF->lasttime ) >= 0 )
			{
				return( TF );
			}

			delta.tv_sec = TF->delta.tv_sec;
			delta.tv_usec = TF->delta.tv_usec;

			index++;
		}

		TF = TF->next;
	}

	if ( !createflag )
		return( (TMPFILE) NULL );

	TF = create_tmpfile();

	TF->tid = tid;

	if ( index > 1 )
		sprintf( fname, "%s/tmp.%x.%d", DIRNAME, tid, index );

	else
		sprintf( fname, "%s/tmp.%x", DIRNAME, tid );

	TF->fname = trc_copy_str( fname );

	if ( !verify_fp( TF, "w" ) )
	{
		free( TF );
		return( (TMPFILE) NULL );
	}

	if ( index > 1 )
	{
		TF->delta.tv_sec = delta.tv_sec;
		TF->delta.tv_usec = delta.tv_usec;
	}

	TF->next = TMPF;

	TMPF = TF;

	return( TF );
}


void
get_tevrec_tmpfile( TF )
TMPFILE TF;
{
	FILE *savefp;

	if ( TF->eof )
		return;

	CURRENTTF = TF;

	if ( !verify_fp( TF, "r" ) )
		bail();

	savefp = ID->trace_in;

	ID->trace_in = TF->fpbuf->fp;

	trc_read_trace_event( ID, &(TF->eof) );

	ID->trace_in = savefp;
}


void
sync_time( TF )
TMPFILE TF;
{
	TF->nexttime.tv_sec += TF->delta.tv_sec;
	TF->nexttime.tv_usec += TF->delta.tv_usec;

	if ( TF->nexttime.tv_usec > 1000000 )
	{
		TF->nexttime.tv_usec -= 1000000;

		(TF->nexttime.tv_sec)++;
	}

	else if ( TF->nexttime.tv_usec < 0 )
	{
		TF->nexttime.tv_usec += 1000000;

		(TF->nexttime.tv_sec)--;
	}
}


/* FPBUF Routines */

FPBUF
create_fpbuf()
{
	FPBUF tmp;

	tmp = (FPBUF) malloc( sizeof( struct fp_buffer_struct ) );
	trc_memcheck( tmp, "File Buffer Structure" );

	tmp->fp = (FILE *) NULL;

	tmp->rwstr = (char *) NULL;

	tmp->tmpfile = (TMPFILE) NULL;

	tmp->next = (FPBUF) NULL;

	if ( vflag )
		{ printf( "+" ); fflush( stdout ); }

	return( tmp );
}


void
free_fpbuf( ptr )
FPBUF *ptr;
{
	FPBUF FPB;

	if ( ptr == NULL || *ptr == NULL )
		return;
	
	FPB = *ptr;

	/* Close Any Open File Ptr */

	if ( FPB->fp != NULL )
	{
		if ( FPB->tmpfile != NULL )
			FPB->tmpfile->lastseek = ftell( FPB->fp );

		fclose( FPB->fp );

		FPB->fp = (FILE *) NULL;
	}

	FPB->rwstr = (char *) NULL;

	if ( FPB->tmpfile != NULL )
	{
		FPB->tmpfile->fpbuf = (FPBUF) NULL;

		FPB->tmpfile = (TMPFILE) NULL;
	}

	FPB->next = (FPBUF) NULL;

	free( FPB );

	*ptr = (FPBUF) NULL;

	if ( vflag )
		{ printf( "\b \b" ); fflush( stdout ); }
}


int
verify_fp( TF, rwstr )
TMPFILE TF;
char *rwstr;
{
	FPBUF FPBlast;
	FPBUF FPBptr;
	FPBUF FPB;

	/* File Ptr Buffer Already There */

	if ( TF->fpbuf )
	{
		/* Verify Same Mode */

		if ( !strcmp( TF->fpbuf->rwstr, rwstr ) )
		{
			/* Move to Top of List */

			if ( FPS != TF->fpbuf )
			{
				if ( FPS == NULL )
				{
					printf( "Warning:  Empty File Ptr Buffer List\n" );

					return( TRC_TRUE );
				}

				FPB = FPS;

				while ( FPB->next != NULL )
				{
					if ( FPB->next == TF->fpbuf )
					{
						FPB->next = TF->fpbuf->next;

						TF->fpbuf->next = FPS;

						FPS = TF->fpbuf;

						return( TRC_TRUE );
					}

					FPB = FPB->next;
				}

				printf(
					"Warning:  Verify File Ptr Buffer Not Found\n" );
			}

			return( TRC_TRUE );
		}

		/* Switching File Modes, Close File Ptr */

		else
			close_fp( TF );
	}

	/* Check for Flush */

	if ( NFPS >= MAX_OPEN_FP )
	{
		FPBptr = FPS;

		FPBlast = (FPBUF) NULL;

		while ( FPBptr->next != NULL )
		{
			FPBlast = FPBptr;
			FPBptr = FPBptr->next;
		}

		if ( FPBlast != NULL )
			free_fpbuf( &(FPBlast->next) );

		else
			free_fpbuf( &(FPS) );

		NFPS--;
	}

	/* Create New File Ptr Buffer */

	FPB = create_fpbuf();

	FPB->fp = fopen( TF->fname, rwstr );

	if ( !trc_filecheck( FPB->fp, TF->fname ) )
	{
		free_fpbuf( &FPB );

		return( TRC_FALSE );
	}

	else
	{
		/* Add File Ptr Buffer to List */

		FPB->rwstr = rwstr;

		FPB->tmpfile = TF;
		TF->fpbuf = FPB;

		FPB->next = FPS;
		FPS = FPB;

		NFPS++;

		/* Check for File Seek */

		if ( TF->lastseek != 0 )
		{
			if ( fseek( FPB->fp, TF->lastseek, 0 ) )
			{
				printf( "\nError in fseek() of Temp File\n\n" );
				bail();
			}
		}

		/* Success */

		return( TRC_TRUE );
	}
}


void
close_fp( TF )
TMPFILE TF;
{
	FPBUF FPBlast;
	FPBUF FPB;

	int flag;

	/* Check for Null File Ptr Buffer */

	if ( TF->fpbuf == NULL )
	{
		TF->lastseek = (long) 0;

		return;
	}

	/* Remove Buffer From List */

	if ( FPS != NULL )
	{
		if ( FPS == TF->fpbuf )
		{
			FPS = TF->fpbuf->next;

			NFPS--;
		}

		else
		{
			FPB = FPS;

			flag = 0;

			while ( FPB->next != NULL && !flag )
			{
				if ( FPB->next == TF->fpbuf )
				{
					FPB->next = TF->fpbuf->next;

					NFPS--;

					flag++;
				}

				else
					FPB = FPB->next;
			}

			if ( !flag )
			{
				printf(
					"Warning:  Close File Ptr Buffer Not Found.\n" );
			}
		}
	}

	free_fpbuf( &(TF->fpbuf) );

	TF->lastseek = (long) 0;
}


/* Clean Up Temp Directory */

void
cleanup()
{
	TMPFILE TF;

	TF = TMPF;

	while ( TF != NULL )
	{
		if ( TF->fname != NULL )
			unlink( TF->fname );

		TF = TF->next;
	}

	unlink( DIRNAME );
}


void
bail()
{
	cleanup();

	exit( -1 );
}

