/*  This file is part of the Pablo Performance Analysis Environment
// 
//           (R)
//  The Pablo    Performance Analysis Environment software is NOT in
//  the public domain.  However, it is freely available without fee for
//  education, research, and non-profit purposes.  By obtaining copies
//  of this and other files that comprise the Pablo Performance Analysis
//  Environment, you, the Licensee, agree to abide by the following
//  conditions and understandings with respect to the copyrighted software:
//  
//  1.  The software is copyrighted in the name of the Board of Trustees
//      of the University of Illinois (UI), and ownership of the software
//      remains with the UI. 
// 
//  2.  Permission to use, copy, and modify this software and its documentation
//      for education, research, and non-profit purposes is hereby granted
//      to Licensee, provided that the copyright notice, the original author's
//      names and unit identification, and this permission notice appear on
//      all such copies, and that no charge be made for such copies.  Any
//      entity desiring permission to incorporate this software into commercial
//      products should contact:
// 
//           Professor Daniel A. Reed                 reed@cs.uiuc.edu
//           University of Illinois
//           Department of Computer Science
//           2413 Digital Computer Laboratory
//           1304 West Springfield Avenue
//           Urbana, Illinois  61801
//           USA
// 
//  3.  Licensee may not use the name, logo, or any other symbol of the UI
//      nor the names of any of its employees nor any adaptation thereof in
//      advertizing or publicity pertaining to the software without specific
//      prior written approval of the UI.
// 
//  4.  THE UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE
//      SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS
//      OR IMPLIED WARRANTY.
// 
//  5.  The UI shall not be liable for any damages suffered by Licensee from
//      the use of this software.
// 
//  6.  The software was developed under agreements between the UI and the
//      Federal Government which entitle the Government to certain rights.
// 
// *************************************************************************
// 
//  Developed by: The Pablo Research Group
//                University of Illinois at Urbana-Champaign
//                Department of Computer Science
//                1304 W. Springfield Avenue
//                Urbana, IL     61801
// 
//                http://www-pablo.cs.uiuc.edu
// 
//  Send comments to: pablo-feedback@guitar.cs.uiuc.edu
// 
//  Copyright (c) 1987-1998
//  The University of Illinois Board of Trustees.
//       All Rights Reserved.
// 
//  PABLO is a registered trademark of
//  The Board of Trustees of the University of Illinois
//  registered in the U.S. Patent and Trademark Office.
// 
//  Project Manager and Principal Investigator:
//       Daniel A. Reed (reed@cs.uiuc.edu)
//
// Funded in part by the Defense Advanced Research Projects Agency 
// under DARPA contracts DABT63-94-C0049 (SIO Initiative), 
// F30602-96-C-0161, and DABT63-96-C-0027 by the National Science 
// Foundation under the PACI program and grants NSF CDA 94-01124 and
// ASC 97-20202, and by the Department of Energy under contracts
// DOE B-341494, W-7405-ENG-48, and 1-B-333164.
*/ 
/*======================================================================*
// File:  PabloHDF_RT							*
// Purpose: support use of Pablo trace library to analyze HDF 		*
//	    performance							*
// Contents:								*
//  HDFinitTrace_RT        : initialize real-time tracing		*
//  HDFendTrace_RT         : complete trace 				*
//  initHDFProcTrace_RT    : sets up data structures at init time.	*
//  initproctracert_()	   : fortran interface				*
//  HDFtraceEvent_RT	   : called to record event information		*
//  HDFrecordSum 	   : adds fields of one record to those of 	*
//			     another					*
//  HDFnodeInit 	   : initializes linked list node		*
//  HDFrecordFileName	   : records named HDF identifiers 		*
//  BeginIOEventRecord     : initialize before I/O call			*
//  EndIOEventRecord 	   : finalize after I/O call			*
//  BeginMPIOEventRecord   : initialize before MPI-I/O call		*
//  EndMPIOEventRecord 	   : finalize after MPI-I/O call		*
//  BeginHDFEventRecord    : initialize before HDF call			*
//  EndHDFEventRecord 	   : finalizie after HDF call			*
//  HDFrecordFileName	   : record named identifier information	*
//  HDFassignPabloIDs	   : assigns a number to named identifiers	*
//  writeHDFNamePacketsRT  : write SDDF packets for identifier names	*
//  HDFupdateProcLists     : adds records in queue to entries in 	*
//			     tables					*
//  HDFupdateProcs	   : called by HDFupdateProcLists to do 	*
//			     addition					*
//  HDFSummarySDDF	   : write SDDF event summary packets		*
//  HDFnodeInit 	   : initialize event node			*
//  HDFrecordSum 	   : add one event record to another		*
//  getHDFFieldIndex	   : get Field Index for counts and times	*
//  getHDFByteFieldIndex   : get field index for bytes 			*
//  writeHDFRecDescrptrsRT : write HDF Record Descriptor packets	*
//  printFileMappingsRT	   : print map of named identifiers		*
//  _hdfNameDescriptor()   : writes SDDF descriptor packet for names	*
//======================================================================*/
#ifndef PCF_BUILD
#ifdef _HDF5_
#include "H5config.h"
#endif
#include "SystemDepend.h"
#include "Trace.h"
#include "TraceParam.h"
#include "ProcIDs.h"
#include "IO_TraceParams.h"
#include "IOTrace.h"
#include "HDFTrace.h"
#include "SDDFparam.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
/*======================================================================* 
// on ipsc/860 don't include this or you'll get multiply defined SEEK_  *
//======================================================================*/
#ifndef __NX
#include <unistd.h>
#endif
 
#ifndef SUCCESS
#define SUCCESS 0
#define FAILURE	1
#endif

#ifndef TRUE
#define TRUE 1
#define FALSE	0
#endif
#define NEG_THREAD_ID -999

#include "HDFrecord_RT.h"

#ifdef H5_HAVE_MPIOTRACE
#include "mpi.h"
#include "MPIO_EventArgs.h"
#endif

#ifndef TRgetThreadID
#define TRgetThreadID  TRgetNode
#endif

#ifndef TRnumNodes
#define TRnumNodes 1
#endif

#define AllThreads -1

/*======================================================================*
//  User output file pointer.						*
//======================================================================*/
FILE *outP;
/*======================================================================*
// Data Structures:							*
//									*
// HDFQueues:   an array of linked list.  Each list corresponds to an	*
//              HDF event and contains an entry for each different 	*
//		thread and data set referenced by a call to that event  *
//									*
// CallStack: 	a stack of HDFnode_t objects.  At any given time, the   *
//		stack represents the calling stack of the HDF routines 	*
//									*
// HDFfileList: a linked list of named identifiers and identifier 	*
//		numbers.  This is processed later to assign a single 	*
//		numbers to identifiers with the same name.		*
//======================================================================*/
HDFnode_t **HDFQueues;
HDFnode_t *CallStack;
HDFnode_t *TagQueue;
fileRec_t *HDFfileList;
/*======================================================================*
// Internal Function prototypes						*
//======================================================================*/
void HDFinitTrace_RT( char *, int, int );
void HDFendTrace_RT();
int initproctracert_( void );
int initHDFProcTrace_RT( void );
void HDFtraceEvent_RT( int , char *, unsigned ) ;
void BeginIOEventRecord ( int, CLOCK , void * );
void EndIOEventRecord ( int , CLOCK , void * );
void BeginMPIOEventRecord ( int, CLOCK , void *, int ); 
void EndMPIOEventRecord ( int , CLOCK  , void *, int);
void BeginHDFEventRecord( int , CLOCK  );
void EndHDFEventRecord ( CLOCK  ,void *);
void HDFrecordFileName( HDFsetInfo * );
void HDFassignPabloIDs( int *, char *** );
void writeHDFNamePacketsRT( char **, int );
void HDFupdateProcLists( void );
void HDFupdateProcs( HDFnode_t * );
void HDFSummarySDDF( HDFnode_t *, int );
void HDFnodeInit ( HDFnode_t * ) ;
void HDFrecordSum ( HDFrec_t *, HDFrec_t * );
int getHDFFieldIndex( int );
int getHDFByteFieldIndex( int );
void writeHDFRecDescrptrsRT( void );
void printFileMappingsRT( char *, char **, int );
void _hdfNameDescriptor( void );
void _hdfDescriptorRT( char *, char *, int );
void HDFfinalTimeStamp( void );
void getHDFprocName( int index, char buff[41] );
void IOtraceInit( char*, int, int );
/*======================================================================*
// Global variables           						*
//======================================================================*/
HDFnode_t InitNode;		/* Node used in initialization		*/
HDFrec_t Tally;			/* Node used to get total		*/
char *FileName;			/* Name of Trace file			*/
HDFsetInfo openInfo;		/* Info about file opened		*/
char openName[256];		/* Name of opened file			*/
extern char *hdfRecordPointer;
extern char HDFprocNames[][40];
/*======================================================================*
// NAME									*
//     HDFinitTrace_RT-- initialize HDF real-time tracing		*
// USAGE								*
//     VOID HDFinitTrace_RT( fileName )					*
//									*
//     char *fileName;		IN: name of output file			*
// RETURNS								*
//     None.								*
//======================================================================*/
void HDFinitTrace_RT( char *fileName, int procNum, int OUTSW )
{
   int error;
   TRgetClock( &epoch );
   error = initHDFProcTrace_RT() ;
   if ( error != SUCCESS ) 
   {
      fprintf (stderr,"Unable to Initialize properly.  Exiting program\n");
      exit(-1);
   }
   FileName = ( char * ) malloc ( strlen( fileName ) + 10 );
   /*===================================================================*
   // Here the library was built to linked with the MPI and MPIO	*
   // libraries.  However, the use may chose not to run with MPI.	*
   // A check is made to see if MPI has been initialized.  If so,  	*
   // a trace file is assigned to the current node with the number 	*
   // of the node as a suffix; if not, only one file is opened  	*
   // and it is not given a suffix.					*
   //===================================================================*/
   IOtraceInit( fileName, procNum, OUTSW );
}
/*======================================================================*
// NAME									*
//     HDFendTrace-- end HDF tracing					*
// USAGE								*
//     VOID HDFendTrace_RT(void)					*
// RETURNS								*
//     None.								*
//======================================================================*/
void HDFendTrace_RT( )
{
   int j, numSetIDs;
   HDFnode_t *P;
   char **Names;

   HDFfinalTimeStamp();
   /*===================================================================*
   //  Assing pablo ids to named identifiers and tag records		*
   //===================================================================*/
   HDFassignPabloIDs( &numSetIDs, &Names );
   /*===================================================================*
   //  Create a file name for the File map file.			*
   //  Disable for now.							*
   //===================================================================*/
/*	mapFile = (char *)malloc( strlen(FileName) + 4 );
   strcpy(mapFile,FileName);
   strcat(mapFile,".map"); */
   /*===================================================================*
   //  print the file mappings.						*
   //===================================================================*/
/*        printFileMappingsRT( mapFile, Names, numSetIDs ); */
   /*===================================================================*
   // Print SDDF summary records					*
   //===================================================================*/
   writeHDFRecDescrptrsRT();
   writeHDFNamePacketsRT( Names, numSetIDs );
     	for ( j = 0; j < NumHDFProcs; ++j ) {
      HDFSummarySDDF( HDFQueues[j], j );
   }  
   endTracing();
}
/*======================================================================*
// initHFDProcTrace_RT							*
//	This function initializes data structures specific to		* 
//	the HDF real-time procedure entry/exit tracing extensions of 	*
//      the Pablo instrumentation library.  				*
//======================================================================*/
int initproctracert_( void )

{
   return initHDFProcTrace_RT();
}

int initHDFProcTrace_RT( void )
{
   int i; 
   int j; 
   int size;
   int numProcs = NumHDFProcs;

   if ( traceProcessorNumber == -1 ) 
   {
      traceProcessorNumber = TRgetDefaultProcessor();
   }
   /*===================================================================*
   // Initialize InitNode used for node initialization.		*
   //===================================================================*/
   InitNode.ptr = NULL;
   InitNode.eventID = 0;             
   InitNode.lastIOtime = zeroClock;
   InitNode.record.nCalls = 0;             
   InitNode.record.lastCall = zeroClock;
   InitNode.record.incDur = zeroClock;              
   InitNode.record.excDur = zeroClock;              
   InitNode.record.hdfID = 0;             
   InitNode.record.xRef = 0;             
   for ( j = 0; j < nTallyFields; ++j ) 
   {
           InitNode.record.times[j] = zeroClock; 
   }
   for ( j = 0; j < nTallyFields; ++j ) 
   {
           InitNode.record.counts[j] = 0; 
   }
   for ( j = 0; j < nByteFields; ++j ) 
   {
           InitNode.record.bytes[j] = 0; 
   }
   for ( i = 0; i < nByteFields; ++i ) 
   {
      for ( j = 0; j < nBkts; ++j ) 
      {
         InitNode.record.Hists[i][j] = 0;
      }
   }
   /*===================================================================*
   // initialize linked list used to keep track of named hdf 		*
   // identifiers.							*
   //===================================================================*/
   HDFfileList = NULL;
   /*===================================================================*
   // Allocate a one dimensional array of pointers to queues of 	*
   // HDFnodes.  There is one queue for each thread and one for 	*
   // each HDF procedure.  Each queue will be a list of summary 	*
   // records distinquished by file type and 				*
   //===================================================================*/
   size = (int)(numProcs*sizeof( HDFnode_t * ));
   HDFQueues = (HDFnode_t **)malloc( size );
   if ( HDFQueues == NULL ) 
   {
      fprintf(stderr,"Failed to allocate HDFQueues in initHDFProcTrace\n");
      return FAILURE;
   }
   for ( j = 0; j < numProcs; ++j ) 
   {
      HDFQueues[j] = NULL;
   }
   /*===================================================================*
   // Initialize call stack to a dummy node and TagQueue to NULL   	*
   //===================================================================*/
   CallStack = (HDFnode_t *)malloc( sizeof(HDFnode_t) );
   *CallStack = InitNode;
   TagQueue = NULL ;
   return SUCCESS;
}
/*======================================================================*
// This is called from the HDF and I/O routines when real-time summary	*
// tracing is used.  It sets up a call stack for the specific thread in *
// use if no stack is yet set up.  It then calls calls a routine to 	*
// handle the event based on whether it is an I/O or HDF call.		*
//======================================================================*/
void HDFtraceEvent_RT( int eventType, char *dataPtr, unsigned dataLen ) 
{
   CLOCK	seconds;

   seconds = getClock();

   if ( isBeginIOEvent ( eventType ) || eventType == ID_malloc ) 
   {
      BeginIOEventRecord ( eventType, seconds, dataPtr ) ;
   } 
   else if ( isEndIOEvent( eventType )  || eventType == -ID_malloc) 
   {
      EndIOEventRecord ( eventType, seconds, dataPtr );
   } 
   else if ( isBeginHDFEvent( eventType ) ) 
   { 
      BeginHDFEventRecord ( eventType , seconds ) ;
   } 
   else if ( isEndHDFEvent( eventType ) ) 
   {
      EndHDFEventRecord ( seconds, dataPtr );
#ifdef  H5_HAVE_MPIOTRACE
   } 
   else if ( isBeginMPIOEvent( eventType ) ) 
   { 
      BeginMPIOEventRecord ( eventType, seconds, dataPtr, dataLen ) ;
   } 
   else if ( isEndMPIOEvent( eventType ) ) 
   {
      EndMPIOEventRecord ( eventType, seconds, dataPtr, dataLen );
#endif  /* H5_HAVE_MPIOTRACE */
   } 
   else 
   {
      fprintf(stderr,"eventType %d, dataLen = %u\n",eventType,dataLen);
   } 
}
/*======================================================================* 
// BeginIOEventRecord:                                               	*
//  This routine simply records the time in the record on the top of 	*
//  the stack.								*
//======================================================================*/ 
void BeginIOEventRecord ( int eventType, CLOCK seconds, void *dataPtr  )
{
   char *name;
   /*===================================================================*
   // save the time value temporarily in top of stack			*
   // When the end record is received, the duration can be computed.	*
   //===================================================================*/
   CallStack->lastIOtime = seconds;
   /*===================================================================*
   // get the ID or name of the file accessed from the structure	*
   // passed as dataPtr.  						*
   //===================================================================*/
   switch ( eventType )
   {
      case fopenBeginID:
      case openBeginID:
      {
         name = (char *)(dataPtr) + 2*sizeof(int);
         strcpy( openName, name );
	 break;
      }
      case fcloseBeginID:
      case closeBeginID:
      {
         CallStack->record.hdfID = *( long *)dataPtr;
	 break;
      }
      case readBeginID:
      case freadBeginID:
      case writeBeginID:
      case fwriteBeginID:
      case lseekBeginID:
      case fseekBeginID:
      case fsetposBeginID:
      case rewindBeginID:
      {
         CallStack->record.hdfID = *(int *)dataPtr;
	 break;
      }
      default:
      {
         break;
      }
   }
}
/*======================================================================* 
// EndIOEventRecord:							*
//  This routine retrieves the entry time saved on the top of the stack *
//  and computes the duration of the I/O event.  This is added to the   *
//  record field corresponding to this type of I/O.  The Bytes field in *
//  the record is updated if this is a read or write operation.		*
//======================================================================*/ 
void EndIOEventRecord ( int eventType, CLOCK secs, void *dataPtr )
{
   CLOCK incDur;
   int i, Field, ByteField, bytes;
   
   incDur = clockSubtract(secs,CallStack->lastIOtime) ;
   Field = getHDFFieldIndex( eventType ) ;
   CallStack->record.times[Field] 
   = clockAdd ( CallStack->record.times[Field] , incDur ) ;
   ++CallStack->record.counts[Field];
   ByteField = getHDFByteFieldIndex( Field ) ;
   switch ( eventType ) 
   {
      case readEndID:
      case freadEndID:
      case writeEndID:
      case fwriteEndID:
      case -ID_malloc:
      {
         bytes = *((int *)dataPtr);
         CallStack->record.bytes[ByteField] += bytes;
         /*======================================================
         // update histogram					*
         //=====================================================*/
         i = -1;
         while ( bytes >= BktLim[i+1] ) ++i  ;
         if ( i >= 0 ) ++CallStack->record.Hists[ByteField][i];
         break;
      }
      case fopenEndID:
      case openEndID:
      {
         openInfo.setName = openName;
         openInfo.setID = (int)(*((long *)dataPtr));
         CallStack->record.hdfID = openInfo.setID;
         HDFrecordFileName ( &openInfo );
         break;
      }
      default:
      {
         break;
      }
   }
			
}
#ifdef H5_HAVE_MPIOTRACE
/*======================================================================*
// BeginMPIOEventRecord:                                               	*
//  This routine simply records the time in the record on the top of 	*
//  the stack.								*
//======================================================================*/ 
void BeginMPIOEventRecord( int eventType, 
	                   CLOCK seconds, 
	                   void *data,
	                   int dataLen )
{
   HDFsetInfo *dataPtr;
   dataPtr = (HDFsetInfo *)data;
   /*===================================================================*
   // save the time value temporarily in top of stack			*
   // When the end record is received, the duration can be 		*
   // computed.								*
   //===================================================================*/
   CallStack->lastIOtime = seconds;
   /*===================================================================*
   // get useful info from the structure pointed to by dataPtr.		*
   // Form most cases, this is the file ID.  For mpiOpen, it is the	*
   // name of the file.  For mpiDelete, no information is of any	*
   // use.								*
   //===================================================================*/
   if ( dataLen == 0 ) return;
   CallStack->record.hdfID = dataPtr->setID; 
   switch ( eventType ) 
   {
      case HDFmpiOpenID:
      {
         strcpy( openName, dataPtr->setName );
         break;
      }
      case HDFmpiReadAtID:
      {
         CallStack->record.bytes[MPIOreadBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiReadAtAllID:
      {
         CallStack->record.bytes[MPIOreadAllBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiWriteAtID:
      {
         CallStack->record.bytes[MPIOwriteBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiWriteAtAllID:
      {
         CallStack->record.bytes[MPIOwriteAllBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiIreadAtID:
      {
         CallStack->record.bytes[MPIOiReadBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiIwriteAtID:
      {
         CallStack->record.bytes[MPIOiWriteBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiReadID:
      {
         CallStack->record.bytes[MPIOreadBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiReadAllID:
      {
         CallStack->record.bytes[MPIOreadAllBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiWriteID:
      {
         CallStack->record.bytes[MPIOwriteBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiWriteAllID:
      {
         CallStack->record.bytes[MPIOwriteAllBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiIreadID:
      {
         CallStack->record.bytes[MPIOiReadBytesReq] += dataPtr->numBytes;
         break;
      }
      case HDFmpiIwriteID:
      {
         CallStack->record.bytes[MPIOiWriteBytesReq] += dataPtr->numBytes;
         break;
      }
      default:
      {
         break;
      }
   }
}
/*======================================================================*
// EndMPIOEventRecord:							*
//  This routine retrieves the entry time saved on the top of the stack *
//  and computes the duration of the MPI-I/O event.  This is added to   *
//  the record field corresponding MPI-I/O.  				*
//======================================================================*/ 
void EndMPIOEventRecord ( int eventType, 
	                  CLOCK secs, 
	                  void *data,
	                  int dataLen )
{
   CLOCK incDur;

   HDFsetInfo* dataPtr;
   incDur = clockSubtract(secs,CallStack->lastIOtime) ;
   CallStack->record.times[MPI] 
          = clockAdd ( CallStack->record.times[MPI], incDur );
   ++CallStack->record.counts[MPI];
   if ( dataLen == 0 ) 
   {
      return;
   }
   dataPtr = (HDFsetInfo *)data;
   switch ( eventType ) 
   {
      case -HDFmpiOpenID:
      {
         /*===========================================================*
         // open and record the information.			      *
         //===========================================================*/
	 openInfo.setName = openName;
	 openInfo.setID = dataPtr->setID;
	 CallStack->record.hdfID = openInfo.setID;
	 HDFrecordFileName ( &openInfo );
         break;
      }
      case -HDFmpiReadAtID:
      {
         ++CallStack->record.counts[MPIOread] ;
         CallStack->record.times[MPIOread] 
          = clockAdd ( CallStack->record.times[MPIOread], incDur );
         CallStack->record.bytes[MPIOreadBytesTrans] += dataPtr->numBytes; 
         break;
      }
      case -HDFmpiReadAtAllID:
      {
         ++CallStack->record.counts[MPIOreadAll] ;
         CallStack->record.times[MPIOreadAll] 
          = clockAdd ( CallStack->record.times[MPIOreadAll], incDur );
         CallStack->record.bytes[MPIOreadAllBytesTrans] += dataPtr->numBytes;
         break;
      }
      case -HDFmpiWriteAtID:
      {
         ++CallStack->record.counts[MPIOwrite] ;
         CallStack->record.times[MPIOwrite] 
          = clockAdd ( CallStack->record.times[MPIOwrite], incDur );
         CallStack->record.bytes[MPIOwriteBytesTrans] += dataPtr->numBytes;
         break;
      }
      case -HDFmpiWriteAtAllID:
      {
         ++CallStack->record.counts[MPIOwriteAll] ;
         CallStack->record.times[MPIOwriteAll] 
          = clockAdd ( CallStack->record.times[MPIOwriteAll], incDur );
         CallStack->record.bytes[MPIOwriteAllBytesTrans] += dataPtr->numBytes;
         break;
      }
      case -HDFmpiIreadAtID:
      {
         ++CallStack->record.counts[MPIOiRead] ;
         CallStack->record.times[MPIOiRead] 
             = clockAdd ( CallStack->record.times[MPIOiRead], incDur );
         break;
      }
      case -HDFmpiIwriteAtID:
      {
         ++CallStack->record.counts[MPIOiWrite] ;
         CallStack->record.times[MPIOiWrite] 
             = clockAdd ( CallStack->record.times[MPIOiWrite], incDur );
         break;
      }
      case -HDFmpiReadID:
      {
         ++CallStack->record.counts[MPIOread] ;
         CallStack->record.times[MPIOread] 
          = clockAdd ( CallStack->record.times[MPIOread], incDur );
         CallStack->record.bytes[MPIOreadBytesTrans] += dataPtr->numBytes;
         break;
      }
      case -HDFmpiReadAllID:
      {
         ++CallStack->record.counts[MPIOreadAll] ;
         CallStack->record.times[MPIOreadAll] 
          = clockAdd ( CallStack->record.times[MPIOreadAll], incDur );
         CallStack->record.bytes[MPIOreadAllBytesTrans] += dataPtr->numBytes;
         break;
      }
      case -HDFmpiWriteID:
      {
         ++CallStack->record.counts[MPIOwrite] ;
         CallStack->record.times[MPIOwrite] 
          = clockAdd ( CallStack->record.times[MPIOwrite], incDur );
         CallStack->record.bytes[MPIOwriteBytesTrans] += dataPtr->numBytes;
         break;
      }
      case -HDFmpiWriteAllID:
      {
         ++CallStack->record.counts[MPIOwriteAll] ;
         CallStack->record.times[MPIOwriteAll] 
          = clockAdd ( CallStack->record.times[MPIOwriteAll], incDur );
         CallStack->record.bytes[MPIOwriteAllBytesTrans] += dataPtr->numBytes;
         break;
      }
      case -HDFmpiIreadID:
      {
         ++CallStack->record.counts[MPIOiRead] ;
         CallStack->record.times[MPIOiRead] 
          = clockAdd ( CallStack->record.times[MPIOiRead], incDur );
         break;
      }
      case -HDFmpiIwriteID:
      {
         ++CallStack->record.counts[MPIOiWrite] ;
         CallStack->record.times[MPIOiWrite] 
          = clockAdd ( CallStack->record.times[MPIOiWrite], incDur );
         break;
      }
      default:
      {
         ++CallStack->record.counts[MPIOother] ;
         CallStack->record.times[MPIOother] 
          = clockAdd ( CallStack->record.times[MPIOiWrite], incDur );
         break;
      }
   }
}
#endif /* H5_HAVE_MPIOTRACE */
/*======================================================================*
//   BeginHDFEventRecord:						* 
// 	This function puts a trace record on the stack corresponding to	*
//   	this thread.  If no stack exists, one is created.  If no record	* 
//   	exist, a record is created.                                   	* 
//======================================================================*/ 
void BeginHDFEventRecord( int eventID, CLOCK secs )
{
	HDFnode_t *HDFrec;
	/*==============================================================*
	// Create a record. Push it onto the call stack.                *
	//==============================================================*/
        HDFrec = (HDFnode_t *)malloc( sizeof(HDFnode_t) );
        HDFnodeInit( HDFrec ) ;
	HDFrec->eventID = eventID;
	HDFrec->ptr = CallStack;
	CallStack = HDFrec ;
	/*==============================================================*
	// save time stamp in record.					*
	//==============================================================*/
	HDFrec->record.lastCall = secs;
}
/*======================================================================* 
// EndHDFEventRecord:							*
//  This routine pops the HDF record from the top of the stack 		*
//  corresponding to this thread and computes the inclusive duration    *
//  and adds it to the inclusive duration field of this record and to   *
//  the HDF time field of the calling routines record.			*
//======================================================================*/ 
void EndHDFEventRecord ( CLOCK secs, void *dataPtr )
{
        HDFsetInfo 	*info;
	HDFnode_t	*HDFrec;
	CLOCK  		incSecs;
	static int	dummyIDs = -4;
	/*==============================================================*
	// pop record from top of the stack, compute inclusive duration	*
	// and set the corresponding record field and increment nCalls.	*
	//==============================================================*/
	HDFrec = CallStack;
	CallStack = CallStack->ptr;
	if ( CallStack == NULL ) {
	   fprintf(stderr,">>> EndHDFEventRecord: Call Stack is empty. <<<\n");
	   return;
	}
	incSecs = clockSubtract(secs,HDFrec->record.lastCall) ;
                  
	HDFrec->record.incDur = incSecs;
	++HDFrec->record.nCalls;
   	/*==============================================================*
	// add old record to chain to have its xRef field tagged.	*
	//==============================================================*/
	HDFrec->ptr = TagQueue;
	TagQueue = HDFrec;
	/*==============================================================*
	// Add set ID information.					*
	//==============================================================*/
	if ( dataPtr != NULL ) {
	   info = (HDFsetInfo *)dataPtr;
	   if ( info->setName != NULL ) {
	      if ( info->setID == 0 ) {
	         info->setID = dummyIDs--;
	      }
	      HDFrecordFileName ( info );
	   }
	   HDFrec->record.hdfID = info->setID;
	}
	/*==============================================================*
	// Update the HDF totals for the calling program.		*
	//==============================================================*/
        CallStack->record.times[ HDF_ ] 
           = clockAdd( CallStack->record.times[ HDF_ ] , incSecs ) ;
        ++CallStack->record.counts[ HDF_ ] ;
	/*==============================================================*
	// If the stack has only one record it represents the main 	* 
	// program.  Tag all of the records on the TagQueue and tally   * 
	// them up.							* 
	//==============================================================*/
        if ( CallStack->ptr == NULL ) {
           HDFupdateProcLists( );
	}
}
/*======================================================================* 
// This routine keeps track of the identifier names and tags.  Some	*
// names may be associated with more than one tag.  This will be 	*
// rectified when final tallies are done.				*
//======================================================================*/
void HDFrecordFileName( HDFsetInfo *info )
{
	fileRec_t *P;
	char *t;
	int match; 
	long id;
	P = HDFfileList;
	match = FALSE;
	id = info->setID;
	while ( P != NULL && match == FALSE ) {
	   if ( strcmp( P->fileName, info->setName ) != 0 && P->hdfID == id ) {
	      match = TRUE;
	   } else {
	      P = P->ptr;
	   }
	}
	if ( match == FALSE ) {
	   P = ( fileRec_t *) malloc( sizeof( fileRec_t ) );
	   P->ptr = HDFfileList;
	   HDFfileList = P;
	   t = (char *)malloc( strlen( info->setName ) + 1 );
	   strcpy ( t, info->setName ) ;
	   P->fileName = t;
	   P->hdfID = info->setID;
	   P->PabloID = 0;
	} 
}  
/*======================================================================* 
// This routine assigns a unique Pablo ID to each unique name 		*
// regardless of the HDF tag.						*
// It then goes through the HDFRecordQueue and marks each record with   *
// the PabloID corresponding to the hdfID and xRef fields or 0.		*
//======================================================================*/
void HDFassignPabloIDs( int *nSetIDs, char ***Names )
{
	fileRec_t *F, *G;
	HDFnode_t *P;
	int j; 
	long PabloID = 1;
	long hdfID, xRef;
	char *fName, **T;

	F = HDFfileList;
        /*==============================================================*
        // Assign the same ID to identical names.			*
        //==============================================================*/
	while ( F != NULL ) {
	   if ( F->PabloID == 0 ) {
	      F->PabloID = PabloID++;
	      fName = F->fileName;
	      G = F->ptr;
	      while ( G != NULL ) {
	         if ( strcmp( G->fileName , fName ) == 0 ) {
	            G->PabloID = F->PabloID;
	         }
	         G = G->ptr;
	      }
	   }
	   F = F->ptr;
	}
	*nSetIDs = (int)(PabloID - 1);
        if ( *nSetIDs <= 0 ) return;
        /*==============================================================*
	// Repace hdfID and xRef fields with corresponding Pablo ID	*
        //==============================================================*/
   	for ( j = 0; j < NumHDFProcs; ++j ) {
   	   P = HDFQueues[j] ;
   	   while ( P != NULL ) {
   	      hdfID = P->record.hdfID;
   	      if ( hdfID != 0 ) {
   	         PabloID = 0;
                 F = HDFfileList;
   	         while ( F != NULL && PabloID == 0 ) {
                    if ( hdfID == F->hdfID ) {
   	               PabloID = F->PabloID;
   	            }
   	            F = F->ptr;
   	         }
   	         P->record.hdfID = PabloID;
   	      }
   	      xRef = P->record.xRef;
   	      if ( xRef != 0 ) {
   	         PabloID = 0;
                 F = HDFfileList;
   	         while ( F != NULL && PabloID == 0 ) {
                    if ( xRef == F->hdfID ) {
   	               PabloID = F->PabloID;
   	            }
   	            F = F->ptr;
   	         }
   	         P->record.xRef = PabloID;
   	      }
              P = P->ptr;
   	   } /* end while ( P != NULL ) */
        } /* end for */
	/*==============================================================*
	// get a list of all the unique names and order them according  *
	// to their Pablo IDs.						*
	//==============================================================*/
	T = ( char ** )malloc( (*nSetIDs+1) * sizeof( char * ) );
	for ( j = 0; j <= *nSetIDs; ++j ) {
	   T[j] = NULL;
	}
	F = HDFfileList;
	while ( F != NULL ) {
	   PabloID = F->PabloID  ;
	   if ( T[PabloID] == NULL ) {
	      T[PabloID] = ( char * )malloc( strlen( F->fileName ) + 1 );
	      strcpy( T[PabloID], F->fileName ) ;
	   }
	   free((void *)(F->fileName));
	   G = F;
	   F = F->ptr;
	   free ( (void *)G );
	}
	*Names = T;
}
/*======================================================================* 
// This routine writes SDDF packets to SDDF file containing information	*
// about the named identifiers found in the program.			*
//======================================================================*/
void writeHDFNamePacketsRT( char **Names, int numSetIDs )
{
	int j;
	HDFNamePacket_t NamePkt;
	char *BUFF, *fName;
	int buffSize;
	/*==============================================================*
	// Allocate a buffer to hold the packet.  Allow 80 chars for 	*
	// identifier name.						*
	//==============================================================*/
	buffSize = sizeof(HDFNamePacket_t) + 80;
	BUFF = (char *)malloc(buffSize);
	/*==============================================================*
	// Fill in constant information					*
	//==============================================================*/
	NamePkt.packetType = PKT_DATA;
	NamePkt.packetTag = FAMILY_NAME;
	/*==============================================================*
	// Fill in named identifier information and write to SDDF file	*
	//==============================================================*/
	for ( j = 1; j <= numSetIDs; ++j ) {
	   fName = Names[j];
	   NamePkt.packetLength = (int)(sizeof(NamePkt) + strlen(fName));
	   NamePkt.fileType = 0;		/* not currently used	*/
	   NamePkt.fileID = j;
	   NamePkt.nameLen = (int)strlen(fName) ;
	   if ( buffSize < NamePkt.packetLength ) {
	      free((void *)BUFF) ;
	      buffSize = NamePkt.packetLength + 80;
	      BUFF = (char *)malloc( buffSize ) ;
	   }
	   /*===========================================================*
	   // Copy packet data and tack on identifier name		*
	   //===========================================================*/
	   memcpy( BUFF, &NamePkt, sizeof(NamePkt) );
	   memcpy( BUFF + sizeof(NamePkt) , fName, strlen(fName) );
	   putBytes( BUFF , NamePkt.packetLength ) ;
	}
	free((void *)BUFF);
}
/*======================================================================*
// Tag xRef field of all records in this queue with the hdfID of the 	*
// highest level caller. Also						*
// This routine takes the records after they have been tagged and adds  *
// their fields to the apporopriate position in the HDFQueues structure *
//======================================================================*/
void HDFupdateProcLists( void )
{
	HDFnode_t *P, *Q;
	long hdfID;

	hdfID = TagQueue->record.hdfID;
	P = TagQueue;
	while ( P != NULL ) {
	   P->record.xRef = hdfID;
	   Q = P->ptr;
	   HDFupdateProcs( P );
	   P = Q;
	}
        TagQueue = NULL;
}
/*======================================================================*
// This routine takes as input a node pointer P  and looks for a Total  *
// record with this same eventID, hdfID and xRef.  If such a record     *
// exists, P is added to the record, otherwise a record is created and  *
// its values are set to P's.						*
//======================================================================*/
void HDFupdateProcs( HDFnode_t *P )
{
	int procIndex, eventID;
	long hdfID, xRef;
	HDFnode_t *Q;
	eventID = P->eventID;
	procIndex = ProcIndexForHDFEntry( eventID );
        hdfID = P->record.hdfID;
        xRef = P->record.xRef;
	Q = HDFQueues[ procIndex ];
	/*==============================================================*
	// First determine if a tally node exists that matches the     	*
	// eventID, hdfID and xRef of P.				*
	//==============================================================*/
	while ( Q != NULL && 
            (( Q->record.hdfID != hdfID ) || ( Q->record.xRef != xRef )) ) {
           Q = Q->ptr;
	}
	if ( Q == NULL ) {
	   /*===========================================================*
	   // No tally record matches the hdfID and xRef so put P in    *
	   // the queue.                 				*
	   //===========================================================*/
	   P->ptr = HDFQueues[ procIndex ];
	   HDFQueues[ procIndex ] = P;
	} else {
	   /*===========================================================*
	   // add P to the exiting record and free it.			*
	   //===========================================================*/
           HDFrecordSum ( &Q->record , &P->record );
	   free((void *)P);
	}
}
/*======================================================================*
// Print SDDF records for all records in this linked list.		*
//======================================================================*/
void HDFSummarySDDF( HDFnode_t *P, int procIndex )
{
	int i, j, arrayLen, nodeID, nCalls;
	int allIOCount;
	CLOCK allIOTime, excDur;
	double t;
	char buff[1024];
	char *Packet;
	HDFnode_t *Q;
        struct
        {
           int packetLen;
           int packetType;
           int packetTag;
           int eventID;
           double Seconds;
           double IncDur;
           double ExcDur;
           long HDFid;
           long XREFid;
	}  Header;

	Header.packetLen = sizeof(Header) 
	                 + sizeof(int)                   /* n Calls     */
	                 + sizeof(int)			 /* array len   */
			 + nTallyFields*sizeof(double)	 /* times array */
	                 + sizeof(int)			 /* array len   */
	                 + nTallyFields*sizeof(int) 	 /* count array */
	                 + sizeof(int)			 /* array len   */
	                 + nByteFields*sizeof(int) 	 /* bytes array */
	                 + nHistFields*sizeof(int)     	 /* array lens  */
	                 + nHistFields*nBkts*sizeof(int) /* byte hist   */
	                 + sizeof(int);                  /* nodeID      */
	Header.packetTag = HDF_SUMMARY_FAMILY +
			   ( procIndex + 1 )*8 + RECORD_TRACE ;
	Header.packetType = PKT_DATA;
	nodeID = TRgetNode();
        while ( P != NULL ) {
	   Q = P->ptr;
	   /*===========================================================*
	   // Total the I/O time and counts                        	*
	   //===========================================================*/
           allIOTime = zeroClock;
           for ( j = FirstIO; j <= LastIO; ++j ) {
              allIOTime = clockAdd( allIOTime, P->record.times[j] );
           }
           P->record.times[AllIO] = allIOTime;
 
           allIOCount = 0;
           for ( j = FirstIO; j <= LastIO; ++j ) {
              allIOCount += P->record.counts[j];
           }
           P->record.counts[AllIO] = allIOCount;
	   /*===========================================================*
	   // compute exclusive duration.				*
	   //===========================================================*/
	   excDur = clockSubtract(P->record.incDur,allIOTime);
	   excDur = clockSubtract(excDur,P->record.times[HDF_]);
	   excDur = clockSubtract(excDur,P->record.times[MPI]);
	   /*===========================================================*
	   // print header information.					*
	   //===========================================================*/
	   Header.eventID = P->eventID;
           Header.Seconds = clockToSeconds(P->record.lastCall);
	   Header.IncDur  = clockToSeconds( P->record.incDur );
	   Header.ExcDur  = clockToSeconds(excDur);
	   Header.HDFid   = P->record.hdfID;
	   Header.XREFid  = P->record.xRef;
	   Packet = buff;
	   memcpy( Packet, &Header, sizeof(Header) );
	   Packet += sizeof(Header);
	   /*===========================================================*
	   // copy number of calls to Packet.				*
	   //===========================================================*/
	   nCalls  = P->record.nCalls;
	   memcpy( Packet, &nCalls, sizeof(int) );
	   Packet += sizeof(int);
	   /*===========================================================*
	   // copy length of times array and times array to Packet.	*
	   //===========================================================*/
	   arrayLen = nTallyFields;
	   memcpy( Packet, &arrayLen, sizeof(int) );
	   Packet += sizeof(int);
           for ( j = 0; j < nTallyFields; ++j ) {
	      t = clockToSeconds(P->record.times[j]); 
	      memcpy( Packet, &t, sizeof(double) );
	      Packet += sizeof(double);
	   }
	   /*===========================================================*
	   // copy length of counts array and counts array to Packet.	*
	   //===========================================================*/
	   arrayLen = nTallyFields;
	   memcpy( Packet, &arrayLen, sizeof(int) );
	   Packet += sizeof(int);
	   memcpy( Packet, P->record.counts, nTallyFields*sizeof(int) );
	   Packet += nTallyFields*sizeof(int);
	   /*===========================================================*
	   // copy length of bytes array and bytes array to Packet.	*
	   //===========================================================*/
	   arrayLen = nByteFields;
	   memcpy( Packet, &arrayLen, sizeof(int) );
	   Packet += sizeof(int);
	   memcpy( Packet, P->record.bytes, nByteFields*sizeof(int) );
	   Packet += nByteFields*sizeof(int);
	   /*===========================================================*
	   // copy length of historgram arrays and arrays to Packet.	*
	   //===========================================================*/
	   arrayLen = nBkts;
	   for ( i = 0; i < nHistFields; ++i ) 
           {
	      memcpy( Packet, &arrayLen, sizeof(int) );
	      Packet += sizeof(int);
	      memcpy( Packet, P->record.Hists[i], nBkts*sizeof(int) );
	      Packet += nBkts*sizeof(int);
	   }
	   memcpy( Packet, &nodeID, sizeof(int) );
	   Packet += sizeof(int);
           arrayLen = Packet-buff;
           memcpy(buff,&arrayLen,sizeof(int));
  	   putBytes( buff, Packet-buff ); 
           P = Q;
	} 
}
/*======================================================================*
// Initialize a node.							*
//======================================================================*/
void HDFnodeInit ( HDFnode_t *S ) 
{
   *S = InitNode;
}
/*======================================================================*
//      Compute IO totals, exclusive durations of the input record T    *
//      then add the fields of T to that of S.                          *
//======================================================================*/
void HDFrecordSum ( HDFrec_t *S, HDFrec_t *T )
{
   int i;
   int j;
	
   S->nCalls    += T->nCalls;
   if ( clockCompare ( S->lastCall, T->lastCall ) < 0 ) 
   {
      S->lastCall  =  T->lastCall ;
   }
   S->incDur    = clockAdd ( S->incDur, T->incDur );
   for ( j = 0; j < nTallyFields; ++j ) 
   {
      S->times[j] =  clockAdd( S->times[j] , T->times[j] ) ;
   }
   for ( j = 0; j < nTallyFields; ++j ) 
   {
      S->counts[j] += T->counts[j] ;
   }
   for ( j = 0; j < nByteFields; ++j ) 
   {
      S->bytes[j] += T->bytes[j] ;
   }
   for ( j = 0; j < nHistFields; ++j ) 
   {
      for ( i = 0; i < nBkts; ++i ) 
      {
         S->Hists[j][i] += T->Hists[j][i] ;
      }
   }
}
/*======================================================================*
// Return the field index corresponding to an IO event ID.  The fields  *
// are specified in an enum statement in an include file.		*
//======================================================================*/
int getHDFFieldIndex( int eventID )
{
   int result = -1;
   switch ( eventID )
   {
      case ID_malloc:
      case -ID_malloc:
      {
         result = Malloc;
         break;
      }
      case openBeginID:
      case openEndID:
      case fopenBeginID:
      case fopenEndID:
      {
         result = Open;
         break;
      }
      case closeBeginID:
      case closeEndID:
      case fcloseBeginID:
      case fcloseEndID:
      {
         result = Close;
         break;
      }
      case readBeginID:
      case readEndID:
      case freadBeginID:
      case freadEndID:
      {
         result = Read;
         break;
      }
      case lseekBeginID:
      case lseekEndID:
      case fseekBeginID:
      case fseekEndID:
      {
         result = Seek;
         break;
      }
      case writeBeginID:
      case writeEndID:
      case fwriteBeginID:
      case fwriteEndID:
      {
         result = Write;
         break;
      }
      case fflushBeginID:
      case fflushEndID:
      case flushBeginID:
      case flushEndID:
      {
         result = Misc;
         break;
      }
      case rewindBeginID:
      case rewindEndID:
      case fsetposBeginID:
      case fsetposEndID:
      {
         result = Misc;
         break;
      }
#ifdef	creadBeginID
      case creadBeginID:
      case creadEndID:
      case creadvBeginID:
      case creadvEndID:
      {
         result = Read;
         break;
      }
      case cwriteBeginID:
      case cwriteEndID:
      case cwritevBeginID:
      case cwritevEndID:
      {
         result = Write;
         break;
      }
      case ireadBeginID:
      case ireadEndID:
      case ireadvBeginID:
      case ireadvEndID:
      {
         result = Aread;
         break;
      }
      case iwriteBeginID:
      case iwriteEndID:
      case iwritevBeginID:
      case iwritevEndID:
      {
         result = Awrite;
         break;
      }
      case iowaitBeginID:
      case iowaitEndID:
      {
         result = Wait;
         break;
      }
      case iodoneBeginID:
      case iodoneEndID:
      {
         result = Misc;
         break;
      }
      case gopenBeginID:
      case gopenEndID:
      {
         result = Open;
         break;
      }
      case iomodeBeginID:
      case iomodeEndID:
      case setiomodeBeginID:
      case setiomodeEndID:
      case lsizeBeginID:
      case lsizeEndID:
      case forflushBeginID:
      case forflushEndID:
      {
         result = Misc;
         break;
      }
#endif	
   }
   return result;
}
/*======================================================================*
// This routine determines the field index in the bytes array of the 	*
// HDF records which correspond to a given IO operation.  If the  	*
// operation does not transfer bytes, (e.g., open operation), -1 is	*
// returned.								*
//======================================================================*/
int getHDFByteFieldIndex( int Operation ) 
{
   int result;
   switch ( Operation )
   {
      case Malloc:
      {
         result = MallocBytes;
         break;
      }
      case Read:
      {
         result = ReadBytes;
         break;
      }
      case Write:
      {
         result = WriteBytes;
         break;
      }
      case Aread:
      {
         result = AreadBytes;
         break;
      }
      case Awrite:
      {
         result = AwriteBytes;
         break;
      }
      case MPIOread:
      {
         result = MPIOreadBytesReq;
         break;
      }
      case MPIOwrite:
      {
         result = MPIOwriteBytesReq;
         break;
      }
      case MPIOreadAll:
      {
         result = MPIOreadAllBytesReq;
         break;
      }
      case MPIOwriteAll:
      {
         result = MPIOwriteAllBytesReq;
         break;
      }
      case MPIOiRead:
      {
         result = MPIOiReadBytesReq;
         break;
      }
      case MPIOiWrite:
      {
         result = MPIOiWriteBytesReq;
         break;
      }
      case MPIOreadTrans:
      {
         result = MPIOreadBytesTrans;
         break;
      }
      case MPIOwriteTrans:
      {
         result = MPIOwriteBytesTrans;
         break;
      }
      case MPIOreadAllTrans:
      {
         result = MPIOreadAllBytesTrans;
         break;
      }
      case MPIOwriteAllTrans:
      {
         result = MPIOwriteAllBytesTrans;
         break;
      }
      default:
      {
         result = -1;
         break;
      }
   }
   return result;
}
/*======================================================================*
// This routine writes the SDDF packet descriptors for the HDF summary	*
// records to the output file.						*
//======================================================================*/
void _hdfDescriptorRT( char *recordName, char *recordDescription, 
                                                       int recordFamily )
{
    static char	recordBuffer[ 4096 ];
    int		recordLength;

    hdfRecordPointer = recordBuffer;
    /*==================================================================*
    // Allow space at the beginning of the record for the packet 	*
    //length which will be computed after the packet is complete.	*
    //==================================================================*/
    sddfWriteInteger( &hdfRecordPointer, 0 );
    /*==================================================================* 
    // The record type, tag, and name 					* 
    //==================================================================*/
    sddfWriteInteger( &hdfRecordPointer, PKT_DESCRIPTOR );
    sddfWriteInteger( &hdfRecordPointer, ( recordFamily | RECORD_TRACE ) );
    sddfWriteString( &hdfRecordPointer, recordName );
    /*==================================================================* 
    // The record attribute count and string pair 			* 
    //==================================================================*/
    sddfWriteInteger( &hdfRecordPointer, 1 );
    sddfWriteString( &hdfRecordPointer, "description" );
    sddfWriteString( &hdfRecordPointer, recordDescription );
    /*==================================================================*
    // The record field count 						*
    //==================================================================*/
    sddfWriteInteger( &hdfRecordPointer, 16 );
    WRITE_HDF_FIELD( "Event Identifier", 
	             "Event ID",
                     "Corresponding Event",
	             INTEGER, 0 );
    WRITE_HDF_FIELD( "Seconds", 
	   	     "Seconds", 
                     "Floating Point Timestamp", 
		      DOUBLE, 0 );
    WRITE_HDF_FIELD( "Inclusive Duration", 
  	             "Inclusive Duration", 
	             "Inclusive Duration of this Procedure",
		      DOUBLE, 0 );
    WRITE_HDF_FIELD( "Exclusive Duration", 
  	             "Exclusive Duration", 
	             "Excludes IO, MPI-IO and other HDF calls",
		      DOUBLE, 0 );
    WRITE_HDF_FIELD2("HDF ID",
                     "HDF ID", "Identifier number",
                     "0", "No HDF ID specified",
                     LONG, 0 );
    WRITE_HDF_FIELD( "Xref ID",
                     "Cross Reference", 
                     "Index of related HDF ID or 0 if none",
		     LONG, 0 );
    WRITE_HDF_FIELD( "N Calls", 
		     "N Calls", 
		     "Number of Calls to this Proc", 
		     INTEGER, 0 );
    WRITE_HDF_FIELD( "Times Array",
                     "Times Array",
	             "Array of Total Operation Times",
		     DOUBLE, 1 );
    WRITE_HDF_FIELD( "Counts Array",
                     "Counts Array",
	             "Array of Total Operation Counts",
		     INTEGER, 1 );
    WRITE_HDF_FIELD( "Bytes Array",
                     "Bytes Array",
	             "Array of Total Bytes Transferred",
		     INTEGER, 1 );
    WRITE_HDF_FIELD( "Malloc Histogram",
                     "Malloc Histogram",
	             "Historgram of size Malloc Requests",
		     INTEGER, 1 );
    WRITE_HDF_FIELD( "Read Histogram",
                     "Read Histogram",
	             "Historgram of size Read Requests",
		     INTEGER, 1 );
    WRITE_HDF_FIELD( "Write Histogram",
                     "Write Histogram",
	             "Historgram of size Write Requests",
		     INTEGER, 1 );
    WRITE_HDF_FIELD( "Aread Histogram",
                     "Aread Histogram",
	             "Historgram of size Asynch Read Requests",
		     INTEGER, 1 );
    WRITE_HDF_FIELD( "Awrite Histogram",
                     "Awrite Histogram",
	             "Historgram of size Asynch Write Requests",
		     INTEGER, 1 );
    WRITE_HDF_FIELD( "Processor Number", 
		     "Node", 
		     "Processor number", 
		     INTEGER, 0 );
    /*=================================================================== 
    // The entire record descriptor packet has been written.		* 
    // Compute and update the record length.				* 
    // Write the completed record.					* 
    //==================================================================*/
    recordLength = (int)(hdfRecordPointer - recordBuffer);

    hdfRecordPointer = recordBuffer;
    sddfWriteInteger( &hdfRecordPointer, recordLength );

    putBytes( recordBuffer, (unsigned) recordLength );
}

/*======================================================================* 
//   Internal Routine:  writeHDFRecDescrptrsRT	                        * 
//                      Writes record descriptors for the HDF events.   * 
//======================================================================*/
void writeHDFRecDescrptrsRT( void ) 
{
	int j, FAMILY;
        char BUF1[256], BUF2[256] ;
        char buff[41];
	_hdfNameDescriptor();	/* Descriptor for named identifiers	*/
        for ( j = 0; j < NumHDFProcs; ++j ) {
           if ( HDFQueues[j] != NULL ) {
              getHDFprocName( j, buff );
              strcpy( BUF2, "HDF ");
              strcat( BUF2, buff );
              strcat( BUF2, " Procedure Summary");
              strcpy( BUF1, BUF2 );
              strcat( BUF1, " Trace");
	      FAMILY = HDF_SUMMARY_FAMILY + (j + 1)*8;
              _hdfDescriptorRT( BUF1, BUF2, FAMILY );
           }
        }
        return;
}
/*======================================================================*
// This routine prints the Pablo IDs assigned to named HDF identifiers  *
//======================================================================*/
void printFileMappingsRT( char *mapFile, char **Names, int nPabloIDs )
{
	int i;
	FILE *ptr;
	ptr = fopen( mapFile, "w" );
 
        if ( ptr == NULL ) {
	    fprintf(stderr,
                    "Couldn't open map file %s - none created.\n",mapFile);
            return;
        }
 
	fprintf(ptr,"\n\nPablo ID to HDF Name mappings:\n");
	fprintf(ptr,"------------------------------\n");
        for ( i = 1; i <= nPabloIDs; i++ ) {
	     fprintf(ptr,"%4d %s\n",i,Names[i] );
	}
	fprintf(ptr,"\n\n");
	fclose( ptr );
}
/************************************************************************/
/*	_hdfNameDescriptor	     					*/
/*	   Generate a SDDF binary format record descriptor for the	*/
/*	   named identifiers used during execution.              	*/
/************************************************************************/
void _hdfNameDescriptor( void )
{
    static char recordBuffer[ 4096 ];
    int         recordLength;

#ifdef PABLODEBUG
	fprintf( debugFile, "_hdfExitTraceDescriptor entered\n" );
	fflush( debugFile );
#endif /* PABLODEBUG */
    hdfRecordPointer = recordBuffer;
    /********************************************************************/
    /* Allow space at the beginning of the record for the packet        */
    /*length which will be computed after the packet is complete.       */
    /********************************************************************/
    sddfWriteInteger( &hdfRecordPointer, 0 );
    /********************************************************************/
    /* The record type, tag, and name                                   */
    /********************************************************************/
    sddfWriteInteger( &hdfRecordPointer, PKT_DESCRIPTOR );
    sddfWriteInteger( &hdfRecordPointer, ( FAMILY_NAME ) );
    sddfWriteString( &hdfRecordPointer, "HDF Name Identifier Record" );
    /********************************************************************/
    /* The record attribute count and string pair                       */
    /********************************************************************/
    sddfWriteInteger( &hdfRecordPointer, 1 );
    sddfWriteString( &hdfRecordPointer, "description" );
    sddfWriteString( &hdfRecordPointer, "HDF Name Identifier Record" );
    /********************************************************************/
    /* The record field count                                           */
    /********************************************************************/
    sddfWriteInteger( &hdfRecordPointer, 3);
    /********************************************************************/
    /* Create fields                                               	*/
    /********************************************************************/
    WRITE_HDF_FIELD(  "Identifier Type", 
		      "Data Set Type", 
		      "Data Set Identifier Type", 
		      INTEGER, 0 );
    WRITE_HDF_FIELD2( "HDF ID",
                      "HDF ID", "File, Data Set or Dim Identifier number",
                      "0", "No HDF ID specified",
                      INTEGER, 0 ); 
    WRITE_HDF_FIELD( "HDF Name",
                     "HDF Name", "Name of File, Data Set or Dim",
                      CHARACTER, 1 );

    recordLength = (int)(hdfRecordPointer - recordBuffer);

    hdfRecordPointer = recordBuffer;
    sddfWriteInteger( &hdfRecordPointer, recordLength );

    putBytes( recordBuffer, (unsigned) recordLength );
}
#endif /* PCF_BUILD */