Class dec.trek.CallThe Call class is used to insert instrumentation calls into a classfile. Method Summary
OverviewHow to insert a callThe steps to insert a call into a classfile are:
Other considerations:
Position of the inserted codeThe inserted code becomes part of the statement it is associated with. This point becomes significant when break, continue, end-loop, and other jumping statements are considered. That is, a jump to a statement with inserted code will execute the inserted code. Conversely when a call is inserted using addAfter(stmt), a jump to the statement following stmt will not execute the inserted code. If multiple calls have the same insert point, the inserted code will be ordered from lowest to highest AddXxx priority and within a priority in the order the AddXxxs were done. The AddXxx priorities, in increasing order, are addBefore(Trek), addBefore(ClassFile), addBefore(Method), all other Adds, addAfter(return/throw statement), addAfter(Method), addAfter(ClassFile), addAfter(Trek). Thus code inserted by addBefore(Trek) is guaranteed to precede all other code inserted at the same point, and code inserted by addAfter(Trek) is guaranteed to follow all other code inserted at the same point. Specifying valuesWhen doing an if-test or passing an argument, you can specify:
The cases where an insert point has a value are:
An insert point has an object if it is at a getfield, putfield, invokevirtual, invokespecial, or invokevirtual instruction. Defining methods in your instrumentation classEach such method must conform to the following. The data type of each of its arguments must be defined according to the following rules.
The method being called can contain any code you wish to write. However if you are instrumenting a package that this method itself calls, you must guard against infinite recursion. For example, if you instrumented com.me.PrintUtil to call MyLogger, then MyLogger cannot blithely call PrintUtil.println. One way to deal with this is to define a flag, say active, that MyLogger would check and set on entry, and clear on exit. So if MyLogger was called and active was already set, MyLogger would return immediately. If you subclass RunLog, you can use the active field inherited from RunLog. Inserting calls at the start and end of an programA "simple" program has a well-defined start and end point -- the start and end of its main method. If you need to insert code at these points, you can use addBefore(Trek) and AddAfter(Trek). To get approximately the same effect with applets, you would need to insert code at the start of the applet's start method and at the end of the applet's stop method. To get approximately the same effect with servlets, you would need to insert code at the start and end of the servlet's DoPost and DoGet methods. In a GUI program, the main thread often terminates before the overall program is done. So for the code you inserted at the end of main to have the desired effect, you would need to cause the main thread to not terminate until your program has finished its work. For example, you could call wait() (in a try block with an empty catch) at the end of your main method, and you could call mainthread.interrupt() when the user closes your program. Real programs often have multiple end points because they make calls to System.exit (or runtime.exit). Thus to instrument the termination of such programs, you must also insert your desired end point code at each such call. Fieldspublic static boolean systemMethodsSafe
Methodspublic static Call addAfter(String mname, Trek trek) Inserts a call at the end of the main classs main method. The main class is the first class on the applications command line or passed to the SetClassList method. Returns a Call object so you can invoke doIfXxx, passXxx, and done to define the tests and arguments that you want for this call. However if the main class has no main method, then null is returned and the call is not inserted. If main contains multiple return/throw statements, the call is inserted after all of them. Parameters: mname the qualified-class-name.method-name of the method to call (eg. mypkg.Logger.printValue). The identified method must be a static method. trek the call is placed at the end of the main method of the trek identified by trek public static Call addAfter(String mname, ClassFile cf) Inserts a call at the end of the specified classs finalize() method. If the class has no such method, null is returned instead. Returns a Call object so you can invoke doIfXxx, passXxx, and done to define the tests and arguments that you want for this call. However if cf identifies a class that has no constructor code like an abstract class, then null is returned and the call is not inserted. Parameters: mname the qualified-class-name.method-name of the method to call. The identified method must be a static method. cf the call is placed at the end of the classfile identified by cf. public static Call addAfter(String mname, Method meth) Inserts a call at the end of meth. Also returns a Call object so you can invoke doIfXxx, passXxx, and done to define the tests and arguments that you want for this call. However if meth identifies a method that contains no code like an abstract method, then null is returned and the call is not inserted. If meth contains multiple return/throw statements, the call is inserted at all of them. Parameters: mname the qualified-class-name.method-name of the method to call. The identified method must be a static method. meth the call is placed at the end of the method identified by meth. public static Call addAfter(String mname, Statement stmt) Inserts a call after stmt. However if the specified statement is a block-starting statement (eg. for, else, try), the call is actually inserted before the first statement within the block. The specified statement may not be a "jumping" statement, with these exceptions. If it identifies a switch statement, the call is inserted after the key value has been loaded on the stack. If it identifies a throw statement, the call is inserted after the throwable has been loaded on the stack. If it identifies a return statement that has a return value, the call is inserted after the return value has been loaded on the stack. If it identifies a return statement that does not have a return value, the call is inserted before the return statement. Returns a Call object so you can invoke doIfXxx, passXxx, and done to define the tests and arguments that you want for this call. However if the call cannot be inserted, null is returned instead. If you do call.pass() and the statement is a switch statement, throw statement, or a return statement with a return value, the switch key, throwable, or return value is what will be passed to mname. Parameters: mname the qualified-class-name.method-name of the method to call. The identified method must be a static method. stmt the call is placed after the statement identified by stmt. public static Call addAfter(String mname, Statement stmt, boolean truepath) Inserts a call after the specified if statement. Also returns a Call object so you can invoke doIfXxx, passXxx, and done to define the tests and arguments that you want for this call. However if the requested path does not exist, null is returned instead. A Then path will exist unless the if statement's if instruction itself does a break or continue. An Else path will exist if the source code contained an else and the compiler did not optimize it away. Parameters: mname the qualified-class-name.method-name of the method to call. The identified method must be a static method. stmt the call is placed after the statement identified by stmt, which must be an if statement. truepath if true is passed, the call is inserted such that it will be executed only when the if statement evaluates to true. If false is passed, the call is inserted such that it will be executed only when the if statement evaluates to false. Example: When the following statements are scanned: if (aa > 0) . aa++; else aa--; The following atStartOf(Statement stmt) method would insert a call to watch.ifTrue before the aa++ and a call to watch.ifFalse before the aa--. if (!stmt.getType() != ST_IF) return; Call call = Call.addAfter("watch.ifTrue", stmt, true); call.done(); call = Call.addAfter("watch.ifFalse", stmt, false); call.done(); public static Call addAt(String mname, Instruction inst) Inserts a call immediately before the specified instruction. Returns a Call object so you can invoke doIfXxx, passXxx, and done to define the tests and arguments that you want for this call. (In particular, if you do call.passStack(), the instructions stack arguments will be passed to mname). However if the call cannot be inserted, null is returned instead. Parameters: mname the qualified-class-name.method-name of the method to call. The identified method must be a static method. inst the instruction at which to insert code Example: When the following statement is scanned: obj.end = temp + obj.size; and inst is the Store instruction for setting the new value of obj.end, the following at(Instruction inst) method would insert a call to watch.printValue, passing it this new value. Call call = Call.addAt("watch.printValue", inst); call.pass(); call.done(); public static Call addBefore(String mname, Trek trek) Inserts a call at the start of the main classs main method. The main class is the first class on the applications command line or passed to the SetClassList method. Returns a Call object so you can invoke doIfXxx, passXxx, and done to define the tests and arguments that you want for this call. However if the main class has no main method, then null is returned and the call is not inserted. Parameters: mname the qualified-class-name.method-name of the method to call. The identified method must be a static method. trek the call is placed at the start of the main method of the trek identified by trek public static Call addBefore(String mname, ClassFile cf) Inserts a call at the start of the specified classs constructors. Returns a Call object so you can invoke doIfXxx, passXxx, and done to define the tests and arguments that you want for this call. However if cf identifies a class that has no constructor code like an abstract class, then null is returned and the call is not inserted. Parameters: mname the qualified-class-name.method-name of the method to call. The identified method must be a static method. cf the call is placed at the start of the constructors identified by cf. public static Call addBefore(String mname, Method meth) Inserts a call at the start of meth. Also returns a Call object so you can invoke doIfXxx, passXxx, and done to define the tests and arguments that you want for this call. However if meth identifies a method that contains no code like an abstract method, then null is returned and the call is not inserted. Parameters: mname the qualified-class-name.method-name of the method to call. The identified method must be a static method. stmt the call is placed at the start of the method identified by meth. public static Call addBefore(String mname, Statement stmt) Inserts a call before stmt. Also returns a Call object so you can invoke doIfXxx, passXxx, and done to define the tests and arguments that you want for this call. However if the call cannot be inserted, null is returned instead. Parameters: mname the qualified-class-name.method-name of the method to call. The identified method must be a static method. stmt the call is placed before the statement identified by stmt. Aborts building of the call. You can now discard the Call object. This might be used if pass(variable) threw an exception, for example. public void doIf(String var1, int opr, String val2) Inserts an if-test prior to the call. The operands derived from var1 and val2 must have the same data type. If either is invalid (say from incorrect user input), an exception is thrown.
Parameters: var1 a variable reference that is valid at the current insert point. opr one of the DOIF constants defined in the Trek class. But if the variables are objects or boolean, only DOIF_EQ and DOIF_NE are allowed. val2 "xxx", 'xxx', true, false, a numeric constant (eg. -31.4), or a variable reference that is valid at the current insert point. Note that var1 is compared to the string xxx if val2 has either of the first two formats. That is, the quotes are not part of the value. public void doIfBoolean(String var, boolean val) If val is true, inserts if (var) prior to the call. If val is false, inserts if (!var) prior to the call. Parameters: var a variable reference whose data type is boolean. val a boolean value public void doIfInt(String var, int opr, int val) Inserts if (var opr val) prior to the call. Parameters: var a variable reference whose data type is int, short, char, or byte. opr one of the DOIF constants defined in the Trek class. val an int value public void doIfString(String var, int opr, String val) Inserts if (var.equals(val)) or if (!var.equals(val)) prior to the call. Parameters: var a variable reference whose data type is String. opr either DOIF_EQ or DOIF_NE. val a String value Inserts the instructions to do the call into the in-memory representation of the program. If there is already inserted code there, the new code is positioned according to the priority rules described in Position of the inserted code. The calls insert point must have a value, and that value is appended to the argument list of the call being built.
public void pass(String variable) Appends the specified variable reference to the argument list of the call being built. The data type of this argument is set to the value's data type unless the value is a non-String object or a short integer (ie. a byte, a char, or a short). In these two cases, it is respectively set to Object and int. Also if this argument is a method reference whose return type is void, the method is called but the String "<Side Effects>" is what is actually added to the argument list. Parameters: variable a variable reference that is valid at the current insert point. If it is invalid (say from incorrect user input), an exception is thrown. public void passAs(String classname) The calls insert point must have a value that is an object reference, and that value is appended to the argument list of the call being built. The data type of this argument in the method reference is set via classname. If classname is null, the argument's data type is set to the value's class. If it is non-null, it is set to the specified class and that class must be a superclass of the value's class. Parameters: classname the fully qualified name of the class to pass the object reference as, or null public void passAs(String classname, String variable) Appends the specified object reference to the argument list of the call being built. The data type of this argument in the method reference is set via classname. If classname is null, the argument's data type is set to the value's class. If it is non-null, it is set to the specified class and that class must be a superclass of the value's class. Thus except for String references, pass(object ref) is equivalent to passAs("java.lang.Object", object ref). Parameters: classname the fully qualified name of the class to pass the object reference as, or null variable a variable reference that is valid at the current insert point public void passBoolean(boolean ival) Appends the specified boolean value to the argument list of the call being built Parameters: ival the boolean value to use public void passDouble(double dval) Appends the specified double value to the argument list of the call being built Parameters: dval the double value to use public void passFloat(float fval) Appends the specified float value to the argument list of the call being built Parameters: fval the float value to use Appends an array elements index to the argument list of the call being built. The Call object must be from an addAt (method name, instruction that loads or stores an array element). Appends the specified integer value to the argument list of the call being built Parameters: ival the integer value to use. May be an integer constant, character constant, or a trek-time variable whose data type is byte, char, short, or int. public void passLong(long lval) Appends the specified long value to the argument list of the call being built Parameters: lval the long value to use If the Call object is from an addAt(), the instructions stack arguments if any are passed. However passStack(desc) must be used for instructions with untyped stack arguments. passStack must be used with care. If the arguments generated by passStack do not match the argument data types expected for the addAt()'s mname, an error will occur at runtime. public void passStack(String desc) If the Call object is from an addAt(), the instructions stack arguments are passed. However passStack(desc) may only be used for instructions with untyped stack arguments: dup*, pop*, and swap. passStack must be used with care. If the arguments generated by passStack do not match the argument data types expected for the addAt()'s mname, an error will occur at runtime. Parameters: desc the one or two descriptors that identify the data type(s) of this instruction's stack arguments, as described in The Java Virtual Machine Specification. For example, if a dup2's stack arguments were two floats, desc should be FF. public void passString(String str) Appends the specified String value to the argument list of the call being built Parameters: str the String value to use Example: Statement stmt = any Statement object; Call call = Call.addAfter("class.gather-method", stmt); call.passString(stmt.toString()); call.done(); would call the specified gather-method, passing one argument containing the source code for the statement after which the call is being inserted.
|