001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.adaptive.measurements.instrumentation;
014
015import java.util.Hashtable;
016import org.jikesrvm.VM;
017import org.jikesrvm.adaptive.measurements.Reportable;
018import org.jikesrvm.compilers.opt.InstrumentedEventCounterManager;
019import org.jikesrvm.compilers.opt.ir.Instruction;
020
021/**
022 * A generic data object that maps strings to counters.  The key
023 * method is "Instruction getCounterInstructionForEvent(String)"
024 * which, given a string, returns a counter instruction that
025 * increments the corresponding counter for that string.
026 */
027public class StringEventCounterData extends ManagedCounterData implements Reportable {
028
029  static final boolean DEBUG = false;
030
031  /**
032   * @param manager The manager that will provide the counter space
033   * @param name human-readable name for the data
034   **/
035  StringEventCounterData(InstrumentedEventCounterManager manager, String name) {
036    super(manager);
037    dataName = name;
038  }
039
040  /**
041   * Given a string, find or create the counter associated and return
042   * and instruction to increment that counter.
043   *
044   * @param event The name of the event
045   * @return An instruction to increment the count associated with the event.
046   */
047  public Instruction getCounterInstructionForEvent(String event) {
048    return getCounterInstructionForEvent(event, 1.0);
049  }
050
051  /**
052   * Given a string, find or create the counter associated and return
053   * and instruction to increment that counter.
054   *
055   * @param event The name of the event
056   * @param incrementValue The value to add to counter
057   * @return An instruction that will update the count associated with the event.
058   */
059  public Instruction getCounterInstructionForEvent(String event, double incrementValue) {
060
061    // Get (or create) the counter for this string and return it.
062    int counterIdx = getOrCreateCounterIndexForString(event);
063
064    return createEventCounterInstruction(counterIdx, incrementValue);
065  }
066
067  /**
068   * Converts a double to string with maximum precision.
069   * @param num double to convert
070   * @return the new string
071   */
072  protected static String doubleToString(double num) {
073    long whole = (long) num;
074    if (whole == Long.MAX_VALUE || whole == Long.MIN_VALUE) {
075      return Double.toString(whole);
076    }
077    double fract = Math.abs(num - whole);
078    String res = Long.toString(whole);
079    if (fract != 0.0) {
080      String f2s = Double.toString(fract + 1.0);
081      res += f2s.substring(1);
082    }
083    return res;
084  }
085
086  /**
087   * Print a report at the end of execution
088   */
089  @Override
090  public void report() {
091    // Turn off future instrumentation to avoid hanging during
092    // iteration
093    Instrumentation.disableInstrumentation();
094
095    VM.sysWrite("Printing " + dataName + ":\n");
096    VM.sysWrite("--------------------------------------------------\n");
097    double total = 0;
098    for (String stringName : stringToCounterMap.keySet()) {
099      int counterIdx = getCounterIndexForString(stringName);
100      double counterVal = getCounter(counterIdx);
101      VM.sysWrite(doubleToString(counterVal) + " " + stringName + "\n");
102      total += counterVal;
103    }
104    VM.sysWrite("Total: " + doubleToString(total) + "\n");
105  }
106
107  /**
108   * For a given string, return the number of the counter associated
109   * with this string.  If this string doesn't already have a counter,
110   * reserve one.
111   *
112   * @param str The string for which you want the counter number
113   * @return The counter number for this string
114
115   */
116  public int getOrCreateCounterIndexForString(String str) {
117
118    int counterIdx = getCounterIndexForString(str);
119    if (counterIdx == -1) {
120      // Use new counter
121      counterIdx = ++eventNumber;
122      // remember it, and return it
123      stringToCounterMap.put(str, eventNumber);
124    }
125
126    return counterIdx;
127  }
128
129  /**
130   * For a given string, return the number of the counter associated
131   * with this string.  Ideally this number would be completely hidden
132   * from the outside world, but for efficiency it is made public.
133   *
134   * @param str The string for which you want the counter number
135   * @return The counter number for this string, or -1 if the string has no
136  counter associated with it.
137   */
138  public int getCounterIndexForString(String str) {
139
140    int counter = -1;
141    Integer counterNum = stringToCounterMap.get(str);
142    if (counterNum != null) {
143      counter = counterNum;
144    }
145
146    return counter;
147  }
148
149  @Override
150  public void reset() {
151    for (String stringName : stringToCounterMap.keySet()) {
152      int counterIdx = getCounterIndexForString(stringName);
153      setCounter(counterIdx, 0.0);
154    }
155  }
156
157  /**
158   *  Map strings to a counter location
159   */
160  protected final Hashtable<String, Integer> stringToCounterMap = new Hashtable<String, Integer>();
161
162  /**
163   * A string description of this data;
164   */
165  final String dataName;
166
167  /**
168   * Used to keep track of how many counters have been used so far.
169   */
170  int eventNumber = -1;
171
172}
173
174