108 lines
4.1 KiB
Java
108 lines
4.1 KiB
Java
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.util.*;
|
|
import java.util.jar.JarEntry;
|
|
import java.util.jar.JarFile;
|
|
|
|
import org.objectweb.asm.ClassReader;
|
|
import org.objectweb.asm.Opcodes;
|
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
|
import org.objectweb.asm.tree.ClassNode;
|
|
import org.objectweb.asm.tree.JumpInsnNode;
|
|
import org.objectweb.asm.tree.MethodNode;
|
|
import org.objectweb.asm.util.Printer;
|
|
|
|
public class Analyzer {
|
|
|
|
private static long instructionCount = 0L;
|
|
private static long invokeInstructionCount = 0L;
|
|
private static long branchInstructionCount = 0L;
|
|
private static final Map<Integer, Long> instructionCounts = new HashMap<>();
|
|
private static long classCount = 0L;
|
|
private static long methodCount = 0L;
|
|
|
|
public static void main(final String[] args) throws IOException {
|
|
// initialize instruction map
|
|
for (int i = 0; i < 256; i++) instructionCounts.put(i, 0L);
|
|
|
|
final String jarFileName = args[0];
|
|
System.out.println("Analyzing " + jarFileName);
|
|
final JarFile jar = new JarFile(jarFileName);
|
|
analyzeJar(jar);
|
|
printStats();
|
|
}
|
|
|
|
private static void printStats() {
|
|
System.out.println("=== STATISTICS ===");
|
|
System.out.println("classes:\t" + classCount);
|
|
System.out.println("methods:\t" + methodCount);
|
|
System.out.println("instructions:\t" + instructionCount);
|
|
System.out.println("invoke instructions:\t" + invokeInstructionCount);
|
|
System.out.println("branch instructions:\t" + branchInstructionCount);
|
|
|
|
System.out.println("OPCODE\tMNEMONIC\tCOUNT");
|
|
instructionCounts.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getKey))
|
|
.forEach(e -> System.out.printf("%d\t%s\t%d\n",
|
|
e.getKey(),
|
|
e.getKey() >= Printer.OPCODES.length ? "" : Printer.OPCODES[e.getKey()],
|
|
e.getValue()));
|
|
}
|
|
|
|
private static void analyzeJar(final JarFile jar) throws IOException {
|
|
final Enumeration<JarEntry> entries = jar.entries();
|
|
while (entries.hasMoreElements()) {
|
|
final JarEntry entry = entries.nextElement();
|
|
analyzeJarEntry(entry, jar);
|
|
}
|
|
}
|
|
|
|
private static void analyzeJarEntry(final JarEntry entry, final JarFile jar) throws IOException {
|
|
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
|
|
System.out.println(" File " + entry.getName());
|
|
final InputStream is = jar.getInputStream(entry);
|
|
final ClassReader classReader = new ClassReader(is);
|
|
final ClassNode classNode = new ClassNode();
|
|
classReader.accept(classNode, ClassReader.SKIP_FRAMES);
|
|
analyzeClass(classNode);
|
|
}
|
|
}
|
|
|
|
private static void analyzeClass(final ClassNode classNode) {
|
|
System.out.println(" Class " + classNode.name);
|
|
classCount++;
|
|
|
|
final List<MethodNode> methods = classNode.methods;
|
|
for (final MethodNode methodNode : methods) {
|
|
analyzeMethod(methodNode);
|
|
}
|
|
}
|
|
|
|
private static void analyzeMethod(final MethodNode methodNode) {
|
|
System.out.println(" Method " + methodNode.name + methodNode.desc);
|
|
methodCount++;
|
|
|
|
for (int i = 0; i < methodNode.instructions.size(); i++) {
|
|
final AbstractInsnNode ins = methodNode.instructions.get(i);
|
|
if (ins.getOpcode() == -1) continue;
|
|
|
|
instructionCount++;
|
|
switch (ins.getType()) {
|
|
case AbstractInsnNode.LOOKUPSWITCH_INSN:
|
|
case AbstractInsnNode.TABLESWITCH_INSN:
|
|
branchInstructionCount++;
|
|
break;
|
|
|
|
case AbstractInsnNode.JUMP_INSN:
|
|
if (ins.getOpcode() != Opcodes.GOTO) branchInstructionCount++;
|
|
break;
|
|
|
|
case AbstractInsnNode.METHOD_INSN:
|
|
case AbstractInsnNode.INVOKE_DYNAMIC_INSN:
|
|
invokeInstructionCount++;
|
|
}
|
|
|
|
instructionCounts.compute(ins.getOpcode(), (k, v) -> v == null ? 1 : (v + 1));
|
|
}
|
|
}
|
|
}
|