stats.c 6.07 KB
/*
 * Copyright (C) 1996-1998 by the Board of Trustees
 *    of Leland Stanford Junior University.
 * 
 * This file is part of the SimOS distribution. 
 * See LICENSE file for terms of the license. 
 *
 */

/*****************************************************************
 * stats.c 
 * Statistics support for mipsy. More specific statistics are kept
 * with the caches and memory systems right now. I'll abstract them
 * later. 
 *
 * $Author: blythe $
 * $Date: 2002/05/29 01:09:10 $
 *****************************************************************/

#include <sys/time.h>
#include <stdio.h>
#include <strings.h>
#include "mipsy.h"
#include "cpu_stats.h"
#include "simstats.h"
#include "simstats.h"
#include "cpu_state.h"
#include "cpu.h"
#include "sim_error.h"
#include "eventcallback.h"
#include "cpu_interface.h"
#include "hw_events.h"
#include "machine_params.h"
#include "memref.h"

#ifdef MIPSY_MXS
#include "ms.h"
#endif
#include "simutil.h"

#ifdef SOLO
#include "solo.h"
#include "tcl_init.h"
#endif
#include "addr_layout.h"



/* Local Functions */
static void EnablePeriodicStats(int cpuNum, SimTime interval);
static void PeriodicStats(SimTime cycleCount);

/*****************************************************************
 * MipsyInitStats
 * 
 *****************************************************************/
void
MipsyInitStats(void)
{
   MemRefInit();
   InitCPUStats();
   EnablePeriodicStats(0, SAMPLE_PC_INTERVAL);
}


/*****************************************************************
 * MipsyResetStats
 *
 *****************************************************************/ 
void
MipsyResetStats(void)
{
   unsigned i;

   ResetCPUStats();

   for (i=0; i < TOTAL_CPUS; i++) {
      MemRefResetStats(i);
   }

#ifdef SOLO
   Tcl_Eval(TCLInterp, "annotation exec solo resetStats");
#endif
}

/*****************************************************************
 * MipsyStatsCallback:
 *
 * This is a heavily used callback. Right now I check almost every 
 * periodic event here and process it if needed. It also prints out 
 * a whole mess of statistics at regular intervals and then reinstalls 
 * itelf everytime. This event is called every SAMPLE_PC_INTERVAL. 
 *
 * We do all kinds of things here that aren't directly related to
 * stats, including checking for interrupts and for exit conditions.
 * Additionally, every STAT_INTERVAL cycles we call out to print the
 * periodic statistics.
 *****************************************************************/

static struct StatsHdr {
    EventCallbackHdr hdr;
    SimTime statsInterval;
    int periodicStats;
    int intervalsLeft;

} statsEvent[MIPSY_MAX_CPUS];


static void 
MipsyStatsCallback(int cpuNum, EventCallbackHdr *hdr, void *arg)
{
   int cpu;

   if (simosCPUType != MIPSY) {
      return;
   }

   for (cpu = 0; cpu < TOTAL_CPUS; cpu++) {
      CPUState *P = &(PE[cpu]);
      if (P->cpuStatus == cpu_halted) {
         continue;
      }

#ifndef SOLO      
      if (!IS_KSEG1(P->PC) && P->PC) {
         CYCLE_SAMPLE_EVENT(MipsyReadTime(cpu), cpu, P->PC);
      } else { 
         /* we sample all backdoor PC to the same address.  */
         CYCLE_SAMPLE_EVENT(MipsyReadTime(cpu),cpu,K1BASE);
      }
#else
      CYCLE_SAMPLE_EVENT(MipsyReadTime(cpu), cpu, P->PC);
#endif

#ifdef MIPSY_MXS
      if (PE[cpu].inMXS)
         MxsSampleStats(PE[cpu].st, CURRENT_MODE(&PE[cpu]));
#endif
   }
   
   --statsEvent[cpuNum].intervalsLeft;
   if (statsEvent[cpuNum].intervalsLeft <= 0) { 
      statsEvent[cpuNum].intervalsLeft = statsEvent[cpuNum].periodicStats;
      PERIODIC_EVENT;
      PeriodicStats((SimTime)MipsyReadTime(cpuNum));
   } /* Things to do each STAT_INTERVAL */
   
   EventDoCallback(cpuNum, MipsyStatsCallback,
                   (EventCallbackHdr *)&statsEvent[cpuNum],
                   0, statsEvent[cpuNum].statsInterval);
}

/*****************************************************************
 * EnablePeriodicStats
 * 
 * Installs the first statistics callback. When the callback runs, it
 * will reinsert itself into the callback queue.
 *****************************************************************/
static void 
EnablePeriodicStats(int cpuNum, SimTime statInterval)
{
   statsEvent[cpuNum].statsInterval = statInterval;
   statsEvent[cpuNum].periodicStats = STAT_INTERVAL / statInterval;
   statsEvent[cpuNum].intervalsLeft = statsEvent[cpuNum].periodicStats;

   EventDoCallback(0, MipsyStatsCallback, 
                   (EventCallbackHdr *)&statsEvent[cpuNum],
                   0, statInterval);
}

/*****************************************************************
 * PeriodicStats
 * 
 * Current means of printing out periodic statistics. This is
 * called at the end of each "STAT_INTERVAL" and when it is time to
 * exit mipsy. Every "DETAILED_STAT_INTERVAL" some extra stuff will be
 * printed out.
 *****************************************************************/
static SimCounter oldTotalIcnt;
static SimTime oldCycleCnt;
static long secondsRun;

static void
PeriodicStats(SimTime cycleCount)
{
   int cpuNum;

   PrintPeriodicCPUStats();
   for (cpuNum = 0; cpuNum < TOTAL_CPUS; cpuNum++) {
      /* The memory subsystem should do any sampling and printing in
         this procedure. */
      if (!SKIP_CACHES(P)) {
         MemRefPeriodicStats(cpuNum);
      }
#ifdef MIPSY_MXS
      {
         CPUState *P = &(PE[cpuNum]);
         MxsPrintStatus(P->st);
         dump_stats(P->st);
      }
#endif
   }
}


/*****************************************************************
 * MipsyDumpAllStats
 * 
 * Complete dumping of stats. 
 * This is typically called at the end of execution.
 *****************************************************************/
void
MipsyDumpAllStats(void) 
{
   int cpuNum;

   CPUPrint("*******************DUMPING MIPSY STATS******************* \n");
   PrintAllCPUStats();
   CPUPrint("************\n");
   CPUPrint("Cache Stats\n");
   CPUPrint("************\n");
   for (cpuNum=0; cpuNum<TOTAL_CPUS; cpuNum++) {
      CPUPrint("C%d Stall Time: %d\n", cpuNum, STATS_VALUE(cpuNum, stallTime));
#ifdef MIPSY_MXS
      dump_stats(PE[cpuNum].st);
#endif
      if (!SKIP_CACHES(&PE[cpuNum])) {
         MemRefDumpStats(cpuNum);
      }
   }
}