Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set up the bootstrap instrumentation plugin framework #3152

Merged
merged 17 commits into from
Jul 25, 2019
Merged

Conversation

wu-sheng
Copy link
Member

@wu-sheng wu-sheng commented Jul 23, 2019

Hi, All

This PR is following #2932, in these days, I work on building the bootstrap instrumentation plugin framework by not breaking any existing plugin. Look like it works.

For plugin developer, one line required only in plugin define.

     // This is the only change for plugin developer
    @Override public boolean isBootstrapInstrumentation() {
        return true;
    }

@wu-sheng wu-sheng added TBD To be decided later, need more discussion or input. core feature Core and important feature. Sometimes, break backwards compatibility. agent Language agent related. labels Jul 23, 2019
@wu-sheng wu-sheng added this to the 6.3.0 milestone Jul 23, 2019
@wu-sheng
Copy link
Member Author

If you want to understand how I do this, follow BootstrapInstrumentBoost class, it will link the things together. Also, plugin interceptor point definitions have been as public, as BootstrapInstrumentBoost needs to access it.

Also, I add a demo jre-HttpUrlConnection plugin for demo, and instrumen the following application codes.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class Main {
    public static void main(String[] args) throws IOException {
        URL obj = new URL("http://www.baidu.com");
        HttpURLConnection con = (HttpURLConnection)obj.openConnection();

        con.setRequestMethod("GET");

        int responseCode = con.getResponseCode();
        System.out.println("Response Code : " + responseCode);

        BufferedReader in = new BufferedReader(
            new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();

        //print result
        System.out.println(response.toString());
    }
}

Like the existing plugins, the bootstrap intrumentation only requires you to declare it in plugin define, such as following codes I included in this PR

/**
 * @author wusheng
 */
public class HttpUrlConnectionInstrumentation extends ClassEnhancePluginDefine {
    private static String CLASS_NAME = "java.net.HttpURLConnection";

     // This is the only change for plugin developer
    @Override public boolean isBootstrapInstrumentation() {
        return true;
    }

    @Override protected ClassMatch enhanceClass() {
        return byName(CLASS_NAME);
    }

    @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named("setRequestMethod");
                }

                @Override public String getMethodsInterceptor() {
                    return "org.apache.skywalking.apm.plugin.jre.httpurlconnection.Interceptor";
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }
}

Also, I haven't finished construct and static method plugin, but they are very similar, as we have this, they are simple. I will push those later.

I hope you could test this new mechanism in other demo codes, Spring Boot and Tomcat especially, in case I miss anything.

@ascrutae Please lead the tests.

@wu-sheng wu-sheng changed the title [Don't merge] Set up the bootstrap instrumentation plugin framework Set up the bootstrap instrumentation plugin framework Jul 24, 2019
@wu-sheng
Copy link
Member Author

This PR is ready to review and test. I have used it to instrument URL and HttpURLConnection successfully. But just a demo code, of course.

Part of my test codes are posted here, https://gist.github.com/wu-sheng/25a8877971db089e196ada0fe4395813

In this PR, there is no real plugin. But further pull request is welcome.

@wu-sheng wu-sheng removed the TBD To be decided later, need more discussion or input. label Jul 24, 2019
@candyleer
Copy link
Member

I have tested in bootstrap class and 3rd part class,both work well.
I have a question,the classes in HIGH_PRIORITY_CLASSES still will be loaded by two classloader,right?

but I debug found, eg.EnhancedInstance is loaded by bootstrap classloader in bootstrap interceptor (generated by InstanceMethodInterTemplate),and in normal InstMethodsInter it is loaded by application classloader .is it right?

I think EnhancedInstance will be find in bootstrap classloader first .

@wu-sheng
Copy link
Member Author

I have tested in bootstrap class and 3rd part class,both work well.
I have a question,the classes in HIGH_PRIORITY_CLASSES still will be loaded by two classloader,right?
but I debug found, eg.EnhancedInstance is loaded by bootstrap classloader in bootstrap interceptor (generated by InstanceMethodInterTemplate),and in normal InstMethodsInter it is loaded by application classloader .is it right?
I think EnhancedInstance will be find in bootstrap classloader first .

You do the tests quickly :) Thanks.
To your question, from my understanding, when bootstrap instrumentation actived, EnhancedInstance should only be loaded in Bootstrap classloader. Otherwise, you will face Class Cast Exception, because you can't cat Bootstrap's EnhancedInstance to ApplicationClassLoader's EnhancedInstance.

Load the generated class to class loader w/o initialization should be safe. That is why I use #injectRaw rather than #inject.

This is my guess and understanding from test results, I used to try #inject, which cause that cast exception. :)

@wu-sheng
Copy link
Member Author

@candyleer If you are OK, please approve this PR.

@kezhenxu94
Copy link
Member

@candyleer If you are OK, please approve this PR.

is this file (debug/org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.bootstrap.Interceptor_internal.class) committed by mistake?

@wu-sheng
Copy link
Member Author

is this file (debug/org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.bootstrap.Interceptor_internal.class) committed by mistake?

Yes. Removing it.

@wu-sheng
Copy link
Member Author

@candyleer Have you tested Tomcat? And JDK 6?

@wu-sheng
Copy link
Member Author

/run ci

@candyleer
Copy link
Member

@candyleer Have you tested Tomcat? And JDK 6?

I have test Tomcat .but JDK 6 not test ,maybe tomorrow I can run a test manually.

@candyleer
Copy link
Member

candyleer commented Jul 25, 2019

It seems not support JDK6 for the error,looks like javassist dependent on some feature above jdk7
image

@wu-sheng
Copy link
Member Author

Look like javassist has an error.

@candyleer
Copy link
Member

I have tested ,javassist v3.21.0-GA is the last version support jdk6,and we can work well by using it

@AllArguments Object[] allArguments) {
try {
EnhancedInstance targetObject = (EnhancedInstance)obj;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should call prepare()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

@@ -37,6 +38,7 @@
<grpc.version>1.14.0</grpc.version>
<guava.version>20.0</guava.version>
<bytebuddy.version>1.9.2</bytebuddy.version>
<javaassist.version>3.23.2-GA</javaassist.version>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3.21.0-GA support JDK6

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I am doing downgrade

* Prepare the context. Link to the agent core in AppClassLoader.
*/
private static void prepare() {
if (INTERCEPTOR == null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the INTERCPETOR Initialization shoud be synchronized?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't' have to. Create multiple instances is still safe. This could just avoid creating too many instances, and also avoid the lock.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

@wu-sheng
Copy link
Member Author

@candyleer Updated.

@wu-sheng
Copy link
Member Author

@ascrutae Please run our autotests, let's see whether this breaks anything.

Copy link
Member

@candyleer candyleer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BootstrapPluginLogBridge method not implemented

*
* @author wusheng
*/
public class BootstrapPluginLogBridge implements IBootstrapLog {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the method in this class should be implemented, or the log will be NoopLog.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

}

@Override public void info(String format, Object... arguments) {
logger.info(format, arguments);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@candyleer Could you test this log method? I am little concerned about arguments(Object...) could work or not. I haven't the test codes at my hand now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could simple call log("", arg1, arg2) in InstanceMethodInterTemplate, and see whether log works well?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK ,I just tested info(String format)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure you use args list, no matter which method you test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test passed

@wu-sheng
Copy link
Member Author

/run ci

1 similar comment
@wu-sheng
Copy link
Member Author

/run ci

@wu-sheng
Copy link
Member Author

From the test report, it is same as before. After CI pass, I will merge this to the core. The new plugin is welcome, especially for HttpUrlConnection.

@wu-sheng wu-sheng merged commit c76fb40 into master Jul 25, 2019
@wu-sheng wu-sheng deleted the bootstrap-cl branch July 25, 2019 14:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
agent Language agent related. core feature Core and important feature. Sometimes, break backwards compatibility.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants