/*
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.vm.hosted;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import com.sun.max.lang.*;
import com.sun.max.program.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.classfile.*;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.hosted.GraphPrototype.ClassInfo;
import com.sun.max.vm.object.*;
/**
* This class collects and reports statistics about the size of a {@link GraphPrototype}, which
* contains objects, classes, and code.
*/
public class GraphStats {
static class ClassStats {
final GraphPrototype.ClassInfo classInfo;
int objectCount;
int objectSize;
int hubSize;
int classActorSize;
int staticSize;
int staticHubSize;
int methodsSize;
int targetSize;
int methodsCount;
ClassStats(GraphPrototype.ClassInfo classInfo) {
this.classInfo = classInfo;
}
int classSize() {
return hubSize + classActorSize + staticSize + staticHubSize + methodsSize + targetSize;
}
}
static class MethodStats {
final MethodActor methodActor;
int actorSize;
int bytecodeSize;
int targetMethodSize;
int targetCodeSize;
MethodStats(MethodActor methodActor) {
this.methodActor = methodActor;
}
int methodSize() {
return actorSize + bytecodeSize + targetMethodSize + targetCodeSize;
}
}
final GraphPrototype graphPrototype;
final Map<MethodActor, MethodStats> methodStats = new HashMap<MethodActor, MethodStats>();
public GraphStats(GraphPrototype graphPrototype) {
this.graphPrototype = graphPrototype;
}
public void dumpStats(PrintStream printStream) {
printObjectStats(printStream, computeObjectStats());
printClassStats(printStream, computeClassStats());
printMethodStats(printStream);
}
private void printObjectStats(PrintStream printStream, final int total) {
Collection<ClassInfo> cstats = graphPrototype.classInfos.values();
final ClassInfo[] classInfos = cstats.toArray(new ClassInfo[cstats.size()]);
Arrays.sort(classInfos, BY_OBJECT_SIZE);
printStream.println("Object Histogram End");
printStream.println("Cumul Size Objects Avg Class");
printStream.println("==============================================================================");
long cumul = 0;
for (ClassInfo info : classInfos) {
final ClassStats s = getClassStats(info);
if (s.objectCount != 0) {
cumul += s.objectSize;
final String fixedDouble = Strings.padLengthWithSpaces(6, Strings.fixedDouble(cumul * 100.0d / total, 2));
printStream.printf("(%s%%) %-10d (%6d kb) / %-10d = %-10d %s\n", fixedDouble, s.objectSize, s.objectSize / 1024, s.objectCount, s.objectSize / s.objectCount, info.clazz.getName());
}
}
printStream.println("Object Histogram End\n");
}
private int computeObjectStats() {
int total = 0;
for (Object o : graphPrototype.objects) {
final ClassInfo classInfo = graphPrototype.classInfoFor(o.getClass());
final ClassStats classStats = getClassStats(classInfo);
classStats.objectCount++;
final int size = sizeOf(o);
classStats.objectSize += size;
total += size;
}
return total;
}
static ClassStats getClassStats(final ClassInfo classInfo) {
ClassStats classStats = classInfo.stats;
if (classStats == null) {
classStats = new ClassStats(classInfo);
classInfo.stats = classStats;
}
return classStats;
}
void printClassStats(PrintStream printStream, int total) {
Collection<ClassInfo> cstats = graphPrototype.classInfos.values();
final ClassInfo[] classInfos = cstats.toArray(new ClassInfo[cstats.size()]);
Arrays.sort(classInfos, BY_CLASS_SIZE);
printStream.println("Class Histogram Start");
printStream.println("Total Hub Actor StHub Static MethSize TargSize Count Class");
printStream.println("==============================================================================");
for (ClassInfo info : classInfos) {
final ClassStats s = getClassStats(info);
printStream.printf("%-10d %-10d %-10d %-10d %-10d %-10d %-10d %-6d %s\n", s.classSize(), s.hubSize, s.classActorSize,
s.staticHubSize, s.staticSize, s.methodsSize, s.targetSize, s.methodsCount, info.clazz.getName());
}
printStream.println("Class Histogram End\n");
}
void printMethodStats(PrintStream printStream) {
Collection<MethodStats> mstats = this.methodStats.values();
MethodStats[] methodStats = mstats.toArray(new MethodStats[mstats.size()]);
Arrays.sort(methodStats, BY_METHOD_SIZE);
printStream.println("Method Histogram Start");
printStream.println("Total Actor Bytecodes TargMeth TargCode Ratio Method");
printStream.println("==============================================================================");
for (MethodStats s : methodStats) {
final double ratio = s.targetCodeSize / (double) s.bytecodeSize;
final String rstr = Strings.fixedDouble(ratio, 2);
printStream.printf("%-10d %-10d %-10d %-10d %-10d %6s %s\n", s.methodSize(), s.actorSize, s.bytecodeSize,
s.targetMethodSize, s.targetCodeSize, rstr, s.methodActor.toString());
}
printStream.println("Method Histogram End\n");
}
private int computeClassStats() {
for (ClassInfo classInfo : graphPrototype.classInfos.values()) {
final ClassStats classStats = classInfo.stats;
final ClassActor classActor = ClassActor.fromJava(classInfo.clazz);
classStats.hubSize = sizeOf(classActor.dynamicHub());
classStats.classActorSize = computeClassActorSize(classActor);
classStats.staticHubSize = sizeOf(classActor.staticHub());
classStats.staticSize = sizeOf(classActor.staticTuple());
computeMethodStats(classActor, classStats);
}
return 0;
}
private int computeClassActorSize(ClassActor classActor) {
int total = sizeOf(classActor);
total += nondefaultSize(classActor.allVirtualMethodActors(), ClassActor.NO_VIRTUAL_METHODS);
total += nondefaultSize(classActor.localInstanceFieldActors(), ClassActor.NO_FIELDS);
total += nondefaultSize(classActor.localStaticFieldActors(), ClassActor.NO_FIELDS);
total += nondefaultSize(classActor.localInterfaceActors(), ClassActor.NO_INTERFACES);
total += nondefaultSize(classActor.localInterfaceMethodActors(), ClassActor.NO_INTERFACE_METHODS);
total += nondefaultSize(classActor.localStaticMethodActors(), ClassActor.NO_STATIC_METHODS);
total += nondefaultSize(classActor.localVirtualMethodActors(), ClassActor.NO_VIRTUAL_METHODS);
total += sizeOf(classActor.iToV());
return total;
}
private void computeMethodStats(ClassActor classActor, ClassStats classStats) {
final Set<MethodActor> methodActors = new HashSet<MethodActor>();
methodActors.addAll(Arrays.asList(classActor.localInterfaceMethodActors()));
methodActors.addAll(Arrays.asList(classActor.localStaticMethodActors()));
methodActors.addAll(Arrays.asList(classActor.localVirtualMethodActors()));
for (MethodActor methodActor : methodActors) {
final MethodStats methodStats = computeMethodStats(methodActor);
classStats.methodsSize += methodStats.actorSize + methodStats.bytecodeSize;
classStats.targetSize += methodStats.targetCodeSize + methodStats.targetMethodSize;
classStats.methodsCount++;
}
}
private int computeMethodActorSize(MethodActor methodActor) {
int total = sizeOf(methodActor);
if (methodActor instanceof ClassMethodActor) {
final ClassMethodActor classMethodActor = (ClassMethodActor) methodActor;
total += computeCodeAttributeSize(classMethodActor.codeAttribute());
}
return total;
}
/**
* The method {@link ClassMethodActor#codeAttribute()} performs bytecode rewriting when it is called the first time.
* Since we don't want any side effects here, we access the private field via reflection.
*/
private static CodeAttribute getCodeAttribute(ClassMethodActor classMethodActor) {
try {
if (codeAttributeField == null) {
codeAttributeField = ClassMethodActor.class.getDeclaredField("codeAttribute");
codeAttributeField.setAccessible(true);
}
return (CodeAttribute) codeAttributeField.get(classMethodActor);
} catch (Exception ex) {
throw ProgramError.unexpected(ex);
}
}
private static Field codeAttributeField;
private MethodStats computeMethodStats(MethodActor methodActor) {
final MethodStats methodStats = getMethodStats(methodActor);
methodStats.actorSize = sizeOf(methodActor);
if (methodActor instanceof ClassMethodActor) {
final ClassMethodActor classMethodActor = (ClassMethodActor) methodActor;
methodStats.bytecodeSize = computeCodeAttributeSize(getCodeAttribute(classMethodActor));
final TargetMethod targetMethod = classMethodActor.currentTargetMethod();
if (targetMethod != null) {
methodStats.targetMethodSize = computeTargetMethodSize(targetMethod);
methodStats.targetCodeSize = sizeOf(targetMethod.code());
}
}
return methodStats;
}
private int computeTargetMethodSize(TargetMethod targetMethod) {
int total = sizeOf(targetMethod);
total += sizeOf(targetMethod.code());
total += sizeOf(targetMethod.referenceLiterals());
total += sizeOf(targetMethod.directCallees());
total += sizeOf(targetMethod.scalarLiterals());
return total;
}
private int computeCodeAttributeSize(CodeAttribute codeAttribute) {
if (codeAttribute != null) {
int total = sizeOf(codeAttribute);
total += sizeOf(codeAttribute.code());
total += sizeOf(codeAttribute.encodedData());
return total;
}
return 0;
}
private static int nondefaultSize(Object object, Object def) {
if (object == def) {
return 0;
}
return sizeOf(object);
}
private static int sizeOf(Object object) {
if (object == null) {
return 0;
}
return ObjectAccess.size(object).toInt();
}
private MethodStats getMethodStats(MethodActor methodActor) {
MethodStats methodStats = this.methodStats.get(methodActor);
if (methodStats == null) {
methodStats = new MethodStats(methodActor);
this.methodStats.put(methodActor, methodStats);
}
return methodStats;
}
abstract static class ClassInfoComparator implements Comparator<ClassInfo> {
public int compare(ClassInfo o1, ClassInfo o2) {
final int m1 = metric(getClassStats(o1));
final int m2 = metric(getClassStats(o2));
return m1 < m2 ? 1 : m1 > m2 ? -1 : o1.toString().compareTo(o2.toString());
}
public abstract int metric(ClassStats stats);
}
/**
* A comparator that sorts by the cumulative size of objects and then the name.
*/
static final Comparator<ClassInfo> BY_OBJECT_SIZE = new ClassInfoComparator() {
@Override
public int metric(ClassStats stats) {
return stats.objectSize;
}
};
/**
* A comparator that sorts by the class size and then the name.
*/
static final Comparator<ClassInfo> BY_CLASS_SIZE = new ClassInfoComparator() {
@Override
public int metric(ClassStats stats) {
return stats.classSize();
}
};
/**
* A comparator that sorts by the class size and then the name.
*/
static final Comparator<MethodStats> BY_METHOD_SIZE = new Comparator<MethodStats>() {
public int compare(MethodStats o1, MethodStats o2) {
final int m1 = o1.methodSize();
final int m2 = o2.methodSize();
return m1 < m2 ? 1 : m1 > m2 ? -1 : o1.methodActor.toString().compareTo(o2.methodActor.toString());
}
};
}