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