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.database.callgraph; 014 015import java.io.BufferedWriter; 016import java.io.FileOutputStream; 017import java.io.IOException; 018import java.io.OutputStreamWriter; 019import java.util.Comparator; 020import java.util.HashMap; 021import java.util.TreeSet; 022import org.jikesrvm.VM; 023import org.jikesrvm.adaptive.controller.Controller; 024import org.jikesrvm.adaptive.measurements.Decayable; 025import org.jikesrvm.adaptive.measurements.Reportable; 026import org.jikesrvm.adaptive.util.UnResolvedCallSite; 027import org.jikesrvm.adaptive.util.UnResolvedWeightedCallTargets; 028import org.jikesrvm.classloader.RVMMethod; 029import org.jikesrvm.compilers.common.CodeArray; 030import org.jikesrvm.classloader.MethodReference; 031 032/** 033 * A partial call graph (PCG) is a partial mapping from callsites 034 * to weighted targets. 035 */ 036public final class PartialCallGraph implements Decayable, Reportable { 037 038 /** 039 * The dynamic call graph, which is a mapping from 040 * CallSites to WeightedCallTargets. 041 */ 042 private final HashMap<CallSite, WeightedCallTargets> callGraph = 043 new HashMap<CallSite, WeightedCallTargets>(); 044 private final HashMap<UnResolvedCallSite, UnResolvedWeightedCallTargets> unresolvedCallGraph = 045 new HashMap<UnResolvedCallSite, UnResolvedWeightedCallTargets>(); 046 047 /** 048 * sum of all edge weights in the call graph 049 */ 050 private double totalEdgeWeights; 051 052 /** 053 * Initial seed weight; saved for use in the reset method 054 */ 055 private final double seedWeight; 056 057 /** 058 * Create a partial call graph. 059 * @param initialWeight an initial value for totalEdgeWeights. 060 * Used by AOS to cause inlining based in the dynamic call graph 061 * to initially be conservative until sufficient samples have 062 * accumulated that there is more confidence in the accuracy 063 * of the call graph. 064 */ 065 public PartialCallGraph(double initialWeight) { 066 seedWeight = initialWeight; // save for rest function 067 totalEdgeWeights = initialWeight; 068 } 069 070 @Override 071 public synchronized void reset() { 072 callGraph.clear(); 073 totalEdgeWeights = seedWeight; 074 } 075 076 /** 077 * @return sum of all edge weights in the partial call graph 078 */ 079 public double getTotalEdgeWeights() { 080 return totalEdgeWeights; 081 } 082 083 /** 084 * Visit the WeightedCallTargets for every call site send them the 085 * decay message. 086 */ 087 @Override 088 public synchronized void decay() { 089 double rate = Controller.options.DCG_DECAY_RATE; 090 // if we are dumping dynamic call graph, don't decay the graph 091 if (Controller.options.DYNAMIC_CALL_FILE_OUTPUT != null) return; 092 093 for (WeightedCallTargets ct : callGraph.values()) { 094 ct.decay(rate); 095 } 096 totalEdgeWeights /= rate; 097 } 098 099 /** 100 * @param caller caller method 101 * @param bcIndex bytecode index in caller method 102 * @return the WeightedCallTargets currently associated with the 103 * given caller bytecodeIndex pair. 104 */ 105 public WeightedCallTargets getCallTargets(RVMMethod caller, int bcIndex) { 106 MethodReference callerRef = caller.getMemberRef().asMethodReference(); 107 UnResolvedWeightedCallTargets unresolvedTargets = 108 unresolvedCallGraph.get(new UnResolvedCallSite(callerRef, bcIndex)); 109 if (unresolvedTargets != null) { 110 final RVMMethod fCaller = caller; 111 final int fBcIndex = bcIndex; 112 final PartialCallGraph pg = this; 113 unresolvedTargets.visitTargets(new UnResolvedWeightedCallTargets.Visitor() { 114 @Override 115 public void visit(MethodReference calleeRef, double weight) { 116 RVMMethod callee = calleeRef.getResolvedMember(); 117 if (callee != null) { 118 pg.incrementEdge(fCaller, fBcIndex, callee, (float) weight); 119 } 120 } 121 }); 122 } 123 return getCallTargets(new CallSite(caller, bcIndex)); 124 } 125 126 /** 127 * @param callSite the callsite to look for 128 * @return the WeightedCallTargets currently associated with callSite. 129 */ 130 public synchronized WeightedCallTargets getCallTargets(CallSite callSite) { 131 return callGraph.get(callSite); 132 } 133 134 /** 135 * Increment the edge represented by the input parameters, 136 * creating it if it is not already in the call graph. 137 * 138 * @param caller method making the call 139 * @param bcIndex call site, if -1 then no call site is specified. 140 * @param callee method called 141 */ 142 public synchronized void incrementEdge(RVMMethod caller, int bcIndex, RVMMethod callee) { 143 augmentEdge(caller, bcIndex, callee, 1); 144 } 145 146 /** 147 * Increment the edge represented by the input parameters, 148 * creating it if it is not already in the call graph. 149 * 150 * @param caller method making the call 151 * @param bcIndex call site, if -1 then no call site is specified. 152 * @param callee method called 153 * @param weight the frequency of this calling edge 154 */ 155 public synchronized void incrementEdge(RVMMethod caller, int bcIndex, RVMMethod callee, float weight) { 156 augmentEdge(caller, bcIndex, callee, weight); 157 } 158 159 /** 160 * For the calling edge we read from a profile, we may not have 161 * the methods loaded in yet. Therefore, we will record the method 162 * reference information first, the next time we resolved the method, 163 * we will promote it into the regular call graph. 164 * Increment the edge represented by the input parameters, 165 * creating it if it is not already in the call graph. 166 * 167 * @param callerRef method making the call 168 * @param bcIndex call site, if -1 then no call site is specified. 169 * @param calleeRef method called 170 * @param weight the frequency of this calling edge 171 */ 172 public synchronized void incrementUnResolvedEdge(MethodReference callerRef, int bcIndex, 173 MethodReference calleeRef, float weight) { 174 UnResolvedCallSite callSite = new UnResolvedCallSite(callerRef, bcIndex); 175 UnResolvedWeightedCallTargets targets = unresolvedCallGraph.get(callSite); 176 if (targets == null) { 177 targets = UnResolvedWeightedCallTargets.create(calleeRef, weight); 178 unresolvedCallGraph.put(callSite, targets); 179 } else { 180 UnResolvedWeightedCallTargets orig = targets; 181 targets = targets.augmentCount(calleeRef, weight); 182 if (orig != targets) { 183 unresolvedCallGraph.put(callSite, targets); 184 } 185 } 186 } 187 188 /** 189 * Increment the edge represented by the input parameters, 190 * creating it if it is not already in the call graph. 191 * 192 * @param caller method making the call 193 * @param bcIndex call site, if -1 then no call site is specified. 194 * @param callee method called 195 * @param weight the frequency of this calling edge 196 */ 197 private void augmentEdge(RVMMethod caller, int bcIndex, RVMMethod callee, double weight) { 198 CallSite callSite = new CallSite(caller, bcIndex); 199 WeightedCallTargets targets = callGraph.get(callSite); 200 if (targets == null) { 201 targets = WeightedCallTargets.create(callee, weight); 202 callGraph.put(callSite, targets); 203 } else { 204 WeightedCallTargets orig = targets; 205 targets = targets.augmentCount(callee, weight); 206 if (orig != targets) { 207 callGraph.put(callSite, targets); 208 } 209 } 210 totalEdgeWeights += weight; 211 } 212 213 /** 214 * Dump out set of edges in sorted order. 215 */ 216 @Override 217 public synchronized void report() { 218 System.out.println("Partial Call Graph"); 219 System.out.println(" Number of callsites " + callGraph.size() + ", total weight: " + totalEdgeWeights); 220 System.out.println(); 221 222 TreeSet<CallSite> tmp = new TreeSet<CallSite>(new OrderByTotalWeight()); 223 tmp.addAll(callGraph.keySet()); 224 225 for (final CallSite cs : tmp) { 226 WeightedCallTargets ct = callGraph.get(cs); 227 ct.visitTargets(new WeightedCallTargets.Visitor() { 228 @Override 229 public void visit(RVMMethod callee, double weight) { 230 System.out.println(weight + " <" + cs.getMethod() + ", " + cs.getBytecodeIndex() + ", " + callee + ">"); 231 } 232 }); 233 System.out.println(); 234 } 235 } 236 237 /** 238 * Dump all profile data to the given file 239 */ 240 public synchronized void dumpGraph() { 241 dumpGraph(Controller.options.DYNAMIC_CALL_FILE_OUTPUT); 242 } 243 244 /** 245 * Dump all profile data to the given file 246 * @param fn output file name 247 */ 248 public synchronized void dumpGraph(String fn) { 249 final BufferedWriter f; 250 try { 251 f = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fn), "ISO-8859-1")); 252 } catch (IOException e) { 253 VM.sysWrite("\n\nPartialCallGraph.dumpGraph: Error opening output file!!\n\n"); 254 return; 255 } 256 TreeSet<CallSite> tmp = new TreeSet<CallSite>(new OrderByTotalWeight()); 257 tmp.addAll(callGraph.keySet()); 258 259 for (final CallSite cs : tmp) { 260 WeightedCallTargets ct = callGraph.get(cs); 261 ct.visitTargets(new WeightedCallTargets.Visitor() { 262 @Override 263 public void visit(RVMMethod callee, double weight) { 264 CodeArray callerArray = cs.getMethod().getCurrentEntryCodeArray(); 265 CodeArray calleeArray = callee.getCurrentEntryCodeArray(); 266 try { 267 f.write("CallSite " + 268 cs.getMethod().getMemberRef() + 269 " " + 270 callerArray.length() + 271 " " + 272 +cs.getBytecodeIndex() + 273 " " + 274 callee.getMemberRef() + 275 " " + 276 +calleeArray.length() + 277 " weight: " + 278 weight + 279 "\n"); 280 f.flush(); 281 } catch (IOException exc) { 282 System.err.println("I/O error writing to dynamic call graph profile."); 283 } 284 } 285 }); 286 } 287 } 288 289 /** 290 * Used to compare two call sites by total weight. 291 */ 292 private final class OrderByTotalWeight implements Comparator<CallSite> { 293 @Override 294 public int compare(CallSite o1, CallSite o2) { 295 if (o1.equals(o2)) return 0; 296 double w1 = callGraph.get(o1).totalWeight(); 297 double w2 = callGraph.get(o2).totalWeight(); 298 if (w1 < w2) { 299 return 1; 300 } 301 if (w1 > w2) { 302 return -1; 303 } 304 // equal weights; sort lexicographically 305 return o1.toString().compareTo(o2.toString()); 306 } 307 } 308 309}