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.listeners;
014
015import org.jikesrvm.VM;
016import org.jikesrvm.adaptive.AosEntrypoints;
017import org.jikesrvm.scheduler.Synchronization;
018import org.jikesrvm.scheduler.RVMThread;
019import org.vmmagic.pragma.Uninterruptible;
020
021/**
022 * A MethodListener defines a listener to collect method invocation samples.
023 * <p>
024 * Samples are collected in a buffer.
025 * When sampleSize samples have been collected
026 * the listener's organizer is activated to process them.
027 * <p>
028 * Defines update's interface to be a compiled method identifier, CMID.
029 */
030@Uninterruptible
031public final class MethodListener extends Listener {
032
033  /**
034   * Number of samples to be gathered before they are processed
035   */
036  int sampleSize;
037
038  /**
039   * Number of samples taken so far
040   */
041  int numSamples;
042
043  /**
044   * The sample buffer.
045   * Key Invariant: {@code samples.length >= sampleSize}
046   */
047  int[] samples;
048
049  /**
050   * @param sampleSize the initial sampleSize for the listener
051   */
052  public MethodListener(int sampleSize) {
053    this.sampleSize = sampleSize;
054    samples = new int[sampleSize];
055  }
056
057  /**
058   * This method is called when a sample is taken.
059   * It parameter {@code cmid} represents the compiled method ID of the method
060   * which was executing at the time of the sample.  This method
061   * bumps the counter and checks whether a threshold is reached.
062   * <p>
063   * NOTE: There can be multiple threads executing this method at the
064   *       same time. We attempt to ensure that the resulting race conditions
065   *       are safely handled, but make no guarentee that every sample is
066   *       actually recorded.
067   *
068   * @param cmid the compiled method ID to update
069   * @param callerCmid a compiled method id for the caller, -1 if none
070   * @param whereFrom Was this a yieldpoint in a PROLOGUE, BACKEDGE, or
071   *         EPILOGUE?
072   */
073  public void update(int cmid, int callerCmid, int whereFrom) {
074    if (VM.UseEpilogueYieldPoints) {
075      // Use epilogue yieldpoints.  We increment one sample
076      // for every yieldpoint.  On a prologue, we count the caller.
077      // On backedges and epilogues, we count the current method.
078      if (whereFrom == RVMThread.PROLOGUE) {
079        // Before getting a sample index, make sure we have something to insert
080        if (callerCmid != -1) {
081          recordSample(callerCmid);
082        } // nothing to insert
083      } else {
084        // loop backedge or epilogue.
085        recordSample(cmid);
086      }
087    } else {
088      // Original scheme: No epilogue yieldpoints.  We increment two samples
089      // for every yieldpoint.  On a prologue, we count both the caller
090      // and callee.  On backedges, we count the current method twice.
091      if (whereFrom == RVMThread.PROLOGUE) {
092        // Increment both for this method and the caller
093        recordSample(cmid);
094        if (callerCmid != -1) {
095          recordSample(callerCmid);
096        }
097      } else {
098        // loop backedge.  We're only called once, so need to take
099        // two samples to avoid penalizing methods with loops.
100        recordSample(cmid);
101        recordSample(cmid);
102      }
103    }
104  }
105
106  /**
107   * This method records a sample containing the {@code CMID} (compiled method ID)
108   * passed.  Since multiple threads may be taking samples concurrently,
109   * we use fetchAndAdd to distribute indices into the buffer AND to record
110   * when a sample is taken.  (Thread 1 may get an earlier index, but complete
111   * the insertion after Thread 2.)
112   *
113   * @param CMID compiled method ID to record
114   */
115  private void recordSample(int CMID) {
116    // reserve the next available slot
117    int idx = Synchronization.fetchAndAdd(this, AosEntrypoints.methodListenerNumSamplesField.getOffset(), 1);
118    // make sure it is valid
119    if (idx < sampleSize) {
120      samples[idx] = CMID;
121    }
122    if (idx + 1 == sampleSize) {
123      // The last sample.
124      activateOrganizer();
125    }
126  }
127
128  @Override
129  public void report() { }
130
131  @Override
132  public void reset() {
133    numSamples = 0;
134  }
135
136  /**
137   * @return the buffer of samples
138   */
139  public int[] getSamples() {
140    return samples;
141  }
142
143  /**
144   * @return how many samples in the array returned by getSamples are valid
145   */
146  public int getNumSamples() {
147    return (numSamples < sampleSize) ? numSamples : sampleSize;
148  }
149}