dangling changes

This commit is contained in:
Claudio Maggioni 2024-01-03 10:14:18 +01:00
parent 604bb84031
commit e589a83c98
4 changed files with 153 additions and 19 deletions

View file

@ -10,10 +10,13 @@ export PATH="${JAVA_HOME}/bin:$PATH"
### Compile the agent as JAR
```shell
export JAVA_HOME=`/usr/libexec/java_home -v 1.8.0_345`
export PATH="${JAVA_HOME}/bin:$PATH"
cd agent/src
find . -name '*.java' -print -exec javac -cp ../lib/\*:. -d ../../out/production/agent \{\} \;
cd ..
jar cfm agent.jar manifest.txt -C ../out/production/agent .
jar tf agent.jar
```
## Profiler
@ -21,6 +24,8 @@ jar cfm agent.jar manifest.txt -C ../out/production/agent .
### Compile the profiler
```shell
export JAVA_HOME=`/usr/libexec/java_home -v 1.8.0_345`
export PATH="${JAVA_HOME}/bin:$PATH"
cd profiler/src
find . -name '*.java' -print -exec javac -d ../../out/production/profiler \{\} \;
cd ../..
@ -31,6 +36,8 @@ cd ../..
### Compile the application
```shell
export JAVA_HOME=`/usr/libexec/java_home -v 1.8.0_345`
export PATH="${JAVA_HOME}/bin:$PATH"
cd application/src
find . -name '*.java' -print -exec javac -d ../../out/production/application \{\} \;
cd ../..
@ -39,5 +46,7 @@ cd ../..
### Run application with agent
```shell
export JAVA_HOME=`/usr/libexec/java_home -v 1.8.0_345`
export PATH="${JAVA_HOME}/bin:$PATH"
java -javaagent:agent/agent.jar=hello -cp out/production/application -Xbootclasspath/p:out/production/profiler ch.usi.inf.sp.dbi.Application
```

View file

@ -8,6 +8,7 @@ import org.objectweb.asm.tree.*;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.Set;
public class ClassTransformer implements ClassFileTransformer {
public static byte[] instrument(final byte[] bytecode) {
@ -24,36 +25,65 @@ public class ClassTransformer implements ClassFileTransformer {
private static void instrumentClassNode(final ClassNode cn) {
for (final MethodNode mn : cn.methods) {
InsnList patch = new InsnList(); // instructions to be added
patch.add(new LdcInsnNode("Method " + mn.name + mn.desc + " called"));
patch.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
"ch/usi/inf/sp/dbi/profiler/Profiler",
"log",
"(Ljava/lang/String;)V"));
mn.instructions.insert(patch);
instrumentEntry(cn, mn);
instrumentExit(cn, mn);
}
}
private static void instrumentEntry(ClassNode cn, MethodNode mn) {
InsnList patch = new InsnList(); // instructions to be added
patch.add(new LdcInsnNode(cn.name));
patch.add(new LdcInsnNode(mn.name));
patch.add(new LdcInsnNode(mn.desc));
patch.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
"ch/usi/inf/sp/dbi/profiler/Profiler",
"entry",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"));
mn.instructions.insert(patch);
}
private static void instrumentExit(ClassNode cn, MethodNode mn) {
for (AbstractInsnNode i : mn.instructions) {
if (i.getOpcode() >= Opcodes.IRETURN && i.getOpcode() <= Opcodes.RETURN) {
InsnList patch = new InsnList(); // instructions to be added
patch.add(new LdcInsnNode(cn.name));
patch.add(new LdcInsnNode(mn.name));
patch.add(new LdcInsnNode(mn.desc));
patch.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
"ch/usi/inf/sp/dbi/profiler/Profiler",
"exit",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"));
mn.instructions.insertBefore(i, patch);
}
}
}
@Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (className.startsWith("java/") ||
className.startsWith("sun/") ||
className.startsWith("jdk/") ||
className.startsWith("ch/usi/inf/sp/dbi/agent/") ||
className.startsWith("ch/usi/inf/sp/dbi/profiler/")) {
// className.startsWith("java/") ||
// className.startsWith("javax/") ||
// className.startsWith("sun/") ||
// className.startsWith("com/sun/") ||
// className.startsWith("jdk/") ||
if (!className.startsWith("ch/usi/inf/sp/dbi/") ||
className.startsWith("ch/usi/inf/sp/dbi/agent/") ||
className.startsWith("ch/usi/inf/sp/dbi/profiler/")) {
System.out.println("Skipping class <" + loader + ", " + className + ">");
return classfileBuffer;
}
System.out.println("About to transform class <" + loader + ", " + className + ">");
try {
return instrument(classfileBuffer); // no changes
return instrument(classfileBuffer);
} catch (Throwable e) {
e.printStackTrace(System.err);
return classfileBuffer;

View file

@ -2,10 +2,17 @@ package ch.usi.inf.sp.dbi;
public class Application {
public static void main(String... args) {
sayHi();
fib(20);
}
public static void sayHi() {
System.out.println("Hello!");
public static int fib(int n) {
System.out.println("fib(" + n + ")");
if (n == 0 || n == 1) {
return n;
} else {
int i = fib(n-1) + fib(n-2);
System.out.println("fib(" + n + ") = " + i);
return i;
}
}
}

View file

@ -1,7 +1,95 @@
package ch.usi.inf.sp.dbi.profiler;
import javax.sound.midi.*;
import javax.swing.*;
public class Profiler {
public static void log(String what) {
System.out.println("PROFILER LOG: " + what);
private static final DefaultListModel<String> model = new DefaultListModel<>();
private static int size = 0;
static {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Profiler");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
model.add(0, "hello");
JList<String> list = new JList<>(model);
JScrollPane p = new JScrollPane(list);
frame.add(p);
frame.pack();
frame.setVisible(true);
});
}
private static MidiEvent createProgramChangeEvent(int channel, int program) throws InvalidMidiDataException {
ShortMessage message = new ShortMessage();
message.setMessage(ShortMessage.PROGRAM_CHANGE, channel, program, 0);
return new MidiEvent(message, 0);
}
public static void play() throws InterruptedException {
final Thread midi = new Thread(() -> {
try {
Sequencer player = MidiSystem.getSequencer();
player.open();
Sequence seq = new Sequence(Sequence.PPQ, 4);
Track track = seq.createTrack();
int channel = 1;
int pitch = size * 2;
pitch = 20 + pitch % 118;
track.add(createProgramChangeEvent(channel, 54));
ShortMessage a = new ShortMessage();
a.setMessage(ShortMessage.NOTE_ON, channel, pitch, 100);
MidiEvent noteOn = new MidiEvent(a, 0);
track.add(noteOn);
ShortMessage b = new ShortMessage();
b.setMessage(ShortMessage.NOTE_OFF, channel, pitch, 100);
MidiEvent noteOff = new MidiEvent(b, 2);
track.add(noteOff);
player.setSequence(seq);
player.start();
} catch (Exception ex) {
ex.printStackTrace();
}
});
midi.start();
midi.join();
}
public static void entry(String clazz, String method, String desc) {
size++;
model.add(0, clazz + "." + method + desc);
log(clazz, method, desc, "entry");
}
public static void exit(String clazz, String method, String desc) {
size--;
model.remove(0);
log(clazz, method, desc, "exit");
}
private static void log(String clazz, String method, String desc, String type) {
try {
System.err.println("Profiler: " + type + " " + clazz + "." + method + desc);
play();
try {
Thread.sleep(50);
} catch (InterruptedException ignored) {
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}