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}