2013-10-01 24 views
4

對於我的論文研究,我需要在測試套件中爲可定義的方法注入一段代碼,但我沒有源代碼(本例中爲DaCapo基準測試套件,http://dacapobench.org/) 。我的論文的這一部分基於以前的研究,爲此使用了字節碼注入,這也導致我也這樣做。使用BCEL在現有方法中注入代碼

我用Apache的BCEL庫(http://commons.apache.org/proper/commons-bcel/)構建了一個小程序,使我能夠在其他語句之前在方法體中注入斐波那契算法。

現在,我已經做了這個,但它不能正常工作。我注入的一些方法工作正常(因爲它們由於斐波那契代碼而變慢),並且運行DaCapo框架效果很好,而其他注入方法破壞代碼。

問題是,我不知道爲什麼,即使我知道哪些方法崩潰,哪些方法成功,我無法在破碎的方法中找到重複出現的模式。

  • 字節碼似乎很好,目前爲止我可以看到,但我遠離專家。 當我比較注入之前和之後的字節碼時,我看到斐波那契算法後面跟着剩下的方法,只增加堆棧位置(因爲注入的代碼也使用堆棧空間)。
  • 成功的方法包含公有以及私有方法。有和沒有參數。
  • 一些失敗的方法包含異常,其他方法則不包含異常。有些人嘗試捕獲它們,而另一些則沒有。等等等等

我可以在一些失敗的方法粘貼,但是這將讓這個帖子甚至更長的時間比它已經是了。 所以我想知道,有沒有我沒有想到或忽略的東西?

下面你會發現一個java文件的例子,它的結果和我寫的BCEL程序。

一個簡單的例子,我有一個名爲DemoClass.java一個java文件:

public class DemoClass { 

    public static void main(String[] argv) { 
     System.out.println("Demo body"); 
     test(); 
    } 

    public static void test() { 
     System.out.println("Demo test"); 
    } 
} 

在我的外殼調用下面的java命令後:

javac DemoClass.java; java -cp bcel-5.2.jar:. InjectCodeBCEL DemoClass test 123456789 ; java DemoClass

(BCEL的-5.2。 jar文件可以在前面提到的apache網站上找到)

該程序看起來像這樣:

public class DemoClass { 

    public static void main(String[] argv) { 
     System.out.println("Demo body"); 
     test(); 
    } 

    public static void test() { 
     int[] fibNumbers = new int[100]; 
     for (int i = 0; i < 123456789; i++) { 
      int j = i % 100; 
      if (i == 0) { 
       fibNumbers[i] = 0; 
      } 
      else if (i == 1) { 
       fibNumbers[i] = 1; 
      } 
      else { 
       int k = (i - 1) % 100; 
       int m = (i - 2) % 100; 
       int n = fibNumbers[k] + fibNumbers[m]; 
       fibNumbers[j] = n; 
      } 
     } 
     System.out.println("Demo test"); 
    } 
} 

這是InjectCodeBCEL.java的代碼:

import java.io.IOException; 
import org.apache.bcel.classfile.*; 
import org.apache.bcel.generic.*; 
import org.apache.bcel.*; 

public class InjectCodeBCEL { 

    static public void main(String args[]) { 
     //Get class to modify from program argument 
     JavaClass mod = null; 
     String methodName = (args.length >= 2) ? args[1] : ""; 
     int loopsize = (args.length >= 3) ? Integer.parseInt(args[2]) : 1; 
     try { 
      mod = Repository.lookupClass(args[0]); 
     } 
     catch (Exception e) { 
      System.err.println("Could not get class " + args[0]); 
      return; 
     } 

     //Create a generic class to modify 
     ClassGen modClass = new ClassGen(mod); 
     //Create a generic constantpool to modify 
     ConstantPoolGen cp = modClass.getConstantPool(); 
     boolean methodEdited = false; 

     Method[] methods = mod.getMethods(); 
     for (int i = 0; i < methods.length; i++) { 
      if (methods[i].getName().equals(methodName)) { 

       System.out.println("Method: " + methods[i]); 
       // System.out.println("before:\n" + methods[i].getCode()); 
       modClass.removeMethod(methods[i]); 
       Method newMethod = insertCodeInMethod(mod, methods[i], cp, loopsize); 
       // System.out.println("after:\n" + newMethod.getCode()); 
       modClass.addMethod(newMethod); 

       methodEdited = true; 
      } 
     } 
     if (methodEdited) { 
      modClass.update(); 
      try { 
       //Write modified class 
       JavaClass newClass = modClass.getJavaClass(); 
       String classname = args[0].replace(".","/"); 
       newClass.dump(classname + ".class"); 
       System.out.println("Class " + classname + " modified"); 
      } 
      catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    public static Method insertCodeInMethod(JavaClass mod, Method method, ConstantPoolGen cp, int loopsize) { 
     MethodGen mg = new MethodGen(method, mod.getClassName(), cp); 

     InstructionList il = mg.getInstructionList(); 
     InstructionHandle ihs = il.getStart(); 
     InstructionList ils = new InstructionList(); 
     InstructionFactory f = new InstructionFactory(cp); 

     String CLASS_NAME = mod.getClassName(); 

     int ARRAY_SIZE = 100; 
     int LOOP_SIZE = loopsize; 

     int INCREASE_ID = mg.isStatic() ? 0 : 1; // if not static, this has position 0 on the stack 
     Type[] types = mg.getArgumentTypes(); 
     // Increase the stack location(?) so they don't collide with the methods parameters. 
     for (int i = 0; i < types.length; i++) { 
      INCREASE_ID += types[i].getSize(); 
     } 

     int VAR_ARRAY = 0 + INCREASE_ID; 
     int VAR_I = 1 + INCREASE_ID; 
     int VAR_II = 2 + INCREASE_ID; 
     int VAR_I_MIN_1 = 3 + INCREASE_ID; 
     int VAR_I_MIN_2 = 4 + INCREASE_ID; 
     int VAR_SUM = 5 + INCREASE_ID; 
     int VAR_JUMPTO = 6 + INCREASE_ID; 

     // init array 
     ils.append(new PUSH(cp, ARRAY_SIZE)); 
     ils.append(new NEWARRAY(Type.INT)); 
     ils.append(new ASTORE(VAR_ARRAY)); 

     // create iterator = 0 for while 
     ils.append(new PUSH(cp, 0)); 
     ils.append(new ISTORE(VAR_I)); 

     // Main while loop: 
     InstructionHandle beforeWhile = ils.append(new ILOAD(VAR_I)); 
     ils.append(new PUSH(cp, LOOP_SIZE)); 
     // While condition: 
     BranchHandle whileCondition = ils.append(new IF_ICMPLT(null)); // if (VAR_I < LOOP_SIZE): jump to "whileBody" 
     BranchHandle whileConditionFalseGoto = ils.append(new GOTO(null)); // if not: jump to "afterWhile" 

      // While body: 
      InstructionHandle whileBody = ils.append(new ILOAD(VAR_I)); 
      ils.append(new PUSH(cp, ARRAY_SIZE)); 
      ils.append(new IREM()); 
      ils.append(new ISTORE(VAR_II)); // create int ii = i % ARRAY_SIZE; 

      // if (i == 0) 
      ils.append(new ILOAD(VAR_I)); 
      ils.append(new PUSH(cp, 0)); 
      BranchHandle ifIteratorIs0 = ils.append(new IF_ICMPEQ(null)); 
      BranchHandle ifIteratorIs0FalseGoto = ils.append(new GOTO(null)); 
       // If true body 
       InstructionHandle ifIteratorIs0Body = ils.append(new ALOAD(VAR_ARRAY)); 
       ils.append(new ILOAD(VAR_I)); 
       ils.append(new PUSH(cp, 0)); 
       ils.append(new IASTORE()); 
       BranchHandle ifIteratorIs0Done = ils.append(new GOTO(null)); 

      // "else" if (i != 1) 
      InstructionHandle beginIfIteratorIsNot1 = ils.append(new ILOAD(VAR_I)); 
      ils.append(new PUSH(cp, 1)); 
      BranchHandle ifIteratorIsNot1 = ils.append(new IF_ICMPNE(null)); 
       // false: else: so in this case: if (!(i != 1)): 
       ils.append(new ALOAD(VAR_ARRAY)); 
       ils.append(new ILOAD(VAR_I)); 
       ils.append(new PUSH(cp, 1)); 
       ils.append(new IASTORE()); 
       // done, go to i++; 
       BranchHandle ifIteratorIsNot1FalseGoto = ils.append(new GOTO(null)); 

       // If true body (so if i != 1).. 
       // create variable VAR_I_MIN_1 for array index (i-1) 
       InstructionHandle ifIteratorIsNot1Body = ils.append(new ILOAD(VAR_I)); 
       ils.append(new PUSH(cp, 1)); 
       ils.append(new ISUB()); 
       ils.append(new PUSH(cp, ARRAY_SIZE)); 
       ils.append(new IREM()); 
       ils.append(new ISTORE(VAR_I_MIN_1)); // create int i_min_1 = (i - 1) % ARRAY_SIZE; 
       // create variable VAR_I_MIN_2 for array index (i-2) 
       ils.append(new ILOAD(VAR_I)); 
       ils.append(new PUSH(cp, 2)); 
       ils.append(new ISUB()); 
       ils.append(new PUSH(cp, ARRAY_SIZE)); 
       ils.append(new IREM()); 
       ils.append(new ISTORE(VAR_I_MIN_2)); // create int i_min_2 = (i - 2) % ARRAY_SIZE; 
       // load the array values: 
       ils.append(new ALOAD(VAR_ARRAY)); 
       ils.append(new ILOAD(VAR_I_MIN_1)); 
       ils.append(new IALOAD()); 
       ils.append(new ALOAD(VAR_ARRAY)); 
       ils.append(new ILOAD(VAR_I_MIN_2)); 
       ils.append(new IALOAD()); 
       // add the two values, and save them 
       ils.append(new IADD()); 
       ils.append(new ISTORE(VAR_SUM)); 
       // add the new calculated number to the array 
       ils.append(new ALOAD(VAR_ARRAY)); 
       ils.append(new ILOAD(VAR_II)); 
       ils.append(new ILOAD(VAR_SUM)); 
       ils.append(new IASTORE()); 
       // Done; go to i++; 
       BranchHandle ifIteratorIsNot1Done = ils.append(new GOTO(null)); 

      // Increment i with 1 
      InstructionHandle generalIfDoneGoto = ils.append(new IINC(VAR_I,1)); 

      // Goto that whil restart this loop: 
      BranchHandle whileGotoBegin = ils.append(new GOTO(null)); // jumps to "beforeWhile" 

     // We need something to jump to when done with the outer loop. 
     InstructionHandle afterWhile = ils.append(new PUSH(cp, 0)); 
     ils.append(new ISTORE(VAR_JUMPTO)); 

     // While targets: 
     whileCondition.setTarget(whileBody); 
     whileConditionFalseGoto.setTarget(afterWhile); 
     whileGotoBegin.setTarget(beforeWhile); 
     // if (i == 0) 
     ifIteratorIs0.setTarget(ifIteratorIs0Body); 
     ifIteratorIs0FalseGoto.setTarget(beginIfIteratorIsNot1); 
     ifIteratorIs0Done.setTarget(generalIfDoneGoto); 
     // if (i == 1) 
     ifIteratorIsNot1.setTarget(ifIteratorIsNot1Body); 
     ifIteratorIsNot1FalseGoto.setTarget(generalIfDoneGoto); 
     ifIteratorIsNot1Done.setTarget(generalIfDoneGoto); 

     InstructionHandle ihss = il.insert(ihs,ils); 
     il.redirectBranches(ihs, ihss); 
     il.update(); 

     mg.setMaxStack(); 
     mg.setMaxLocals(); 
     mg.update(); 
     return mg.getMethod(); 
    } 
} 

更新

下面你可以visitAll方法的net.sourceforge.pmd失效噴射後看到完整的錯誤。AbstractRuleChainVisitor

===== DaCapo 9.12 pmd starting ===== 
java.lang.reflect.InvocationTargetException 
java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at org.dacapo.harness.Pmd.iterate(Pmd.java:58) 
    at org.dacapo.harness.Benchmark.run(Benchmark.java:166) 
    at org.dacapo.harness.TestHarness.runBenchmark(TestHarness.java:218) 
    at org.dacapo.harness.TestHarness.main(TestHarness.java:171) 
    at Harness.main(Harness.java:17) 
Caused by: java.lang.ClassFormatError: LVTT entry for 'nodes' in class file net/sourceforge/pmd/AbstractRuleChainVisitor does not match any LVT entry 
    at java.lang.ClassLoader.defineClass1(Native Method) 
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631) 
    at java.lang.ClassLoader.defineClass(ClassLoader.java:615) 
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) 
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) 
    at java.net.URLClassLoader.access$000(URLClassLoader.java:58) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:197) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190) 
    at org.dacapo.harness.DacapoClassLoader.loadClass(DacapoClassLoader.java:124) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247) 
    at java.lang.ClassLoader.defineClass1(Native Method) 
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631) 
    at java.lang.ClassLoader.defineClass(ClassLoader.java:615) 
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) 
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) 
    at java.net.URLClassLoader.access$000(URLClassLoader.java:58) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:197) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190) 
    at org.dacapo.harness.DacapoClassLoader.loadClass(DacapoClassLoader.java:124) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247) 
    at net.sourceforge.pmd.RuleSets.<init>(RuleSets.java:27) 
    at net.sourceforge.pmd.RuleSetFactory.createRuleSets(RuleSetFactory.java:82) 
    at net.sourceforge.pmd.RuleSetFactory.createRuleSets(RuleSetFactory.java:70) 
    at net.sourceforge.pmd.PMD.doPMD(PMD.java:359) 
    at net.sourceforge.pmd.PMD.main(PMD.java:415) 
    ... 9 more 

該方法的代碼(由JD-GUI生成):

public void visitAll(List<CompilationUnit> astCompilationUnits, RuleContext ctx) 
    { 
    initialize(); 
    clear(); 

    long start = System.nanoTime(); 
    indexNodes(astCompilationUnits, ctx); 
    long end = System.nanoTime(); 
    Benchmark.mark(8, end - start, 1L); 

    for (RuleSet ruleSet : this.ruleSetRules.keySet()) 
     if (ruleSet.applies(ctx.getSourceCodeFile())) 
     { 
     visits = 0; 
     start = System.nanoTime(); 
     for (Rule rule : (List)this.ruleSetRules.get(ruleSet)) { 
      List nodeNames = rule.getRuleChainVisits(); 
      for (int j = 0; j < nodeNames.size(); j++) { 
      List nodes = (List)this.nodeNameToNodes.get(nodeNames.get(j)); 
      for (SimpleNode node : nodes) 
      { 
       while ((rule instanceof RuleReference)) { 
       rule = ((RuleReference)rule).getRule(); 
       } 
       visit(rule, node, ctx); 
      } 
      visits += nodes.size(); 
      } 
      end = System.nanoTime(); 
      Benchmark.mark(1, rule.getName(), end - start, visits); 
      start = end; 
     } 
     } 
    int visits; 
    } 

這是一個相當的誤差作爲一個當我的代碼錯過了堆疊位置在「insertCodeInMethod增大部我」。這導致了參數,當不是靜態時,這與斐波那契碼內的已定義變量相沖突。

+0

那麼你得到什麼錯誤?我們沒有太多的辦法可以處理「它不起作用」 – Antimony

+0

你有沒有考慮過使用[AspectJ](http://eclipse.org/aspectj/)?這可能更容易使用注入代碼。 – Katona

+0

如果你把你的代碼放到方法的末尾,這可能會用返回語句以及最後的塊來推斷。另外,解釋什麼是失敗或失敗意味着什麼是明智的。例外? - > Stacktrace! –

回答

3

LVTT代表LocalVariableTypeTableLVT代表LocalVariableTable。這些調試信息必須在更改方法時進行調整或刪除。順便說一句,這不是一個成功地將大量代碼注入方法的好方法,因爲它會創建各種錯誤來源,這意味着重新發明輪子(構建編譯器)並導致代碼重複轉化類。大多數使用代碼注入/類文件轉換的應用程序只會注入預編譯方法的小調用。

+0

非常感謝@Holger! 刪除LocalVariableTypeTable修復了它! 只需在注入結束時調用MethodGen的'removeLocalVariables()'就可以工作! – Peter