[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: profiling questions



On Sun, Mar 02, 2003 at 02:45:31AM -0500, Chris Pickett wrote:
> Hi.
> 
> I'm trying to compute the number of silent stores in a Java program.  A 
> silent store occurs when the value stored is identical to the value 
> already there.  I have three questions:
> 
> Q1) Why does the single variable "y" in the following code get split 
> into "b1" and "b2" in the Jimple representation:
> 
> public class HelloWorld {
>     public static void main(String[] args) {
>         int y;
>         y = 2;
>         y = 2;
>     }
> }
> 
> -->
> 
> public class HelloWorld extends java.lang.Object
> {
> 
>     public void <init>()
>     {
>         HelloWorld r0;
> 
>         r0 := @this: HelloWorld;
>         specialinvoke r0.<java.lang.Object: void <init>()>();
>         return;
>     }
> 
>     public static void main(java.lang.String[] )
>     {
>         java.lang.String[] r0;
>         byte b0, b1;
> 
>         r0 := @parameter0: java.lang.String[];
>         b0 = 2;
>         b1 = 2;
>         return;
>     }
> }

By default, Soot splits variables according to def-use chains. This is
mostly because of the variables representing stack slots; they are
typically reused many times, and not splitting them would be very
unfortunate for any analysis trying to use Jimple. You can use the
phase option -p jb unsplit-original-locals to get the original locals
back.

As an aside, what Soot options and what version of Soot did you use to
get the Jimple above? There is a dead assignment eliminator that is
supposed to get rid of dead assignments. The Jimple I get does not
have them.

> Q2) How can I test whether a variable (register?) has been initialized 
> at runtime.  Currently, for the Jimple code:

The Java VM spec says that every variable must be provable to be
initialized before it is first used. The code you are producing
reads the variable before it has been initialized, so the VM cannot
prove that it will always be initialized before being read, so it
refuses to run it. Given that any code that may read uninitialized
variables will be rejected by the VM, it seems kind of pointless to
count reads of uninitialized variables, since there will never be any.

> 
>    b0 = 2;
> 
> I can transform it to produce:
> 
>    if b0 == null goto label0;
>    if b0 != 2 goto label0;
>    tmp = <HelloWorld: long silentStoreCounter>;
>    tmp = tmp + 1L;
>    <HelloWorld: long silentStoreCounter> = tmp;
> label0:
>    b0 = 2;
> 
> However, even with that null test there, I still get the following error 
> at runtime:
> 
> Exception in thread "main" java.lang.VerifyError: (class: HelloWorld, 
> method: main signature: ([Ljava/lang/String;)V) Accessing value from 
> uninitialized register 1
> 
> Is there a a way to test?  Or do I have to keep track of which variables 
> have been initialized inside my internalTransform() method?  If there is 
> a way to test, can I use it on statics and fields as well as locals?
> 
> Q3) Can I otherwise use NeqExpr to compare any two values?  Does this 
> work for object references or do I have to do something else?  I want to 
> know if the operands actually refer to the same object, so I don't think 
> EquivTo() works here.

EquivTo compares values at compile time. A NeqExpr compiles into a
comparison which gets executed at run time.

> 
> Cheers,
> Chris
> 
> Attached please find the files I am talking about.

> public class HelloWorld {
>     
>     //static int x;
> 
>     public static void main(String[] args) {
> 	//System.out.println("Hello World!");
> 	int y;
> 	/*x = 5;
> 	y = x;
> 	if (y == 5)
> 	    y = x;
> 	*/
> 	y = 2;
> 	y = 2;
>     }
> }

> import soot.*;
> import soot.jimple.*;
> import soot.jimple.internal.*;
> import soot.util.*;
> import java.io.*;
> import java.util.*;
> 
> public class SilentStoreMain {
>     
>     public static void main(String[] args) {
> 
> 	if (args.length == 0) {
>             System.out.println(
>               "Syntax: java SilentStoreMain [soot options] classfile");
>             System.exit(0);
>         }            
>         Scene.v().getPack("jtp").add(
>           new Transform("jtp.silentstore", SilentStoreTransformer.v()));
>         soot.Main.main(args);
>     }
> }
> 
> /**
>    This class transforms a body to count the number of silent stores.
> */
> 
> class SilentStoreTransformer extends BodyTransformer {
> 
>     /* standard BodyTransformer stuff */
>     private static SilentStoreTransformer instance = 
> 	new SilentStoreTransformer();
>     private SilentStoreTransformer() {}
>     public static SilentStoreTransformer v() { return instance; }
>     public String getDeclaredOptions() { return super.getDeclaredOptions(); }
>     private boolean addedFieldsToMainClassAndLoadedPrintStream = false;
>     private SootClass javaIoPrintStream;
> 
>     /* the next three methods exist so that the number of silent stores or the
>        number of stores can be printed out
>     */
> 
>     private Local addTmpRef(Body body) {
>         Local tmpRef = Jimple.v().newLocal(
> 	  "tmpRef", RefType.v("java.io.PrintStream"));
>         body.getLocals().add(tmpRef);
>         return tmpRef;
>     }
>      
>     private Local addTmpLong(Body body) {
>         Local tmpLong = Jimple.v().newLocal("tmpLong", LongType.v()); 
>         body.getLocals().add(tmpLong);
>         return tmpLong;
>     }
> 
>     private void addPrintCounter(Chain units,
> 				 Stmt s,
> 				 SootField counter,
> 				 Local tmpRef,
> 				 Local tmpLong) {
> 	
>         // insert "tmpRef = java.lang.System.out;" 
>         units.insertBefore(Jimple.v().newAssignStmt( 
> 	  tmpRef, Jimple.v().newStaticFieldRef( 
> 	    Scene.v().getField(
>               "<java.lang.System: java.io.PrintStream out>"))), s);
> 
>         // insert "tmpLong = counter;" 
>         units.insertBefore(Jimple.v().newAssignStmt(
> 	  tmpLong, Jimple.v().newStaticFieldRef(counter)), s);
>             
>         // insert "tmpRef.println(tmpLong);" 
>         SootMethod toCall = javaIoPrintStream.getMethod("void println(long)");
>         units.insertBefore(Jimple.v().newInvokeStmt(
> 	  Jimple.v().newVirtualInvokeExpr(tmpRef, toCall, tmpLong)), s);
>     }
> 
>     private void addCounterIncrement(Chain units,
> 				     Stmt s,
> 				     SootField counter,
> 				     Local tmpLocal) {
> 	
> 	AssignStmt toAdd1 = Jimple.v().newAssignStmt(tmpLocal, 
> 	  Jimple.v().newStaticFieldRef(counter));
> 	AssignStmt toAdd2 = Jimple.v().newAssignStmt(tmpLocal,
> 	  Jimple.v().newAddExpr(tmpLocal, LongConstant.v(1L)));
> 	AssignStmt toAdd3 = Jimple.v().newAssignStmt(
>           Jimple.v().newStaticFieldRef(counter),tmpLocal);
> 
> 	// insert "tmpLocal = counter;"
> 	units.insertBefore(toAdd1, s);
>                         
> 	// insert "tmpLocal = tmpLocal + 1L;" 
> 	units.insertBefore(toAdd2, s);
> 
> 	// insert "counter = tmpLocal;" 
> 	units.insertBefore(toAdd3, s);
>     }
> 
>     private boolean addSilentStoreTest(Chain units, AssignStmt s,
> 				       SootField counter,
> 				       Local tmpLocal) {
> 	
> 	/* inserts a test to see if a store is silent before an AssignStmt,
> 	   and returns true if the insertion worked properly.
> 	*/
> 
> 	/* create a new ImmediateBox with a dummy value */
> 	ImmediateBox iBox = new ImmediateBox(LongConstant.v(1L));
> 
> 	/* now test that the left and right ops can fit in the box */
> 	if (iBox.canContainValue(s.getLeftOp()) && 
> 	    iBox.canContainValue(s.getRightOp())) {
> 	    
> 	    EqExpr cond1 = 
> 		Jimple.v().newEqExpr(s.getLeftOp(),NullConstant.v());
> 	    IfStmt toAdd1 = Jimple.v().newIfStmt(cond1,s);
> 	    // System.out.println(toAdd1);
> 
> 	    NeExpr cond2 = Jimple.v().newNeExpr(s.getLeftOp(),s.getRightOp());
> 	    IfStmt toAdd2 = Jimple.v().newIfStmt(cond2,s);
> 	
> 	    // insert "if (op1 == null) goto <AssignStmt>"
> 	    units.insertBefore(toAdd1, s);
> 
> 	    // insert "if (op1 != op2) goto <AssignStmt>"
> 	    units.insertBefore(toAdd2, s);
> 
> 	    // insert silentStoreCounter increment
> 	    addCounterIncrement(units, s, counter, tmpLocal);
> 	    
> 	    // fix-up goto targets
> 	    toAdd1.setTarget(s);
> 	    toAdd2.setTarget(s);
> 	    return true;
> 	}
> 	return false;
>     }
> 
>     protected void internalTransform(Body body, 
> 				     String phaseName,
> 				     Map options) {
> 	
> 	SootClass sClass = body.getMethod().getDeclaringClass();
>         SootField storeCounter = null;
>         SootField silentStoreCounter = null;
> 	boolean addedLocals = false;
>         Local tmpRef = null, tmpLong = null;
> 	Chain units = body.getUnits();
> 	Local tmpLocal = Jimple.v().newLocal("tmp", LongType.v());
> 	body.getLocals().add(tmpLocal);
> 	boolean isMainMethod = body.getMethod().getSubSignature().equals(
> 				 "void main(java.lang.String[])");
> 	Iterator stmtIt = units.snapshotIterator();
> 	boolean initializedCounters = false;
> 
> 	synchronized(this) {
> 	    
> 	    /* just check that there is a main method.
> 	    */
> 	    
> 	    if (!Scene.v().getMainClass().
> 		declaresMethod("void main(java.lang.String[])"))
> 		throw new RuntimeException(
> 		  "couldn't find main() in mainClass");
> 
> 	    /* now add two field to the main class for counting stores 
> 	       and silent stores at runtime.
> 	    */
> 	    System.out.println(addedFieldsToMainClassAndLoadedPrintStream);
> 	    if (addedFieldsToMainClassAndLoadedPrintStream) {
> 		storeCounter = Scene.v().getMainClass().
> 		  getFieldByName("storeCounter");
>                 silentStoreCounter = Scene.v().getMainClass().
> 		  getFieldByName("silentStoreCounter");
> 	    }
> 	    else {
> 		/* add storeCounter and silentStoreCounter fields */
> 		storeCounter = new SootField(
> 		  "storeCounter", LongType.v(), Modifier.STATIC);
> 		Scene.v().getMainClass().addField(storeCounter);
> 
> 		silentStoreCounter = new SootField(
>                   "silentStoreCounter", LongType.v(), Modifier.STATIC);
> 		Scene.v().getMainClass().addField(silentStoreCounter);
> 		
> 		/* just in case, resolve the PrintStream SootClass */
> 		Scene.v().loadClassAndSupport("java.io.PrintStream");
> 		javaIoPrintStream = Scene.v().
> 		    getSootClass("java.io.PrintStream");
> 		addedFieldsToMainClassAndLoadedPrintStream = true;
> 	    }
> 	}
> 	    /*
> 	      Scene.v().getJimpleStmtPrinter().printStatementsInBody(body,
> 	      new PrintWriter(System.out), true, true);
> 	    */
> 	    
> 	
>    	while(stmtIt.hasNext()) {
> 	    Stmt s = (Stmt) stmtIt.next();
> 	    //	    System.out.println(s);
> 	    if (isMainMethod && !initializedCounters) {
> 		units.insertBefore(Jimple.v().newAssignStmt(
> 	          Jimple.v().newStaticFieldRef(storeCounter),
> 	          LongConstant.v(0L)),s);
> 		units.insertBefore(Jimple.v().newAssignStmt(
> 		  Jimple.v().newStaticFieldRef(silentStoreCounter),
> 		  LongConstant.v(0L)),s);
> 		initializedCounters = true;
> 	    }
> 	    if (s instanceof AssignStmt) {
> 		addCounterIncrement(units, s, storeCounter, tmpLocal);
> 		addSilentStoreTest(units, (AssignStmt)s, silentStoreCounter,
> 				   tmpLocal);
> 	    }
> 	    if (s instanceof InvokeStmt) {
> 		InvokeExpr iexpr = 
> 		    (InvokeExpr)((InvokeStmt)s).getInvokeExpr();
> 		if (iexpr instanceof StaticInvokeExpr) {
> 		    SootMethod target = 
> 			((StaticInvokeExpr)iexpr).getMethod();
> 		    if (target.getSignature().equals(
> 		      "<java.lang.System: void exit(int)>")) {
> 			if (!addedLocals) {
> 			    tmpRef = addTmpRef(body); 
> 			    tmpLong = addTmpLong(body);
> 			    addedLocals = true;
> 			}
> 			addPrintCounter(units,
> 					s, storeCounter, tmpRef, tmpLong);
> 			addPrintCounter(units,
> 					s, silentStoreCounter, tmpRef, tmpLong);
> 		    }
> 		}
> 	    }
> 	    if (isMainMethod && (s instanceof ReturnStmt ||
> 				 s instanceof ReturnVoidStmt)) {
> 		if (!addedLocals) {
> 		    tmpRef = addTmpRef(body); 
> 		    tmpLong = addTmpLong(body);
> 		    addedLocals = true;
> 		}
> 		addPrintCounter(units, s, storeCounter, tmpRef, tmpLong);
> 		addPrintCounter(units, s, silentStoreCounter, tmpRef, tmpLong);
> 	    }
> 	}
> 	  
>         /*
> 	Scene.v().getJimpleStmtPrinter().printStatementsInBody(body,
> 	  new PrintWriter(System.out), true, true);
> 	*/
>     }
> }
> 
> 	
> 
> 
> 
> 	
>