code: /**
   * Copyright (c) 2004-2005, Regents of the University of California
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * Redistributions of source code must retain the above copyright notice,
   * this list of conditions and the following disclaimer.
   *
   * Redistributions in binary form must reproduce the above copyright
   * notice, this list of conditions and the following disclaimer in the
   * documentation and/or other materials provided with the distribution.
   *
   * Neither the name of the University of California, Los Angeles nor the
   * names of its contributors may be used to endorse or promote products
   * derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  package avrora.monitors;
  
  import avrora.sim.*;
  import avrora.sim.output.SimPrinter;
  import avrora.sim.mcu.MCUProperties;
  import cck.stat.MinMaxMean;
  import cck.text.*;
  import cck.util.Option;
  import java.util.Arrays;
  
  /**
   * The <code>InterruptMonitor</code> class implements a monitor that tracks the interrupts
   * that happen during a simulation. It collects statistics about which interrupts occur, how often,
   * and the latency between the interrupt being posted and being handled. After execution terminates,
   * it generates a textual report to the user.
   *
   * @author Ben L. Titzer
   */
  public class InterruptMonitor extends MonitorFactory {
  
      protected final Option.Bool SHOW = newOption("show-interrupts", false,
              "This option, when specified, will cause the interrupt monitor to bring out changes " +
              "to the state of each interrupt.");
      protected final Option.Bool INV_ONLY = newOption("invocations-only", true,
              "This option, when specified, will cause the interrupt to print only invocations " +
              "of the specified interrupts, and not enablings, disablings, postings, and unpostings.");
  
      class Mon implements Monitor, Simulator.InterruptProbe {
  
          final MCUProperties props;
          final Simulator simulator;
          final InterruptTable interrupts;
          final SimPrinter printer;
          final long[] invocations;
          final long[] lastInvoke;
          final long[] lastPost;
          final MinMaxMean[] meanInvoke;
          final MinMaxMean[] meanLatency;
          final MinMaxMean[] meanWake;
          boolean show;
          boolean invokeOnly;
  
          Mon(Simulator s) {
              simulator = s;
              printer = s.getPrinter();
              props = simulator.getMicrocontroller().getProperties();
              InterruptTable interruptTable = simulator.getInterpreter().getInterruptTable();
              interruptTable.insertProbe(this);
              interrupts = interruptTable;
              int numInts = interrupts.getNumberOfInterrupts();
              invocations = new long[numInts];
              lastInvoke = new long[numInts];
              lastPost = new long[numInts];
              meanInvoke = new MinMaxMean[numInts];
              meanLatency = new MinMaxMean[numInts];
              meanWake = new MinMaxMean[numInts];
              Arrays.fill(lastInvoke, -1);
              Arrays.fill(lastPost, -1);
              for ( int cntr = 0; cntr < numInts; cntr++ ) {
                  meanInvoke[cntr] = new MinMaxMean("Inter-arrival time");
                  meanLatency[cntr] = new MinMaxMean("Latency");
                  meanWake[cntr] = new MinMaxMean("Wakeup time");
              }
              show = SHOW.get();
              invokeOnly = INV_ONLY.get();
          }
  
          private void print(String s, int inum) {
  
              StringBuffer buf = printer.getBuffer();
              Terminal.append(Terminal.COLOR_GREEN, buf, s);
              if ( inum > 0) {
                  buf.append(": ");
                  Terminal.append(Terminal.COLOR_BRIGHT_CYAN, buf, "#"+inum+" ("+props.getInterruptName(inum)+")");
              }
              printer.printBuffer(buf);
          }
  
          /**
           * The <code>fireBeforeInvoke()</code> method of an interrupt probe will be called by the
           * simulator before control is transferred to this interrupt, before the microcontroller
           * has been woken from its current sleep mode.
           * @param s the state of the simulator
           * @param inum the number of the interrupt being entered
           */
          public void fireBeforeInvoke(State s, int inum) {
              if ( show ) {
                  print("invoke interrupt", inum);
              }
              invocations[inum]++;
              long time = s.getCycles();
              if ( lastInvoke[inum] > 0 ) {
                  meanInvoke[inum].record((int)(time - lastInvoke[inum]));
              }
              if ( lastPost[inum] > 0 && lastPost[inum] > lastInvoke[inum]) {
                  meanLatency[inum].record((int)(time - lastPost[inum]));
              }
              lastInvoke[inum] = time;
          }
  
          /**
           * The <code>fireAfterInvoke()</code> method of an interrupt probe will be called by the
           * simulator after control is transferred to this interrupt handler, i.e. after the current
           * PC is pushed onto the stack, interrupts are disabled, and the current PC is set to
           * the start of the interrupt handler.
           * @param s the state of the simulator
           * @param inum the number of the interrupt being entered
           */
          public void fireAfterInvoke(State s, int inum) {
              long time = s.getCycles();
              if ( lastInvoke[inum] > 0 ) {
                  meanWake[inum].record((int)(time - lastInvoke[inum]));
              }
          }
  
          /**
           * The <code>fireWhenDisabled()</code> method of an interrupt probe will be called by the
           * simulator when the interrupt is masked out (disabled) by the program.
           * @param s the state of the simulator
           * @param inum the number of the interrupt being masked out
           */
          public void fireWhenDisabled(State s, int inum) {
              if (show && !invokeOnly) {
                  if ( inum != 0 )
                      print("disable interrupt", inum);
                  else
                      print("disable interrupts", inum);
              }
          }
  
          /**
           * The <code>fireWhenEnabled()</code> method of an interrupt probe will be called by the
           * simulator when the interrupt is unmasked (enabled) by the program.
           * @param s the state of the simulator
           * @param inum the number of the interrupt being unmasked
           */
          public void fireWhenEnabled(State s, int inum) {
              if (show && !invokeOnly) {
                  if ( inum != 0 )
                      print("enable interrupt", inum);
                  else
                      print("enable interrupts", inum);
              }
          }
  
          /**
           * The <code>fireWhenPosted()</code> method of an interrupt probe will be called by the
           * simulator when the interrupt is posted. When an interrupt is posted to the simulator,
           * it will be coming pending if it is enabled (unmasked) and eventually be handled.
           * @param s the state of the simulator
           * @param inum the number of the interrupt being posted
           */
          public void fireWhenPosted(State s, int inum) {
              if (show && !invokeOnly) {
                  print("post interrupt", inum);
              }
              lastPost[inum] = s.getCycles();
          }
  
          /**
           * The <code>fireWhenUnposted()</code> method of an interrupt probe will be called by the
           * simulator when the interrupt is unposted. This can happen if the software resets the
           * flag bit of the corresponding IO register or, for most interrupts, when the pending
           * interrupt is handled.
           * @param s the state of the simulator
           * @param inum the number of the interrupt being unposted
           */
          public void fireWhenUnposted(State s, int inum) {
              if (show && !invokeOnly) {
                  print("unpost interrupt", inum);
              }
          }
  
          public void report() {
              TermUtil.printSeparator("Interrupt monitor results for node "+simulator.getID());
              Terminal.printGreen("Num  Name        Invocations  Separation  Latency     Wakeup");
              Terminal.nextln();
              TermUtil.printThinSeparator(Terminal.MAXLINE);
              for ( int cntr = 1; cntr < invocations.length; cntr++ ) {
                  meanInvoke[cntr].process();
                  meanLatency[cntr].process();
                  Terminal.printBrightCyan(StringUtil.rightJustify(cntr, 3));
                  Terminal.print("  ");
                  Terminal.printGreen(StringUtil.leftJustify(props.getInterruptName(cntr), 15));
                  Terminal.printBrightCyan(StringUtil.rightJustify(invocations[cntr], 8));
                  Terminal.print("  ");
                  Terminal.print(invocationSeparation(cntr));
                  Terminal.print("  ");
                  Terminal.print(invocationLatency(cntr));
                  Terminal.print("  ");
                  Terminal.print(invocationWakeup(cntr));
                  Terminal.nextln();
              }
              Terminal.nextln();
          }
  
          private String invocationLatency(int cntr) {
              if ( invocations[cntr] > 0 )
                  return StringUtil.leftJustify(meanLatency[cntr].mean, 10);
              else
                  return StringUtil.space(10);
          }
  
          private String invocationSeparation(int cntr) {
              if ( invocations[cntr] > 0 )
                  return StringUtil.leftJustify(meanInvoke[cntr].mean, 10);
              else
                  return StringUtil.space(10);
          }
  
          private String invocationWakeup(int cntr) {
              if ( invocations[cntr] > 0 )
                  return StringUtil.leftJustify(meanWake[cntr].mean, 10);
              else
                  return StringUtil.space(10);
          }
      }
  
      public InterruptMonitor() {
          super("The interrupt monitor tracks changes to the state of interrupts, including " +
                  "posting, enabling, and invoking of interrupts.");
      }
  
      /**
       * The <code>newMonitor()</code> method creates a new interrupt monitor for the specified simulator that
       * will collect statistics about the interrupts executed during simulation.
       * @param s the simulator to create the monitor for
       * @return a new monitor instance for the specified simulator
       */
      public Monitor newMonitor(Simulator s) {
          return new Mon(s);
      }
  }
  