/*
* 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.optimizedvcall;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_OVC;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.adoptopenjdk.jitwatch.model.Compilation;
import org.adoptopenjdk.jitwatch.model.IMetaMember;
import org.adoptopenjdk.jitwatch.model.IReadOnlyJITDataModel;
import org.adoptopenjdk.jitwatch.model.LogParseException;
import org.adoptopenjdk.jitwatch.model.MemberSignatureParts;
import org.adoptopenjdk.jitwatch.model.MetaClass;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyBlock;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyInstruction;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyMethod;
import org.adoptopenjdk.jitwatch.model.bytecode.BytecodeInstruction;
import org.adoptopenjdk.jitwatch.model.bytecode.ClassBC;
import org.adoptopenjdk.jitwatch.model.bytecode.MemberBytecode;
import org.adoptopenjdk.jitwatch.model.bytecode.SourceMapper;
import org.adoptopenjdk.jitwatch.util.ParseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OptimizedVirtualCallFinder
{
// optimized virtual_call info output by
// hotspot/src/share/tools/hsdis/vm/code/nmethod.cpp
private static final Logger logger = LoggerFactory.getLogger(OptimizedVirtualCallFinder.class);
private IReadOnlyJITDataModel model;
private List<String> classLocations = new ArrayList<>();
public OptimizedVirtualCallFinder(IReadOnlyJITDataModel model, List<String> classLocations)
{
this.model = model;
this.classLocations = classLocations;
}
public OptimizedVirtualCall findOptimizedCall(AssemblyInstruction instruction)
{
if (DEBUG_LOGGING_OVC)
{
logger.debug("findOptimizedCall: {}", instruction);
}
OptimizedVirtualCall result = null;
if (instruction != null && instruction.isOptimizedVCall())
{
if (DEBUG_LOGGING_OVC)
{
logger.debug("Instruction is an OVC");
}
VirtualCallSite callSite = instruction.getOptimizedVirtualCallSiteOrNull();
if (DEBUG_LOGGING_OVC)
{
logger.debug("Found callSite: {}", callSite);
}
result = getOptimizedVirtualCall(callSite);
}
else
{
if (DEBUG_LOGGING_OVC)
{
logger.debug("Instruction is not an OVC");
}
}
return result;
}
public OptimizedVirtualCall getOptimizedVirtualCall(VirtualCallSite callSite)
{
OptimizedVirtualCall result = null;
if (callSite != null)
{
BytecodeInstruction bytecodeInstruction = null;
MemberBytecode memberBytecode = getMemberBytecodeForCallSite(callSite);
if (DEBUG_LOGGING_OVC)
{
logger.debug("VCS: {} found MemberBytecode {}", callSite, memberBytecode != null);
}
if (memberBytecode != null)
{
IMetaMember callerMember = findMember(memberBytecode.getMemberSignatureParts());
if (DEBUG_LOGGING_OVC)
{
logger.debug("Found member for msp:\n{}\nMember:{}", memberBytecode.getMemberSignatureParts(), callerMember);
}
bytecodeInstruction = memberBytecode.getBytecodeAtOffset(callSite.getBytecodeOffset());
if (DEBUG_LOGGING_OVC)
{
logger.debug("Found BytecodeInstruction: {}", bytecodeInstruction);
}
if (bytecodeInstruction != null)
{
IMetaMember calleeMember = null;
try
{
calleeMember = ParseUtil.getMemberFromBytecodeComment(model, callerMember, bytecodeInstruction);
}
catch (LogParseException e)
{
logger.error("Could not get member from bytecode comment", e);
}
if (DEBUG_LOGGING_OVC)
{
logger.debug("=========================");
logger.debug("callerMember: {}", callerMember);
logger.debug("calleeMember: {}", calleeMember);
logger.debug("callSite : {}", callSite);
logger.debug("bytecodeInstruction : {}", bytecodeInstruction);
logger.debug("=========================");
}
if (callerMember != null && calleeMember != null)
{
result = new OptimizedVirtualCall(callerMember, calleeMember, callSite, bytecodeInstruction);
}
else
{
logger.error("Could not create OVC from\ncaller: {}\ncallee: {}\nCallSite was: {}", callerMember,
calleeMember, callSite);
}
}
else
{
logger.error("Could not find BytecodeInstruction for VCS: {}", callSite);
}
}
}
else
{
if (DEBUG_LOGGING_OVC)
{
logger.warn("Could not find memberBytecode for VCS: {}", callSite);
}
}
return result;
}
public IMetaMember findMember(MemberSignatureParts msp)
{
IMetaMember result = null;
String metaClassName = msp.getFullyQualifiedClassName();
MetaClass metaClass = model.getPackageManager().getMetaClass(metaClassName);
if (DEBUG_LOGGING_OVC)
{
logger.debug("Looking for metaClass: {} found: {}", metaClassName, metaClass);
}
if (metaClass != null)
{
result = metaClass.getMemberForSignature(msp);
}
return result;
}
private MemberBytecode getMemberBytecodeForCallSite(VirtualCallSite callSite)
{
if (DEBUG_LOGGING_OVC)
{
logger.debug("getMemberBytecodeForCallSite({})", callSite);
}
MemberBytecode result = null;
if (callSite != null)
{
String callerClass = callSite.getClassName();
MetaClass metaClass = model.getPackageManager().getMetaClass(callerClass);
if (DEBUG_LOGGING_OVC)
{
logger.debug("Found MetaClass {} for callerClass {}", metaClass, callerClass);
}
if (metaClass != null)
{
ClassBC classBC = metaClass.getClassBytecode(model, classLocations);
if (DEBUG_LOGGING_OVC)
{
logger.debug("Got ClassBC: {}", classBC != null);
}
if (classBC != null)
{
result = SourceMapper.getMemberBytecodeForSourceLine(classBC, callSite.getSourceLine());
}
}
}
if (DEBUG_LOGGING_OVC)
{
logger.debug("Got MemberBytecode: {}", result != null);
}
return result;
}
public List<OptimizedVirtualCall> findOptimizedCalls(IMetaMember member)
{
if (DEBUG_LOGGING_OVC)
{
logger.debug("Looking for OVCs for member: {}", member);
}
Set<OptimizedVirtualCall> squashDuplicatesSet = new HashSet<>();
List<Compilation> compilations = member.getCompilations();
int compilationCount = compilations.size();
AssemblyMethod asmMethod = null;
if (compilationCount > 0)
{
asmMethod = compilations.get(compilationCount - 1).getAssembly();
}
if (DEBUG_LOGGING_OVC)
{
logger.debug("Member assembly\n{}", asmMethod);
}
if (asmMethod != null)
{
for (AssemblyBlock block : asmMethod.getBlocks())
{
squashDuplicatesSet.addAll(findInstructionsForBlock(member, block));
}
}
List<OptimizedVirtualCall> result = new ArrayList<>(squashDuplicatesSet);
return result;
}
public List<OptimizedVirtualCall> findInstructionsForBlock(IMetaMember member, AssemblyBlock block)
{
List<OptimizedVirtualCall> result = new ArrayList<>();
for (AssemblyInstruction instruction : block.getInstructions())
{
OptimizedVirtualCall optimizedVCall = findOptimizedCall(instruction);
if (optimizedVCall != null && !result.contains(optimizedVCall))
{
if (DEBUG_LOGGING_OVC)
{
logger.debug("Found OVC {} for member {}", optimizedVCall, member);
}
result.add(optimizedVCall);
}
}
return result;
}
}