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     */
013    package org.mmtk.utility.statistics;
014    
015    import org.mmtk.utility.Log;
016    import org.mmtk.vm.VM;
017    import org.vmmagic.pragma.Uninterruptible;
018    
019    /**
020     * This class represents a perf event, such as cache misses, etc.
021     */
022    @Uninterruptible
023    public final class PerfEvent extends LongCounter {
024      /** {@code true} if the counter did not run due to contention for a physical counter */
025      private boolean contended;
026    
027      /** {@code true} if the counter did not run all of the time and has been scaled appropriately */
028      private boolean scaled;
029    
030      /** {@code true} if the counter overflowed */
031      private boolean overflowed;
032    
033      /** The index of the counter in the native array */
034      private int index;
035    
036      /** The previously read value of the counter (used to detect overflow) */
037      private long previousValue;
038    
039      /** A buffer passed to the native code when reading values, returns the tuple RAW_COUNT, TIME_ENABLED, TIME_RUNNING */
040      private final long[] readBuffer = new long[3];
041      private static final int RAW_COUNT = 0;
042      private static final int TIME_ENABLED = 1;
043      private static final int TIME_RUNNING = 2;
044    
045      /** {@code true} if any data was scaled */
046      public static boolean dataWasScaled = false;
047    
048      public PerfEvent(int index, String name) {
049        super(name, true, false);
050        this.index = index;
051      }
052    
053      /**
054       * Counters are 64 bit unsigned in the kernel but only 63 bits are available in Java
055       */
056      @Override
057      protected long getCurrentValue() {
058        VM.statistics.perfEventRead(index, readBuffer);
059        if (readBuffer[RAW_COUNT] < 0 || readBuffer[TIME_ENABLED] < 0 || readBuffer[TIME_RUNNING] < 0) {
060          // Negative implies they have exceeded 63 bits.
061          overflowed = true;
062        }
063        if (readBuffer[TIME_ENABLED] == 0) {
064          // Counter never run (assume contention)
065          contended = true;
066        }
067        // Was the counter scaled?
068        if (readBuffer[TIME_ENABLED] != readBuffer[TIME_RUNNING]) {
069          scaled = true;
070          dataWasScaled = true;
071          double scaleFactor;
072          if (readBuffer[TIME_RUNNING] == 0) {
073            scaleFactor = 0;
074          } else {
075            scaleFactor = readBuffer[TIME_ENABLED] / readBuffer[TIME_RUNNING];
076          }
077          readBuffer[RAW_COUNT] = (long) (readBuffer[RAW_COUNT] * scaleFactor);
078        }
079        if (readBuffer[RAW_COUNT] < previousValue) {
080          // value should monotonically increase
081          overflowed = true;
082        }
083        previousValue = readBuffer[RAW_COUNT];
084        return readBuffer[RAW_COUNT];
085      }
086    
087      @Override
088      void printValue(long value) {
089        if (overflowed) {
090          Log.write("OVERFLOWED");
091        } else if (contended) {
092          Log.write("CONTENDED");
093        } else {
094          Log.write(value);
095          if (scaled) {
096            Log.write(" (SCALED)");
097          }
098        }
099      }
100    
101      @Override
102      public String getColumnSuffix() {
103        return
104          overflowed ? "overflowed" :
105          contended ? "contended" :
106          scaled ? ".scaled" :
107          "";
108      }
109    }
110