/* * 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.sequencecount; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_COMMA; 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 SequenceCountOperation implements IJarScanOperation { protected Map<InstructionSequence, Integer> chainCountMap = new TreeMap<>(); private List<Opcode> chain = new LinkedList<>(); private int maxLength = 0; public SequenceCountOperation(int maxLength) { this.maxLength = maxLength; } private void storeChain() { InstructionSequence sequence = new InstructionSequence(chain); Integer count = chainCountMap.get(sequence); if (count == null) { chainCountMap.put(sequence, 1); } else { chainCountMap.put(sequence, count + 1); } } public Map<InstructionSequence, Integer> getSequenceScores() { return chainCountMap; } public List<Map.Entry<InstructionSequence, Integer>> getSortedData() { List<Map.Entry<InstructionSequence, Integer>> result = new ArrayList<>(chainCountMap.entrySet()); Collections.sort(result, new Comparator<Map.Entry<InstructionSequence, Integer>>() { @Override public int compare(Map.Entry<InstructionSequence, Integer> o1, Map.Entry<InstructionSequence, Integer> 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<BytecodeInstruction> instructions = memberBytecode.getInstructions(); for (int i = 0; i < instructions.size(); i++) { handleChainStartingAtIndex(i, instructions); } } private void handleChainStartingAtIndex(int index, List<BytecodeInstruction> instructions) { boolean stopChain = false; boolean abandonChain = false; Set<Integer> 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<BytecodeInstruction> 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<InstructionSequence, Integer> entry : getSortedData()) { builder.append(entry.getKey().toString()).append(S_COMMA).append(entry.getValue()).append(S_NEWLINE); } return builder.toString(); } }