| /* |
| * This file is part of the Jikes RVM project (http://jikesrvm.org). |
| * |
| * This file is licensed to You under the Eclipse Public License (EPL); |
| * You may not use this file except in compliance with the License. You |
| * may obtain a copy of the License at |
| * |
| * http://www.opensource.org/licenses/eclipse-1.0.php |
| * |
| * See the COPYRIGHT.txt file distributed with this work for information |
| * regarding copyright ownership. |
| */ |
| package org.mmtk.utility.statistics; |
| |
| import org.mmtk.utility.Log; |
| import org.mmtk.vm.VM; |
| import org.vmmagic.pragma.Uninterruptible; |
| |
| /** |
| * This class represents a perf event, such as cache misses, etc. |
| */ |
| @Uninterruptible |
| public final class PerfEvent extends LongCounter { |
| /** True if the counter did not run due to contention for a physical counter */ |
| private boolean contended; |
| |
| /** True if the counter did not run all of the time and has been scaled appropriately */ |
| private boolean scaled; |
| |
| /** True if the counter overflowed */ |
| private boolean overflowed; |
| |
| /** The index of the counter in the native array */ |
| private int index; |
| |
| /** The previously read value of the counter (used to detect overflow) */ |
| private long previousValue; |
| |
| /** A buffer passed to the native code when reading values, returns the tuple RAW_COUNT, TIME_ENABLED, TIME_RUNNING */ |
| private final long[] readBuffer = new long[3]; |
| private static final int RAW_COUNT = 0; |
| private static final int TIME_ENABLED = 1; |
| private static final int TIME_RUNNING = 2; |
| |
| /** Three 64 bit values is 24 bytes */ |
| private static final int BYTES_TO_READ = 24; |
| |
| /** True if any data was scaled */ |
| public static boolean dataWasScaled = false; |
| |
| public PerfEvent(int index, String name) { |
| super(name, true, false); |
| this.index = index; |
| } |
| |
| /** |
| * Counters are 64 bit unsigned in the kernel but only 63 bits are available in Java |
| */ |
| @Override |
| protected long getCurrentValue() { |
| VM.statistics.perfEventRead(index, readBuffer); |
| if (readBuffer[RAW_COUNT] < 0 || readBuffer[TIME_ENABLED] < 0 || readBuffer[TIME_RUNNING] < 0) { |
| // Negative implies they have exceeded 63 bits. |
| overflowed = true; |
| } |
| if (readBuffer[TIME_ENABLED] == 0) { |
| // Counter never run (assume contention) |
| contended = true; |
| } |
| // Was the counter scaled? |
| if (readBuffer[TIME_ENABLED] != readBuffer[TIME_RUNNING]) { |
| scaled = true; |
| dataWasScaled = true; |
| double scaleFactor; |
| if (readBuffer[TIME_RUNNING] == 0) { |
| scaleFactor = 0; |
| } else { |
| scaleFactor = readBuffer[TIME_ENABLED] / readBuffer[TIME_RUNNING]; |
| } |
| readBuffer[RAW_COUNT] = (long) (readBuffer[RAW_COUNT] * scaleFactor); |
| } |
| if (readBuffer[RAW_COUNT] < previousValue) { |
| // value should monotonically increase |
| overflowed = true; |
| } |
| previousValue = readBuffer[RAW_COUNT]; |
| return readBuffer[RAW_COUNT]; |
| } |
| |
| /** |
| * Print the given value |
| * @param value The value to be printed |
| */ |
| @Override |
| void printValue(long value) { |
| if (overflowed) { |
| Log.write("OVERFLOWED"); |
| } else if (contended) { |
| Log.write("CONTENDED"); |
| } else { |
| Log.write(value); |
| if (scaled) { |
| Log.write(" (SCALED)"); |
| } |
| } |
| } |
| |
| public String getColumnSuffix() { |
| return |
| overflowed ? "overflowed" : |
| contended ? "contended" : |
| scaled ? ".scaled" : |
| ""; |
| } |
| } |
| |