/**************************************************************************
* Copyright (c) 2006 by Chris Gray, /k/ Embedded Java Solutions. *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of /k/ Embedded Java Solutions nor the names of *
* other contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission.*
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL /K/ EMBEDDED JAVA SOLUTIONS OR OTHER CONTRIBUTORS BE *
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF *
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, *
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE *
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN *
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
package wonka.vm.profile;
import java.io.*;
import java.util.*;
public class Profile {
private class ClassData implements Comparable {
public Hashtable methods = new Hashtable();
public ClassLoaderData cld;
public String name;
public String toString() {
return name;
}
public int compareTo(Object o) {
return name.compareTo(((ClassData)o).name);
}
}
private class ClassLoaderData implements Comparable {
public int id;
public String name;
public Hashtable classes = new Hashtable();
public String toString() {
return "" + id + " -> " + name;
}
public int compareTo(Object o) {
return (id < ((ClassLoaderData)o).id ? -1 : 1);
}
}
private class ParameterData {
public String type;
public int array = 0;
public ClassData cd;
public ParameterData(String type, int array, ClassData cd) {
this.type = type;
this.array = array;
this.cd = cd;
}
}
private class MethodData implements Comparable {
public int runs;
public long bytecodes;
public long acctime;
public long runtime;
public long time;
public long instances;
public char type;
public String name;
public String fullname;
public ParameterData pd[];
public ParameterData retType;
public ClassData cd;
public String toString() {
return "" + runs + " " + bytecodes + " " + acctime + " " + runtime + " " + time + " " + instances + " " + type + " " + cd + "." + name;
}
public int compareTo(Object o) {
return name.compareTo(((MethodData)o).fullname);
}
}
private File logfile;
private File resultDir;
private Hashtable classloaders = new Hashtable();
private MethodData[] allMethods;
private Hashtable allClasses = new Hashtable();
private Profile(String log, String results) throws Exception {
logfile = new File(log);
resultDir = new File(results);
resultDir.mkdir();
System.out.println("Logfile: " + logfile);
System.out.println("Results: " + resultDir);
readLogFile();
outputClassLoaders();
outputMethodTimings();
}
private ClassData getClassData(String name) {
return (ClassData)allClasses.get(name);
}
private ClassData addClass(String name, int clid) {
ClassData result = null;
ClassLoaderData cld = null;
if(clid != -1) {
cld = (ClassLoaderData)classloaders.get(new Integer(clid));
result = (ClassData)cld.classes.get(name);
}
if(result == null) {
result = (ClassData)allClasses.get(name);
if(result == null) {
result = new ClassData();
result.name = name;
result.cld = cld;
}
}
if(result.cld == null & cld != null) {
result.cld = cld;
cld.classes.put(name, result);
}
return result;
}
private MethodData parseMethod(String method, ClassLoaderData cld) {
StringTokenizer st;
MethodData md = new MethodData();
int idx = method.indexOf('<');
if(idx == -1) idx = method.indexOf('(');
idx = method.lastIndexOf('.', idx);
String clazz = method.substring(0, idx);
md.fullname = method.substring(idx + 1, method.length());
ClassData cd = (ClassData)allClasses.get(clazz);
if(cd == null) {
cd = new ClassData();
cd.name = clazz;
allClasses.put(clazz, cd);
if(cld != null) {
cld.classes.put(clazz, cd);
}
}
MethodData md2 = (MethodData)cd.methods.get(md.fullname);
if(md2 != null) return md2;
md.cd = cd;
cd.methods.put(md.fullname, md);
if((idx = md.fullname.indexOf('<')) != -1) {
md.fullname = md.fullname.substring(0, idx) + "<" + md.fullname.substring(idx + 1, md.fullname.length());
}
idx = md.fullname.indexOf('(');
String parameters = md.fullname.substring(idx + 1, md.fullname.indexOf(')'));
String retType = md.fullname.substring(md.fullname.indexOf(')') + 1, md.fullname.length());
md.name = md.fullname.substring(0, idx);
int ac = 0;
idx = 0;
Vector params = new Vector();
while(idx < parameters.length()) {
switch(parameters.charAt(idx)) {
case 'Z': params.add(new ParameterData("boolean", ac, null)); ac = 0; idx++; break;
case 'B': params.add(new ParameterData("byte", ac, null)); ac = 0; idx++; break;
case 'C': params.add(new ParameterData("char", ac, null)); ac = 0; idx++; break;
case 'S': params.add(new ParameterData("short", ac, null)); ac = 0; idx++; break;
case 'I': params.add(new ParameterData("int", ac, null)); ac = 0; idx++; break;
case 'F': params.add(new ParameterData("float", ac, null)); ac = 0; idx++; break;
case 'J': params.add(new ParameterData("long", ac, null)); ac = 0; idx++; break;
case 'D': params.add(new ParameterData("double", ac, null)); ac = 0; idx++; break;
case 'L':
String clazz2 = parameters.substring(idx + 1, parameters.indexOf(';', idx));
String result = "";
st = new StringTokenizer(clazz2, "/");
while(st.hasMoreTokens()) {
result += st.nextToken() + ".";
}
result = result.substring(0, result.length() - 1);
params.add(new ParameterData(null, ac, addClass(result, (cld != null ? cld.id : -1))));
ac = 0;
idx = parameters.indexOf(';', idx) + 1;
break;
case '[': ac++; idx++; break;
}
}
md.pd = (ParameterData[])params.toArray(new ParameterData[0]);
idx = 0;
ac = 0;
while(idx < retType.length()) {
switch(retType.charAt(idx)) {
case 'V': md.retType = new ParameterData("void", ac, null); idx++; break;
case 'Z': md.retType = new ParameterData("boolean", ac, null); idx++; break;
case 'B': md.retType = new ParameterData("byte", ac, null); idx++; break;
case 'S': md.retType = new ParameterData("short", ac, null); idx++; break;
case 'C': md.retType = new ParameterData("char", ac, null); idx++; break;
case 'I': md.retType = new ParameterData("int", ac, null); idx++; break;
case 'F': md.retType = new ParameterData("float", ac, null); idx++; break;
case 'J': md.retType = new ParameterData("long", ac, null); idx++; break;
case 'D': md.retType = new ParameterData("double", ac, null); idx++; break;
case 'L':
String clazz2 = retType.substring(idx + 1, retType.indexOf(';', idx));
String result = "";
st = new StringTokenizer(clazz2, "/");
while(st.hasMoreTokens()) {
result += st.nextToken() + ".";
}
result = result.substring(0, result.length() - 1);
md.retType = new ParameterData(null, ac, addClass(result, (cld != null ? cld.id : -1)));
ac = 0;
idx = retType.indexOf(';', idx) + 1;
break;
case '[': ac++; idx++; break;
}
}
return md;
}
private void readLogFile() throws Exception {
BufferedReader br = new BufferedReader(new FileReader(logfile));
String line;
String clazz;
MethodData md;
while((line = br.readLine()) != null) {
if(line.startsWith("TL")) {
ClassLoaderData cld = new ClassLoaderData();
cld.id = Integer.parseInt(line.substring(2,5));
cld.name = line.substring(7, line.length());
classloaders.put(new Integer(cld.id), cld);
System.out.println("Classloader: " + cld.id + " - " + cld.name);
}
else if(line.startsWith("TD")) {
if(line.charAt(5) == ':') {
StringTokenizer st = new StringTokenizer(line, " |", false);
String tmp = st.nextToken();
ClassLoaderData cld = (ClassLoaderData)classloaders.get(new Integer(Integer.parseInt(tmp.substring(2,5)))); // Classloader
int runs = Integer.parseInt(st.nextToken());
long bytecodes = Long.parseLong(st.nextToken());
st.nextToken(); // avg
long acctime = Long.parseLong(st.nextToken());
st.nextToken(); // avg
long runtime = Long.parseLong(st.nextToken());
st.nextToken(); // avg
long time = Long.parseLong(st.nextToken());
st.nextToken(); // avg
long instances = Long.parseLong(st.nextToken());
st.nextToken(); // avg
char type = st.nextToken().charAt(0);
String method = st.nextToken();
md = parseMethod(method, cld);
md.runs = runs;
md.bytecodes = bytecodes;
md.acctime = acctime;
md.runtime = runtime;
md.time = time;
md.instances = instances;
md.type = type;
}
else if(line.charAt(5) == 'c') {
StringTokenizer st = new StringTokenizer(line, " |");
st.nextToken();
int count = Integer.parseInt(st.nextToken());
st.nextToken();
st.nextToken();
st.nextToken();
st.nextToken();
st.nextToken();
String method = st.nextToken();
System.out.println(" " + count + " " + method);
// MethodData md2 = parseMethod(method, null);
}
else if(line.charAt(5) == 'i') {
}
}
}
}
private void htmlHeader(PrintWriter out) {
out.println("<html>");
out.println("<header>");
out.println("</header>");
out.println("<body>");
}
private void htmlFooter(PrintWriter out) {
out.println("</body>");
out.println("</html>");
}
private void outputClassLoaders() throws Exception {
PrintWriter out = new PrintWriter(new FileWriter(new File(resultDir, "classloaders.html")));
htmlHeader(out);
out.println("<h1>ClassLoaders</h1>");
out.println("<br/>");
ClassLoaderData clds[] = (ClassLoaderData[])classloaders.values().toArray(new ClassLoaderData[0]);
Arrays.sort(clds);
for(int i=0; i<clds.length; i++) {
out.println("" + clds[i].id + ". <a href=\"classloader_" + clds[i].id + "_"+ clds[i].name + ".html\">" + clds[i].name + "</a><br/>");
outputClasses(clds[i]);
}
htmlFooter(out);
out.close();
}
private void outputClasses(ClassLoaderData cld) throws Exception {
PrintWriter out = new PrintWriter(new FileWriter(new File(resultDir, "classloader_" + cld.id + "_" + cld.name + ".html")));
htmlHeader(out);
out.println("<h1>" + cld.name + "</h1>");
out.println("<br/>");
ClassData cds[] = (ClassData[])cld.classes.values().toArray(new ClassData[0]);
Arrays.sort(cds);
for(int i=0; i<cds.length; i++) {
out.println("<a href=\"class_" + cds[i].name + ".html\">" + cds[i].name + "</a><br/>");
outputMethods(cds[i]);
}
htmlFooter(out);
out.close();
}
private void outputMethods(ClassData cd) throws Exception {
PrintWriter out = new PrintWriter(new FileWriter(new File(resultDir, "class_" + cd.name + ".html")));
htmlHeader(out);
out.println("<h1>" + cd.name + "</h1>");
out.println("<br/>");
out.println("<table>");
MethodData mds[] = (MethodData[])cd.methods.values().toArray(new MethodData[0]);
Arrays.sort(mds);
for(int i=0; i<mds.length; i++) {
out.println("<tr>");
writeMethodHtml(out, mds[i], false, true);
out.println("<tr>");
}
out.println("</table>");
htmlFooter(out);
out.close();
}
private void writeMethodHtml(PrintWriter out, MethodData md, boolean classname, boolean retType) throws Exception {
if(retType) {
out.println("<td align=\"right\">");
if(md.retType.type != null) {
out.print(md.retType.type);
}
else {
out.print("<a href=\"class_" + md.retType.cd.name + ".html\">" + md.retType.cd.name + "</a>");
}
for(int j=0; j<md.retType.array; j++) {
out.print("[]");
}
out.println("</td>");
}
out.print("<td>");
if(classname) {
out.print("<a href=\"class_" + md.cd.name + ".html\">" + md.cd.name + "</a>.");
}
out.print("<a href=\"method_" + md.fullname + ".html\">" + md.name + "</a>(");
for(int j=0; j<md.pd.length; j++) {
if(md.pd[j].type != null) {
out.print(md.pd[j].type);
}
else {
out.print("<a href=\"class_" + md.pd[j].cd.name + ".html\">" + md.pd[j].cd.name + "</a>");
}
for(int k=0; k<md.pd[j].array; k++) {
out.print("[]");
}
if(j < md.pd.length - 1) {
out.print(", ");
}
}
out.print(")");
out.println("</td>");
}
private void outputMethodTimings() throws Exception {
Vector methods = new Vector();
Iterator clds = classloaders.values().iterator();
while(clds.hasNext()) {
ClassLoaderData cld = (ClassLoaderData)clds.next();
Iterator cds = cld.classes.values().iterator();
while(cds.hasNext()) {
ClassData cd = (ClassData)cds.next();
methods.addAll(cd.methods.values());
}
}
allMethods = (MethodData[])methods.toArray(new MethodData[0]);
outputMethodTimingsDetail("Runs", "runs", new Comparator() {
public int compare(Object a, Object b) {
MethodData aa = (MethodData)a; MethodData bb = (MethodData)b;
return (aa.runs < bb.runs ? 1 : aa.runs == bb.runs ? 0 : -1);
}
});
outputMethodTimingsDetail("Total Bytecodes", "bytecodes", new Comparator() {
public int compare(Object a, Object b) {
MethodData aa = (MethodData)a; MethodData bb = (MethodData)b;
return (aa.bytecodes < bb.bytecodes ? 1 : aa.bytecodes == bb.bytecodes ? 0 : -1);
}
});
outputMethodTimingsDetail("Avg Bytecodes", "bytecodes_avg", new Comparator() {
public int compare(Object a, Object b) {
MethodData aa = (MethodData)a; MethodData bb = (MethodData)b;
if(aa.runs == 0) return 1;
if(bb.runs == 0) return -1;
return (aa.bytecodes / aa.runs < bb.bytecodes / bb.runs ? 1 : aa.bytecodes / aa.runs == bb.bytecodes / bb.runs ? 0 : -1);
}
});
outputMethodTimingsDetail("Total Time", "ttime", new Comparator() {
public int compare(Object a, Object b) {
MethodData aa = (MethodData)a; MethodData bb = (MethodData)b;
return (aa.acctime < bb.acctime ? 1 : aa.acctime == bb.acctime ? 0 : -1);
}
});
outputMethodTimingsDetail("Avg Total Time", "ttime_avg", new Comparator() {
public int compare(Object a, Object b) {
MethodData aa = (MethodData)a; MethodData bb = (MethodData)b;
if(aa.runs == 0) return 1;
if(bb.runs == 0) return -1;
return (aa.acctime / aa.runs < bb.acctime / bb.runs ? 1 : aa.acctime / aa.runs == bb.acctime / bb.runs ? 0 : -1);
}
});
outputMethodTimingsDetail("Run Time", "rtime", new Comparator() {
public int compare(Object a, Object b) {
MethodData aa = (MethodData)a; MethodData bb = (MethodData)b;
return (aa.runtime < bb.runtime ? 1 : aa.runtime == bb.runtime ? 0 : -1);
}
});
outputMethodTimingsDetail("Avg Run Time", "rtime_avg", new Comparator() {
public int compare(Object a, Object b) {
MethodData aa = (MethodData)a; MethodData bb = (MethodData)b;
if(aa.runs == 0) return 1;
if(bb.runs == 0) return -1;
return (aa.runtime / aa.runs < bb.runtime / bb.runs ? 1 : aa.runtime / aa.runs == bb.runtime / bb.runs ? 0 : -1);
}
});
outputMethodTimingsDetail("Non-Acc Time", "natime", new Comparator() {
public int compare(Object a, Object b) {
MethodData aa = (MethodData)a; MethodData bb = (MethodData)b;
return (aa.time < bb.time ? 1 : aa.time == bb.time ? 0 : -1);
}
});
outputMethodTimingsDetail("Avg Non-Acc Time", "natime_avg", new Comparator() {
public int compare(Object a, Object b) {
MethodData aa = (MethodData)a; MethodData bb = (MethodData)b;
if(aa.runs == 0) return 1;
if(bb.runs == 0) return -1;
return (aa.time / aa.runs < bb.time / bb.runs ? 1 : aa.time / aa.runs == bb.time / bb.runs ? 0 : -1);
}
});
}
private void outputMethodTimingsDetail(String type, String file, Comparator comp) throws Exception {
PrintWriter out = new PrintWriter(new FileWriter(new File(resultDir, "methodtimings_" + file + ".html")));
htmlHeader(out);
out.println("<h1>" + type + "</h1>");
out.println("<br/><table>");
out.print("<tr>");
out.print("<td align=\"right\"><b><a href=\"methodtimings_runs.html\">Runs</a></b></td>");
out.print("<td align=\"right\"><b><a href=\"methodtimings_bytecodes.html\">Total Bytecodes</a></b></td>");
out.print("<td align=\"right\"><b><a href=\"methodtimings_bytecodes_avg.html\">Avg</a></b></td>");
out.print("<td align=\"right\"><b><a href=\"methodtimings_ttime.html\">Total Time</a></b></td>");
out.print("<td align=\"right\"><b><a href=\"methodtimings_ttime_avg.html\">Avg</a></b></td>");
out.print("<td align=\"right\"><b><a href=\"methodtimings_rtime.html\">Run Time</a></b></td>");
out.print("<td align=\"right\"><b><a href=\"methodtimings_rtime_avg.html\">Avg</a></b></td>");
out.print("<td align=\"right\"><b><a href=\"methodtimings_natime.html\">Non-Acc Time</a></b></td>");
out.print("<td align=\"right\"><b><a href=\"methodtimings_natime_avg.html\">Avg</a></b></td>");
out.print("<td align=\"center\"><b>Type</b></td>");
out.print("<td align=\"left\"><b>Method</b></td></tr>");
Arrays.sort(allMethods, comp);
for(int i=0; i<allMethods.length; i++) {
if(allMethods[i].runs != 0) {
out.print("<tr>");
out.print("<td align=\"right\">" + allMethods[i].runs + "</td>");
out.print("<td align=\"right\">" + allMethods[i].bytecodes + "</td>");
out.print("<td align=\"right\">" + (allMethods[i].bytecodes / allMethods[i].runs) + "</td>");
out.print("<td align=\"right\">" + allMethods[i].acctime + "</td>");
out.print("<td align=\"right\">" + (allMethods[i].acctime / allMethods[i].runs) + "</td>");
out.print("<td align=\"right\">" + allMethods[i].runtime + "</td>");
out.print("<td align=\"right\">" + (allMethods[i].runtime / allMethods[i].runs) + "</td>");
out.print("<td align=\"right\">" + allMethods[i].time + "</td>");
out.print("<td align=\"right\">" + (allMethods[i].time / allMethods[i].runs) + "</td>");
out.print("<td align=\"center\">" + allMethods[i].type + "</td>");
writeMethodHtml(out, allMethods[i], true, false);
out.println("</tr>");
}
}
out.println("</table>");
htmlFooter(out);
out.close();
}
public static void main(String args[]) throws Exception {
if(args.length != 2) {
System.out.println("Use: Profile <logfile> <resultdir>");
System.exit(1);
}
Profile p = new Profile(args[0], args[1]);
}
}