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

Throw NoSuchFieldException when call getInt() method #565

Closed
GGGGGHT opened this issue Jul 21, 2022 · 5 comments
Closed

Throw NoSuchFieldException when call getInt() method #565

GGGGGHT opened this issue Jul 21, 2022 · 5 comments

Comments

@GGGGGHT
Copy link
Contributor

GGGGGHT commented Jul 21, 2022

Enviroment Infomation

OS

macOS Monterey
Version 12.3.1

java -version

java version "17.0.3" 2022-04-19 LTS
Java(TM) SE Runtime Environment (build 17.0.3+8-LTS-111)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.3+8-LTS-111, mixed mode, sharing)

btrace version

BTrace v.2.2.2 (c8f81cc)

target

public abstract class HelloWorld extends HelloWorldBase {
    protected int field = 0;

    public static void main(String[] args) throws Exception {
        System.out.println(ProcessHandle.current().pid());
        System.out.println("ready when you are ...");
        System.in.read();

        callA();
    }

    private static void callA() {
        System.out.println("HelloWorld#callA");
        HelloWorld instance = new HelloWorldExt();
        long x = System.nanoTime();
        instance.callA("hello", 13);
        System.out.println("dur = " + (System.nanoTime() - x));
    }
    

    private void callA(String a, int b) {
        field++;
        callB(callC(a, b));
        field--;
    }

    private void callB(String s) {
        field++;
        System.out.println("You want " + s);
        field--;
    }

    protected abstract String callC(String a, int b);
}

final class HelloWorldExt extends HelloWorld {
    @Override
    protected String callC(String a, int b) {
        try {
            field++;
            String s = a + "-" + b;
            for (int i = 0; i < 100; i++) {
                s = callD(s);
            }
            return s;
        } finally {
            field--;
        }
    }
}

abstract class HelloWorldBase {
    protected final String callD(String s) {
        return "# " + s;
    }
}

script content

import org.openjdk.btrace.core.annotations.BTrace;
import org.openjdk.btrace.core.annotations.Kind;
import org.openjdk.btrace.core.annotations.Location;
import org.openjdk.btrace.core.annotations.OnMethod;
import org.openjdk.btrace.core.annotations.ProbeMethodName;
import org.openjdk.btrace.core.annotations.Self;

import static org.openjdk.btrace.core.BTraceUtils.Reflective.getInt;
import static org.openjdk.btrace.core.BTraceUtils.println;
import static org.openjdk.btrace.core.BTraceUtils.str;

@BTrace
public class HelloWorldTrace {
    @OnMethod(clazz="HelloWorld", method="/call.*/", type="void (java.lang.String, int)",location=@Location(Kind.ENTRY))
    public static void onMethod(@ProbeMethodName(fqn = true) String pmn, @Self Object thiz) {
        println("Hello from method " + pmn);
        println("field = " + str(getInt("field", thiz)));
    }
}

command

btrace ${pid} HelloWorldTrace.java

what happen

Java HotSpot(TM) 64-Bit Server VM warning: Option AllowRedefinitionToAddDeleteMethods was deprecated in version 13.0 and will likely be removed in a future release.
btrace INFO: Attaching BTrace to PID: 69347
btrace INFO: Successfully started BTrace probe: HelloWorldTrace.java
Hello from method private void HelloWorld#callA(java.lang.String, int)

! ERROR
java.lang.RuntimeException: java.lang.NoSuchFieldException: field
	at org.openjdk.btrace.core.BTraceUtils.translate(BTraceUtils.java:3124)
	at org.openjdk.btrace.core.BTraceUtils.lambda$getField$0(BTraceUtils.java:3044)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
	at org.openjdk.btrace.core.BTraceUtils.getField(BTraceUtils.java:3035)
	at org.openjdk.btrace.core.BTraceUtils.access$400(BTraceUtils.java:65)
	at org.openjdk.btrace.core.BTraceUtils$Reflective.getInt(BTraceUtils.java:5395)
	at HelloWorld.callA(HelloWorld.java)
	at HelloWorld.callA(HelloWorld.java:16)
	at HelloWorld.main(Unknown Source)
Caused by: java.lang.NoSuchFieldException: field
	at java.base/java.lang.Class.getDeclaredField(Class.java:2610)
	at org.openjdk.btrace.core.BTraceUtils.lambda$getField$0(BTraceUtils.java:3039)
	... 7 more

modify script

import org.openjdk.btrace.core.annotations.BTrace;
import org.openjdk.btrace.core.annotations.Kind;
import org.openjdk.btrace.core.annotations.Location;
import org.openjdk.btrace.core.annotations.OnMethod;
import org.openjdk.btrace.core.annotations.ProbeMethodName;
import org.openjdk.btrace.core.annotations.Self;
import java.lang.Class;
import java.lang.reflect.Field;
import static org.openjdk.btrace.core.BTraceUtils.*;

@BTrace
public class HelloWorldTrace {
    @OnMethod(clazz = "HelloWorld", method = "callA", type = "void (java.lang.String,int)", location = @Location(Kind.RETURN))
    public static void onMethod(@ProbeMethodName(fqn = true) String pmn, @Self Object thiz) {
        println("clazz = " + name(classOf(thiz)));
        println("Hello from method " + pmn);
    }
}

Reprint when I modify the script, the clazz print HelloWorldExt. The complete information is as follows

btrace INFO: Attaching BTrace to PID: 21811
btrace INFO: Successfully started BTrace probe: HelloWorldTrace.java
clazz = HelloWorldExt

Hello from method private void HelloWorld#callA(java.lang.String, int)

confused

  • I marked clazz on the annotation of onmethod is HelloWorld but clazz print HelloWorldExt
  • Why is NoSuchFieldException thrown
@GGGGGHT
Copy link
Contributor Author

GGGGGHT commented Jul 28, 2022

@jbachorik Hello, My friend can you help me.

@jbachorik
Copy link
Collaborator

Sorry for long time to reply. My main job was keeping me busy ...

This seems like a bug in the code resolving the fields by reflection.

The problem here is that the actual class name of the intercepted instance will be HelloWorldEx as the HelloWorld class is abstract and can not be instantiated. However, the intercepted method callA() is defined in HelloWorld and will be matched and instrumented because of that.

The reason for NoSuchFieldxception is the fact that the field field is declared in HelloWorld and the getDeclaredField() method used by BTrace does not perform search in type hierarchy, therefore missing the field declared in the base class.
I think this should be fixed but in the meantime you can use

Field fld = field("HelloWorld", "field");
int value = getInt(fld, thiz);
...

@GGGGGHT
Copy link
Contributor Author

GGGGGHT commented Sep 17, 2022

In fact, this is a bug. Is that right.

@jbachorik
Copy link
Collaborator

Yes. Any contributions would be welcome.
The fix would include only standard reflection API calls so it should be pretty easy to tackle even for newcomers.

@GGGGGHT
Copy link
Contributor Author

GGGGGHT commented Oct 4, 2022

Ok, I've fixed the issue, I'll add unit tests later and I'll make a pull request after verification.

GGGGGHT added a commit to GGGGGHT/btrace that referenced this issue Oct 5, 2022
GGGGGHT added a commit to GGGGGHT/btrace that referenced this issue Oct 6, 2022
GGGGGHT added a commit to GGGGGHT/btrace that referenced this issue Oct 9, 2022
GGGGGHT added a commit to GGGGGHT/btrace that referenced this issue Oct 12, 2022
@GGGGGHT GGGGGHT closed this as completed Oct 14, 2022
@jbachorik jbachorik added this to the 2.3.0 milestone Nov 10, 2022
@jbachorik jbachorik modified the milestones: 2.3.0, 2.2.3 Dec 23, 2022
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