Implementation of Using ASM for method interception to obtain relevant data

Source: Internet
Author: User

How to obtain related data in method Interception

 

If you do not understand my previous article, please go back and run it step by step based on the example.
Program understanding.

This article mainly uses ASM to obtain the data related to method calls during method interception, such as the parameter list, local variable list, and Method
Call Stack, Operation Status Code, and other important data, and method execution time. Based on the current system memory status, CPU usage, and other system information
This can provide the most reliable analysis basis for business logic exceptions. This article requires a certain understanding of bytecode, even if you
Because I have to copy the implementation here, I also hope you can understand it after using these implementation examples. Able to understand and use the content correctly
You have the ability to understand these implementations.

To compare the differences between Using ASM to dynamically generate classes and Using proxy agents to inject code:
When Using proxy to inject code, we actually use reflection to invoke a method object. Therefore, the injected code is directly inserted.
Before and after invoke:

Before (); <br/> invoke (OBJ, M, argS); <br/> after ();
This implementation is very easy to understand. To put it bluntly, if you want to execute method M, please give it to me to help you, so that I can
People show off: I want to execute method M (Before), and I have already executed method M (after ).
When ASM dynamically generates a class, the onmethodenter and onmethodeixt methods of myadviceadaptor are encapsulated in a method.
Called, rather than called when running a method. If we call
System. Out. println ("Hello, world ");
In visitmethod, a system. Out. println ("hello"); that is irrelevant to the new method to be generated is called.
The generated method is not injected with any commands, so system. Out. println ("hello") is not called during execution ");
The correct method is to call the ASM operation in onmethodenter to insert an instruction to the method to be packaged:
Visitfieldinsn (getstatic, "Java/lang/system", "out", "ljava/IO/printstream;"); <br/> visitldcinsn ("Hello world! "); <Br/> visitmethodinsn (invokevirtual," Java/IO/printstream "," println ", <br/>" (ljava/lang/string;) V ");
In this way, system. Out. println ("Hello, world"); can be inserted to the front end of the new method.
For example, the original method is
Void test {<br/> New thread (). Start (); <br/>}
Then, the method generated using ASM should be
Visittypeinsn (new, "Java/lang/thread"); <br/> visitinsn (DUP); <br/> visitmethodinsn (invokespecial, "Java/lang/thread ", "<init>", "() V"); <br/> visitmethodinsn (invokevirtual, "Java/lang/thread", "Start", "() V "); <br/> visitinsn (return );

Now, because the myadviceadaptor's onmethodenterr method contains the code for inserting instructions, the code will be in the methoevisitor
Before the original method is generated, insert the following command to generate the new method:

Visitfieldinsn (getstatic, "Java/lang/system", "out", "ljava/IO/printstream;"); <br/> visitldcinsn ("Hello world! "); <Br/> visitmethodinsn (invokevirtual," Java/IO/printstream "," println ", <br/>" (ljava/lang/string;) V "); <br/> // Add pop if the stack is not balanced. After the code you inserted is executed, the stack is restored to the original state before the original method code starts. <Br/> visittypeinsn (new, "Java/lang/thread"); <br/> visitinsn (DUP ); // do you still remember why the new object operation will always be DUP after I wrote it last year? <Br/> // http://blog.csdn.net/axman/archive/2008/05/05/2393621.aspx <br/> visitmethodinsn (invokespecial, "Java/lang/thread", "<init>", "() V "); <br/> visitmethodinsn (invokevirtual, "Java/lang/thread", "Start", "() V"); <br/> visitinsn (return );

In this way, the original method is reborn as follows:

Void test {<br/> system. Out. println ("Hello, world"); <br/> New thread (). Start (); <br/>}
Similarly, you must use the asm insert command method in onmethodeixt to inject the code into the generated method.

Well, I can understand the above theory. Let's get the relevant data.
First, we can use ASM to obtain the parameter list and local variable list for a method call. And method final operation status code
The time difference between injection and the code before and after the original method can be used to calculate the execution time of the original method code.
Because the code inserted in the onmethodenter method is inserted before all operations of the original method, including passing real parameters, the parameter table is obtained,
For the local variable table, the operation status codes are obtained in the onmethodeixt method:

Get the local variable list:

Void onmethodexit (INT opcode) {<br/> int localvarcnt = 0; <br/> If (nextlocal> firstlocal) localvarcnt = nextlocal-firstlocal; <br/> // these two variables are implemented by the superclass of adviceadapter, because a methodvisitor can analyze the local variable table from a method in the original <br/> // class in visitmethod. Note: The local variable table refers to a static table. The local variable list refers to the list of actual variables in the method stack in the actual memory when the method is called. <Br/> loadvararray (localvarcnt); <br/>}</P> <p> private void loadvararray (INT varcnt) {<br/> type object_type = type. getobjecttype ("Java/lang/object"); <br/> push (varcnt); <br/> newarray (object_type); <br/> for (INT I = 0; I <varcnt; I ++) <br/>{< br/> int Index = super. firstlocal + I; <br/> DUP (); <br/> push (I); <br/> loadlocal (INDEX ); <br/> box (getlocaltype (INDEX); <br/> arraystore (object_type ); <Br/>}< br/> // all local variables have been placed in an object []. This is the list of local variables. <Br/>}</P> <p>

Obtain the method parameter list:
Void onmethodexit (INT opcode) {<br/> loadargarray (); <br/> // This method is also implemented by the superclass of adviceadapter. Actually, it is similar to obtaining the local variable list. Its implementation is as follows: <br/> // push (argumenttypes. length); <br/> // newarray (object_type); <br/> // For (INT I = 0; I <argumenttypes. length; I ++) {<br/> // DUP (); <br/> // push (I); <br/> // loadarg (I ); <br/> // box (argumenttypes [I]); <br/> // arraystore (object_type ); <br/>/}< br/> // The above loadvararray is actually implemented by referring to this code and calculating the actual index of the local variable. <Br/>}

 

Well, let's say we need to output the following information:
The class where the intercepted method is located, the method name, the local variable list (array), the parameter list (array), and the operation status code. So:
Void onmethodexit (INT opcode) {<br/> visitldcinsn (classname); <br/> visitldcinsn (methodname); // in the previous article, we used only sortname of the method, in actual implementation, fullname should be used, because <br/> // The method has a heavy load. Only sortname cannot be used to limit a method. <Br/> visitldcinsn (string. valueof (opcode); <br/> int localvarcnt = 0; <br/> If (nextlocal> firstlocal) {<br/> localvarcnt = nextlocal-firstlocal; <br/> loadvararray (localvarcnt); <br/>}else {<br/> visitinsn (aconst_null); // to occupy a stack location. <Br/>}< br/> int argscnt = 0; <br/> argcnt = xxx; <br/> // the number of parameters can be analyzed from the method signature. <Br/> If (argscnt> 0) {<br/> loadargarray (); <br/>}< br/> else {<br/> visitinsn (aconst_null ); // to occupy a stack location. <Br/>}< br/>}
OK. Now the top-down five operands of the stack Are ARGs, vars, opcode, mthodname, and classname. Of course we can use the same ASM command.

The generated code passes the five operands in the stack to the specified destination. However, this operation is too complicated. We only need to call a method with five parameters

In this method, the common Java code is used to freely transmit data to the destination, rather than the ASM code in the intercepted method.

Therefore, we only need to define a method externally, for example:
Class Org. axman. test. sender {<br/> Public static void send (string classname, string method, string opcode, object [] localvar, object [] ARGs) {<br/> // print or save it to a file/database or send it to another service for processing. <Br/>}</P> <p> In onmethodexit: <br/> void onmethodexit (INT opcode) {<br/> visitldcinsn (classname); <br/> visitldcinsn (methodname); // in the previous article, we only used the sortname of the method. In actual implementation, fullname should be used, because <br/> // The method has been overloaded, only sortname cannot be used to limit a method. <Br/> visitldcinsn (string. valueof (opcode); <br/> int localvarcnt = 0; <br/> If (nextlocal> firstlocal) {<br/> localvarcnt = nextlocal-firstlocal; <br/> loadvararray (localvarcnt); <br/>}else {<br/> visitinsn (aconst_null); // to occupy a stack location. <Br/>}< br/> int argscnt = 0; <br/> argcnt = xxx; <br/> // the number of parameters can be analyzed from the method signature. <Br/> If (argscnt> 0) {<br/> loadargarray (); <br/>}< br/> else {<br/> visitinsn (aconst_null ); // to occupy a stack location. <Br/>}< br/> visitmethodinsn (Opcodes. invokestatic, "org. axman. test. sender "," send ", <br/>" (ljava/lang/string; [ljava/lang/object; [ljava/lang/object;) V "); </P> <p >}< br/>
You can pass the real parameter list, local variable list, and operation status code of the intercepted Method to the outside.

 

 

Get call stack
Using sun. Reflect. reflection. getcallerclass (x); we can only obtain the class of the caller.
The call test between methods cannot clearly view the method call chain.
Use getstacktrace () of throwable to obtain the complete method call stack. As long as we construct a throwable object, we can use
It is used to obtain all callers that call this method:
Public static string getinvokestack () {<br/> throwable T = new throwable (); <br/> stacktraceelement ste [] = T. getstacktrace (); <br/> stringbuilder sb = new stringbuilder (); <br/> for (INT I = 1; I <Ste. length; I ++) <br/>{< br/> Sb. append (STE [I]. tostring ()). append (";"); <br/>}< br/> return sb. tostring (); <br/>} 
Here we only call the tostring () method of stacktraceelement. You can obtain its details as needed.
Since set [0] is the method of t object, we do not need this caller information. Therefore, the cycle starts from 1.
If this method is called in the test. Main () method, the first caller is test. Main ();
Originally, we needed to use the ASM command in the onmethodeixt method to allow the intercepted method to call this method and use the method return value pressure stack as a parameter.
Send to the send method. However, because the intercepted method will certainly call the send method, we can directly call getinvokestack () in send, then the intercepted Method
The method is the second caller. Therefore, we only need to start the loop from 2 and then call getinvokestack () in the send method to obtain the intercepted method.
All call chains.

 

In fact, we will also pass classname and methodname in the onmethidenter method using similar methods. At the same time, when you enter the method to intercept
A random string is constructed for myadviceadaptor. Then, in the onmethidenter and onmethideixt methods, the ASM command is also called to send all the random strings to the constructor, as shown in figure

Sender. Send () method, so that you can pair the send data that the method enters and exits. The execution time can be calculated using the time difference between two pairs of send instead

The code that calculates the execution time of the original method code is injected to the original method.

Other details. I will explain it later. (Contact us to obtain the compression document for the entire test project. In the past, the MMS project compression file was provided on the blog. Many brothers actually took my mobile phone number from the project for development and testing, I have received a large number of test MMS messages)

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.