/* * 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.jikesrvm.adaptive.measurements.listeners; import org.jikesrvm.ArchitectureSpecific.StackframeLayoutConstants; import org.jikesrvm.VM; import org.jikesrvm.adaptive.AosEntrypoints; import org.jikesrvm.compilers.common.CompiledMethod; import org.jikesrvm.compilers.common.CompiledMethods; import org.jikesrvm.runtime.Magic; import org.jikesrvm.scheduler.Synchronization; import org.jikesrvm.scheduler.RVMThread; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.Offset; /** * A EdgeListener defines a listener * that computes a call graph edge from the call stack. * After a parameterized number of edges are collected, * it notifies its organizer that the threshold is reached. * * Defines update's interface. * * EdgeListener communicates with an organizer through a * integer array, buffer. Each time this listener is called, * it places a triple of integers in buffer that correspond to * the callee, caller, and machine code offset of the call site */ @Uninterruptible public class EdgeListener extends ContextListener implements StackframeLayoutConstants { protected static final boolean DEBUG = false; /** * buffer provides the communication channel between the listener and the * organizer. * The buffer contains an array of triples <callee, caller, address> where * the caller and callee are CompiledMethodID's. * Initially, buffer contains zeros. The listener adds triples. * When the listener hits the end of the buffer, notify the organizer. */ private int[] buffer; /** * Number of samples to be taken before issuing callback to controller */ private int desiredSamples; /** * Number of samples taken so far */ protected int samplesTaken; /** * Number of times update is called */ protected int updateCalled; /** * Constructor */ public EdgeListener() { buffer = null; desiredSamples = 0; } /** * @return the number of times that update has been called */ int getTimesUpdateCalled() { return updateCalled; } /** * Setup buffer and buffer size. * This method must be called before any data can be written to * the buffer. * * @param buffer the allocated buffer to contain the samples, size should * be a muliple of 3 */ public void setBuffer(int[] buffer) { // ensure buffer is proper length if (VM.VerifyAssertions) { VM._assert(buffer.length % 3 == 0); } if (DEBUG) { VM.sysWrite("EdgeListener.setBuffer(", buffer.length, "): enter\n"); } this.buffer = buffer; desiredSamples = buffer.length / 3; resetBuffer(); } /** * This method is called when a call stack edge needs to be * sampled. Expect the sfp argument to point to the stack frame that * contains the target of the edge to be sampled. * NOTE: This method is uninterruptible, therefore we don't need to disable * thread switching during stackframe inspection. * * @param sfp a pointer to the stack frame that corresponds to the callee of * the call graph edge that is to be sampled. * @param whereFrom Was this a yieldpoint in a PROLOGUE, BACKEDGE, or * EPILOGUE? */ public final void update(Address sfp, int whereFrom) { if (DEBUG) { VM.sysWrite("EdgeListener.update(", sfp, ",", whereFrom); VM.sysWriteln("): enter ", samplesTaken); } Synchronization.fetchAndAdd(this, AosEntrypoints.edgeListenerUpdateCalledField.getOffset(), 1); // don't take a sample for back edge yield points if (whereFrom == RVMThread.BACKEDGE) return; int calleeCMID = 0; int callerCMID = 0; Address returnAddress = Address.zero(); if (sfp.loadAddress() == STACKFRAME_SENTINEL_FP) { if (DEBUG) VM.sysWrite(" Walking off end of stack!\n"); return; } calleeCMID = Magic.getCompiledMethodID(sfp); if (calleeCMID == INVISIBLE_METHOD_ID) { if (DEBUG) { VM.sysWrite(" INVISIBLE_METHOD_ID (assembler code) "); VM.sysWrite(calleeCMID); VM.sysWrite("\n"); } return; } returnAddress = Magic.getReturnAddress(sfp); // return address in caller sfp = Magic.getCallerFramePointer(sfp); // caller's frame pointer if (sfp.loadAddress() == STACKFRAME_SENTINEL_FP) { if (DEBUG) VM.sysWrite(" Walking off end of stack\n"); return; } callerCMID = Magic.getCompiledMethodID(sfp); if (callerCMID == INVISIBLE_METHOD_ID) { if (DEBUG) { VM.sysWrite(" INVISIBLE_METHOD_ID (assembler code) "); VM.sysWrite(callerCMID); VM.sysWrite("\n"); } return; } // store the offset of the return address from the beginning of the // instruction CompiledMethod callerCM = CompiledMethods.getCompiledMethod(callerCMID); if (callerCM.getCompilerType() == CompiledMethod.TRAP) { if (DEBUG) { VM.sysWriteln(" HARDWARE TRAP FRAME "); } return; } Offset callSite = callerCM.getInstructionOffset(returnAddress); if (DEBUG) { VM.sysWrite(" <"); VM.sysWrite(calleeCMID); VM.sysWrite(","); VM.sysWrite(callerCMID); VM.sysWrite(","); VM.sysWrite(returnAddress); VM.sysWrite(">\n"); } // Find out what sample we are. int sampleNumber = Synchronization.fetchAndAdd(this, AosEntrypoints.edgeListenerSamplesTakenField.getOffset(), 1); int idx = 3 * sampleNumber; // If we got buffer slots that are beyond the end of the buffer, that means // that we're actually not supposed to take the sample at all (the system // is in the process of activating our organizer and processing the buffer). if (idx < buffer.length) { buffer[idx + 1] = callerCMID; buffer[idx + 2] = callSite.toInt(); Magic.sync(); buffer[idx + 0] = calleeCMID; // If we are the last sample, we need to activate the organizer. if (sampleNumber + 1 == desiredSamples) { activateOrganizer(); } } } /** * report() noop */ public final void report() {} /** * Reset (in preparation of starting a new sampling window) */ public void reset() { if (DEBUG) VM.sysWrite("EdgeListener.reset(): enter\n"); samplesTaken = 0; updateCalled = 0; resetBuffer(); } /** * Reset the buffer */ private void resetBuffer() { for (int i = 0; i < buffer.length; i++) { buffer[i] = 0; } } }