Instrumentation using ByteBuddy is not working when I tried to instrument 3-rd party classes
I manage to create code which instrument my own code and everything worked as expected.
When I tried to use the same code for class which are part of 3-rd party dependencies the instrumentation didn't work.
This code is working for me:
public class A {
public void print(){
System.out.println("in class A method 'print'");
}
}
public class AgentLoad {
public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
final ElementMatcher.Junction<NamedElement> matcher = ElementMatchers.named("com.instrumentation.A");
new AgentBuilder.Default()
.type(matcher)
.transform( new AgentBuilder.Transformer.ForAdvice()
.include(AgentLoad.class.getClassLoader())
.advice(named("print"), AAdvice.class.getName()))
.installOn(inst);
}
public static void main(String[] args) throws Exception{
AgentLoader.loadAgentClass(AgentLoad.class.getName(), null);
}
public static class AAdvice {
#Advice.OnMethodEnter
public static void enter() {
System.out.println("Enter Yes!!!!");
}
#Advice.OnMethodExit
public static void exit() {
System.out.println("Exist Yes!!!!");
}
}
}
This code is not working for me:
public static void agentmain(String agentArgs, Instrumentation inst) {
AgentBuilder builder = new AgentBuilder.Default();
builder.type(ElementMatchers.named("com.amazonaws.http.AmazonHttpClient"))
.transform( new AgentBuilder.Transformer.ForAdvice()
.include(AgentLoad.class.getClassLoader())
.advice(ElementMatchers.named("execute").and(isAnnotatedWith(SdkInternalApi.class)),
AmazonHttpClientAdvice.class.getName()));
builder.installOn(inst);
}
public static class AmazonHttpClientAdvice {
#Advice.OnMethodEnter
public static void executeEnter(#Advice.Argument(0) Request<?> request) {
System.out.println("Eenter !!!" + request);
}
#Advice.OnMethodExit
public static void exit(#Advice.Return(readOnly = false, typing = DYNAMIC) Object returned) {
System.out.println("Exist !!!! " + returned);
}
}
}
The expected result is a print of enter and exit when calling the 'execute' in class AmazonHttpClient.
Note: The agent is dynamically attached using the next lib: https://github.com/electronicarts/ea-agent-loader
Byte Buddy's API is immutable, all method calls are without side-effects but only return a new builder instance. This means that:
AgentBuilder builder = new AgentBuilder.Default();
builder.type(ElementMatchers.named("com.amazonaws.http.AmazonHttpClient"))
.transform(new AgentBuilder.Transformer.ForAdvice()
.include(AgentLoad.class.getClassLoader())
.advice(ElementMatchers.named("execute").and(isAnnotatedWith(SdkInternalApi.class)),
AmazonHttpClientAdvice.class.getName()));
builder.installOn(inst);
does not do anything. You need to reassign the result of the builder chain to the builder variable again or call installOn within the chain.
Related
I'm trying to instrument Kotlin coroutines, similar to what's done here using a Javaagent. I don't want a Javaagent.
The first step is to intercept the creation, suspension and resumption of Coroutines defined in the DebugProbes. The code for that is as follows:
public class Instrumentor {
private static final Logger LOG = LoggerFactory.getLogger(Instrumentor.class);
public static void install() {
TypeDescription typeDescription = TypePool.Default.ofSystemLoader()
.describe("kotlin.coroutines.jvm.internal.DebugProbesKt")
.resolve();
new ByteBuddy()
.redefine(typeDescription, ClassFileLocator.ForClassLoader.ofSystemLoader())
.method(ElementMatchers.named("probeCoroutineCreated").and(ElementMatchers.takesArguments(1)))
.intercept(MethodDelegation.to(CoroutineCreatedAdvice.class))
.method(ElementMatchers.named("probeCoroutineResumed").and(ElementMatchers.takesArguments(1)))
.intercept(MethodDelegation.to(CoroutineResumedAdvice.class))
.method(ElementMatchers.named("probeCoroutineSuspended").and(ElementMatchers.takesArguments(1)))
.intercept(MethodDelegation.to(CoroutineSuspendedAdvice.class))
.make()
.load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION);
DebugProbes.INSTANCE.install();
}
public static void uninstall() {
DebugProbes.INSTANCE.uninstall();
}
public static class CoroutineCreatedAdvice {
#Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static Continuation<Object> exit(#Advice.Return(readOnly = false) Continuation<Object> retVal) {
LOG.info("Coroutine created: {}", retVal);
return retVal;
}
}
public static class CoroutineResumedAdvice {
#Advice.OnMethodEnter(suppress = Throwable.class)
public static void enter(#Advice.Argument(0) final Continuation<Object> continuation) {
LOG.info("Coroutine resumed: {}", continuation);
}
}
public static class CoroutineSuspendedAdvice {
#Advice.OnMethodEnter(suppress = Throwable.class)
public static void enter(#Advice.Argument(0) final Continuation<Object> continuation) {
LOG.info("Coroutine suspended: {}", continuation);
}
}
}
JUnit5 test to trigger interception:
class CoroutineInstrumentationTest {
companion object {
#JvmStatic
#BeforeAll
fun beforeAll() {
Instrumentor.install()
}
#JvmStatic
#AfterAll
fun afterAll() {
Instrumentor.uninstall()
}
}
#Test
fun testInterception() {
runBlocking {
println("Test")
}
}
}
However, no interception happens (confirmed by the absence of log statements and by using a debugger). I'm new to Byte Buddy, so it's possible I'm missing something. Any ideas?
Kotlin v1.4.10, Kotlin Coroutines v1.3.9, Byte Buddy v1.10.17.
Are you sure the class is not yet loaded at this point? Try setting a breakpoint in ClassInjector.UsingReflection to see if you acutally walk through or of the injection is aborted due to a previously loaded class.
The cleaner solution would be a Java agent. You can use byte-buddy-agent to create one dynamically by ByteBuddyAgent.install() and then register an AgentBuilder on it.
I switched from Apache Commons CLI to Picocli because of the sub command support (and annotation-based declaration).
Consider a command line tool like git, with sub commands like push. Git have a main switch --verbose or -v for enable verbose mode in all sub commands.
How can I implement a main switch that is executed before any sub commands?
This is my test
#CommandLine.Command(name = "push",
description = "Update remote refs along with associated objects")
class PushCommand implements Callable<Void> {
#Override
public Void call() throws Exception {
System.out.println("#PushCommand.call");
return null;
}
}
#CommandLine.Command(description = "Version control", subcommands = {PushCommand.class})
public class GitApp implements Callable<Void> {
#CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "Display this help message.")
private boolean usageHelpRequested;
#CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting.")
private boolean verboseMode;
public static void main(String[] args) {
GitApp app = new GitApp();
CommandLine.call(app, "--verbose", "push");
System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
}
#Override
public Void call() throws Exception {
System.out.println("#GitApp.call");
return null;
}
}
Output is
#PushCommand.call
#GitApp.main after. verbose: true
I would expect, that GitApp.call get called before the sub command get called. But only the sub command get called.
The CommandLine.call (and CommandLine.run) methods only invoke the last subcommand by design, so what you are seeing in the original post is the expected behaviour.
The call and run methods are actually a shortcut. The following two lines are equivalent:
CommandLine.run(callable, args); // internally uses RunLast, equivalent to:
new CommandLine(callable).parseWithHandler(new RunLast(), args);
Update: from picocli 4.0, the above methods are deprecated, and replaced with new CommandLine(myapp).execute(args). The "handler" is now called the "execution strategy" (example below).
There is also a RunAll handler that runs all commands that were matched. The following main method gives the desired behaviour:
public static void main(String[] args) {
args = new String[] { "--verbose", "push" };
GitApp app = new GitApp();
// before picocli 4.0:
new CommandLine(app).parseWithHandler(new RunAll(), args);
// from picocli 4.0:
//new CommandLine(app).setExecutionStrategy(new RunAll()).execute(args);
System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
}
Output:
#GitApp.call
#PushCommand.call
#GitApp.main after. verbose: true
You may also be interested in the #ParentCommand annotation. This tells picocli to inject an instance of the parent command into a subcommand. Your subcommand can then call methods on the parent command, for example to check whether verbose is true. For example:
Update: from picocli 4.0, use the setExecutionStrategy method to specify RunAll. The below example is updated to use the new picocli 4.0+ API.
import picocli.CommandLine;
import picocli.CommandLine.*;
#Command(name = "push",
description = "Update remote refs along with associated objects")
class PushCommand implements Runnable {
#ParentCommand // picocli injects the parent instance
private GitApp parentCommand;
public void run() {
System.out.printf("#PushCommand.call: parent.verbose=%s%n",
parentCommand.verboseMode); // use parent instance
}
}
#Command(description = "Version control",
mixinStandardHelpOptions = true, // auto-include --help and --version
subcommands = {PushCommand.class,
HelpCommand.class}) // built-in help subcommand
public class GitApp implements Runnable {
#Option(names = {"-v", "--verbose"},
description = "Verbose mode. Helpful for troubleshooting.")
boolean verboseMode;
public void run() {
System.out.println("#GitApp.call");
}
public static void main(String[] args) {
args = new String[] { "--verbose", "push" };
GitApp app = new GitApp();
int exitCode = new CommandLine(app)
.setExecutionStrategy(new RunAll())
.execute(args);
System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
System.exit(exitCode);
}
}
Other minor edits: made the annotations a bit more compact by importing the inner classes. You may also like the mixinStandardHelpOptions attribute and the built-in help subcommand that help reduce boilerplate code.
As Picocli supports inheritance with Options I've extracted the --help and --verbose Option into an abstract class BaseCommand and invoke super.call from the subcommands.
abstract class BaseCommand implements Callable<Void> {
#CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "Display this help message.")
private boolean usageHelpRequested;
#CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting.")
private boolean verboseMode;
#Override
public Void call() throws Exception {
if (verboseMode) {
setVerbose();
}
return null;
}
private void setVerbose() {
System.out.println("enter verbose mode");
}
}
#CommandLine.Command(name = "push",
description = "Update remote refs along with associated objects")
class PushCommand extends BaseCommand {
#Override
public Void call() throws Exception {
super.call();
System.out.println("Execute push command");
return null;
}
}
#CommandLine.Command(description = "Version control", subcommands = {PushCommand.class})
public class GitApp extends BaseCommand {
public static void main(String[] args) {
GitApp app = new GitApp();
CommandLine.call(app, "push", "--verbose");
}
#Override
public Void call() throws Exception {
super.call();
System.out.println("GitApp.call called");
return null;
}
}
SO! I've reached an impasse regarding code-design. Here's the scenario.
I am required to either copy, move or delete files. OK, sure, no problem. I can easily write it like this.
public class SimpleFileManager {
public void copy(String sourcePath, String targetPath) { ... }
public void move(String sourcePath, String targetPath) { ... }
public void delete(String filePath) { ... }
}
and call it from a client code in a manner like this, for example:
public static void main(String[] args) {
...
fileManager.copy(x, y);
...
}
However, a request arises that some particular POJOs which have the FileManager reference perform specific operations, depending on some configuration. The actual specification of which POJO instance should do what is contained in config.
Here's an example of what I mean:
public static void main(String[] args) {
...
InvokerPojo invokerPojo1 = new InvokerPojo(invokerPojo1Config, fileManager); // this config tells it to copy files only
InvokerPojo invokerPojo2 = new InvokerPojo(invokerPojo2Config, fileManager); // this config tells it to move files only
InvokerPojo invokerPojo3 = new InvokerPojo(invokerPojo3Config, fileManager); // this config tells it to delete files only
}
So, FileManager provides the functionality to do actual operations, while InvokerPojo simply invokes and delegates those methods based on config.
However, I do not want to be coupled to FileManager, because, for example, I may find some library that provides the same functionality but is much better than mine.
So, I was thinking something like this:
public interface FileManagerDelegator {
void copy(String sourcePath, String targetPath);
void move(String sourcePath, String targetPath);
void delete(String filePath);
}
public class MyFileManagerDelegator implements FileManagerDelegator {
private SimpleFileManager simpleFileManager;
void copy(String sourcePath, String targetPath) { // delegate to simpleFileManager }
void move(String sourcePath, String targetPath) { // delegate to simpleFileManager }
void delete(String filePath) { // delegate to simpleFileManager }
}
public class ComplexFileManagerDelegator implements FileManagerDelegator {
private ComplexFileManager complexFileManager;
void copy(String sourcePath, String targetPath) { // delegate to complexFileManager }
void move(String sourcePath, String targetPath) { // delegate to complexFileManager }
void delete(String filePath) { // delegate to complexFileManager }
}
public interface Command {
void execute();
}
public class CopyCommand() {
private FileManagerDelegator delegator;
String sourcePath;
String targetPath;
void execute() {
delegator.copy(sourcePath, targetPath);
}
}
public class MoveCommand() {
private FileManagerDelegator delegator;
String sourcePath;
String targetPath;
void execute() {
delegator.move(sourcePath, targetPath);
}
}
public class DeleteCommand() {
private FileManagerDelegator delegator;
String filePath;
void execute() {
delegator.delete(filePath);
}
}
public static void main(String[] args) {
...
Command c = getCommand(context);
c.execute();
...
}
Now, the problem is in actually creating that particular command, because I do not want to know which command is being created. All I know is there is info in context that creates it.
I was thinking about having a factory that would create the appropriate Command from context.
The main issue arises in number of parameters for command.
If the only operations that existed were copy and move, then it would have been easy
public interface Command {
void execute(String a, String b);
}
However, since there is a delete operation which takes only one, then I'd either have to have to ignore the other argument in the call or add another method to interface, and I consider both to be bad.
I also don't want to send a variable number of arguments like this:
public interface Command {
void execute(String ... args);
}
because it's just a bit prettier version of this beforemention bad design.
So, would a factory based on context be a bit more cleaner, in a way that my client code doesn't know which operation is being called, and on which receiver:
Example of 2 contexts:
Context copyContext = new Context();
copyContext.set("OPERATION", "COPY");
copyContext.set("sourcePath", sourcePath);
copyContext.set("targetPath", targetPath);
Context deleteContext = new Context();
deleteContext.set("OPERATION", "DELETE");
deleteContext.set("filePath", filePath);
And then, in the factory, I could do something like this:
Command getCommand(FileManagerDelegator delegator, Context context) {
String operation = context.get("OPERATION");
if (operation.equals("COPY")) {
String sourcePath = context.get("sourcePath");
String targetPath = context.get("targetPath");
return new CopyCommand(sourcePath, targetPath, delegator);
} else if (operation.equals("DELETE")) {
String filePath = context.get("filePath");
return new DeleteCommand(filePath, delegator);
} else {
...
}
}
Is there a cleaner, more configurable way to create parametrized command objects on the fly (dynamically configure them, from context) that operate with different (number of) arguments?
Following is the code I am trying to unit test
public final class ClassToBeTested {
public static void function(String arg) {
ProcessBuilder pb = new ProcessBuilder(arg);
//Using pb here
}
}
I want to mock the constructor invocation (new File(arg)), here
I tried using Power Mock :
#PrepareForTest({ClassToBeTested.class})
public class TestClass {
#Test
public void functionTest() throws Exception {
String str = "abc";
ProcessBuilder mockProcessBuilder = PowerMock.createMock(ProcessBuilder.class);
PowerMock.expectNew(ProcessBuilder.class, str).andReturn(mockProcessBuilder);
PowerMock.replay(mockProcessBuilder, ProcessBuilder.class);
ClassToBeTested.function(abc);
}
}
This doesn't seem to work. As new ProcessBuilder(arg) is not returning the mocked object.
Adding #RunWith(PowerMockRunner.class) helped to resolve the issue.
Also using PowerMock.replayAll().
I am creating a set of junit test classes ,all of which read from the same input data files.I created a test suite as below,but found that I would be replicating the filenames in each test class.
So, how do I do this without repeating the code..
#RunWith(Suite.class)
#SuiteClasses({SomeTests.class,someOtherTests.class})
public class AllTests{
}
-------------------
public class SomeTests{
private String[] allfiles;
public SomeTests() {
allfiles = new String[] {"data1.txt","data2.txt"};
}
#Test
public void testXX1(){
//
}
#Test
public void testXX2(){
//
}
}
public class someOtherTests{
private String[] allfiles;
public someOtherTests() {
allfiles = new String[] {"data1.txt","data2.txt"};
}
#Test
public void testYY(){
//
}
}
I thought I would have to make another class to provide the filenames as a String array..sothat the test classes can initialize the allfiles variable by calling the getFileNames() static method,combining this this with BeforeClass annotation
public class FileNames {
public static String[] getFileNames() {
return new String[]{"data1.txt","data2.txt"};
}
}
public class SomeTests{
private String[] allfiles;
public SomeTests() {
}
#BeforeClass
public void setUp(){
allfiles = FileNames.getFileNames();
}
#Test
public void testXX1(){
//
}
#Test
public void testXX2(){
//
}
}
but I am not sure that is the right way. This will require setUp() to be declared as static ,and that means I will have to make the instance variable allfiles static !
I think this is a common scenario in junit testing ..so can someone please tell me how to do this properly?
Use #Before instead of #BeforeClass, then your setUp() method need not be static.
However, unless you are going to modify your filename array in your tests, you could also create a base class for your tests and declare a protected constant with those names:
public class FileBasedTests {
protected static final String[] FILENAMES = {"data1.txt","data2.txt"}
}
public class SomeTests extends FileBasedTests {
...
}
If you really are concerned about each test having its own copy of those file names, you can write allFiles = FILENAMES.clone().