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}