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.mmtk.utility.statistics;
014
015import org.mmtk.utility.Log;
016import org.mmtk.vm.VM;
017import org.vmmagic.pragma.Uninterruptible;
018
019/**
020 * This class represents a perf event, such as cache misses, etc.
021 */
022@Uninterruptible
023public 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 final 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