/**-----------------------------------------------------------
 * $Id: ProfileMon.h,v 1.2 1999/12/07 03:50:02 yamauchi Exp $
 *------------------------------------------------------------
 *	Profile Monitor 
 *	1999/10/14 
 *	Copyright (C) 1999 YAMAUCHI Hitoshi : 山内 斉
 *
 * 性能・動作解析用
 *	send と recv を除いて同じ event が続く場合には出力しない．
 */
#ifndef _ProfileMon_h
#define _ProfileMon_h 1

#include <iostream.h>
#include <stdio.h>
#include "myString.h"
#include "ElapsedTimer.h"
#include "config.h"
#include "FileInfo.h"

/*
 * USE_PROFILE_MON が define されていない場合にはこの関数群は使用しない．
 */
#ifdef USE_PROFILE_MON
static const int Use_Profile_Monitor = 1; // Use
#else 
static const int Use_Profile_Monitor = 0; // not use
#endif

/*
 * USE_IBM_PMARRAY が define されていない場合には ibm_pm_array 関
 * 係の関数群を使用しない ．
 */
#ifdef USE_IBM_PMARRAY 
static const int Use_IBM_Program_MarkerArray = 1; // Use
#else 
static const int Use_IBM_Program_MarkerArray = 0; // not use
#endif

/*
 * USE_MPI2_PMARRAY が define されていない場合には mpi2_pm_array 関係
 * の関数群を使用しない ．
 */
#ifdef USE_MPI2_PMARRAY 
static const int Use_Mpi2_Program_MarkerArray = 1; // Use
#else 
static const int Use_Mpi2_Program_MarkerArray = 0; // not use
#endif
    
/**
 * 性能解析用
 */
class ProfileMon {
private:
    ProfileMon(const ProfileMon& rhs);
    const ProfileMon &operator=(const ProfileMon& rhs);
    
private:
    // singleton method
    static ProfileMon *PProfMon;
public:
    static ProfileMon *getInstance() 
    {
	if(PProfMon == NULL)
	{
	    PProfMon = new ProfileMon();
	}
	return(PProfMon);
    }

public:
    /*
     * stage 
     */
    enum ProfileMon_stage {
	ProfSt_radiosity,
	ProfSt_prepareShading,
	ProfSt_raytracing,
	ProfSt_Else
    };

    /*
     * event  
     */
    enum ProfileMon_event {
	ProfEv_genHemiCube,	// radiosity
	ProfEv_genRays,		// ray-tracing
	ProfEv_StopRayGen,	// global load balancing
	ProfEv_ResumeRayGen,	// 
	ProfEv_ProcRadRays,	// processing radiosity		*
	ProfEv_ProcRayRays,	// processing ray-tracing
	ProfEv_SendingWait,	// send waiting only
	ProfEv_Idle,		// Idle

	ProfEv_Send,		
	ProfEv_Recv,		
	ProfEv_ELSE
    };

private:
    int 	mStage;
    int 	mLastEvent;
    int 	mThisRank;
    // tracefile の各行のヘッダ (stdout の時に他の情報と分離したい場合に使用)
    myString	mOutHeadder;	
    FILE *	mpOutFileFp;    
    double 	mStartTime;

    myString *  mpStageNameTab;
    myString *  mpEventNameTab;

private:
    double getElapsedTime() const
    {
	double etime = 
	    ElapsedTimerClass::getNowRawTime() - this->mStartTime;
	return(etime);
    }
    
public:
    /**
     * constructor
     */
    ProfileMon()
    {
	this->mStage		= ProfSt_Else;
	this->mLastEvent	= ProfEv_ELSE;
	this->mThisRank		= -1;
	this->mpOutFileFp	= NULL;
	this->mStartTime	= 0.0;

	this->mpStageNameTab	= new myString[ProfSt_Else];
	this->mpStageNameTab[ProfSt_radiosity] = "radiosity";
	this->mpStageNameTab[ProfSt_prepareShading] = "prepareshade";
	this->mpStageNameTab[ProfSt_raytracing] = "raytrace";
	
	this->mpEventNameTab	= new myString[ProfEv_ELSE];
	this->mpEventNameTab[ProfEv_genHemiCube] = "gh";
	this->mpEventNameTab[ProfEv_genRays]	 = "gr";
	this->mpEventNameTab[ProfEv_StopRayGen]	 = "sr";
	this->mpEventNameTab[ProfEv_ResumeRayGen]= "rr";
	this->mpEventNameTab[ProfEv_ProcRadRays] = "rad";
	this->mpEventNameTab[ProfEv_ProcRayRays] = "ray";
	this->mpEventNameTab[ProfEv_SendingWait] = "sw";
	this->mpEventNameTab[ProfEv_Idle]	 = "id";
	this->mpEventNameTab[ProfEv_Send]	 = "s";		
	this->mpEventNameTab[ProfEv_Recv]	 = "r";
    }

    /**
     * initialize 
     *	@param myrank 		この trace data を出力するプロセスのランク
     *	@param outfilebasename 	trace file の basename．
     * 	       "trace file を示す接頭辞 + outfilebasename + rank 番号" 
     * 	       が各 trace file の名前になる．ただし，`-' あるいは 
     *	       `(stdout)' の場合には出力は stdout にヘッダ()文字を付加し
     * 	       て出力される．
     */
    void init(int myrank, const myString &outfilebasename)
    {
	if(!Use_Profile_Monitor)
	{
	    return;		// not use profile monitor
	}

	this->mStage		= ProfSt_Else;
	this->mLastEvent	= ProfEv_ELSE;
	this->mThisRank		= myrank;
	this->mpOutFileFp	= NULL;
	this->mStartTime	= ElapsedTimerClass::getNowRawTime();

	if(FileInfo::getInstance()->isProgramMarkerOn() == YES)	
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}
	    
	if(FileInfo::getInstance()->isTraceFileOut() == YES)
	{
	    if((outfilebasename == "-") ||
	       (outfilebasename == "(stdout)"))
	    {
		// to standard out 
		this->mpOutFileFp = stdout;
		this->mOutHeadder = TRACE_STDOUT_HEAD; // Trace 情報のヘッダ
	    }else
	    {
		char tracefilename[256];
#ifdef HAVE_SNPRINTF
		snprintf(tracefilename, 256, "trace.%s.%03d", 
			 (const char*)outfilebasename, this->mThisRank);
#else
		sprintf(tracefilename, "trace.%s.%03d", 
			(const char*)outfilebasename, this->mThisRank);
#endif
	    
		if((this->mpOutFileFp = fopen(tracefilename, "r")) != NULL)
		{
		    cout << "Error! : There is already trace file [" << 
			tracefilename << "], exit." << endl;
		    fclose(this->mpOutFileFp);
		    exit(1);
		}

		if((this->mpOutFileFp = fopen(tracefilename, "w")) == NULL)
		{
		    cout << "Error! : can not open trace file [" << 
			tracefilename << "], exit." << endl;
		    fclose(this->mpOutFileFp);
		    exit(1);
		}
		// ファイルへ出力する場合には情報が分離するのでヘッダは不要
		this->mOutHeadder = "";
	    }
	    fprintf(this->mpOutFileFp, 
		    "%s%s Trace file : rank %d : starttime %g\n",
		    (const char*)this->mOutHeadder, VERBOSEHEAD, 
		    this->mThisRank, this->mStartTime);
	}
    }

    /*------------------------------------------------------------
     * 以下は各 event の出力を行う際に呼ぶ init が先に呼ばれていなくて
     * はならない
     *-----------------------------------------------------------*/
    /**
     * stage の start 時に call することでそれを記録する．
     */
    void startStage(int stage)
    {
	if(!Use_Profile_Monitor)
	{
	    return;		// not use profile monitor
	}
	
	this->mStage = stage;
	
	if(FileInfo::getInstance()->isProgramMarkerOn() == YES)	
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
	    }
	    
	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}
	
	if(FileInfo::getInstance()->isTraceFileOut() == YES)
	{
	    // startstage 
	    double etime = 
		ElapsedTimerClass::getNowRawTime() - this->mStartTime;
	    fprintf(this->mpOutFileFp, 
		    "%sstartstage %s R %d T %g\n",
		    (const char*)this->mOutHeadder, 
		    (const char*)this->mpStageNameTab[this->mStage],
		    this->mThisRank, etime);
	}
	this->mLastEvent = ProfEv_ELSE;
    }

    /**
     * stage の end  時に call することで記録を行う
     */
    void endStage(int stage)
    {
	if(!Use_Profile_Monitor)
	{
	    return;		// not use profile monitor
	}

	if(this->mStage != stage)
	{
	    cout << "Error! : ProfileMon : Stage " << stage << 
		" is not start, but done." << endl;
	    exit(1);
	}
	
	if(FileInfo::getInstance()->isProgramMarkerOn() == YES)	
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}

	if(FileInfo::getInstance()->isTraceFileOut() == YES)
	{
	    // endstage R (rank) T (elapsed time)
	    fprintf(this->mpOutFileFp, 
		    "%sendstage %s R %d T %g\n",
		    (const char*)this->mOutHeadder, 
		    (const char*)this->mpStageNameTab[this->mStage],
		    this->mThisRank, this->getElapsedTime());
	}
	this->mLastEvent = ProfEv_ELSE;
    }

    /** 
     * event 全般の記録
     * 	いくつか特別な記録が必要なもの (genHemicube, procRays, send,
     * 	recv) を除く event の記録用
     */
    void occurEv(int evType)
    {
	if(!Use_Profile_Monitor)
	{
	    return;		// not use profile monitor
	}

	if(FileInfo::getInstance()->isProgramMarkerOn() == YES)
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
		/*
		    pm_change_stat(PC_proc_ray_e); 
		*/
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}
	
	if(FileInfo::getInstance()->isTraceFileOut() == YES)
	{
	    switch(evType)
	    {
	    case ProfEv_genHemiCube:
	    case ProfEv_genRays:	    
	    case ProfEv_StopRayGen:
	    case ProfEv_ResumeRayGen:
	    case ProfEv_SendingWait:
	    case ProfEv_Idle:
	    case ProfEv_ProcRadRays:
	    case ProfEv_ProcRayRays:
		if(this->mLastEvent != evType)
		{
		    // (event name) R (rank) T (elapsed time)
		    fprintf(this->mpOutFileFp, "%s%s R %d T %g\n", 
			    (const char*)this->mOutHeadder, 
			    (const char*)this->mpEventNameTab[evType],
			    this->mThisRank,
			    this->getElapsedTime());
		}
		this->mLastEvent = evType;
		break;

	    case ProfEv_Send:
	    case ProfEv_Recv:	    
		cout << "Internal Error! : do not use occurEv for "
		    "this event. " << this->mpEventNameTab[evType] << endl;
		exit(1);
		break;

	    default:
		cout << "Internal Error! : unrecognized event" << endl;
		exit(1);
		break;
	    }
	}
    }

    /** 
     * HemiCube 生成時の情報記録用
     */
    void outHemiCubeInfo(int rest_hemicubes, double restEnergy)
    {
	if(!Use_Profile_Monitor)
	{
	    return;		// not use profile monitor
	}

	if(FileInfo::getInstance()->isProgramMarkerOn() == YES)
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}

	if(FileInfo::getInstance()->isTraceFileOut() == YES)
	{
	    // hcinfo R (rank) T (elapsed time) : (hemicubes in queue)
	    // (rest of unshotted energy)
	    fprintf(this->mpOutFileFp, "%s%s R %d T %g : %d %g\n", 
		    (const char*)this->mOutHeadder, "hcinfo",
		    this->mThisRank,   		    this->getElapsedTime(),
		    rest_hemicubes, 		    restEnergy);
	}
    }

    /** 
     * ray 生成時の記録用
     */
    /*
	何本生成したかを記録するには生成してみないとわからない
	Hemicube 生成時の情報記録のように発生した ray の数を count す
	るか?    
    void genRays(int procRays)
    {
	if(!Use_Profile_Monitor)
	{
	    return;		// not use profile monitor
	}

	if(Use_IBM_Program_MarkerArray)
	{
	    
	}

	if(Use_Mpi2_Program_MarkerArray)
	{
	    // gr R (rank) T (elapsed time) #r (number of generated rays)
	    fprintf(this->mpOutFileFp, "%s%s R %d T %g #r %d\n", 
		    (const char*)this->mOutHeadder, 
		    (const char*)this->mpEventNameTab[ProfEv_genRays],
		    this->mThisRank,   this->getElapsedTime(), procRays);
	}
    }
    */

    /** 
     * send 時の記録用
     *	@param destRank 送信先
     *  @param size     object 数
     *  @param tag      何を送信したかを示す tag
     */
    void send(int destRank, int size, int tag)
    {
	if(!Use_Profile_Monitor)
	{
	    return;		// not use profile monitor
	}

	if(FileInfo::getInstance()->isProgramMarkerOn() == YES)
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}

	if(FileInfo::getInstance()->isTraceFileOut() == YES)
	{
	    // s R (rank) T (elapsed time) : (destination rank) (size) (tag)
	    fprintf(this->mpOutFileFp, "%s%s R %d T %g : %d %d %d\n", 
		    (const char*)this->mOutHeadder, 
		    (const char*)this->mpEventNameTab[ProfEv_Send],
		    this->mThisRank,   this->getElapsedTime(), 
		    destRank, size, tag);
	}
	this->mLastEvent = ProfEv_Send;
    }

    /** 
     * recv 時の記録用
     *	@param srcRank 送信元
     *  @param size    object 数
     *  @param tag     何を受信したかを示す tag
     */
    void recv(int srcRank, int size, int tag)
    {
	if(!Use_Profile_Monitor)
	{
	    return;		// not use profile monitor
	}

	if(FileInfo::getInstance()->isProgramMarkerOn() == YES)
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
		
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
		
	    }
	}

	if(FileInfo::getInstance()->isTraceFileOut() == YES)
	{
	    // r R (rank) T (elapsed time) : (source rank) (size) (tag)
	    fprintf(this->mpOutFileFp, "%s%s R %d T %g : %d %d %d\n", 
		    (const char*)this->mOutHeadder, 
		    (const char*)this->mpEventNameTab[ProfEv_Recv],
		    this->mThisRank,   this->getElapsedTime(), 
		    srcRank, size, tag);
	}
	this->mLastEvent = ProfEv_Recv;
    }
};

// singleton pattern
#ifdef THIS_IS_GLOBAL_DEFINE_FILE
ProfileMon *ProfileMon::PProfMon = NULL;	
#endif				// #ifdef THIS_IS_GLOBAL_DEFINE_FILE

#endif				// _ProfileMon_h







