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.compilers.baseline; 014 015import java.io.FileOutputStream; 016import java.io.FileReader; 017import java.io.IOException; 018import java.io.LineNumberReader; 019import java.io.PrintStream; 020import java.util.StringTokenizer; 021import org.jikesrvm.VM; 022import org.jikesrvm.adaptive.controller.Controller; 023import org.jikesrvm.classloader.MemberReference; 024import org.jikesrvm.classloader.NormalMethod; 025import org.jikesrvm.runtime.Callbacks; 026import org.jikesrvm.runtime.Magic; 027import org.vmmagic.pragma.Entrypoint; 028 029/** 030 * A repository of edge counters for bytecode-level edge conditional branches. 031 */ 032public final class EdgeCounts implements Callbacks.ExitMonitor { 033 /** 034 * Adjustment to offset in data from the bytecode index for taken 035 * branch counts 036 */ 037 public static final int TAKEN = 0; 038 /** 039 * Adjustment to offset in data from the bytecode index for not 040 * taken branch counts 041 */ 042 public static final int NOT_TAKEN = 1; 043 044 /** For a non-adaptive system, have we registered the exit call back yet? */ 045 private static boolean registered = false; 046 047 /** 048 * Array of edge count data. The first index is the ID of the 049 * method, the second index is the bytecode index within the method 050 * plus either TAKEN or NOT_TAKEN. The value is the count of the 051 * number of times a particular branch event occurs. 052 */ 053 @Entrypoint 054 private static int[][] data; 055 056 @Override 057 public void notifyExit(int value) { 058 dumpCounts(); 059 } 060 061 /** 062 * Attempt to use edge counts from an input file. If the source 063 * file is not {@code null}, then clear any existing counts and read in new 064 * counts from the file provided. 065 * 066 * @param inputFileName The name of the edge count file (possibly null) 067 */ 068 public static void loadCountsFromFileIfAvailable(String inputFileName) { 069 if (inputFileName != null) { 070 /* first clear all counts */ 071 for (int i = 0; i < data.length; i++) { 072 int[] d = data[i]; 073 if (d != null) { 074 for (int j = 0; j < d.length; j++) { 075 d[j] = 0; 076 } 077 } 078 } 079 /* then read in the provided counts */ 080 if (Controller.options.BULK_COMPILATION_VERBOSITY >= 1) { 081 VM.sysWrite("Loading edge count file: ", inputFileName, " "); 082 } 083 readCounts(inputFileName); 084 if (Controller.options.BULK_COMPILATION_VERBOSITY >= 1) { 085 VM.sysWriteln(); 086 } 087 } 088 } 089 090 public static synchronized void allocateCounters(NormalMethod m, int numEntries) { 091 if (numEntries == 0) return; 092 if (!VM.BuildForAdaptiveSystem && !registered) { 093 // Assumption: If edge counters were enabled in a non-adaptive system 094 // then the user must want us to dump them when the system 095 // exits. Otherwise why would they have enabled them... 096 registered = true; 097 Callbacks.addExitMonitor(new EdgeCounts()); 098 } 099 allocateCounters(m.getId(), numEntries); 100 } 101 102 private static synchronized void allocateCounters(int id, int numEntries) { 103 if (data == null) { 104 data = new int[id + 500][]; 105 } 106 if (id >= data.length) { 107 int newSize = data.length * 2; 108 if (newSize <= id) newSize = id + 500; 109 int[][] tmp = new int[newSize][]; 110 System.arraycopy(data, 0, tmp, 0, data.length); 111 Magic.sync(); 112 data = tmp; 113 } 114 data[id] = new int[numEntries]; 115 } 116 117 public static BranchProfiles getBranchProfiles(NormalMethod m) { 118 int id = m.getId(); 119 if (data == null || id >= data.length) return null; 120 if (data[id] == null) return null; 121 return new BranchProfiles(m, data[id]); 122 } 123 124 /** 125 * Dump all the profile data to the file BaselineCompiler.options.PROFILE_EDGE_COUNTER_FILE 126 */ 127 public static void dumpCounts() { 128 dumpCounts(BaselineCompiler.options.PROFILE_EDGE_COUNTER_FILE); 129 } 130 131 /** 132 * Dump all profile data to the given file 133 * @param fn output file name 134 */ 135 public static void dumpCounts(String fn) { 136 PrintStream f; 137 try { 138 f = new PrintStream(new FileOutputStream(fn)); 139 } catch (IOException e) { 140 VM.sysWrite("\n\nEdgeCounts.dumpCounts: Error opening output file!!\n\n"); 141 return; 142 } 143 if (data == null) return; 144 for (int i = 0; i < data.length; i++) { 145 if (data[i] != null) { 146 NormalMethod m = 147 (NormalMethod) MemberReference.getMethodRef(i).peekResolvedMethod(); 148 if (m != null) { 149 new BranchProfiles(m, data[i]).print(f); 150 } 151 } 152 } 153 } 154 155 public static void readCounts(String fn) { 156 LineNumberReader in = null; 157 try { 158 in = new LineNumberReader(new FileReader(fn)); 159 } catch (IOException e) { 160 e.printStackTrace(); 161 VM.sysFail("Unable to open input edge counter file " + fn); 162 } 163 try { 164 int[] cur = null; 165 int curIdx = 0; 166 for (String s = in.readLine(); s != null; s = in.readLine()) { 167 s = s.replaceAll("\\{urls[^\\}]*\\}", ""); // strip classloader cruft we can't parse 168 StringTokenizer parser = new StringTokenizer(s, " \t\n\r\f,{}"); 169 String firstToken = parser.nextToken(); 170 if (firstToken.equals("M")) { 171 int numCounts = Integer.parseInt(parser.nextToken()); 172 MemberReference key = MemberReference.parse(parser); 173 int id = key.getId(); 174 allocateCounters(id, numCounts); 175 cur = data[id]; 176 curIdx = 0; 177 if (Controller.options.BULK_COMPILATION_VERBOSITY >= 1) { 178 VM.sysWrite("M"); 179 } 180 } else { 181 String type = parser.nextToken(); // discard bytecode index, we don't care. 182 if (type.equals("switch")) { 183 parser.nextToken(); // discard '<' 184 for (String nt = parser.nextToken(); !nt.equals(">"); nt = parser.nextToken()) { 185 cur[curIdx++] = Integer.parseInt(nt); 186 } 187 if (Controller.options.BULK_COMPILATION_VERBOSITY >= 1) { 188 VM.sysWrite("S"); 189 } 190 } else if (type.equals("forwbranch") || type.equals("backbranch")) { 191 parser.nextToken(); // discard '<' 192 cur[curIdx + TAKEN] = Integer.parseInt(parser.nextToken()); 193 cur[curIdx + NOT_TAKEN] = Integer.parseInt(parser.nextToken()); 194 curIdx += 2; 195 if (Controller.options.BULK_COMPILATION_VERBOSITY >= 1) { 196 VM.sysWrite("B"); 197 } 198 } else { 199 VM.sysFail("Format error in edge counter input file"); 200 } 201 } 202 } 203 in.close(); 204 } catch (IOException e) { 205 e.printStackTrace(); 206 VM.sysFail("Error parsing input edge counter file" + fn); 207 } 208 // Enable debug of input by dumping file as we exit the VM. 209 if (false) { 210 Callbacks.addExitMonitor(new EdgeCounts()); 211 BaselineCompiler.processCommandLineArg("-X:base:", "edge_counter_file=DebugEdgeCounters"); 212 } 213 } 214 215}