diff --git a/build.xml b/build.xml index cc00ffdfb..2031a103a 100644 --- a/build.xml +++ b/build.xml @@ -141,6 +141,9 @@ + + + @@ -152,7 +155,7 @@ - + diff --git a/java/src/org/broadinstitute/sting/gatk/datasources/utilities/BAMFileStat.java b/java/src/org/broadinstitute/sting/gatk/datasources/utilities/BAMFileStat.java index 2eed6a99b..5419e18f5 100644 --- a/java/src/org/broadinstitute/sting/gatk/datasources/utilities/BAMFileStat.java +++ b/java/src/org/broadinstitute/sting/gatk/datasources/utilities/BAMFileStat.java @@ -23,14 +23,14 @@ * THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.gatk.datasources; +package org.broadinstitute.sting.gatk.datasources.utilities; import org.broadinstitute.sting.commandline.CommandLineProgram; import org.broadinstitute.sting.commandline.Argument; import org.broadinstitute.sting.utils.StingException; +import org.broadinstitute.sting.utils.instrumentation.Sizeof; import java.io.File; -import java.io.PrintStream; import java.lang.reflect.Field; import java.util.Map; import java.util.List; @@ -175,6 +175,9 @@ public class BAMFileStat extends CommandLineProgram { } System.out.printf("%nOverall: %d bins, %d chunks, %d linear index entries",numBins,numChunks,numLinearIndexEntries); + if(Sizeof.isEnabled()) + System.out.printf(", total index size in bytes: %d",Sizeof.getObjectGraphSize(index)); + System.out.println(); reader.close(); } diff --git a/java/src/org/broadinstitute/sting/utils/instrumentation/Sizeof.java b/java/src/org/broadinstitute/sting/utils/instrumentation/Sizeof.java new file mode 100644 index 000000000..12311f391 --- /dev/null +++ b/java/src/org/broadinstitute/sting/utils/instrumentation/Sizeof.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2010, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.utils.instrumentation; + +import org.broadinstitute.sting.utils.StingException; + +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.Array; +import java.util.IdentityHashMap; + +/** + * A sizeof implementation for Java. Relies on the Java instrumentation API, so + * it must be added as an agent to function properly. + * + * To run, add -javaagent:/Users/mhanna/src/Sting/dist/StingUtils.jar as a command-line + * JVM argument. + * + * @author mhanna + * @version 0.1 + */ +public class Sizeof { + /** + * Instrumentation object. Registered by the JVM via the premain() method. + */ + private static Instrumentation instrumentation; + + /** + * Called by the JVM before the agent is started. + * @param args Arguments? + * @param inst Instrumentation object, used to perform instrumentation in the JVM. + */ + public static void premain(String args, Instrumentation inst) { + instrumentation = inst; + } + + /** + * Is this Sizeof operator enabled? To enable, add the -javaagent directive listed in the class-level javadoc. + * @return True if sizeof() is enabled. If false, any calls to utility methods of this class will throw an exception. + */ + public static boolean isEnabled() { + return instrumentation != null; + } + + /** + * Gets the size of the given object. Retrieves the size for only this object; any reference fields in the object will only be + * counted as single pointers. + * @param o The object to sizeof(). + * @return Gets the best possible approximation we can get of the size of the object in memory. On Sun JVM, includes some object padding. + */ + public static long getObjectSize(Object o) { + if(!isEnabled()) + throw new StingException("Sizeof operator is currently disabled! To enable, review the documentation in Sizeof.java"); + return instrumentation.getObjectSize(o); + } + + /** + * Gets the size of the given object, including the size of the objects to which this object refers. + * @param o The object to sizeof(). + * @return Gets the best possible approximation we can get of the size of the object in memory, including all references within each object. + */ + public static long getObjectGraphSize(Object o) { + if(!isEnabled()) + throw new StingException("Sizeof operator is currently disabled! To enable, review the documentation in Sizeof.java"); + IdentityHashMap objectsSeen = new IdentityHashMap(); + return getObjectGraphSize(o,objectsSeen); + } + + /** + * The engine for walking the graph of all objects and their children. + * @param o The object to traverse. + * @param objectsSeen A list of all objects already seen. + * @return Gets the best possible approximation we can get of the size of the object in memory, including all references within each object. + */ + private static long getObjectGraphSize(Object o,IdentityHashMap objectsSeen) { + // Size of a null object itself (as opposed to the reference to the null object) is 0. + if(o == null) + return 0; + + // Don't allow repeated traversals of the same object. + if(objectsSeen.containsKey(o)) + return 0; + objectsSeen.put(o,o); + + // Get the size of the object itself, plus all contained primitives. + long totalSize = instrumentation.getObjectSize(o); + + // Get the size of (non-primitive) array elements. + Class classToInspect = o.getClass(); + if(classToInspect.isArray()) { + if(!classToInspect.getComponentType().isPrimitive()) { + for(int i = 0; i < Array.getLength(o); i++) + totalSize += getObjectGraphSize(Array.get(o,i),objectsSeen); + } + } + + // Walk the descendents of each field of this class. Be sure to avoid synthetic fields like this$0 -- these + // are back references to the parent of the object contained in the inner class. + // Potential BUG: Are there other types of synthetic fields we should be tracking? + while(classToInspect != null) { + for(Field field: classToInspect.getDeclaredFields()) { + if(field.getType().isPrimitive()) + continue; + if(Modifier.isStatic(field.getModifiers())) + continue; + if(field.isSynthetic()) + continue; + field.setAccessible(true); + Object fieldValue; + try { + fieldValue = field.get(o); + } + catch(IllegalAccessException ex) { + throw new StingException("Unable to access field " + field.getName(),ex); + } + totalSize += getObjectGraphSize(fieldValue,objectsSeen); + } + classToInspect = classToInspect.getSuperclass(); + } + return totalSize; + } +}