⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 manual.txt

📁 一个用于对.class文件进行插桩的开源工具
💻 TXT
📖 第 1 页 / 共 4 页
字号:
trated by (incomplete) figure 9 in the appendix. The most important family of instructions arethe branch instructions, e.g. goto, that branch to targets somewhere within the byte code. Ob-viously, this makes them candidates for playing an InstructionTargeter role, too. Instruc-tions are further grouped by the interfaces they implement, there are, e.g., TypedInstruc-tions that are associated with a specific type like ldc, or ExceptionThrower instructionsthat may raise exceptions when executed.    All instructions can be traversed via accept(Visitor  v) methods, i.e., the Visitor designpattern.  There is however some special trick in these methods that allows to merge the han-dling of certain instruction groups. The accept() do not only call the corresponding visit()method, but call visit() methods of their respective super classes and implemented inter-faces first, i.e. the most specific visit() call is last. Thus one can group the handling of, say,all BranchInstructions into one single method.    For debugging purposes it may even make sense to "invent" your own instructions.  In asophisticated code generator like the one used as a backend of the Barat framework [BS98 ] oneoften has to insert temporary nop (No operation) instructions. When examining the producedcode it may be very difficult to track back where the nop was actually inserted.  One couldthink of a derived nop2 instruction that contains additional debugging information. When theinstruction list is dumped to byte code, the extra data is simply dropped.    One could also think of new byte code instructions operating on complex numbers that arereplaced by normal byte code upon load-time or are recognized by a new JVM.3.3.4   Instruction listsAn instruction list is implemented by a list of instruction handles encapsulating instruction objec* *ts.References to instructions in the list are thus not implemented by direct pointers to instructionsbut by pointers to instruction handles.  This makes appending, inserting and deleting areas ofcode very simple. Since we use symbolic references, computation of concrete byte code offsetsdoes not need to occur until finalization, i.e. until the user has finished the process of generati* *ngor transforming code. We will use the term instruction handle and instruction synonymouslythroughout the rest of the paper. Instruction handles may contain additional user-defined datausing the addAttribute() method.Appending.   One can append instructions or other instruction lists anywhere to an existinglist.  The instructions are appended after the given instruction handle.  All append methodsreturn a new instruction handle which may then be used as the target of a branch instruction,e.g..   InstructionList  il  =  new  InstructionList();   ...   GOTO  g  =  new  GOTO(null);   il.append(g);   ...   InstructionHandle  ih  =  il.append(InstructionConstants.ACONST`NULL);                                                 15   g.setTarget(ih);Inserting.   Instructions may be inserted anywhere into an existing list. They are inserted be-fore the given instruction handle. All insert methods return a new instruction handle which maythen be used as the start address of an exception handler, for example.   InstructionHandle  start  =  il.insert(insertion`point,                                                      InstructionConstants.NOP);   ...   mg.addExceptionHandler(start,  end,  handler,  "java.io.IOException");Deleting.   Deletion of instructions is also very straightforward; all instruction handles andthe contained instructions within a given range are removed from the instruction list and dis-posed. The delete() method may however throw a TargetLostException when there areinstruction targeters still referencing one of the deleted instructions. The user is forced to hand* *lesuch exceptions in a try-catch block and redirect these references elsewhere.  The peep holeoptimizer described in section A.3 gives a detailed example for this.   try  -      il.delete(first,  last);   "  catch(TargetLostException  e)  -      InstructionHandle[]  targets  =  e.getTargets();      for(int  i=0;  i  <  targets.length;  i++)  -         InstructionTargeter[]  targeters  =  targets[i].getTargeters();         for(int  j=0;  j  <  targeters.length;  j++)             targeters[j].updateTarget(targets[i],  new`target);      "   "Finalizing.   When the instruction list is ready to be dumped to pure byte code, all symbolicreferences must be mapped to real byte code offsets.  This is done by the getByteCode()method which is called by default by MethodGen.getMethod(). Afterwards you should calldispose() so that the instruction handles can be reused internally. This helps to reduce mem-ory usage.   InstructionList  il  =  new  InstructionList();   ClassGen   cg  =  new  ClassGen("HelloWorld",  "java.lang.Object",                                           "<generated>",  ACC`PUBLIC  --  ACC`SUPER,                                           null);   MethodGen  mg  =  new  MethodGen(ACC`STATIC  --  ACC`PUBLIC,                                            Type.VOID,  new  Type[]  -                                               new  ArrayType(Type.STRING,  1)                                            ",  new  String[]  -  "argv"  ",                                            "main",  "HelloWorld",  il,  cp);                                                 16   ...   cg.addMethod(mg.getMethod());   il.dispose();  //  Reuse  instruction  handles  of  list3.3.5   Code example revisitedUsing instruction lists gives us a generic view upon the code: In Figure 5 we again present thecode chunk of the readInt() method of the faculty example in section 2.6: The local variablesn and e1 both hold two references to instructions, defining their scope.  There are two gotosbranching to the iload at the end of the method. One of the exception handlers is displayed,too: it references the start and the end of the try block and also the exception handler code.                      Figure 5: Instruction list for readInt() method3.3.6   Instruction factoriesTo simplify the creation of certain instructions the user can use the supplied InstructionFac-tory class which offers a lot of useful methods to create instructions from scratch. Alternatively,he can also use compound instructions: When producing byte code, some patterns typically occurvery frequently, for instance the compilation of arithmetic or comparison expressions. You cer-tainly do not want to rewrite the code that translates such expressions into byte code in every                                                 17place they may appear. In order to support this, the BCEL API includes a compound instruction(an interface with a single getInstructionList() method). Instances of this class may beused in any place where normal instructions would occur, particularly in append operations.Example: Pushing constants.   Pushing constants onto the operand stack may be coded indifferent ways. As explained in section 2.2 there are some "short-cut" instructions that can beused to make the produced byte code more compact. The smallest instruction to push a single 1onto the stack is iconst_1, other possibilities are bipush (can be used to push values between-128 and 127), sipush (between -32768 and 32767), or ldc (load constant from constant pool).    Instead of repeatedly selecting the most compact instruction in, say, a switch, one can usethe compound PUSH instruction whenever pushing a constant number or string. It will producethe appropriate byte code instruction and insert entries into to constant pool if necessary.   il.append(new  PUSH(cp,  "Hello,  world"));   il.append(new  PUSH(cp,  4711));3.3.7   Code patterns using regular expressionsWhen transforming code, for instance during optimization or when inserting analysis methodcalls, one typically searches for certain patterns of code to perform the transformation at.  Tosimplify handling such situations BCEL introduces a special feature: One can search for givencode patterns within an instruction list using regular expressions. In such expressions, instructio* *nsare represented by symbolic names, e.g. "`IfInstruction'". Meta characters like +, *, and(..--..) have their usual meanings. Thus, the expression   "`NOP'+(`ILOAD``'--`ALOAD``')*"    represents a piece of code consisting of at least one NOP followed by a possibly empty se-quence of ILOAD and ALOAD instructions.    The search() method of class FindPattern gets an instruction list and a regular expres-sion as arguments and returns an array describing the area of matched instructions. Additionalconstraints to the matching area of instructions, which can not be implemented via regular ex-pressions, may be expressed via code constraints.3.3.8   Example: Optimizing boolean expressions.In Java, boolean values are mapped to 1 and to 0, respectively. Thus, the simplest way to evaluateboolean expressions is to push a 1 or a 0 onto the operand stack depending on the truth value ofthe expression. But this way, the subsequent combination of boolean expressions (with &&, e.g)yields long chunks of code that push lots of 1s and 0s onto the stack.    When the code has been finalized these chunks can be optimized with a peep hole algorithm:An IfInstruction (e.g. the comparison of two integers: if_icmpeq) that either produces a1 or a 0 on the stack and is followed by an ifne instruction (branch if stack value 6= 0) maybe replaced by the IfInstruction with its branch target replaced by the target of the ifneinstruction:                                                 18   InstructionList  il  =  new  InstructionList();   ...   CodeConstraint  constraint  =  new  CodeConstraint()  -      public  boolean  checkCode(InstructionHandle[]  match)  -         IfInstruction  if1  =  (IfInstruction)match[0].getInstruction();         GOTO               g     =  (GOTO)match[2].getInstruction();         return  (if1.getTarget()  ==  match[3])  &&                   (g.getTarget()  ==  match[4]);      "   ";   FindPattern  f     =  new  FindPattern(il);   String         pat  =  "`IfInstruction'`ICONST`0'`GOTO'`ICONST`1'"  +                             "`NOP'(`IFEQ'--`IFNE')";   InstructionHandle[]  match;   for(InstructionHandle  ih  =  f.search(pat,  constraint);         ih  !=  null;  ih  =  f.search(pat,  match[0],  constraint))  -      match  =  f.getMatch();  //  Constraint  already  checked      ...      match[0].setTarget(match[5].getTarget());  //  Update  target      ...      try  -         il.delete(match[1],  match[5]);      "  catch(TargetLostException  e)  -  ...  "   "    The applied code constraint object ensures that the matched code really corresponds to thetargeted expression pattern. Subsequent application of this algorithm removes all unnecessarystack operations and branch instructions from the byte code. If any of the deleted instructionsis still referenced by an InstructionTargeter object, the reference has to be updated in thecatch-clause.    Code example A.1 gives a verbose example of how to create a class file, while example A.3shows how to implement a simple peephole optimizer and how to deal with TargetLost ex-ceptions.Example application:   The expression   if((a  ==  null)  ----  (i  <  2))      System.out.println("Ooops");    can be mapped to both of the chunks of byte code shown in figure 3.3.8.  The left columnrepresents the unoptimized code while the right column displays the same code after an aggres-sively optimizing peep hole algorithm has been applied:                                                 195:   aload`0                                      10:  aload`06:   ifnull            #13                        11:  ifnull            #199:   iconst`0                                     14:  iload`110:  goto               #14                       15:  iconst`213:  iconst`1                                     16:  if`icmpge       #2714:  nop                                          19:  getstatic       System.out15:  ifne               #36                       22:  ldc                "Ooops"18:  iload`1                                      24:  invokevirtual  println19:  iconst`2                                     27:  return20:  if`icmplt       #2723:  iconst`024:  goto               #2827:  iconst`128:  nop29:  ifne               #3632:  iconst`033:  goto               #3736:  iconst`137:  nop38:  ifeq               #5241:  getstatic       System.out44:  ldc                "Ooops"46:  invokevirtual  println52:  return                          Figure 6: Optimizing boolean expressions                                                 204     Application areasThere are many possible application areas for BCEL ranging from class browsers, profilers, bytecode optimizers, and compilers to sophisticated run-time analysis tools and extensions to theJava language [AFM97   , MBL97  ].    Compilers like the Barat compiler [BS98 ] use BCEL to implement a byte code generatingback end. Other possible application areas are the static analysis of byte code [TK98  ] or examin-ing the run-time behavior of classes by inserting calls to profiling methods into the code. Furtherexamples are extending Java with Eiffel-like assertions [FM98  ], automated delegation [Cos98  ],or with the concepts of "Aspect-Oriented Programming" [KLM+ 97   ].4.1    Class loadersClass loaders are responsible for loading class files from the file system or other resources andpassing the byte code to the Virtual Machine [LB98  ].  A custom ClassLoader object may beused to intercept the standard procedure of loading a class, i.e.  the system class loader, andperform some transformations before actually passing the byte code to the JVM.    A possible scenario is described in figure 7: During run-time the Virtual Machine requestsa custom class loader to load a given class. But before the JVM actually sees the byte code, theclass loader makes a "side-step" and performs some transformation to the class. To make surethat the modified byte code is still valid and does not violate any of the JVM's rules it is checkedby the verifier before the JVM finally executes it.                                     Figure 7: Class loaders    Using class loaders is an elegant way of extending the Java Virtual Machine with new fea-tures without actually modifying it. This concept enables developers to use load-time reflection toimplement their ideas as opposed to the static reflection supported by the Java Reflection API[Jav98 ]. Load-time transformations supply the user with a new level of abstraction. He is notstrictly tied to the static constraints of the original authors of the classes but may customize theapplications with third-party code in order to benefit from new features. Such transformationsmay be executed on demand and neither interfere with other users, nor alter the original bytecode. In fact, class loaders may even create classes ad hoc without loading a file at all.                                                 214.1.1   Example: Poor Man's GenericityThe "Poor Man's Genericity" project [BD98  ] that extends Java with parameterized classes, for ex-ample, uses BCEL in two places to generate instances of parameterized classes: During compile-time (the standard javac with some slightly changed classes) and at run-time using a cus-tom class loader. The compiler puts some additional type information into class files which isevaluated at load-time by the class loader. The class loader performs some transformations onthe loaded class and passes them to the VM. The following algorithm illustrates how the loadmethod of the class loader fulfills the request for a parameterized class, e.g. Stack<String>    1. Search for class Stack, load it, and check for a certain class attribute containing addition* *al       type information. I.e. the attribute defines the "real" name of the class, i.e. Stack<A>.    2. Replace all occurrences and references to the formal type A with references to the actual       type String. For example the method         void  push(A  obj)  -  ...  "       becomes         void  push(String  obj)  -  ...  "    3. Return the resulting class to the Virtual Machine.References[AFM97]     O. Agesen, S. N. Freund, and J. C. Mitchell.  Adding Type Parameterization to the            Java Language. In Proceedings OOPSLA'97, Atlanta, GA, 1997.[AP98]      D. Antonioli and M. Pilz. Statistische Analyse von Java-Classfiles. In Clemens Cap,            editor, Proceedings JIT'98. Springer, 1998.[BD98]      B. Bokowski and M. Dahm. Poor Man's Genericity for Java. In Clemens Cap, editor,            Proceedings JIT'98. Springer, 1998.[BS98]      B. Bokowski and A. Spiegel.  Barat - A Front-End for Java.  Technical report, Freie            Universit"at Berlin, 1998.[CCK98]     Geoff Cohen, Jeff Chase, and David Kaminsky. Automatic Program Transformation            with JOIE. In Proceedings USENIX Annual Technical Symposium, 1998.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -