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.architecture.StackFrameLayout; 018import org.jikesrvm.compilers.common.CompiledMethod; 019import org.jikesrvm.compilers.common.CompiledMethods; 020import org.jikesrvm.runtime.Magic; 021import org.jikesrvm.scheduler.Synchronization; 022import org.jikesrvm.scheduler.RVMThread; 023import org.vmmagic.pragma.Uninterruptible; 024import org.vmmagic.unboxed.Address; 025import org.vmmagic.unboxed.Offset; 026 027/** 028 * A EdgeListener defines a listener 029 * that computes a call graph edge from the call stack. 030 * After a parameterized number of edges are collected, 031 * it notifies its organizer that the threshold is reached. 032 * <p> 033 * Defines update's interface. 034 * <p> 035 * EdgeListener communicates with an organizer through a 036 * integer array, buffer. Each time this listener is called, 037 * it places a triple of integers in buffer that correspond to 038 * the callee, caller, and machine code offset of the call site 039 */ 040@Uninterruptible 041public class EdgeListener extends ContextListener { 042 043 protected static final boolean DEBUG = false; 044 045 /** 046 * buffer provides the communication channel between the listener and the 047 * organizer. 048 * The buffer contains an array of triples <callee, caller, address> where 049 * the caller and callee are CompiledMethodID's. 050 * Initially, buffer contains zeros. The listener adds triples. 051 * When the listener hits the end of the buffer, notify the organizer. 052 */ 053 private int[] buffer; 054 055 /** 056 * Number of samples to be taken before issuing callback to controller 057 */ 058 private int desiredSamples; 059 060 /** 061 * Number of samples taken so far 062 */ 063 protected int samplesTaken; 064 065 /** 066 * Number of times update is called 067 */ 068 protected int updateCalled; 069 070 /** 071 * Constructor 072 */ 073 public EdgeListener() { 074 buffer = null; 075 desiredSamples = 0; 076 } 077 078 /** 079 * @return the number of times that update has been called 080 */ 081 int getTimesUpdateCalled() { 082 return updateCalled; 083 } 084 085 /** 086 * Setup buffer and buffer size. 087 * This method must be called before any data can be written to 088 * the buffer. 089 * 090 * @param buffer the allocated buffer to contain the samples, size should 091 * be a muliple of 3 092 */ 093 public void setBuffer(int[] buffer) { 094 // ensure buffer is proper length 095 if (VM.VerifyAssertions) { 096 VM._assert(buffer.length % 3 == 0); 097 } 098 099 if (DEBUG) { 100 VM.sysWrite("EdgeListener.setBuffer(", buffer.length, "): enter\n"); 101 } 102 103 this.buffer = buffer; 104 desiredSamples = buffer.length / 3; 105 resetBuffer(); 106 } 107 108 /** 109 * This method is called when a call stack edge needs to be 110 * sampled. Expect the sfp argument to point to the stack frame that 111 * contains the target of the edge to be sampled. 112 * <p> 113 * NOTE: This method is uninterruptible, therefore we don't need to disable 114 * thread switching during stackframe inspection. 115 * 116 * @param sfp a pointer to the stack frame that corresponds to the callee of 117 * the call graph edge that is to be sampled. 118 * @param whereFrom Was this a yieldpoint in a PROLOGUE, BACKEDGE, or 119 * EPILOGUE? 120 */ 121 @Override 122 public final void update(Address sfp, int whereFrom) { 123 if (DEBUG) { 124 VM.sysWrite("EdgeListener.update(", sfp, ",", whereFrom); 125 VM.sysWriteln("): enter ", samplesTaken); 126 } 127 128 Synchronization.fetchAndAdd(this, AosEntrypoints.edgeListenerUpdateCalledField.getOffset(), 1); 129 130 // don't take a sample for back edge yield points 131 if (whereFrom == RVMThread.BACKEDGE) return; 132 133 int calleeCMID = 0; 134 int callerCMID = 0; 135 Address returnAddress = Address.zero(); 136 137 if (sfp.loadAddress().EQ(StackFrameLayout.getStackFrameSentinelFP())) { 138 if (DEBUG) VM.sysWrite(" Walking off end of stack!\n"); 139 return; 140 } 141 142 calleeCMID = Magic.getCompiledMethodID(sfp); 143 if (calleeCMID == StackFrameLayout.getInvisibleMethodID()) { 144 if (DEBUG) { 145 VM.sysWrite(" INVISIBLE_METHOD_ID (assembler code) "); 146 VM.sysWrite(calleeCMID); 147 VM.sysWrite("\n"); 148 } 149 return; 150 } 151 152 returnAddress = Magic.getReturnAddress(sfp); // return address in caller 153 sfp = Magic.getCallerFramePointer(sfp); // caller's frame pointer 154 if (sfp.loadAddress().EQ(StackFrameLayout.getStackFrameSentinelFP())) { 155 if (DEBUG) VM.sysWrite(" Walking off end of stack\n"); 156 return; 157 } 158 callerCMID = Magic.getCompiledMethodID(sfp); 159 if (callerCMID == StackFrameLayout.getInvisibleMethodID()) { 160 if (DEBUG) { 161 VM.sysWrite(" INVISIBLE_METHOD_ID (assembler code) "); 162 VM.sysWrite(callerCMID); 163 VM.sysWrite("\n"); 164 } 165 return; 166 } 167 168 // store the offset of the return address from the beginning of the 169 // instruction 170 CompiledMethod callerCM = CompiledMethods.getCompiledMethod(callerCMID); 171 if (callerCM.getCompilerType() == CompiledMethod.TRAP) { 172 if (DEBUG) { 173 VM.sysWriteln(" HARDWARE TRAP FRAME "); 174 } 175 return; 176 } 177 Offset callSite = callerCM.getInstructionOffset(returnAddress); 178 179 if (DEBUG) { 180 VM.sysWrite(" <"); 181 VM.sysWrite(calleeCMID); 182 VM.sysWrite(","); 183 VM.sysWrite(callerCMID); 184 VM.sysWrite(","); 185 VM.sysWrite(returnAddress); 186 VM.sysWrite(">\n"); 187 } 188 189 // Find out what sample we are. 190 int sampleNumber = 191 Synchronization.fetchAndAdd(this, AosEntrypoints.edgeListenerSamplesTakenField.getOffset(), 1); 192 int idx = 3 * sampleNumber; 193 194 // If we got buffer slots that are beyond the end of the buffer, that means 195 // that we're actually not supposed to take the sample at all (the system 196 // is in the process of activating our organizer and processing the buffer). 197 if (idx < buffer.length) { 198 buffer[idx + 1] = callerCMID; 199 buffer[idx + 2] = callSite.toInt(); 200 Magic.sync(); 201 buffer[idx + 0] = calleeCMID; 202 203 // If we are the last sample, we need to activate the organizer. 204 if (sampleNumber + 1 == desiredSamples) { 205 activateOrganizer(); 206 } 207 } 208 } 209 210 /** 211 * No-op. 212 */ 213 @Override 214 public final void report() {} 215 216 @Override 217 public void reset() { 218 if (DEBUG) VM.sysWrite("EdgeListener.reset(): enter\n"); 219 samplesTaken = 0; 220 updateCalled = 0; 221 resetBuffer(); 222 } 223 224 /** 225 * Reset the buffer 226 */ 227 private void resetBuffer() { 228 for (int i = 0; i < buffer.length; i++) { 229 buffer[i] = 0; 230 } 231 } 232}