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

Java 9 compatibility #295

Closed
GotoFinal opened this issue Apr 23, 2017 · 5 comments
Closed

Java 9 compatibility #295

GotoFinal opened this issue Apr 23, 2017 · 5 comments
Assignees
Milestone

Comments

@GotoFinal
Copy link

GotoFinal commented Apr 23, 2017

Seems that between build 153 and 166 of jdk 9 (jigsaw) something changed as byte-buddy (version 1.6.13) fails to inject:
(also package of Module class was changed, not sure if this is already updated, from java.lang.reflect.Module to java.lang.Module)

[01:55:10 WARN]: Caused by: java.lang.IllegalStateException: Error during attachment using: net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider$Compound@f15400bb
[01:55:10 WARN]: 	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:366)
[01:55:10 WARN]: 	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:332)
[01:55:10 WARN]: 	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:300)
[01:55:10 WARN]: 	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:286)
[01:55:10 WARN]: 	at org.diorite.inject.Injection.<clinit>(Injection.java:40)
[01:55:10 WARN]: 	... 3 more
[01:55:10 WARN]: Caused by: java.lang.reflect.InvocationTargetException
[01:55:10 WARN]: 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[01:55:10 WARN]: 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[01:55:10 WARN]: 	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[01:55:10 WARN]: 	at java.base/java.lang.reflect.Method.invoke(Method.java:563)
[01:55:10 WARN]: 	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:353)
[01:55:10 WARN]: 	... 7 more
[01:55:10 WARN]: Caused by: java.io.IOException: Can not attach to current VM
[01:55:10 WARN]: 	at jdk.attach/sun.tools.attach.HotSpotVirtualMachine.<init>(HotSpotVirtualMachine.java:75)
[01:55:10 WARN]: 	at jdk.attach/sun.tools.attach.VirtualMachineImpl.<init>(VirtualMachineImpl.java:48)
[01:55:10 WARN]: 	at jdk.attach/sun.tools.attach.AttachProviderImpl.attachVirtualMachine(AttachProviderImpl.java:69)
[01:55:10 WARN]: 	at jdk.attach/com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:207)
[01:55:10 WARN]: 	... 12 more
@GotoFinal
Copy link
Author

GotoFinal commented Apr 23, 2017

After some small debug, seems that oracle blocked attach for this same VM by default.... and there need to be special flag to allow this.
http://jigsaw-dev.1059479.n5.nabble.com/Disallowing-the-dynamic-loading-of-agents-by-default-revised-td5716181i20.html

// class HotSpotVirtualMachine

    private static final boolean ALLOW_ATTACH_SELF;
// some code
    static {
        String s = VM.getSavedProperty("jdk.attach.allowAttachSelf");
        ALLOW_ATTACH_SELF = (s != null) && !"false".equals(s);
    }
    // some code

    // inside constructor
        if (!ALLOW_ATTACH_SELF && (pid == 0 || pid == CURRENT_PID)) {
            throw new IOException("Can not attach to current VM");
        }

With dirty hack this can be resolved using unsafe hack to access this saved properties object in jdk.internal.misc.VM#savedProps

    static
    {
            Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor();
            unsafeConstructor.setAccessible(true);
            unsafe = unsafeConstructor.newInstance();
            constructorModifiers = Constructor.class.getDeclaredField("modifiers");
            constructorModifiersOffset = unsafe.objectFieldOffset(constructorModifiers);
            methodModifiers = Method.class.getDeclaredField("modifiers");
            methodModifiersOffset = unsafe.objectFieldOffset(methodModifiers);
            fieldModifiers = Field.class.getDeclaredField("modifiers");
            fieldModifiersOffset = unsafe.objectFieldOffset(fieldModifiers);
            setAccessible = AccessibleObject.class.getDeclaredMethod("setAccessible0", boolean.class);
            setForceAccessible(setAccessible);
    }
    private static boolean setForceAccessible(AccessibleObject accessibleObject)
    {
        try
        {
            if (accessibleObject instanceof Constructor)
            {
                Constructor<?> object = (Constructor<?>) accessibleObject;
                unsafe.getAndSetInt(object, constructorModifiersOffset, addPublicModifier(object.getModifiers()));
                return true;
            }
            if (accessibleObject instanceof Method)
            {
                Method object = (Method) accessibleObject;
                unsafe.getAndSetInt(object, methodModifiersOffset, addPublicModifier(object.getModifiers()));
                return true;
            }
            if (accessibleObject instanceof Field)
            {
                Field object = (Field) accessibleObject;
                unsafe.getAndSetInt(object, fieldModifiersOffset, addPublicModifier(object.getModifiers()));
                return true;
            }
            return false;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }
    }

    private static int addPublicModifier(int mod)
    {
        mod &= ~ (Modifier.PRIVATE);
        mod &= ~ (Modifier.PROTECTED);
        mod |= (Modifier.PUBLIC);
        return mod;
    }

and setAccessible can be used to get access to any other place without access checks.

But this is very dirty hack... so probably people will just need to use that flag. Djdk.attach.allowAttachSelf=oracle likes to break stuff for us
So if you agree then you can just close issue.

@raphw
Copy link
Owner

raphw commented Apr 23, 2017

Hi, thanks for reporting this. Adding this flag was discussed on the mailing list but was discarded as a bad idea for Java 9 to be set to true by default. I expect this flag to not be set in future releases.

Then again, this hack is not too bad to offer as an attempt to make the installation process work out of the box. For many users, this might just work at least for test environments where this is intended. I do however consider to just start a new VM process to attach the agent from there. Its a bit of a dirty hack but the whole self-attachment is a bit tricky to begin with, despite its usefulness.

@raphw raphw self-assigned this Apr 23, 2017
@raphw raphw added this to the 1.6.12 milestone Apr 23, 2017
@GotoFinal
Copy link
Author

GotoFinal commented Apr 23, 2017

@raphw
but this unsafe hack isn't perfect too, if other code will load HotSpotVirtualMachine before you will change this flag it will fail ;/

You might try to use other VM to attach agent and remove that check in HotSpotVirtualMachine so then you can again use it from this same VM. (I did not test if that will work)

@raphw
Copy link
Owner

raphw commented Apr 23, 2017

I will check the source to see if there is a way around, therefore I but I think that I will go for the "secondary VM hack" without relying on Unsafe to ease the migration for test frameworks that rely on this feature. Long term, people will probably need to set the flag but there needs to be a short-time solution that is less disruptive.

@raphw
Copy link
Owner

raphw commented Apr 26, 2017

ByteBuddyAgent.install() does now detect Java 9 VMs where self-attachment is forbidden and creates a helper process to attach from there. This was already added to master and will be included in the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants