/**-----------------------------------------------------------
 * $Id: ProfileMon.java,v 1.2 1999/11/27 22:43:07 yamauchi Exp $
 *------------------------------------------------------------
 *	Profile Monitor 
 *	1999/10/14 
 *	Copyright (C) 1999 YAMAUCHI Hitoshi : 山内 斉
 *	Java Version 1999年10月26日(火) 
 *
 * 性能・動作解析用
 *	send と recv を除いて同じ event が続く場合には出力しない．
 */

package jp.ac.tohoku.is.archi.mpi2.common;

import  java.io.*;
import  jp.ac.tohoku.is.archi.mpi2.util.*;
import  jp.ac.tohoku.is.archi.mpi2.sysparam.*;

/**
 * 性能解析用
 */
public final class ProfileMon 
implements Config
{
    // singleton method
    private static ProfileMon mPProfMon;
    public  static ProfileMon getInstance() 
    {
	if(mPProfMon == null)
	{
	    mPProfMon = new ProfileMon();
	}
	return(mPProfMon);
    }


    /*
     * stage 
     */
    public final static int ProfSt_radiosity		= 0;
    public final static int ProfSt_prepareShading	= 1;
    public final static int ProfSt_raytracing		= 2;
    public final static int ProfSt_Else			= 3;
    
    /*
     * event  
     */
    public final static int ProfEv_genHemiCube  = 0; // radiosity
    public final static int ProfEv_genRays	= 1; // ray-tracing
    public final static int ProfEv_StopRayGen   = 2; // global load balancing
    public final static int ProfEv_ResumeRayGen = 3; // 
    public final static int ProfEv_ProcRadRays	= 4; // processing radiosity
    public final static int ProfEv_ProcRayRays	= 5; // processing ray-tracing
    public final static int ProfEv_SendingWait	= 6; // send waiting only
    public final static int ProfEv_Idle		= 7; // Idle
    public final static int ProfEv_Send		= 8; // send
    public final static int ProfEv_Recv		= 9; // recv
    public final static int ProfEv_ELSE		= 10;

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

    private String [] mStageNameTab;
    private String [] mEventNameTab;

    /**
     * get elapsed time : use this class only and need initialize mStartTime
     */
    private double getElapsedTime() 
    {
	double etime = ElapsedTimer.getNowRawTime() - this.mStartTime;
	return(etime);
    }

    /**
     * constructor
     */
    public ProfileMon()
    {
	this.mStage		= ProfSt_Else;
	this.mLastEvent		= ProfEv_ELSE;
	this.mThisRank		= -1;
	this.mOutFileFp		= null;
	this.mStartTime		= 0.0;

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

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

	this.mStage		= ProfSt_Else;
	this.mLastEvent		= ProfEv_ELSE;
	this.mThisRank		= myrank;
	this.mOutFileFp		= null;
	this.mStartTime		= ElapsedTimer.getNowRawTime();

	if(FileInfo.getInstance().isProgramMarkerOn())	
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}
	    
	if(FileInfo.getInstance().isTraceFileOut())
	{
	    if((outfilebasename.equals("-")) ||
	       (outfilebasename.equals("(stdout)")))
	    {
		// to standard out 
		this.mOutFileFp = new PrintWriter(System.out);
		/*
		catch(IOException e)
		{
		    String s = "ProfileMon:: Can not open PrintStream "
			+ "with System.out. " + e;
		    throw new RuntimeException(s);
		}
		*/
		// Trace 情報のヘッダ
		this.mOutHeadder = Mpi2Constants.TRACE_STDOUT_HEAD; 
	    }else
	    {
		String tracefilename = 
		    "trace." + outfilebasename + "." + 
		    PrintUsing.getDigitString(3, this.mThisRank);

		File f = new File(tracefilename);
		if(f.exists())
		{
		    String s = "Error! : There is already file [" + 
			tracefilename + "], exit.";
		    throw new RuntimeException(s);
		}
		
		try {
		    this.mOutFileFp = 
			new PrintWriter(new FileOutputStream(tracefilename));
		}
		catch(IOException e)
		{
		    String s = "ProfileMon:: Can not open PrintStream "+
			tracefilename + ", " + e;
		    throw new RuntimeException(s);
		}

		// ファイルへ出力する場合には情報が分離するのでヘッダは不要
		this.mOutHeadder = "";
	    }
	    // "%s%s Trace file : rank %d : starttime %g\n",
	    this.mOutFileFp.println(this.mOutHeadder +
				    Mpi2Constants.VERBOSEHEAD + 
				    "Trace file : rank " + this.mThisRank + 
				    " : starttime " + this.mStartTime);
	}
    }

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

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

	if(this.mStage != stage)
	{
	    String s = "Error! : ProfileMon : Stage " + stage + 
		" is not start, but done.";
	    throw new RuntimeException(s);
	}
	
	if(FileInfo.getInstance().isProgramMarkerOn())
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}

	if(FileInfo.getInstance().isTraceFileOut())
	{
	    // endstage R (rank) T (elapsed time)
	    this.mOutFileFp.println(this.mOutHeadder +
				    "endstage " + 
				    this.mStageNameTab[this.mStage] +
				    " R " + this.mThisRank + 
				    " T " + this.getElapsedTime());
	}
	this.mLastEvent = ProfEv_ELSE;
    }

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

	if(FileInfo.getInstance().isProgramMarkerOn())
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
		/*
		    pm_change_stat(PC_proc_ray_e); 
		*/
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}
	
	if(FileInfo.getInstance().isTraceFileOut())
	{
	    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)
		    // "%s%s R %d T %g\n", 
		    this.mOutFileFp.println(this.mOutHeadder +
					    this.mEventNameTab[evType] + 
					    " R " + this.mThisRank + 
					    " T " + this.getElapsedTime());
		}
		this.mLastEvent = evType;
		break;

	    case ProfEv_Send:
	    case ProfEv_Recv:	    
		String s1 = "Internal Error! : do not use occurEv for " +
		    "this event. " + this.mEventNameTab[evType];
		throw new RuntimeException(s1);
		
	    default:
		String s2 = "Internal Error! : unrecognized event";
		throw new RuntimeException(s2);
	    }
	}
    }

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

	if(FileInfo.getInstance().isProgramMarkerOn())
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}

	if(FileInfo.getInstance().isTraceFileOut())
	{
	    // hcinfo R (rank) T (elapsed time) : (hemicubes in queue)
	    // (rest of unshotted energy)
	    this.mOutFileFp.println(this.mOutHeadder +
				    this.mEventNameTab[ProfEv_genHemiCube] + 
				    " R " + this.mThisRank + 
				    " T " + this.getElapsedTime() +
				    " : " + rest_hemicubes	  + 
				    " "   + restEnergy);
	}
    }

    /** 
     * ray 生成時の記録用
     */
    /*
	何本生成したかを記録するには生成してみないとわからない
	Hemicube 生成時の情報記録のように発生した ray の数を count す
	るか?    
    public 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", 
		    (char*)this.mOutHeadder, 
		    (char*)this.mpEventNameTab[ProfEv_genRays],
		    this.mThisRank,   this.getElapsedTime(), procRays);
	}
    }
    */

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

	if(FileInfo.getInstance().isProgramMarkerOn())
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
	    }
	}

	if(FileInfo.getInstance().isTraceFileOut())
	{
	    // s R (rank) T (elapsed time) : (destination rank) (size) (tag)
	    // "%s%s R %d T %g : %d %d %d\n", 
	    this.mOutFileFp.println(this.mOutHeadder +
				    this.mEventNameTab[ProfEv_Send] + 
				    " R " + this.mThisRank + 
				    " T " + this.getElapsedTime() +
				    " : " + destRank 	+ 
				    " "   + size	+ 
				    " "   + tag);
	}
	this.mLastEvent = ProfEv_Send;
    }

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

	if(FileInfo.getInstance().isProgramMarkerOn())
	{
	    if(Use_IBM_Program_MarkerArray)
	    {
		
	    }

	    if(Use_Mpi2_Program_MarkerArray)
	    {
		
	    }
	}

	if(FileInfo.getInstance().isTraceFileOut())
	{
	    // r R (rank) T (elapsed time) : (source rank) (size) (tag)
	    this.mOutFileFp.println(this.mOutHeadder +
				    this.mEventNameTab[ProfEv_Recv] + 
				    " R " + this.mThisRank + 
				    " T " + this.getElapsedTime() +
				    " : " + srcRank 	+ 
				    " "   + size	+ 
				    " "   + tag);
	}
	this.mLastEvent = ProfEv_Recv;
    }
}







