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

transform not working when attaching agentmain dynamically #1742

Closed
qujsh opened this issue Jan 2, 2025 · 11 comments
Closed

transform not working when attaching agentmain dynamically #1742

qujsh opened this issue Jan 2, 2025 · 11 comments
Assignees
Labels
Milestone

Comments

@qujsh
Copy link

qujsh commented Jan 2, 2025

Hey, I had a simple static agent that I attached with -javaagent and everything worked fine. Now I tried changing it to work dyamically in agentmain, it worked without println in transform, I don't no why.

I had a check in issue similarly, ref: #1164. But It didn't solve my problem. Here's my code:

public static void agentmain(String agentArgs, Instrumentation inst) {

        System.out.println("Agentmain initialized.");

        new AgentBuilder.Default()
                .disableClassFormatChanges()
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
//                .type(ElementMatchers.any())   //it's also ineffective;
                .type(ElementMatchers.nameContains("example"))
                .transform((builder, type, classLoader, module, protectionDomain) -> {

                    System.out.println("className: " + type.getName());

                    Advice advice = Advice.to(TimingAdvice.class);
                    return builder.visit(advice.on(ElementMatchers.any()));
                })
                .installOn(inst);

        System.out.println("Agent initialized successfully!");
    }

I can see the "Agentmain initialized." and "Agent initialized successfully!" print when I attach the agent so I know it's being attached, but the transform is never called with no "className: xxx" print.

image

bytebuddy: 1.15.11
java: 1.8.0

@qujsh
Copy link
Author

qujsh commented Jan 2, 2025

my goal is to print the execution time for each method execution. now I execute the spring HealthController.health() by curl, I can get response but no execution time print.

@qujsh
Copy link
Author

qujsh commented Jan 2, 2025

TimingAdvice code

public class TimingAdvice {

    @Advice.OnMethodEnter
    public static long onEnter(@Advice.Origin String method) {
//        System.out.println("Entering method: " + method);
        return System.nanoTime();
    }

    @Advice.OnMethodExit
    public static void onExit(@Advice.Origin String method,
                              @Advice.Enter long startTime) {
        long duration = System.nanoTime() - startTime;
        System.out.println("Exiting method: " + method + " took " + duration + "ns");
    }
}

main code

    public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {

        String jarStartupPath = System.getProperty("user.dir");
        System.out.println("JAR startup path: " + jarStartupPath);
        for (VirtualMachineDescriptor vmd : VirtualMachine.list()) {

            if (vmd.displayName().startsWith("example")) {
                System.out.println("Found JVM: " + vmd.displayName() + " [" + vmd.id() + "]");

                VirtualMachine vm = VirtualMachine.attach(vmd.id());
                vm.loadAgent("/src/main/resources/gatherv2-1.0-SNAPSHOT.jar");
                vm.detach();
                System.out.println("Agent attached to JVM with ID: " + vmd.id());
                break;
            }
        }
    }

pom.xml

  <dependencies>

        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.15.11</version> <!-- 请检查 Byte Buddy 的最新版本 -->
        </dependency>

        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.15.11</version> <!-- 请检查 Byte Buddy 的最新版本 -->
        </dependency>

        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.8.0</version>  <!-- 需要根据你的 JDK 版本选择合适的版本 -->
            <scope>system</scope>
            <!-- 需要正确配置环境变量 JAVA_HOME的值,常规 java -version使用的是jre环境,tools.jar在Java 8中落在jdk环境中 -->
            <systemPath>${java.home}/../lib/tools.jar</systemPath>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.6.0</version> <!-- 使用最新版本 -->
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <createDependencyReducedPom>false</createDependencyReducedPom>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                    </excludes>
                                </filter>
                            </filters>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.4.2</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>org.example.ByteBuddyAdvice</Premain-Class>
                            <Main-Class>org.example.Main</Main-Class>

                            <Agent-Class>org.example.ByteBuddyAdvice</Agent-Class>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

@raphw
Copy link
Owner

raphw commented Jan 2, 2025

Could you register an Agent builder.Listener to see if the classes you wonder about are picked up?

@raphw raphw self-assigned this Jan 2, 2025
@raphw raphw added the question label Jan 2, 2025
@raphw raphw added this to the 1.15.11 milestone Jan 2, 2025
@qujsh
Copy link
Author

qujsh commented Jan 3, 2025

I add the builder.Listener and can print typeName, even when I execute curl to HealthController.health() is also do print, but transform do nothing, no className print. Here's my code and result:

public static void agentmain(String agentArgs, Instrumentation inst) {

        System.out.println("Agentmain initialized.");

        // add Agent Listener
        ByteBuddyListener listener = new ByteBuddyListener();

        new AgentBuilder.Default()
                .disableClassFormatChanges()
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
//                .type(ElementMatchers.any()) 
                .type(ElementMatchers.nameContains("HealthController"))  //only use HealthController to test
                .transform((builder, type, classLoader, module, protectionDomain) -> {

                    System.out.println("className: " + type.getName()); //no className print

                    Advice advice = Advice.to(TimingAdvice.class);
                    return builder.visit(advice.on(ElementMatchers.any()));
                })
                .with(listener)  //add Agent Listener here
                .installOn(inst);

        System.out.println("Agent initialized successfully!");
}

ByteBuddyListener:

public class ByteBuddyListener implements AgentBuilder.Listener {
    @Override
    public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule javaModule, boolean b) {
        System.out.println("Discovered: " + typeName);
    }

    @Override
    public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {
        System.out.println("Transformed: " + typeDescription.getName());
    }

    @Override
    public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {
        System.out.println("Ignored: " + typeDescription.getName());
    }

    @Override
    public void onError(String typeName, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {
        System.out.println("Error: " + typeName);
    }

    @Override
    public void onComplete(String typeName, ClassLoader classLoader, JavaModule javaModule, boolean b) {
        System.out.println("Complete: " + typeName);
    }
}

result:
image

@raphw
Copy link
Owner

raphw commented Jan 3, 2025

I see an error in the log. What does the exception say?

@qujsh
Copy link
Author

qujsh commented Jan 4, 2025

Thank you for your reply. I will go to the company on Monday to test what the printing error is. At that time, the project can only be debugged in the local network. Inspired by you, I started a very simple Spring project, and its Spring HealthController has been successfully adjusted.

@qujsh
Copy link
Author

qujsh commented Jan 6, 2025

I found the issue. It's because the spring-test plugin I depend on introduces mockio, which relies on an older version 1.10. The latest version 1.15 that I'm using throws an error when using agentmain, with the following error message. So, to ensure compatibility across multiple projects and versions, I can only use the -javaagent mode, right? It works fine when using that.

image

@raphw
Copy link
Owner

raphw commented Jan 6, 2025

Can't you use dependency configuration to pin the latest version?

@qujsh
Copy link
Author

qujsh commented Jan 7, 2025

It seems like I can't handle it this way. It's a dependency of a dependency of a dependency.

image

@raphw
Copy link
Owner

raphw commented Jan 9, 2025

If you use Maven, for example, you'd use dependency configuration.

@qujsh
Copy link
Author

qujsh commented Jan 10, 2025

Thank you for your reply, I will check it again.

@qujsh qujsh closed this as completed Jan 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants