content: /*
   * Copyright (c) 2009, Ingo Kofler, ITEC, Klagenfurt University, Austria
   *
   * Developed by Ingo Kofler (ingo.kofler@itec.uni-klu.ac.at) Based on the
   * Instruction Counter tool by Felipe Lessa (felipe.lessa@gmail.com)
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   *
   * (MIT license, http://www.opensource.org/licenses/mit-license.html)
   */
  package mars.tools;
  
  import java.awt.GridBagConstraints;
  import java.awt.GridBagLayout;
  import java.awt.Insets;
  import java.util.Observable;
  
  import javax.swing.JComponent;
  import javax.swing.JLabel;
  import javax.swing.JPanel;
  import javax.swing.JProgressBar;
  import javax.swing.JTextField;
  import javax.swing.SwingConstants;
  
  import mars.ProgramStatement;
  import mars.mips.hardware.AccessNotice;
  import mars.mips.hardware.AddressErrorException;
  import mars.mips.hardware.Memory;
  import mars.mips.hardware.MemoryAccessNotice;
  
  /**
   * A MARS tool for obtaining instruction statistics by instruction category.
   * <p>
   * The code of this tools is initially based on the Instruction counter tool by
   * Felipe Lassa.
   *
   * @author Ingo Kofler <ingo.kofler@itec.uni-klu.ac.at>
   */
  // @SuppressWarnings("serial")
  public class InstructionStatistics extends AbstractMarsToolAndApplication {
  
  	/**
  	 *
  	 */
  	private static final long serialVersionUID = -3559205813588760770L;
  
  	/** name of the tool */
  	private static String NAME = "Instruction Statistics";
  
  	/** version and author information of the tool */
  	private static String VERSION = "Version 1.0 (Ingo Kofler)";
  
  	/** heading of the tool */
  	private static String HEADING = "";
  
  	/** number of instruction categories used by this tool */
  	private static final int MAX_CATEGORY = 5;
  
  	/** constant for ALU instructions category */
  	private static final int CATEGORY_ALU = 0;
  
  	/** constant for jump instructions category */
  	private static final int CATEGORY_JUMP = 1;
  
  	/** constant for branch instructions category */
  	private static final int CATEGORY_BRANCH = 2;
  
  	/** constant for memory instructions category */
  	private static final int CATEGORY_MEM = 3;
  
  	/** constant for any other instruction category */
  	private static final int CATEGORY_OTHER = 4;
  
  	/** text field for visualizing the total number of instructions processed */
  	private JTextField m_tfTotalCounter;
  
  	/** array of text field - one for each instruction category */
  	private JTextField m_tfCounters[];
  
  	/** array of progress pars - one for each instruction category */
  	private JProgressBar m_pbCounters[];
  
  	/** counter for the total number of instructions processed */
  	private int m_totalCounter = 0;
  
  	/** array of counter variables - one for each instruction category */
  	private final int m_counters[] = new int[MAX_CATEGORY];
  
  	/** names of the instruction categories as array */
  	private final String m_categoryLabels[] = { "ALU", "Jump", "Branch", "Memory", "Other" };
  
  	// From Felipe Lessa's instruction counter.  Prevent double-counting of instructions
  	// which happens because 2 read events are generated.
  	/**
  	 * The last address we saw. We ignore it because the only way for a program to
  	 * execute twice the same instruction is to enter an infinite loop, which is not
  	 * insteresting in the POV of counting instructions.
  	 */
  	protected int lastAddress = -1;
  
  	/**
  	 * Simple constructor, likely used to run a stand-alone enhanced instruction
  	 * counter.
  	 *
  	 * @param title   String containing title for title bar
  	 * @param heading String containing text for heading shown in upper part of
  	 *                window.
  	 */
  	public InstructionStatistics(final String title, final String heading) {
  		super(title, heading);
  	}
  
  	/**
  	 * Simple construction, likely used by the MARS Tools menu mechanism.
  	 */
  	public InstructionStatistics() {
  		super(InstructionStatistics.NAME + ", " + InstructionStatistics.VERSION, InstructionStatistics.HEADING);
  	}
  
  	/**
  	 * returns the name of the tool
  	 *
  	 * @return the tools's name
  	 */
  	@Override
  	public String getName() { return NAME; }
  
  	/**
  	 * creates the display area for the tool as required by the API
  	 *
  	 * @return a panel that holds the GUI of the tool
  	 */
  	@Override
  	protected JComponent buildMainDisplayArea() {
  
  		// Create GUI elements for the tool
  		final JPanel panel = new JPanel(new GridBagLayout());
  
  		m_tfTotalCounter = new JTextField("0", 10);
  		m_tfTotalCounter.setEditable(false);
  
  		m_tfCounters = new JTextField[MAX_CATEGORY];
  		m_pbCounters = new JProgressBar[MAX_CATEGORY];
  
  		// for each category a text field and a progress bar is created
  		for (int i = 0; i < InstructionStatistics.MAX_CATEGORY; i++) {
  			m_tfCounters[i] = new JTextField("0", 10);
  			m_tfCounters[i].setEditable(false);
  			m_pbCounters[i] = new JProgressBar(SwingConstants.HORIZONTAL);
  			m_pbCounters[i].setStringPainted(true);
  		}
  
  		final GridBagConstraints c = new GridBagConstraints();
  		c.anchor = GridBagConstraints.LINE_START;
  		c.gridheight = c.gridwidth = 1;
  
  		// create the label and text field for the total instruction counter
  		c.gridx = 2;
  		c.gridy = 1;
  		c.insets = new Insets(0, 0, 17, 0);
  		panel.add(new JLabel("Total: "), c);
  		c.gridx = 3;
  		panel.add(m_tfTotalCounter, c);
  
  		c.insets = new Insets(3, 3, 3, 3);
  
  		// create label, text field and progress bar for each category
  		for (int i = 0; i < InstructionStatistics.MAX_CATEGORY; i++) {
  			c.gridy++;
  			c.gridx = 2;
  			panel.add(new JLabel(m_categoryLabels[i] + ":   "), c);
  			c.gridx = 3;
  			panel.add(m_tfCounters[i], c);
  			c.gridx = 4;
  			panel.add(m_pbCounters[i], c);
  		}
  
  		return panel;
  	}
  
  	/**
  	 * registers the tool as observer for the text segment of the MIPS program
  	 */
  	@Override
  	protected void addAsObserver() {
  		addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress);
  	}
  
  	/**
  	 * decodes the instruction and determines the category of the instruction. The
  	 * instruction is decoded by extracting the operation and function code of the
  	 * 32-bit instruction. Only the most relevant instructions are decoded and
  	 * categorized.
  	 *
  	 * @param stmt the instruction to decode
  	 * @return the category of the instruction
  	 * @see InstructionStatistics#CATEGORY_ALU
  	 * @see InstructionStatistics#CATEGORY_JUMP
  	 * @see InstructionStatistics#CATEGORY_BRANCH
  	 * @see InstructionStatistics#CATEGORY_MEM
  	 * @see InstructionStatistics#CATEGORY_OTHER
  	 */
  	protected int getInstructionCategory(final ProgramStatement stmt) {
  
  		final int opCode = stmt.getBinaryStatement() >>> 32 - 6;
  		final int funct = stmt.getBinaryStatement() & 0x1F;
  
  		if (opCode == 0x00) {
  			if (funct == 0x00) {
  				return InstructionStatistics.CATEGORY_ALU; // sll
  			}
  			if (0x02 <= funct && funct <= 0x07) {
  				return InstructionStatistics.CATEGORY_ALU; // srl, sra, sllv, srlv, srav
  			}
  			if (funct == 0x08 || funct == 0x09) {
  				return InstructionStatistics.CATEGORY_JUMP; // jr, jalr
  			}
  			if (0x10 <= funct && funct <= 0x2F) {
  				return InstructionStatistics.CATEGORY_ALU; // mfhi, mthi, mflo, mtlo, mult, multu, div, divu, add, addu, sub, subu, and, or, xor, nor, slt, sltu
  			}
  			return InstructionStatistics.CATEGORY_OTHER;
  		}
  		if (opCode == 0x01) {
  			if (0x00 <= funct && funct <= 0x07) {
  				return InstructionStatistics.CATEGORY_BRANCH; // bltz, bgez, bltzl, bgezl
  			}
  			if (0x10 <= funct && funct <= 0x13) {
  				return InstructionStatistics.CATEGORY_BRANCH; // bltzal, bgezal, bltzall, bgczall
  			}
  			return InstructionStatistics.CATEGORY_OTHER;
  		}
  		if (opCode == 0x02 || opCode == 0x03) {
  			return InstructionStatistics.CATEGORY_JUMP; // j, jal
  		}
  		if (0x04 <= opCode && opCode <= 0x07) {
  			return InstructionStatistics.CATEGORY_BRANCH; // beq, bne, blez, bgtz
  		}
  		if (0x08 <= opCode && opCode <= 0x0F) {
  			return InstructionStatistics.CATEGORY_ALU; // addi, addiu, slti, sltiu, andi, ori, xori, lui
  		}
  		if (0x14 <= opCode && opCode <= 0x17) {
  			return InstructionStatistics.CATEGORY_BRANCH; // beql, bnel, blezl, bgtzl
  		}
  		if (0x20 <= opCode && opCode <= 0x26) {
  			return InstructionStatistics.CATEGORY_MEM; // lb, lh, lwl, lw, lbu, lhu, lwr
  		}
  		if (0x28 <= opCode && opCode <= 0x2E) {
  			return InstructionStatistics.CATEGORY_MEM; // sb, sh, swl, sw, swr
  		}
  
  		return InstructionStatistics.CATEGORY_OTHER;
  	}
  
  	/**
  	 * method that is called each time the MIPS simulator accesses the text segment.
  	 * Before an instruction is executed by the simulator, the instruction is
  	 * fetched from the program memory. This memory access is observed and the
  	 * corresponding instruction is decoded and categorized by the tool. According
  	 * to the category the counter values are increased and the display gets
  	 * updated.
  	 *
  	 * @param resource the observed resource
  	 * @param notice   signals the type of access (memory, register etc.)
  	 */
  	@Override
  	protected void processMIPSUpdate(final Observable resource, final AccessNotice notice) {
  
  		if (!notice.accessIsFromMIPS()) { return; }
  
  		// check for a read access in the text segment
  		if (notice.getAccessType() == AccessNotice.READ && notice instanceof MemoryAccessNotice) {
  
  			// now it is safe to make a cast of the notice
  			final MemoryAccessNotice memAccNotice = (MemoryAccessNotice) notice;
  
  			// The next three statments are from Felipe Lessa's instruction counter.  Prevents double-counting.
  			final int a = memAccNotice.getAddress();
  			if (a == lastAddress) { return; }
  			lastAddress = a;
  
  			try {
  
  				// access the statement in the text segment without notifying other tools etc.
  				final ProgramStatement stmt = Memory.getInstance().getStatementNoNotify(memAccNotice.getAddress());
  
  				// necessary to handle possible null pointers at the end of the program
  				// (e.g., if the simulator tries to execute the next instruction after the last instruction in the text segment)
  				if (stmt != null) {
  					final int category = getInstructionCategory(stmt);
  
  					m_totalCounter++;
  					m_counters[category]++;
  					updateDisplay();
  				}
  			} catch (final AddressErrorException e) {
  				// silently ignore these exceptions
  			}
  		}
  	}
  
  	/**
  	 * performs initialization tasks of the counters before the GUI is created.
  	 */
  	@Override
  	protected void initializePreGUI() {
  		m_totalCounter = 0;
  		lastAddress = -1; // from Felipe Lessa's instruction counter tool
  		for (int i = 0; i < InstructionStatistics.MAX_CATEGORY; i++) {
  			m_counters[i] = 0;
  		}
  	}
  
  	/**
  	 * resets the counter values of the tool and updates the display.
  	 */
  	@Override
  	protected void reset() {
  		m_totalCounter = 0;
  		lastAddress = -1; // from Felipe Lessa's instruction counter tool
  		for (int i = 0; i < InstructionStatistics.MAX_CATEGORY; i++) {
  			m_counters[i] = 0;
  		}
  		updateDisplay();
  	}
  
  	/**
  	 * updates the text fields and progress bars according to the current counter
  	 * values.
  	 */
  	@Override
  	protected void updateDisplay() {
  		m_tfTotalCounter.setText(String.valueOf(m_totalCounter));
  
  		for (int i = 0; i < InstructionStatistics.MAX_CATEGORY; i++) {
  			m_tfCounters[i].setText(String.valueOf(m_counters[i]));
  			m_pbCounters[i].setMaximum(m_totalCounter);
  			m_pbCounters[i].setValue(m_counters[i]);
  		}
  	}
  }
  