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    }