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    }