diff --git a/src/main/java/org/adoptopenjdk/jitwatch/core/JITWatchConstants.java b/src/main/java/org/adoptopenjdk/jitwatch/core/JITWatchConstants.java index a35be288..36c99120 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/core/JITWatchConstants.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/core/JITWatchConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ @@ -236,6 +236,7 @@ public static final Set getAutoGeneratedClassPrefixes() public static final String S_XML_DOCTYPE_START = " instructions = memberBytecode.getInstructions(); + + if (instructions != null && instructions.size() > 0) + { + BytecodeInstruction lastInstruction = instructions.get(instructions.size() - 1); + + // final instruction is a return for 1 byte + int bcSize = 1 + lastInstruction.getOffset(); + + MemberSignatureParts msp = memberBytecode.getMemberSignatureParts(); + + if (bcSize >= freqInlineSize && !S_STATIC_INIT.equals(msp.getMemberName())) + { + builder.append(C_DOUBLE_QUOTE); + builder.append(className); + builder.append(C_DOUBLE_QUOTE); + builder.append(C_COMMA); + + builder.append(C_DOUBLE_QUOTE); + builder.append(msp.getMemberName()); + builder.append(C_DOUBLE_QUOTE); + builder.append(C_COMMA); + + builder.append(bcSize); + builder.append(S_NEWLINE); + } + } + } +} diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/IJarScanOperation.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/IJarScanOperation.java new file mode 100644 index 00000000..b20bde6d --- /dev/null +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/IJarScanOperation.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2013-2016 Chris Newland. + * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD + * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki + */ +package org.adoptopenjdk.jitwatch.jarscan; + +import org.adoptopenjdk.jitwatch.model.bytecode.MemberBytecode; + +public interface IJarScanOperation +{ + void processInstructions(String className, MemberBytecode memberBytecode); + + String getReport(); +} \ No newline at end of file diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/JarScan.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/JarScan.java index 089faf08..ff56d11c 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/JarScan.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/JarScan.java @@ -1,35 +1,51 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ package org.adoptopenjdk.jitwatch.jarscan; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COLON; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOT; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOT_CLASS; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SLASH; + import java.io.File; import java.io.IOException; import java.io.PrintWriter; +import java.io.Writer; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import org.adoptopenjdk.jitwatch.jarscan.bytecodefrequency.BytecodeFrequencyTree; +import org.adoptopenjdk.jitwatch.jarscan.chains.ChainCounter; +import org.adoptopenjdk.jitwatch.jarscan.freqinline.FreqInlineCounter; +import org.adoptopenjdk.jitwatch.jarscan.invokecounter.InvokeCounter; import org.adoptopenjdk.jitwatch.loader.BytecodeLoader; -import org.adoptopenjdk.jitwatch.model.MemberSignatureParts; -import org.adoptopenjdk.jitwatch.model.bytecode.BytecodeInstruction; import org.adoptopenjdk.jitwatch.model.bytecode.ClassBC; import org.adoptopenjdk.jitwatch.model.bytecode.MemberBytecode; -import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.*; - -public final class JarScan +public class JarScan { - private JarScan() + private List operations = new ArrayList<>(); + + private Writer writer; + + public JarScan(Writer writer) { + this.writer = writer; } - @SuppressWarnings("unchecked") - public static void iterateJar(File jarFile, int maxMethodBytes, PrintWriter writer) throws IOException + public void addOperation(IJarScanOperation op) + { + operations.add(op); + } + + public void iterateJar(File jarFile) throws IOException { List classLocations = new ArrayList<>(); @@ -37,6 +53,7 @@ public static void iterateJar(File jarFile, int maxMethodBytes, PrintWriter writ try (ZipFile zip = new ZipFile(jarFile)) { + @SuppressWarnings("unchecked") Enumeration list = (Enumeration) zip.entries(); while (list.hasMoreElements()) @@ -47,15 +64,28 @@ public static void iterateJar(File jarFile, int maxMethodBytes, PrintWriter writ if (name.endsWith(S_DOT_CLASS)) { - String fqName = name.replace(S_SLASH, S_DOT).substring(0, name.length() - 6); + String fqName = name.replace(S_SLASH, S_DOT).substring(0, name.length() - S_DOT_CLASS.length()); - process(classLocations, fqName, maxMethodBytes, writer); + process(classLocations, fqName); } } } + + createReport(); + } + + private void createReport() throws IOException + { + for (IJarScanOperation op : operations) + { + String report = op.getReport(); + + writer.write(report); + writer.flush(); + } } - private static void process(List classLocations, String className, int maxMethodBytes, PrintWriter writer) + private void process(List classLocations, String className) { ClassBC classBytecode = BytecodeLoader.fetchBytecodeForClass(classLocations, className); @@ -63,33 +93,18 @@ private static void process(List classLocations, String className, int m { for (MemberBytecode memberBytecode : classBytecode.getMemberBytecodeList()) { - List instructions = memberBytecode.getInstructions(); - - if (instructions != null && instructions.size() > 0) + for (IJarScanOperation op : operations) { - BytecodeInstruction lastInstruction = instructions.get(instructions.size() - 1); - - // final instruction is a return for 1 byte - int bcSize = 1 + lastInstruction.getOffset(); - - MemberSignatureParts msp = memberBytecode.getMemberSignatureParts(); - - if (bcSize >= maxMethodBytes && !S_STATIC_INIT.equals(msp.getMemberName())) + try { - writer.print(C_DOUBLE_QUOTE); - writer.print(className); - writer.print(C_DOUBLE_QUOTE); - writer.print(C_COMMA); - - writer.print(C_DOUBLE_QUOTE); - writer.print(msp.getMemberName()); - writer.print(C_DOUBLE_QUOTE); - writer.print(C_COMMA); - - writer.print(bcSize); - writer.println(); - - writer.flush(); + op.processInstructions(className, memberBytecode); + } + catch (Exception e) + { + System.err.println("Could not process " + className + " " + memberBytecode.getMemberSignatureParts().getMemberName()); + System.err.println(memberBytecode.toString()); + e.printStackTrace(); + System.exit(-1); } } } @@ -101,23 +116,68 @@ private static void process(List classLocations, String className, int m } } + private static void showUsage() + { + System.err.println("JarScan [options] [jar]..."); + System.err.println("Options:"); + System.err.println("-DmaxMethodSize=n\t\t\tFind methods with bytecode larger than n bytes"); + System.err.println("-DmaxBytecodeChain=n\t\t\tCount bytecode chains of length n"); + System.err.println("-DmaxFrequencyTreeChildren=n\t\t\tFind the n most frequent next bytecodes"); + System.err.println("-DinvokeCount\t\t\tCount methods by invoke type"); + } + public static void main(String[] args) throws IOException { - int maxMethodBytes = Integer.getInteger("maxMethodSize", 325); + if (args.length == 0) + { + showUsage(); + System.exit(-1); + } + + Writer writer = new PrintWriter(System.out); + + JarScan scanner = new JarScan(writer); - PrintWriter writer = new PrintWriter(System.out); + if (System.getProperty("maxMethodSize") != null) + { + int maxMethodBytes = Integer.getInteger("maxMethodSize"); + scanner.addOperation(new FreqInlineCounter(maxMethodBytes)); + } + else if (System.getProperty("maxBytecodeChain") != null) + { + int maxBytecodeChain = Integer.getInteger("maxBytecodeChain"); + scanner.addOperation(new ChainCounter(maxBytecodeChain)); + } + else if (System.getProperty("maxFrequencyTreeChildren") != null) + { + int maxFrequencyTreeChildren = Integer.getInteger("maxFrequencyTreeChildren"); + + scanner.addOperation(new BytecodeFrequencyTree(maxFrequencyTreeChildren)); + } + else if (System.getProperty("invokeCount") != null) + { + scanner.addOperation(new InvokeCounter()); + } + + if (scanner.operations.size() == 0) + { + // default mode is to report methods > 325 bytes + int maxMethodBytes = Integer.getInteger("maxMethodSize", 325); + scanner.addOperation(new FreqInlineCounter(maxMethodBytes)); + } for (String jar : args) { File jarFile = new File(jar); - writer.print(jarFile.getAbsolutePath()); + writer.write(jarFile.getAbsolutePath()); - writer.println(C_COLON); + writer.write(C_COLON); + writer.write(S_NEWLINE); - iterateJar(jarFile, maxMethodBytes, writer); + scanner.iterateJar(jarFile); - writer.println(); + writer.write(S_NEWLINE); } writer.flush(); diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/bytecodefrequency/BytecodeFrequencyTree.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/bytecodefrequency/BytecodeFrequencyTree.java new file mode 100644 index 00000000..3f511311 --- /dev/null +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/bytecodefrequency/BytecodeFrequencyTree.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2013-2016 Chris Newland. + * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD + * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki + */ +package org.adoptopenjdk.jitwatch.jarscan.bytecodefrequency; + +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_COMMA; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; + +import java.text.NumberFormat; +import java.util.EnumMap; +import java.util.Map; + +import org.adoptopenjdk.jitwatch.jarscan.chains.ChainCounter; +import org.adoptopenjdk.jitwatch.jarscan.chains.OpcodeSequence; +import org.adoptopenjdk.jitwatch.model.bytecode.Opcode; + +public class BytecodeFrequencyTree extends ChainCounter +{ + private Map nextBytecodeMap; + + private int maxChildren = 0; + + public BytecodeFrequencyTree(int maxChildren) + { + super(2); + + this.maxChildren = maxChildren; + } + + private void calculate() + { + nextBytecodeMap = new EnumMap<>(Opcode.class); + + for (Map.Entry entry : chainCountMap.entrySet()) + { + OpcodeSequence sequence = entry.getKey(); + + Opcode root = sequence.getOpcodeAtIndex(0); + Opcode next = sequence.getOpcodeAtIndex(1); + + int count = entry.getValue(); + + NextBytecodeList nextBytecodeList = nextBytecodeMap.get(root); + + if (nextBytecodeList == null) + { + nextBytecodeList = new NextBytecodeList(); + + nextBytecodeMap.put(root, nextBytecodeList); + } + + nextBytecodeList.add(new NextBytecode(next, count)); + } + } + + public Map getNextBytecodeMap() + { + if (nextBytecodeMap == null) + { + calculate(); + } + + return nextBytecodeMap; + } + + @Override + public String getReport() + { + if (nextBytecodeMap == null) + { + calculate(); + } + + StringBuilder builder = new StringBuilder(); + + for (Map.Entry entry : nextBytecodeMap.entrySet()) + { + Opcode root = entry.getKey(); + + NextBytecodeList nextBytecodeList = entry.getValue(); + + int sum = nextBytecodeList.getSum(); + + NumberFormat percentFormatter = NumberFormat.getPercentInstance(); + percentFormatter.setMinimumFractionDigits(1); + + int reportLines = 0; + + for (NextBytecode nextBytecode : nextBytecodeList.getList()) + { + double percent = (double) nextBytecode.getCount() / (double) sum; + + String percentString = percentFormatter.format(percent); + + builder.append(root.getMnemonic()).append(S_COMMA).append(nextBytecode.getOpcode().getMnemonic()).append(S_COMMA) + .append(nextBytecode.getCount()).append(S_COMMA).append(sum).append(S_COMMA).append(percentString) + .append(S_NEWLINE); + + reportLines++; + + if (reportLines == maxChildren) + { + break; + } + } + } + + return builder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/bytecodefrequency/NextBytecode.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/bytecodefrequency/NextBytecode.java new file mode 100644 index 00000000..25f2f77f --- /dev/null +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/bytecodefrequency/NextBytecode.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013-2016 Chris Newland. + * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD + * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki + */ +package org.adoptopenjdk.jitwatch.jarscan.bytecodefrequency; + +import org.adoptopenjdk.jitwatch.model.bytecode.Opcode; + +public class NextBytecode +{ + private Opcode opcode; + + private int count; + + public NextBytecode(Opcode opcode, int count) + { + this.opcode = opcode; + this.count = count; + } + + public Opcode getOpcode() + { + return opcode; + } + + public int getCount() + { + return count; + } +} diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/bytecodefrequency/NextBytecodeList.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/bytecodefrequency/NextBytecodeList.java new file mode 100644 index 00000000..88ff01ce --- /dev/null +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/bytecodefrequency/NextBytecodeList.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013-2016 Chris Newland. + * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD + * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki + */ +package org.adoptopenjdk.jitwatch.jarscan.bytecodefrequency; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class NextBytecodeList +{ + private List list = new ArrayList<>(); + + private int sum = 0; + + public List getList() + { + Collections.sort(list, new Comparator() + { + @Override + public int compare(NextBytecode o1, NextBytecode o2) + { + return Integer.compare(o2.getCount(), o1.getCount()); + } + }); + + return list; + } + + public int getSum() + { + return sum; + } + + public void add(NextBytecode nextBytecode) + { + list.add(nextBytecode); + + sum += nextBytecode.getCount(); + } +} diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/chains/ChainCounter.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/chains/ChainCounter.java new file mode 100755 index 00000000..fde6baf3 --- /dev/null +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/chains/ChainCounter.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2013-2016 Chris Newland. + * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD + * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki + */ +package org.adoptopenjdk.jitwatch.jarscan.chains; + +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.adoptopenjdk.jitwatch.jarscan.IJarScanOperation; +import org.adoptopenjdk.jitwatch.model.bytecode.BCParamNumeric; +import org.adoptopenjdk.jitwatch.model.bytecode.BytecodeInstruction; +import org.adoptopenjdk.jitwatch.model.bytecode.MemberBytecode; +import org.adoptopenjdk.jitwatch.model.bytecode.Opcode; + +public class ChainCounter implements IJarScanOperation +{ + protected Map chainCountMap = new TreeMap<>(); + + private List chain = new LinkedList<>(); + + private int maxLength = 0; + + public ChainCounter(int maxLength) + { + this.maxLength = maxLength; + } + + private void storeChain() + { + OpcodeSequence sequence = new OpcodeSequence(chain); + + Integer count = chainCountMap.get(sequence); + + if (count == null) + { + chainCountMap.put(sequence, 1); + } + else + { + chainCountMap.put(sequence, count + 1); + } + } + + public Map getSequenceScores() + { + return chainCountMap; + } + + public List> getSortedData() + { + List> result = new ArrayList<>(chainCountMap.entrySet()); + + Collections.sort(result, new Comparator>() + { + @Override + public int compare(Map.Entry o1, Map.Entry o2) + { + return o2.getValue().compareTo(o1.getValue()); + } + }); + + return result; + } + + public int getCountForChain(String chain) + { + return chainCountMap.get(chain); + } + + public void reset() + { + chain.clear(); + } + + @Override + public void processInstructions(String className, MemberBytecode memberBytecode) + { + reset(); + + List instructions = memberBytecode.getInstructions(); + + for (int i = 0; i < instructions.size(); i++) + { + handleChainStartingAtIndex(i, instructions); + } + } + + private void handleChainStartingAtIndex(int index, List instructions) + { + boolean stopChain = false; + boolean abandonChain = false; + + Set visitedBCI = new HashSet<>(); + + while (chain.size() < maxLength) + { + BytecodeInstruction instruction = instructions.get(index); + + int instrBCI = instruction.getOffset(); + + visitedBCI.add(instrBCI); + + Opcode opcode = instruction.getOpcode(); + + // ======================= + // The Rules + // ======================= + + // *RETURN ends a chain. Chain is discarded if not required length + // INVOKE* drops through to next bytecode + // GOTO* is followed + // IF*, TABLESWITCH, and LOOKUPSWITCH - drop through + // JSR, JSR_W, RET are not followed - discard the chain + // ATHROW ends a chain + // loops are detected and end the parsing + + switch (opcode) + { + case IRETURN: + case LRETURN: + case FRETURN: + case DRETURN: + case ARETURN: + case RETURN: + stopChain = true; + break; + + case ATHROW: + stopChain = true; + break; + + case JSR: + case JSR_W: + case RET: + abandonChain = true; + break; + + case GOTO: + case GOTO_W: + int gotoBCI = ((BCParamNumeric) instruction.getParameters().get(0)).getValue(); + + if (!visitedBCI.contains(gotoBCI)) + { + index = getIndexForBCI(instructions, gotoBCI); + } + break; + + default: + index++; + break; + } + + chain.add(opcode); + + if (stopChain) + { + if (chain.size() == maxLength) + { + storeChain(); + } + + reset(); + break; + } + else if (abandonChain) + { + reset(); + break; + } + else if (chain.size() == maxLength) + { + storeChain(); + reset(); + break; + } + } + } + + private int getIndexForBCI(List instructions, int bci) + { + int index = -1; + + for (int i = 0; i < instructions.size(); i++) + { + BytecodeInstruction instruction = instructions.get(i); + + if (instruction.getOffset() == bci) + { + index = i; + break; + } + } + + return index; + } + + @Override + public String getReport() + { + StringBuilder builder = new StringBuilder(); + + for (Map.Entry entry : getSortedData()) + { + builder.append(entry.getKey().toString()).append(" : ").append(entry.getValue()).append(S_NEWLINE); + } + + return builder.toString(); + } +} diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/chains/OpcodeSequence.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/chains/OpcodeSequence.java new file mode 100644 index 00000000..cdb549f3 --- /dev/null +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/chains/OpcodeSequence.java @@ -0,0 +1,53 @@ +package org.adoptopenjdk.jitwatch.jarscan.chains; + +import java.util.ArrayList; +import java.util.List; + +import org.adoptopenjdk.jitwatch.model.bytecode.Opcode; + +public class OpcodeSequence implements Comparable +{ + private List sequence = new ArrayList<>(); + + public OpcodeSequence(List opcodeList) + { + sequence.addAll(opcodeList); + } + + public String toString() + { + StringBuilder builder = new StringBuilder(); + + for (Opcode opcode : sequence) + { + builder.append(opcode.getMnemonic()).append("->"); + } + + builder.delete(builder.length() - 2, builder.length()); + + return builder.toString(); + } + + public Opcode getOpcodeAtIndex(int index) + { + return sequence.get(index); + } + + @Override + public int hashCode() + { + return toString().hashCode(); + } + + @Override + public boolean equals(Object obj) + { + return toString().equals(obj.toString()); + } + + @Override + public int compareTo(OpcodeSequence o) + { + return toString().compareTo(o.toString()); + } +} \ No newline at end of file diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/freqinline/FreqInlineCounter.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/freqinline/FreqInlineCounter.java new file mode 100644 index 00000000..71498600 --- /dev/null +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/freqinline/FreqInlineCounter.java @@ -0,0 +1,84 @@ +package org.adoptopenjdk.jitwatch.jarscan.freqinline; + +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COMMA; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_DOUBLE_QUOTE; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_STATIC_INIT; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; + +import java.util.List; + +import org.adoptopenjdk.jitwatch.jarscan.IJarScanOperation; +import org.adoptopenjdk.jitwatch.model.MemberSignatureParts; +import org.adoptopenjdk.jitwatch.model.bytecode.BytecodeInstruction; +import org.adoptopenjdk.jitwatch.model.bytecode.MemberBytecode; +import org.adoptopenjdk.jitwatch.util.StringUtil; + +public class FreqInlineCounter implements IJarScanOperation +{ + private int freqInlineSize; + + private StringBuilder builder = new StringBuilder(); + + public FreqInlineCounter(int freqInlineSize) + { + this.freqInlineSize = freqInlineSize; + } + + public String getReport() + { + return builder.toString(); + } + + @Override + public void processInstructions(String className, MemberBytecode memberBytecode) + { + List instructions = memberBytecode.getInstructions(); + + if (instructions != null && instructions.size() > 0) + { + BytecodeInstruction lastInstruction = instructions.get(instructions.size() - 1); + + // final instruction is a return for 1 byte + int bcSize = 1 + lastInstruction.getOffset(); + + MemberSignatureParts msp = memberBytecode.getMemberSignatureParts(); + + if (bcSize >= freqInlineSize && !S_STATIC_INIT.equals(msp.getMemberName())) + { + String fqClassName = msp.getFullyQualifiedClassName(); + + builder.append(C_DOUBLE_QUOTE); + builder.append(StringUtil.getPackageName(fqClassName)); + builder.append(C_DOUBLE_QUOTE); + builder.append(C_COMMA); + + builder.append(C_DOUBLE_QUOTE); + builder.append(StringUtil.getUnqualifiedClassName(fqClassName)); + builder.append(C_DOUBLE_QUOTE); + builder.append(C_COMMA); + + builder.append(C_DOUBLE_QUOTE); + builder.append(msp.getMemberName()); + builder.append(C_DOUBLE_QUOTE); + builder.append(C_COMMA); + + builder.append(C_DOUBLE_QUOTE); + + if (msp.getParamTypes().size() > 0) + { + for (String param : msp.getParamTypes()) + { + builder.append(param).append(C_COMMA); + } + + builder.deleteCharAt(builder.length() - 1); + } + builder.append(C_DOUBLE_QUOTE); + builder.append(C_COMMA); + + builder.append(bcSize); + builder.append(S_NEWLINE); + } + } + } +} diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/invokecounter/InvokeCountMap.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/invokecounter/InvokeCountMap.java new file mode 100644 index 00000000..b942f187 --- /dev/null +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/invokecounter/InvokeCountMap.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013-2016 Chris Newland. + * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD + * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki + */ +package org.adoptopenjdk.jitwatch.jarscan.invokecounter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.adoptopenjdk.jitwatch.model.bytecode.Opcode; + +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COMMA; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_DOUBLE_QUOTE; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; + +public class InvokeCountMap +{ + private Map methodCountMap = new TreeMap<>(); + + public void count(String method) + { + Integer count = methodCountMap.get(method); + + if (count == null) + { + count = new Integer(1); + } + else + { + count++; + } + + methodCountMap.put(method, count); + } + + public String toString(Opcode prefix) + { + StringBuilder builder = new StringBuilder(); + + List> sortedList = new ArrayList<>(methodCountMap.entrySet()); + + Collections.sort(sortedList, new Comparator>() + { + @Override + public int compare(Map.Entry o1, Map.Entry o2) + { + return o2.getValue().compareTo(o1.getValue()); + } + }); + + for (Map.Entry entry : sortedList) + { + String methodName = entry.getKey(); + Integer count = entry.getValue(); + + builder.append(C_DOUBLE_QUOTE).append(prefix.getMnemonic()).append(C_DOUBLE_QUOTE).append(C_COMMA); + builder.append(C_DOUBLE_QUOTE).append(methodName).append(C_DOUBLE_QUOTE).append(C_COMMA); + builder.append(count).append(S_NEWLINE); + } + + return builder.toString(); + } +} diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/invokecounter/InvokeCounter.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/invokecounter/InvokeCounter.java new file mode 100644 index 00000000..a4490fd3 --- /dev/null +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/invokecounter/InvokeCounter.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013-2016 Chris Newland. + * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD + * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki + */ +package org.adoptopenjdk.jitwatch.jarscan.invokecounter; + +import java.util.List; +import org.adoptopenjdk.jitwatch.jarscan.IJarScanOperation; +import org.adoptopenjdk.jitwatch.model.bytecode.BytecodeInstruction; +import org.adoptopenjdk.jitwatch.model.bytecode.MemberBytecode; +import org.adoptopenjdk.jitwatch.model.bytecode.Opcode; +import org.adoptopenjdk.jitwatch.util.ParseUtil; + +public class InvokeCounter implements IJarScanOperation +{ + private OpcodeInvokeCountMap opcodeInvokeCountMap; + + public InvokeCounter() + { + opcodeInvokeCountMap = new OpcodeInvokeCountMap(); + } + + @Override + public String getReport() + { + return opcodeInvokeCountMap.toString(); + } + + private void count(String className, BytecodeInstruction instruction) + { + //System.out.println(className + " == " + instruction.getComment()); + + String comment = instruction.getCommentWithMemberPrefixStripped(); + + String methodSig = ParseUtil.bytecodeMethodCommentToReadableString(className, comment); + + opcodeInvokeCountMap.count(instruction.getOpcode(), methodSig); + } + + @Override + public void processInstructions(String className, MemberBytecode memberBytecode) + { + List instructions = memberBytecode.getInstructions(); + + for (BytecodeInstruction instruction : instructions) + { + Opcode opcode = instruction.getOpcode(); + + switch (opcode) + { + case INVOKEDYNAMIC: + case INVOKEINTERFACE: + case INVOKESPECIAL: + case INVOKESTATIC: + case INVOKEVIRTUAL: + count(className, instruction); + break; + + default: + break; + } + } + + } +} diff --git a/src/main/java/org/adoptopenjdk/jitwatch/jarscan/invokecounter/OpcodeInvokeCountMap.java b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/invokecounter/OpcodeInvokeCountMap.java new file mode 100644 index 00000000..f80500c3 --- /dev/null +++ b/src/main/java/org/adoptopenjdk/jitwatch/jarscan/invokecounter/OpcodeInvokeCountMap.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013-2016 Chris Newland. + * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD + * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki + */ +package org.adoptopenjdk.jitwatch.jarscan.invokecounter; + +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; + +import java.util.EnumMap; +import java.util.Map; + +import org.adoptopenjdk.jitwatch.model.bytecode.Opcode; + +public class OpcodeInvokeCountMap +{ + private Map opcodeMap = new EnumMap<>(Opcode.class); + + public void count(Opcode opcode, String method) + { + InvokeCountMap invokeCountMap = opcodeMap.get(opcode); + + if (invokeCountMap == null) + { + invokeCountMap = new InvokeCountMap(); + opcodeMap.put(opcode, invokeCountMap); + } + + invokeCountMap.count(method); + } + + @Override + public String toString() + { + StringBuilder builder = new StringBuilder(); + + for (Map.Entry entry : opcodeMap.entrySet()) + { + Opcode opcode = entry.getKey(); + InvokeCountMap invokeCountMap = entry.getValue(); + + builder.append(invokeCountMap.toString(opcode)).append(S_NEWLINE); + } + + return builder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/adoptopenjdk/jitwatch/loader/BytecodeLoader.java b/src/main/java/org/adoptopenjdk/jitwatch/loader/BytecodeLoader.java index 7663ccd0..8ea226b2 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/loader/BytecodeLoader.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/loader/BytecodeLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ @@ -108,7 +108,7 @@ public static MetaClass buildMetaClassFromClass(String fqClassName) } public static ClassBC fetchBytecodeForClass(List classLocations, String fqClassName) - { + { if (DEBUG_LOGGING_BYTECODE) { logger.debug("fetchBytecodeForClass: {}", fqClassName); @@ -140,7 +140,7 @@ private static ClassBC parsedByteCodeFrom(String fqClassName, String byteCodeStr if (byteCodeString != null) { String[] bytecodeLines = byteCodeString.split(S_NEWLINE); - + try { result = parse(fqClassName, bytecodeLines); @@ -705,7 +705,9 @@ public static List parseInstructions(final String bytecode) String paramString = matcher.group(3); String comment = matcher.group(4); - if(mnemonic.endsWith("_w")) { + if (mnemonic.endsWith("_w") && !Opcode.GOTO_W.equals(mnemonic) && !Opcode.JSR_W.equals(mnemonic) + && !Opcode.LDC_W.equals(mnemonic) && !Opcode.LDC2_W.equals(mnemonic)) + { mnemonic = mnemonic.substring(0, mnemonic.length() - "_w".length()); } diff --git a/src/main/java/org/adoptopenjdk/jitwatch/model/AbstractMetaMember.java b/src/main/java/org/adoptopenjdk/jitwatch/model/AbstractMetaMember.java index 6aebeae5..f8690319 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/model/AbstractMetaMember.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/model/AbstractMetaMember.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ @@ -66,10 +66,15 @@ public abstract class AbstractMetaMember implements IMetaMember, Comparable returnType; protected List> paramTypes; + public AbstractMetaMember(String memberName) + { + this.memberName = memberName; + } + protected void checkPolymorphicSignature(Method method) { for (Annotation anno : method.getAnnotations()) @@ -116,10 +121,17 @@ private boolean nameMatches(MemberSignatureParts msp) { if (DEBUG_LOGGING_SIG_MATCH) { - logger.debug("Name: '{}' v '{}'", memberName, msp.getMemberName()); + logger.debug("nameMatches this.memberName: '{}' fq: '{}' other '{}' fq: '{}'", memberName, getFullyQualifiedMemberName(), msp.getMemberName(), msp.getFullyQualifiedClassName()); } + + boolean match = memberName.equals(msp.getMemberName()); - return memberName.equals(msp.getMemberName()); + if (DEBUG_LOGGING_SIG_MATCH) + { + logger.debug("nameMatches {}", match); + } + + return match; } private boolean returnTypeMatches(MemberSignatureParts msp) throws ClassNotFoundException diff --git a/src/main/java/org/adoptopenjdk/jitwatch/model/MemberSignatureParts.java b/src/main/java/org/adoptopenjdk/jitwatch/model/MemberSignatureParts.java index 94508b56..28840f56 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/model/MemberSignatureParts.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/model/MemberSignatureParts.java @@ -1,18 +1,16 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ package org.adoptopenjdk.jitwatch.model; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_CLOSE_ANGLE; -import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COLON; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COMMA; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_DOT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_NEWLINE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_ANGLE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_QUESTION; -import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SLASH; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SPACE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_ASSEMBLY; @@ -21,7 +19,6 @@ import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_COMMA; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CONSTRUCTOR_INIT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOT; -import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOUBLE_QUOTE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_EMPTY; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_ENTITY_APOS; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OPEN_PARENTHESES; @@ -87,8 +84,8 @@ private MemberSignatureParts() paramTypeList = new ArrayList<>(); modifier = 0; } - - //TODO really??? + + // TODO really??? public void setClassBC(ClassBC classBytecode) { this.classBytecode = classBytecode; @@ -98,14 +95,16 @@ private static void completeSignature(String origSig, MemberSignatureParts msp) { if (msp.memberName != null) { - - - // Constructors will return void for returnType if (S_CONSTRUCTOR_INIT.equals(msp.memberName) || msp.memberName.equals(msp.fullyQualifiedClassName)) { - msp.memberName = msp.fullyQualifiedClassName; + msp.memberName = StringUtil.getUnqualifiedClassName(msp.fullyQualifiedClassName); msp.returnType = Void.TYPE.getName(); + + if (DEBUG_LOGGING) + { + logger.debug("Found constructor: {} ", msp.memberName); + } } } else @@ -169,12 +168,9 @@ private static boolean isStaticInitialiser(String bytecodeSignature) // TODO unit test me! public static MemberSignatureParts fromBytecodeComment(String toParse) throws LogParseException { - String replace1 = toParse.replace(C_DOT, C_SPACE); - String replace2 = replace1.replace(C_COLON, C_SPACE); - String replace3 = replace2.replace(C_SLASH, C_DOT); - String replace4 = replace3.replace(S_DOUBLE_QUOTE, S_EMPTY); + String logCompilationSignature = ParseUtil.bytecodeCommentSignatureToLogCompilationSignature(toParse); - return fromLogCompilationSignature(replace4); + return fromLogCompilationSignature(logCompilationSignature); } public static MemberSignatureParts fromBytecodeSignature(String fqClassName, String toParse) @@ -212,8 +208,8 @@ public static MemberSignatureParts fromBytecodeSignature(String fqClassName, Str builder.append(regexParams); builder.append(regexRest); - //logger.info("\n{}\n{}", toParse, builder); - + // logger.info("\n{}\n{}", toParse, builder); + final Pattern patternBytecodeSignature = Pattern.compile(builder.toString()); Matcher matcher = patternBytecodeSignature.matcher(toParse); @@ -485,7 +481,7 @@ else if (classBytecode != null && classBytecode.getGenericsMap().containsKey(typ result = classBytecode.getGenericsMap().get(typeName); } } - + return result; } @@ -503,7 +499,7 @@ public String getFullyQualifiedClassName() { return fullyQualifiedClassName; } - + public String getPackageName() { return StringUtil.getAbbreviatedFQName(getFullyQualifiedClassName()); @@ -518,7 +514,6 @@ public String toString() if (modifierList.size() > 0) { - for (String mod : modifierList) { sb.append(mod).append(C_COMMA); @@ -533,7 +528,6 @@ public String toString() if (genericsMap.size() > 0) { - for (Map.Entry entry : genericsMap.entrySet()) { if (entry.getValue() != null) @@ -564,7 +558,25 @@ public String toString() if (paramTypeList.size() > 0) { + for (String param : paramTypeList) + { + sb.append(param).append(C_COMMA); + } + sb.deleteCharAt(sb.length() - 1); + } + + return sb.toString(); + } + + public String toStringSingleLine() + { + StringBuilder sb = new StringBuilder(); + + sb.append(fullyQualifiedClassName).append(C_DOT).append(memberName).append(S_OPEN_PARENTHESES); + + if (paramTypeList.size() > 0) + { for (String param : paramTypeList) { sb.append(param).append(C_COMMA); @@ -573,6 +585,8 @@ public String toString() sb.deleteCharAt(sb.length() - 1); } + sb.append(S_CLOSE_PARENTHESES); + return sb.toString(); } diff --git a/src/main/java/org/adoptopenjdk/jitwatch/model/MetaConstructor.java b/src/main/java/org/adoptopenjdk/jitwatch/model/MetaConstructor.java index 3fb417a8..e5f2d25e 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/model/MetaConstructor.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/model/MetaConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ @@ -11,6 +11,7 @@ import java.util.Arrays; import org.adoptopenjdk.jitwatch.core.JITWatchConstants; +import org.adoptopenjdk.jitwatch.util.StringUtil; public class MetaConstructor extends AbstractMetaMember { @@ -18,11 +19,13 @@ public class MetaConstructor extends AbstractMetaMember public MetaConstructor(Constructor constructor, MetaClass methodClass) { + super(StringUtil.getUnqualifiedMemberName(constructor.getName())); + this.constructorToString = constructor.toString(); this.metaClass = methodClass; returnType = Void.TYPE; - memberName = constructor.getName(); + paramTypes = Arrays.asList(constructor.getParameterTypes()); modifier = constructor.getModifiers(); diff --git a/src/main/java/org/adoptopenjdk/jitwatch/model/MetaMethod.java b/src/main/java/org/adoptopenjdk/jitwatch/model/MetaMethod.java index c9600e71..d60401b0 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/model/MetaMethod.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/model/MetaMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ @@ -18,10 +18,11 @@ public class MetaMethod extends AbstractMetaMember public MetaMethod(Method method, MetaClass methodClass) { + super(method.getName()); + this.methodToString = method.toString(); this.metaClass = methodClass; - memberName = method.getName(); returnType = method.getReturnType(); paramTypes = Arrays.asList(method.getParameterTypes()); @@ -38,12 +39,7 @@ public MetaMethod(Method method, MetaClass methodClass) logger.debug("Created MetaMethod: {}", toString()); } } - - public void setMemberName(String name) - { - this.memberName = name; - } - + public void setParamTypes(List> types) { this.paramTypes = types; diff --git a/src/main/java/org/adoptopenjdk/jitwatch/model/bytecode/BytecodeInstruction.java b/src/main/java/org/adoptopenjdk/jitwatch/model/bytecode/BytecodeInstruction.java index 2b52b544..706ded86 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/model/bytecode/BytecodeInstruction.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/model/bytecode/BytecodeInstruction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ @@ -11,7 +11,9 @@ import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SPACE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_BYTECODE_INTERFACEMETHOD_COMMENT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_BYTECODE_METHOD_COMMENT; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_BYTECODE_INVOKEDYNAMIC_COMMENT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CLOSE_BRACE; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; import java.util.ArrayList; import java.util.List; @@ -33,17 +35,17 @@ public class BytecodeInstruction private static final Logger logger = LoggerFactory.getLogger(BytecodeInstruction.class); private boolean isEliminated = false; - + public void setEliminated(boolean eliminated) { isEliminated = eliminated; } - + public boolean isEliminated() { return isEliminated; } - + public int getOffset() { return offset; @@ -84,25 +86,42 @@ public String getComment() return comment; } + //TODO unit test INDY public String getCommentWithMemberPrefixStripped() { - if (comment != null && comment.startsWith(S_BYTECODE_METHOD_COMMENT)) - { - return comment.substring(S_BYTECODE_METHOD_COMMENT.length()).trim(); - } - else if (comment != null && comment.startsWith(S_BYTECODE_INTERFACEMETHOD_COMMENT)) - { - return comment.substring(S_BYTECODE_INTERFACEMETHOD_COMMENT.length()).trim(); - } - else + String stripped = comment; + + if (comment != null) { - return comment; + if (comment.startsWith(S_BYTECODE_METHOD_COMMENT)) + { + return comment.substring(S_BYTECODE_METHOD_COMMENT.length()).trim(); + } + else if (comment.startsWith(S_BYTECODE_INTERFACEMETHOD_COMMENT)) + { + return comment.substring(S_BYTECODE_INTERFACEMETHOD_COMMENT.length()).trim(); + } + else if (comment.startsWith(S_BYTECODE_INVOKEDYNAMIC_COMMENT)) + { + // InvokeDynamic #1:run:(Ljavafx/embed/swt/FXCanvas$HostContainer;)Ljava/lang/Runnable; + + int firstColon = comment.indexOf(C_COLON); + + if (firstColon != -1) + { + stripped = comment.substring(firstColon+1, comment.length()); + } + } } + + return stripped; + } public void setComment(String comment) { this.comment = comment; + hasComment = true; } @@ -119,15 +138,13 @@ public String toString() public boolean isInvoke() { - return opcode != null - && (opcode == Opcode.INVOKEVIRTUAL || opcode == Opcode.INVOKESPECIAL || opcode == Opcode.INVOKESTATIC + return opcode != null && (opcode == Opcode.INVOKEVIRTUAL || opcode == Opcode.INVOKESPECIAL || opcode == Opcode.INVOKESTATIC || opcode == Opcode.INVOKEINTERFACE || opcode == Opcode.INVOKEDYNAMIC); - } - + public boolean isLock() { - return opcode != null && opcode ==Opcode.MONITORENTER; + return opcode != null && opcode == Opcode.MONITORENTER; } public boolean isSwitch() @@ -148,6 +165,20 @@ public int getLabelLines() return result; } + public String toStringComplete() + { + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < getLabelLines(); i++) + { + builder.append(toString(1000, i)).append(S_NEWLINE); + } + + builder.deleteCharAt(builder.length() - 1); + + return builder.toString(); + } + public String toString(int maxOffset, int line) { if (isSwitch()) @@ -171,12 +202,7 @@ private String toStringNonSwitch(int maxOffset) if (opcode != null) { String mnemonic = opcode.getMnemonic(); - -// if (isEliminated) -// { -// mnemonic = "(" + mnemonic + ")"; -// } - + toStringBuilder.append(StringUtil.alignLeft(mnemonic, 16)); } diff --git a/src/main/java/org/adoptopenjdk/jitwatch/model/bytecode/Opcode.java b/src/main/java/org/adoptopenjdk/jitwatch/model/bytecode/Opcode.java index 68b7aaba..143e53d6 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/model/bytecode/Opcode.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/model/bytecode/Opcode.java @@ -8,7 +8,7 @@ import java.util.HashMap; import java.util.Map; -public enum Opcode +public enum Opcode implements Comparable { NOP(0,"nop"), ACONST_NULL(1,"aconst_null"), @@ -249,4 +249,9 @@ public static Opcode getOpcodeForMnemonic(String mnemonic) { return opcodeMap.get(mnemonic); } + + public boolean equals(String mnemonic) + { + return this.mnemonic.equals(mnemonic); + } } \ No newline at end of file diff --git a/src/main/java/org/adoptopenjdk/jitwatch/util/ParseUtil.java b/src/main/java/org/adoptopenjdk/jitwatch/util/ParseUtil.java index 4ded302a..a1d5a127 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/util/ParseUtil.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/util/ParseUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ @@ -12,6 +12,8 @@ import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_STAMP; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_STAMP_COMPLETED; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_CLOSE_ANGLE; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COLON; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COMMA; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_DOT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OBJECT_REF; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_ANGLE; @@ -29,6 +31,7 @@ import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CLOSE_ANGLE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_CLOSE_PARENTHESES; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOT; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOUBLE_QUOTE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_EMPTY; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OBJECT_ARRAY_DEF; @@ -78,14 +81,14 @@ public final class ParseUtil // classMETHOD(PARAMS)RETURN // http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2 - public static String CLASS_NAME_REGEX_GROUP = "([^;\\[/<>]+)"; + public static String CLASS_NAME_REGEX_GROUP = "([\\p{L}0-9$=_{};\\.\\[/<>]+)"; public static String METHOD_NAME_REGEX_GROUP = "([^;\\[/]+)"; public static String PARAM_REGEX_GROUP = "(\\(.*\\))"; public static String RETURN_REGEX_GROUP = "(.*)"; - private static final Pattern PATTERN_LOG_SIGNATURE = Pattern.compile("^" + CLASS_NAME_REGEX_GROUP + " " - + METHOD_NAME_REGEX_GROUP + " " + PARAM_REGEX_GROUP + RETURN_REGEX_GROUP); + private static final Pattern PATTERN_LOG_SIGNATURE = Pattern + .compile("^" + CLASS_NAME_REGEX_GROUP + " " + METHOD_NAME_REGEX_GROUP + " " + PARAM_REGEX_GROUP + RETURN_REGEX_GROUP); public static final char TYPE_SHORT = 'S'; public static final char TYPE_CHARACTER = 'C'; @@ -264,8 +267,12 @@ public static String[] splitLogSignatureWithRegex(final String logSignature) thr String paramTypes = matcher.group(3).replace(S_OPEN_PARENTHESES, S_EMPTY).replace(S_CLOSE_PARENTHESES, S_EMPTY); String returnType = matcher.group(4); - return new String[] { className, methodName, paramTypes, returnType }; + return new String[] { + className, methodName, paramTypes, returnType + }; } + + logger.debug("Could not apply {} to {}", PATTERN_LOG_SIGNATURE, logSignature); throw new LogParseException("Could not split signature with regex: '" + logSignature + C_QUOTE); } @@ -291,20 +298,16 @@ public static IMetaMember findMemberWithSignature(IReadOnlyJITDataModel model, S public static Class[] getClassTypes(String typesString) throws LogParseException { - List> classes = new ArrayList>(); + List> classes = null; - if (typesString.length() > 0) + try { - try - { - findClassesForTypeString(typesString, classes); - } - catch (Throwable t) - { - throw new LogParseException("Could not parse types: " + typesString, t); - } - - } // end if empty + classes = findClassesForTypeString(typesString); + } + catch (Throwable t) + { + throw new LogParseException("Could not parse types: " + typesString, t); + } return classes.toArray(new Class[classes.size()]); } @@ -608,12 +611,9 @@ public static int getArrayBracketCount(String param) return count; } - /* - * Converts (III[Ljava.lang.String;) into a list of Class - */ - private static void findClassesForTypeString(final String typesString, List> classes) throws ClassNotFoundException + public static List parseTypeString(final String typesString) { - // logger.debug("Parsing: {}", typesString); + List result = new ArrayList<>(); String toParse = typesString.replace(C_SLASH, C_DOT); @@ -668,8 +668,7 @@ private static void findClassesForTypeString(final String typesString, List arrayClass = ClassUtil.loadClassWithoutInitialising(builder.toString()); - classes.add(arrayClass); + result.add(builder.toString()); builder.delete(0, builder.length()); break; case C_OBJECT_REF: @@ -687,19 +686,47 @@ private static void findClassesForTypeString(final String typesString, List refClass = ClassUtil.loadClassWithoutInitialising(builder.toString()); - classes.add(refClass); + result.add(builder.toString()); builder.delete(0, builder.length()); break; default: // primitive - Class primitiveClass = ParseUtil.getPrimitiveClass(c); - classes.add(primitiveClass); + result.add(Character.toString(c)); pos++; } // end switch } // end while + + return result; + } + + /* + * Converts (III[Ljava.lang.String;) into a list of Class + */ + public static List> findClassesForTypeString(final String typesString) throws ClassNotFoundException + { + List> result = new ArrayList<>(); + + List typeNames = parseTypeString(typesString); + + for (String typeName : typeNames) + { + Class clazz = null; + + if (typeName.length() == 1) + { + clazz = getPrimitiveClass(typeName.charAt(0)); + } + else + { + clazz = ClassUtil.loadClassWithoutInitialising(typeName); + } + + result.add(clazz); + } + + return result; } public static String findBestMatchForMemberSignature(IMetaMember member, List lines) @@ -747,8 +774,8 @@ public static int findBestLineMatchForMemberSignature(IMetaMember member, List genericsMap) { String mspTypeName = inMspTypeName; + if (memberTypeName != null && memberTypeName.equals(mspTypeName)) { return true; @@ -812,7 +840,9 @@ else if (mspTypeName != null) // java.lang.Object> T[] copyOf(U[], int, java.lang.Class)"; // U[] -> java.lang.Object[] + String mspTypeNameWithoutArray = getParamTypeWithoutArrayBrackets(mspTypeName); + String genericSubstitution = genericsMap.get(mspTypeNameWithoutArray); if (genericSubstitution != null) @@ -951,8 +981,8 @@ public static IMetaMember lookupMember(String methodId, IParseDictionary parseDi if (metaClass != null) { - MemberSignatureParts msp = MemberSignatureParts.fromParts(metaClass.getFullyQualifiedName(), methodName, - returnType, argumentTypes); + MemberSignatureParts msp = MemberSignatureParts.fromParts(metaClass.getFullyQualifiedName(), methodName, returnType, + argumentTypes); result = metaClass.getMemberForSignature(msp); } @@ -1064,7 +1094,54 @@ public static String getClassFromSource(String source) return result; } - private static boolean commentMethodHasNoClassPrefix(String comment) + public static String bytecodeMethodCommentToReadableString(String className, String comment) + { + StringBuilder builder = new StringBuilder(); + + if (bytecodeMethodCommentHasNoClassPrefix(comment)) + { + comment = className.replace(S_DOT, S_SLASH) + C_DOT + comment; + } + + String logCompilationSignature = bytecodeCommentSignatureToLogCompilationSignature(comment); + + try + { + String[] parts = ParseUtil.splitLogSignatureWithRegex(logCompilationSignature); + + String fullyQualifiedClassName = parts[0]; + String memberName = parts[1]; + String paramTypes = parts[2]; + + builder.append(fullyQualifiedClassName).append(S_DOT); + builder.append(memberName).append(S_OPEN_PARENTHESES); + + List paramTypeNames = parseTypeString(paramTypes); + + if (paramTypeNames.size() > 0) + { + for (String paramTypeName : paramTypeNames) + { + + builder.append(expandParameterType(paramTypeName)); + + builder.append(C_COMMA); + } + + builder.deleteCharAt(builder.length() - 1); + } + + builder.append(S_CLOSE_PARENTHESES); + } + catch (LogParseException e) + { + e.printStackTrace(); + } + + return builder.toString(); + } + + public static boolean bytecodeMethodCommentHasNoClassPrefix(String comment) { return (comment.indexOf(C_DOT) == -1); } @@ -1078,6 +1155,12 @@ private static String prependCurrentMember(String comment, IMetaMember member) return currentClass + C_DOT + comment; } + public static String bytecodeCommentSignatureToLogCompilationSignature(String bytcodeCommentSignature) + { + return bytcodeCommentSignature.replace(C_DOT, C_SPACE).replace(C_COLON, C_SPACE).replace(C_SLASH, C_DOT) + .replace(S_DOUBLE_QUOTE, S_EMPTY); + } + public static IMetaMember getMemberFromBytecodeComment(IReadOnlyJITDataModel model, IMetaMember currentMember, BytecodeInstruction instruction) throws LogParseException { @@ -1094,7 +1177,7 @@ public static IMetaMember getMemberFromBytecodeComment(IReadOnlyJITDataModel mod if (comment != null) { - if (commentMethodHasNoClassPrefix(comment) && currentMember != null) + if (bytecodeMethodCommentHasNoClassPrefix(comment) && currentMember != null) { comment = prependCurrentMember(comment, currentMember); } @@ -1107,4 +1190,4 @@ public static IMetaMember getMemberFromBytecodeComment(IReadOnlyJITDataModel mod return result; } -} +} \ No newline at end of file diff --git a/src/main/java/org/adoptopenjdk/jitwatch/util/StringUtil.java b/src/main/java/org/adoptopenjdk/jitwatch/util/StringUtil.java index 260ce984..c8024ef1 100644 --- a/src/main/java/org/adoptopenjdk/jitwatch/util/StringUtil.java +++ b/src/main/java/org/adoptopenjdk/jitwatch/util/StringUtil.java @@ -234,6 +234,11 @@ public static String getUnqualifiedClassName(String fqClassName) return result; } + + public static String getUnqualifiedMemberName(String memberName) + { + return getUnqualifiedClassName(memberName); + } public static String getPackageName(String fqClassName) { diff --git a/src/test/java/org/adoptopenjdk/jitwatch/test/HelperMetaMethod.java b/src/test/java/org/adoptopenjdk/jitwatch/test/HelperMetaMethod.java index 1d291d64..5afa168b 100644 --- a/src/test/java/org/adoptopenjdk/jitwatch/test/HelperMetaMethod.java +++ b/src/test/java/org/adoptopenjdk/jitwatch/test/HelperMetaMethod.java @@ -5,19 +5,44 @@ */ package org.adoptopenjdk.jitwatch.test; +import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_MEMBER_CREATION; + import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.List; +import org.adoptopenjdk.jitwatch.model.AbstractMetaMember; import org.adoptopenjdk.jitwatch.model.MetaClass; -import org.adoptopenjdk.jitwatch.model.MetaMethod; import org.adoptopenjdk.jitwatch.model.bytecode.BytecodeInstruction; -public class HelperMetaMethod extends MetaMethod +public class HelperMetaMethod extends AbstractMetaMember { - public HelperMetaMethod(Method method, MetaClass methodClass) + public HelperMetaMethod(String methodName, MetaClass metaClass,Class[] params, + Class returnType) throws NoSuchMethodException, SecurityException { - super(method, methodClass); - } + super(methodName); + + Method dummyMethodObject = java.lang.String.class.getDeclaredMethod("length", new Class[0]); + + this.metaClass = metaClass; + + this.returnType = returnType; + this.paramTypes = Arrays.asList(params); + + // Can include non-method modifiers such as volatile so AND with + // acceptable values + this.modifier = dummyMethodObject.getModifiers() & Modifier.methodModifiers(); + + this.isVarArgs = dummyMethodObject.isVarArgs(); + + checkPolymorphicSignature(dummyMethodObject); + + if (DEBUG_MEMBER_CREATION) + { + logger.debug("Created MetaMethod: {}", toString()); + } + } private List instructions; diff --git a/src/test/java/org/adoptopenjdk/jitwatch/test/TestBytecodeLoader.java b/src/test/java/org/adoptopenjdk/jitwatch/test/TestBytecodeLoader.java index 603e6e49..beb97144 100644 --- a/src/test/java/org/adoptopenjdk/jitwatch/test/TestBytecodeLoader.java +++ b/src/test/java/org/adoptopenjdk/jitwatch/test/TestBytecodeLoader.java @@ -420,7 +420,7 @@ public void testLineNumberTable() throws ClassNotFoundException MetaClass metaClass = UnitTestUtil.createMetaClassFor(model, getClass().getName()); IMetaMember constructor = metaClass.getFirstConstructor(); - + MemberBytecode memberBytecode2 = classBytecode.getMemberBytecode(constructor); assertNotNull(memberBytecode2); diff --git a/src/test/java/org/adoptopenjdk/jitwatch/test/TestFindOptimizedVirtualCall.java b/src/test/java/org/adoptopenjdk/jitwatch/test/TestFindOptimizedVirtualCall.java index 187c2446..53a97199 100644 --- a/src/test/java/org/adoptopenjdk/jitwatch/test/TestFindOptimizedVirtualCall.java +++ b/src/test/java/org/adoptopenjdk/jitwatch/test/TestFindOptimizedVirtualCall.java @@ -336,7 +336,7 @@ public void testCallSiteFoundMultiLineComment() throws ClassNotFoundException MemberSignatureParts mspTest1 = MemberSignatureParts.fromBytecodeSignature(fqClassName, bcSigTest1); MemberSignatureParts mspTest2 = MemberSignatureParts.fromBytecodeSignature(fqClassName, bcSigTest2); - + IMetaMember memberTest1 = metaClass.getMemberForSignature(mspTest1); IMetaMember memberTest2 = metaClass.getMemberForSignature(mspTest2); diff --git a/src/test/java/org/adoptopenjdk/jitwatch/test/TestJarScan.java b/src/test/java/org/adoptopenjdk/jitwatch/test/TestJarScan.java new file mode 100644 index 00000000..2b007515 --- /dev/null +++ b/src/test/java/org/adoptopenjdk/jitwatch/test/TestJarScan.java @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2013-2016 Chris Newland. + * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD + * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki + */ +package org.adoptopenjdk.jitwatch.test; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.adoptopenjdk.jitwatch.jarscan.chains.ChainCounter; +import org.adoptopenjdk.jitwatch.jarscan.chains.OpcodeSequence; +import org.adoptopenjdk.jitwatch.loader.BytecodeLoader; +import org.adoptopenjdk.jitwatch.model.bytecode.BytecodeInstruction; +import org.adoptopenjdk.jitwatch.model.bytecode.MemberBytecode; +import org.adoptopenjdk.jitwatch.model.bytecode.Opcode; +import org.junit.Test; + +public class TestJarScan +{ + public static final boolean SHOW_OUTPUT = false; + + private List getInstructions(String[] lines) + { + StringBuilder builder = new StringBuilder(); + + for (String line : lines) + { + builder.append(line).append("\n"); + } + + List instructions = BytecodeLoader.parseInstructions(builder.toString()); + + return instructions; + } + + private MemberBytecode createMemberBytecode(String[] lines) + { + MemberBytecode mbc = new MemberBytecode(null, null); + + mbc.setInstructions(getInstructions(lines)); + + return mbc; + } + + @Test + public void testLongBytecodeChain1() + { + String[] lines = new String[] { + "0: aload_1", + "1: invokevirtual #38 // Method org/adoptopenjdk/jitwatch/model/Tag.getName:()Ljava/lang/String;", + "4: astore_3", + "5: iconst_m1", + "6: istore 4", + "8: aload_3", + "9: invokevirtual #39 // Method java/lang/String.hashCode:()I", + "12: lookupswitch { // 3", + " -1067517155: 63", + " 106437299: 48", + " 922165544: 78", + " default: 90", + " }", + "48: aload_3", + "49: ldc #14 // String parse", + "51: invokevirtual #40 // Method java/lang/String.equals:(Ljava/lang/Object;)Z", + "54: ifeq 90", + "57: iconst_0", + "58: istore 4", + "60: goto 90", + "63: aload_3", + "64: ldc #41 // String eliminate_allocation", + "66: invokevirtual #40 // Method java/lang/String.equals:(Ljava/lang/Object;)Z", + "69: ifeq 90", + "72: iconst_1", + "73: istore 4", + "75: goto 90", + "78: aload_3", + "79: ldc #42 // String eliminate_lock", + "81: invokevirtual #40 // Method java/lang/String.equals:(Ljava/lang/Object;)Z", + "84: ifeq 90", + "87: iconst_2", + "88: istore 4", + "90: iload 4", + "92: tableswitch { // 0 to 2", + " 0: 120", + " 1: 129", + " 2: 138", + " default: 147", + " }", + "120: aload_0", + "121: aload_1", + "122: aload_2", + "123: invokespecial #43 // Method visitTagParse:(Lorg/adoptopenjdk/jitwatch/model/Tag;Lorg/adoptopenjdk/jitwatch/model/IParseDictionary;)V", + "126: goto 152", + "129: aload_0", + "130: aload_1", + "131: aload_2", + "132: invokespecial #44 // Method visitTagEliminateAllocation:(Lorg/adoptopenjdk/jitwatch/model/Tag;Lorg/adoptopenjdk/jitwatch/model/IParseDictionary;)V", + "135: goto 152", + "138: aload_0", + "139: aload_1", + "140: aload_2", + "141: invokespecial #45 // Method visitTagEliminateLock:(Lorg/adoptopenjdk/jitwatch/model/Tag;Lorg/adoptopenjdk/jitwatch/model/IParseDictionary;)V", + "144: goto 152", + "147: aload_0", + "148: aload_1", + "149: invokevirtual #46 // Method handleOther:(Lorg/adoptopenjdk/jitwatch/model/Tag;)V", + "152: return" + }; + + MemberBytecode memberBytecode = createMemberBytecode(lines); + + ChainCounter counter = new ChainCounter(1); + + counter.processInstructions("Foo", memberBytecode); + + Map result = counter.getSequenceScores(); + + log(result); + + assertEquals(19, result.size()); + + checkSequence(result, 5, Opcode.ALOAD_1); + checkSequence(result, 6, Opcode.INVOKEVIRTUAL); + checkSequence(result, 1, Opcode.ASTORE_3); + checkSequence(result, 1, Opcode.ICONST_M1); + checkSequence(result, 4, Opcode.ISTORE); + checkSequence(result, 4, Opcode.ALOAD_3); + checkSequence(result, 1, Opcode.LOOKUPSWITCH); + checkSequence(result, 3, Opcode.LDC); + checkSequence(result, 3, Opcode.IFEQ); + checkSequence(result, 1, Opcode.ICONST_0); + checkSequence(result, 1, Opcode.ICONST_1); + checkSequence(result, 1, Opcode.ICONST_2); + checkSequence(result, 1, Opcode.ILOAD); + checkSequence(result, 1, Opcode.TABLESWITCH); + checkSequence(result, 4, Opcode.ALOAD_0); + checkSequence(result, 3, Opcode.ALOAD_2); + checkSequence(result, 3, Opcode.INVOKESPECIAL); + checkSequence(result, 5, Opcode.GOTO); + checkSequence(result, 1, Opcode.RETURN); + } + + @Test + public void testLongBytecodeChain6() + { + String[] lines = new String[] { + "0: aload_1", + "1: invokevirtual #38 // Method org/adoptopenjdk/jitwatch/model/Tag.getName:()Ljava/lang/String;", + "4: astore_3", + "5: iconst_m1", + "6: istore 4", + "8: aload_3", + "9: invokevirtual #39 // Method java/lang/String.hashCode:()I", + "12: lookupswitch { // 3", + " -1067517155: 63", + " 106437299: 48", + " 922165544: 78", + " default: 90", + " }", + "48: aload_3", + "49: ldc #14 // String parse", + "51: invokevirtual #40 // Method java/lang/String.equals:(Ljava/lang/Object;)Z", + "54: ifeq 90", + "57: iconst_0", + "58: istore 4", + "60: goto 90", + "63: aload_3", + "64: ldc #41 // String eliminate_allocation", + "66: invokevirtual #40 // Method java/lang/String.equals:(Ljava/lang/Object;)Z", + "69: ifeq 90", + "72: iconst_1", + "73: istore 4", + "75: goto 90", + "78: aload_3", + "79: ldc #42 // String eliminate_lock", + "81: invokevirtual #40 // Method java/lang/String.equals:(Ljava/lang/Object;)Z", + "84: ifeq 90", + "87: iconst_2", + "88: istore 4", + "90: iload 4", + "92: tableswitch { // 0 to 2", + " 0: 120", + " 1: 129", + " 2: 138", + " default: 147", + " }", + "120: aload_0", + "121: aload_1", + "122: aload_2", + "123: invokespecial #43 // Method visitTagParse:(Lorg/adoptopenjdk/jitwatch/model/Tag;Lorg/adoptopenjdk/jitwatch/model/IParseDictionary;)V", + "126: goto 152", + "129: aload_0", + "130: aload_1", + "131: aload_2", + "132: invokespecial #44 // Method visitTagEliminateAllocation:(Lorg/adoptopenjdk/jitwatch/model/Tag;Lorg/adoptopenjdk/jitwatch/model/IParseDictionary;)V", + "135: goto 152", + "138: aload_0", + "139: aload_1", + "140: aload_2", + "141: invokespecial #45 // Method visitTagEliminateLock:(Lorg/adoptopenjdk/jitwatch/model/Tag;Lorg/adoptopenjdk/jitwatch/model/IParseDictionary;)V", + "144: goto 152", + "147: aload_0", + "148: aload_1", + "149: invokevirtual #46 // Method handleOther:(Lorg/adoptopenjdk/jitwatch/model/Tag;)V", + "152: return" + }; + + MemberBytecode memberBytecode = createMemberBytecode(lines); + + ChainCounter counter = new ChainCounter(6); + + counter.processInstructions("Foo", memberBytecode); + + Map result = counter.getSequenceScores(); + + log(result); + + assertEquals(29, result.size()); + + checkSequence(result, 1, Opcode.ALOAD_1, Opcode.INVOKEVIRTUAL, Opcode.ASTORE_3, Opcode.ICONST_M1, Opcode.ISTORE, Opcode.ALOAD_3); + checkSequence(result, 1, Opcode.INVOKEVIRTUAL, Opcode.ASTORE_3, Opcode.ICONST_M1, Opcode.ISTORE, Opcode.ALOAD_3, Opcode.INVOKEVIRTUAL); + checkSequence(result, 1, Opcode.ASTORE_3, Opcode.ICONST_M1, Opcode.ISTORE, Opcode.ALOAD_3, Opcode.INVOKEVIRTUAL, Opcode.LOOKUPSWITCH); + checkSequence(result, 1, Opcode.ICONST_M1, Opcode.ISTORE, Opcode.ALOAD_3, Opcode.INVOKEVIRTUAL, Opcode.LOOKUPSWITCH, Opcode.ALOAD_3); + checkSequence(result, 1, Opcode.ISTORE, Opcode.ALOAD_3, Opcode.INVOKEVIRTUAL, Opcode.LOOKUPSWITCH, Opcode.ALOAD_3, Opcode.LDC); + checkSequence(result, 1, Opcode.ALOAD_3, Opcode.INVOKEVIRTUAL, Opcode.LOOKUPSWITCH, Opcode.ALOAD_3, Opcode.LDC, Opcode.INVOKEVIRTUAL); + checkSequence(result, 1, Opcode.INVOKEVIRTUAL, Opcode.LOOKUPSWITCH, Opcode.ALOAD_3, Opcode.LDC, Opcode.INVOKEVIRTUAL, Opcode.IFEQ); + checkSequence(result, 1, Opcode.LOOKUPSWITCH, Opcode.ALOAD_3, Opcode.LDC, Opcode.INVOKEVIRTUAL, Opcode.IFEQ, Opcode.ICONST_0); + checkSequence(result, 1, Opcode.ALOAD_3, Opcode.LDC, Opcode.INVOKEVIRTUAL, Opcode.IFEQ, Opcode.ICONST_0, Opcode.ISTORE); + checkSequence(result, 1, Opcode.LDC, Opcode.INVOKEVIRTUAL, Opcode.IFEQ, Opcode.ICONST_0, Opcode.ISTORE, Opcode.GOTO); + checkSequence(result, 1, Opcode.INVOKEVIRTUAL, Opcode.IFEQ, Opcode.ICONST_0, Opcode.ISTORE, Opcode.GOTO, Opcode.ILOAD); + checkSequence(result, 1, Opcode.IFEQ, Opcode.ICONST_0, Opcode.ISTORE, Opcode.GOTO, Opcode.ILOAD, Opcode.TABLESWITCH); + checkSequence(result, 1, Opcode.ICONST_0, Opcode.ISTORE, Opcode.GOTO, Opcode.ILOAD, Opcode.TABLESWITCH, Opcode.ALOAD_0); + checkSequence(result, 2, Opcode.ISTORE, Opcode.GOTO, Opcode.ILOAD, Opcode.TABLESWITCH, Opcode.ALOAD_0, Opcode.ALOAD_1); + checkSequence(result, 2, Opcode.GOTO, Opcode.ILOAD, Opcode.TABLESWITCH, Opcode.ALOAD_0, Opcode.ALOAD_1, Opcode.ALOAD_2); + checkSequence(result, 1, Opcode.ILOAD, Opcode.TABLESWITCH, Opcode.ALOAD_0, Opcode.ALOAD_1, Opcode.ALOAD_2, Opcode.INVOKESPECIAL); + checkSequence(result, 1, Opcode.TABLESWITCH, Opcode.ALOAD_0, Opcode.ALOAD_1, Opcode.ALOAD_2, Opcode.INVOKESPECIAL, Opcode.GOTO); + checkSequence(result, 3, Opcode.ALOAD_0, Opcode.ALOAD_1, Opcode.ALOAD_2, Opcode.INVOKESPECIAL, Opcode.GOTO, Opcode.RETURN); + checkSequence(result, 1, Opcode.ALOAD_3, Opcode.LDC, Opcode.INVOKEVIRTUAL, Opcode.IFEQ, Opcode.ICONST_1, Opcode.ISTORE); + checkSequence(result, 1, Opcode.LDC, Opcode.INVOKEVIRTUAL, Opcode.IFEQ, Opcode.ICONST_1, Opcode.ISTORE, Opcode.GOTO); + checkSequence(result, 1, Opcode.INVOKEVIRTUAL, Opcode.IFEQ, Opcode.ICONST_1, Opcode.ISTORE, Opcode.GOTO, Opcode.ILOAD); + checkSequence(result, 1, Opcode.IFEQ, Opcode.ICONST_1, Opcode.ISTORE, Opcode.GOTO, Opcode.ILOAD, Opcode.TABLESWITCH); + checkSequence(result, 1, Opcode.ICONST_1, Opcode.ISTORE, Opcode.GOTO, Opcode.ILOAD, Opcode.TABLESWITCH, Opcode.ALOAD_0); + checkSequence(result, 1, Opcode.ALOAD_3, Opcode.LDC, Opcode.INVOKEVIRTUAL, Opcode.IFEQ, Opcode.ICONST_2, Opcode.ISTORE); + checkSequence(result, 1, Opcode.LDC, Opcode.INVOKEVIRTUAL, Opcode.IFEQ, Opcode.ICONST_2, Opcode.ISTORE, Opcode.ILOAD); + checkSequence(result, 1, Opcode.INVOKEVIRTUAL, Opcode.IFEQ, Opcode.ICONST_2, Opcode.ISTORE, Opcode.ILOAD, Opcode.TABLESWITCH); + checkSequence(result, 1, Opcode.IFEQ, Opcode.ICONST_2, Opcode.ISTORE, Opcode.ILOAD, Opcode.TABLESWITCH, Opcode.ALOAD_0); + checkSequence(result, 1, Opcode.ICONST_2, Opcode.ISTORE, Opcode.ILOAD, Opcode.TABLESWITCH, Opcode.ALOAD_0, Opcode.ALOAD_1); + checkSequence(result, 1, Opcode.ISTORE, Opcode.ILOAD, Opcode.TABLESWITCH, Opcode.ALOAD_0, Opcode.ALOAD_1, Opcode.ALOAD_2); + } + + private OpcodeSequence buildSequence(Opcode... opcodes) + { + return new OpcodeSequence(Arrays.asList(opcodes)); + } + + private void checkSequence(Map scoreMap, int count, Opcode... opcodes) + { + OpcodeSequence key = buildSequence(opcodes); + + int score = scoreMap.get(key); + + assertEquals(count, score); + } + + private void log(Map result) + { + if (SHOW_OUTPUT) + { + for (Map.Entry entry : result.entrySet()) + { + System.out.println(entry.getValue() + "\t=>\t" + entry.getKey()); + } + } + } + + @Test + public void testInfiniteLoopChain1() + { + String[] lines = new String[] { + "0: aload_1", + "1: invokevirtual #38 // Method org/adoptopenjdk/jitwatch/model/Tag.getName:()Ljava/lang/String;", + "4: astore_3", + "5: iconst_m1", + "6: istore 4", + "8: goto 0" + }; + + MemberBytecode memberBytecode = createMemberBytecode(lines); + + ChainCounter counter = new ChainCounter(1); + + counter.processInstructions("Foo", memberBytecode); + + Map result = counter.getSequenceScores(); + + log(result); + + assertEquals(6, result.size()); + + checkSequence(result, 1, Opcode.ALOAD_1); + checkSequence(result, 1, Opcode.INVOKEVIRTUAL); + checkSequence(result, 1, Opcode.ASTORE_3); + checkSequence(result, 1, Opcode.ICONST_M1); + checkSequence(result, 1, Opcode.ISTORE); + checkSequence(result, 1, Opcode.GOTO); + } + + @Test + public void testInfiniteLoopChain2() + { + String[] lines = new String[] { + "0: aload_1", + "1: invokevirtual #38 // Method org/adoptopenjdk/jitwatch/model/Tag.getName:()Ljava/lang/String;", + "4: astore_3", + "5: iconst_m1", + "6: istore 4", + "8: goto 0" + }; + + MemberBytecode memberBytecode = createMemberBytecode(lines); + + ChainCounter counter = new ChainCounter(2); + + counter.processInstructions("Foo", memberBytecode); + + Map result = counter.getSequenceScores(); + + log(result); + + assertEquals(6, result.size()); + + checkSequence(result, 1, Opcode.ALOAD_1, Opcode.INVOKEVIRTUAL); + checkSequence(result, 1, Opcode.INVOKEVIRTUAL, Opcode.ASTORE_3); + checkSequence(result, 1, Opcode.ASTORE_3, Opcode.ICONST_M1); + checkSequence(result, 1, Opcode.ICONST_M1, Opcode.ISTORE); + checkSequence(result, 1, Opcode.ISTORE, Opcode.GOTO); + checkSequence(result, 1, Opcode.GOTO, Opcode.ALOAD_1); + } + + @Test + public void testInfiniteLoopChain3() + { + String[] lines = new String[] { + "0: aload_1", + "1: invokevirtual #38 // Method org/adoptopenjdk/jitwatch/model/Tag.getName:()Ljava/lang/String;", + "4: astore_3", + "5: iconst_m1", + "6: istore 4", + "8: goto 0" + }; + + MemberBytecode memberBytecode = createMemberBytecode(lines); + + ChainCounter counter = new ChainCounter(3); + + counter.processInstructions("Foo", memberBytecode); + + Map result = counter.getSequenceScores(); + + log(result); + + assertEquals(6, result.size()); + + checkSequence(result, 1, Opcode.ALOAD_1, Opcode.INVOKEVIRTUAL, Opcode.ASTORE_3); + checkSequence(result, 1, Opcode.INVOKEVIRTUAL, Opcode.ASTORE_3, Opcode.ICONST_M1); + checkSequence(result, 1, Opcode.ASTORE_3, Opcode.ICONST_M1, Opcode.ISTORE); + checkSequence(result, 1, Opcode.ICONST_M1, Opcode.ISTORE, Opcode.GOTO); + checkSequence(result, 1, Opcode.ISTORE, Opcode.GOTO, Opcode.ALOAD_1); + checkSequence(result, 1, Opcode.GOTO, Opcode.ALOAD_1, Opcode.INVOKEVIRTUAL); + + } + + @Test + public void testFollowGotoChain1() + { + String[] lines = new String[] { + "0: goto 2", + "1: goto 3", + "2: goto 1", + "3: return " + }; + + MemberBytecode memberBytecode = createMemberBytecode(lines); + + ChainCounter counter = new ChainCounter(1); + + counter.processInstructions("Foo", memberBytecode); + + Map result = counter.getSequenceScores(); + + log(result); + + assertEquals(2, result.size()); + + checkSequence(result, 3, Opcode.GOTO); + checkSequence(result, 1, Opcode.RETURN); + } + + @Test + public void testFollowGotoChain2() + { + String[] lines = new String[] { + "0: goto 2", + "1: goto 3", + "2: goto 1", + "3: return " + }; + + MemberBytecode memberBytecode = createMemberBytecode(lines); + + ChainCounter counter = new ChainCounter(2); + + counter.processInstructions("Foo", memberBytecode); + + Map result = counter.getSequenceScores(); + + log(result); + + assertEquals(2, result.size()); + + checkSequence(result, 2, Opcode.GOTO, Opcode.GOTO); + checkSequence(result, 1, Opcode.GOTO, Opcode.RETURN); + } + + @Test + public void testAthrow() + { + String[] lines = new String[] { + "0: getstatic #5 // Field socketImplCtor:Ljava/lang/reflect/Constructor;", + "3: iconst_0 ", + "4: anewarray #6 // class java/lang/Object", + "7: invokevirtual #7 // Method java/lang/reflect/Constructor.newInstance:([Ljava/lang/Object;)Ljava/lang/Object;", + "10: checkcast #8 // class java/net/SocketImpl", + "13: areturn ", + "14: astore_0 ", + "15: new #10 // class java/lang/AssertionError", + "18: dup ", + "19: aload_0 ", + "20: invokespecial #11 // Method java/lang/AssertionError.\"\":(Ljava/lang/Object;)V", + "23: athrow ", + "24: astore_0 ", + "25: new #10 // class java/lang/AssertionError", + "28: dup ", + "29: aload_0 ", + "30: invokespecial #11 // Method java/lang/AssertionError.\"\":(Ljava/lang/Object;)V", + "33: athrow ", + "34: astore_0 ", + "35: new #10 // class java/lang/AssertionError", + "38: dup ", + "39: aload_0 ", + "40: invokespecial #11 // Method java/lang/AssertionError.\"\":(Ljava/lang/Object;)V", + "43: athrow " + }; + + MemberBytecode memberBytecode = createMemberBytecode(lines); + + ChainCounter counter = new ChainCounter(3); + + counter.processInstructions("Foo", memberBytecode); + + Map result = counter.getSequenceScores(); + + log(result); + + assertEquals(8, result.size()); + + checkSequence(result, 1, Opcode.GETSTATIC, Opcode.ICONST_0, Opcode.ANEWARRAY); + checkSequence(result, 1, Opcode.ICONST_0, Opcode.ANEWARRAY, Opcode.INVOKEVIRTUAL); + checkSequence(result, 1, Opcode.ANEWARRAY, Opcode.INVOKEVIRTUAL, Opcode.CHECKCAST); + checkSequence(result, 1, Opcode.INVOKEVIRTUAL, Opcode.CHECKCAST, Opcode.ARETURN); + + checkSequence(result, 3, Opcode.ASTORE_0, Opcode.NEW, Opcode.DUP); + checkSequence(result, 3, Opcode.NEW, Opcode.DUP, Opcode.ALOAD_0); + checkSequence(result, 3, Opcode.DUP, Opcode.ALOAD_0, Opcode.INVOKESPECIAL); + checkSequence(result, 3, Opcode.ALOAD_0, Opcode.INVOKESPECIAL, Opcode.ATHROW); + } +} \ No newline at end of file diff --git a/src/test/java/org/adoptopenjdk/jitwatch/test/TestMemberSignatureParts.java b/src/test/java/org/adoptopenjdk/jitwatch/test/TestMemberSignatureParts.java index e0fbd8d1..b0c208dd 100644 --- a/src/test/java/org/adoptopenjdk/jitwatch/test/TestMemberSignatureParts.java +++ b/src/test/java/org/adoptopenjdk/jitwatch/test/TestMemberSignatureParts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ @@ -54,7 +54,7 @@ public void testPackageConstructorNoParams() throws Exception assertEquals(0, modListBC.size()); assertEquals(0, mspBC.getGenerics().size()); assertEquals(S_TYPE_NAME_VOID, mspBC.getReturnType()); - assertEquals("java.lang.String", mspBC.getMemberName()); + assertEquals("String", mspBC.getMemberName()); assertEquals(0, mspBC.getParamTypes().size()); checkSame(mspBC, mspLog); @@ -75,7 +75,7 @@ public void testPublicConstructorNoParams() throws Exception assertEquals("public", modListBC.get(0)); assertEquals(0, mspBC.getGenerics().size()); assertEquals(S_TYPE_NAME_VOID, mspBC.getReturnType()); - assertEquals("java.lang.String", mspBC.getMemberName()); + assertEquals("String", mspBC.getMemberName()); assertEquals(0, mspBC.getParamTypes().size()); checkSame(mspBC, mspLog); @@ -96,7 +96,7 @@ public void testConstructorWithParams() throws Exception assertEquals("public", modListBC.get(0)); assertEquals(0, mspBC.getGenerics().size()); assertEquals(S_TYPE_NAME_VOID, mspBC.getReturnType()); - assertEquals("java.lang.String", mspBC.getMemberName()); + assertEquals("String", mspBC.getMemberName()); assertEquals(2, mspBC.getParamTypes().size()); List paramTypeListBC = mspBC.getParamTypes(); diff --git a/src/test/java/org/adoptopenjdk/jitwatch/test/TestParseUtil.java b/src/test/java/org/adoptopenjdk/jitwatch/test/TestParseUtil.java index d22f0091..17a04cca 100644 --- a/src/test/java/org/adoptopenjdk/jitwatch/test/TestParseUtil.java +++ b/src/test/java/org/adoptopenjdk/jitwatch/test/TestParseUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ @@ -223,7 +223,7 @@ public void testMemberSignaturePartsConstructor() throws LogParseException MemberSignatureParts msp = MemberSignatureParts.fromLogCompilationSignature("java.lang.Object ()V"); assertEquals("java.lang.Object", msp.getFullyQualifiedClassName()); - assertEquals("java.lang.Object", msp.getMemberName()); + assertEquals("Object", msp.getMemberName()); assertEquals(S_TYPE_NAME_VOID, msp.getReturnType()); assertEquals(0, msp.getParamTypes().size()); } @@ -270,6 +270,30 @@ public void testMemberSignaturePartsMultiDimensionalArrayParamPrimitiveReturn() assertEquals("int", msp.getParamTypes().get(3)); } + @Test + public void testMemberSignaturePartsClassIsArrayClone() throws LogParseException + { + MemberSignatureParts msp = MemberSignatureParts + .fromLogCompilationSignature("[Ljava.lang.String; clone ()Ljava.lang.Object;"); + + assertEquals("[Ljava.lang.String;", msp.getFullyQualifiedClassName()); + assertEquals("clone", msp.getMemberName()); + assertEquals("java.lang.Object", msp.getReturnType()); + assertEquals(0, msp.getParamTypes().size()); + } + + @Test + public void testMemberSignaturePartsClassHasUnderscores() throws LogParseException + { + MemberSignatureParts msp = MemberSignatureParts + .fromLogCompilationSignature("org.omg.CORBA_2_3.portable.ObjectImpl ()V"); + + assertEquals("org.omg.CORBA_2_3.portable.ObjectImpl", msp.getFullyQualifiedClassName()); + assertEquals("ObjectImpl", msp.getMemberName()); + assertEquals(S_TYPE_NAME_VOID, msp.getReturnType()); + assertEquals(0, msp.getParamTypes().size()); + } + // test varargs method public void doSomethingWithVarArgs(String... args) { diff --git a/src/test/java/org/adoptopenjdk/jitwatch/test/UnitTestUtil.java b/src/test/java/org/adoptopenjdk/jitwatch/test/UnitTestUtil.java index 9378f5cb..d3401810 100644 --- a/src/test/java/org/adoptopenjdk/jitwatch/test/UnitTestUtil.java +++ b/src/test/java/org/adoptopenjdk/jitwatch/test/UnitTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Chris Newland. + * Copyright (c) 2013-2016 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ @@ -7,7 +7,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.util.Arrays; import org.adoptopenjdk.jitwatch.core.IJITListener; import org.adoptopenjdk.jitwatch.core.ILogParseErrorListener; @@ -42,11 +41,7 @@ public static HelperMetaMethod createTestMetaMember(String fqClassName, String m try { - helper = new HelperMetaMethod(java.lang.String.class.getDeclaredMethod("length", new Class[0]), metaClass); - - helper.setMemberName(methodName); - helper.setParamTypes(Arrays.asList(params)); - helper.setReturnType(returnType); + helper = new HelperMetaMethod(methodName, metaClass, params, returnType); } catch (NoSuchMethodException | SecurityException e) {