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