/*
* This file is part of JOP, the Java Optimized Processor
* see <http://www.jopdesign.com/>
*
* Copyright (C) 2010, Stefan Hepp (stefan@stefant.org).
* Copyright (C) 2010, Benedikt Huber (benedikt.huber@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.wcet;
import com.jopdesign.common.AppInfo;
import com.jopdesign.common.AppSetup;
import com.jopdesign.common.ClassInfo;
import com.jopdesign.common.EmptyTool;
import com.jopdesign.common.MethodInfo;
import com.jopdesign.common.code.BasicBlock;
import com.jopdesign.common.code.CFGCallgraphBuilder;
import com.jopdesign.common.code.CFGProvider;
import com.jopdesign.common.code.CallGraph;
import com.jopdesign.common.code.CallString;
import com.jopdesign.common.code.ControlFlowGraph;
import com.jopdesign.common.code.ControlFlowGraph.BasicBlockNode;
import com.jopdesign.common.code.ControlFlowGraph.CFGEdge;
import com.jopdesign.common.code.ControlFlowGraph.CFGNode;
import com.jopdesign.common.code.DefaultCallgraphBuilder;
import com.jopdesign.common.code.ExecutionContext;
import com.jopdesign.common.code.InfeasibleEdgeProvider;
import com.jopdesign.common.code.InvokeSite;
import com.jopdesign.common.code.LoopBound;
import com.jopdesign.common.config.Config;
import com.jopdesign.common.config.Config.BadConfigurationException;
import com.jopdesign.common.config.OptionGroup;
import com.jopdesign.common.misc.AppInfoException;
import com.jopdesign.common.misc.BadGraphError;
import com.jopdesign.common.misc.BadGraphException;
import com.jopdesign.common.misc.MethodNotFoundException;
import com.jopdesign.common.misc.MiscUtils;
import com.jopdesign.common.processormodel.JOPConfig;
import com.jopdesign.common.processormodel.ProcessorModel;
import com.jopdesign.dfa.DFATool;
import com.jopdesign.dfa.analyses.LoopBounds;
import com.jopdesign.dfa.framework.DFACallgraphBuilder;
import com.jopdesign.dfa.framework.FlowEdge;
import com.jopdesign.timing.TimingTable;
import com.jopdesign.timing.jop.JOPTimingTable;
import com.jopdesign.wcet.allocation.BlockAllocationModel;
import com.jopdesign.wcet.allocation.HandleAllocationModel;
import com.jopdesign.wcet.allocation.HeaderAllocationModel;
import com.jopdesign.wcet.allocation.ObjectAllocationModel;
import com.jopdesign.wcet.analysis.WcetCost;
import com.jopdesign.wcet.annotations.BadAnnotationException;
import com.jopdesign.wcet.annotations.SourceAnnotations;
import com.jopdesign.wcet.ipet.IPETConfig;
import com.jopdesign.wcet.jop.JOPWcetModel;
import com.jopdesign.wcet.jop.LinkerInfo;
import com.jopdesign.wcet.jop.LinkerInfo.LinkInfo;
import com.jopdesign.wcet.report.Report;
import com.jopdesign.wcet.report.ReportConfig;
import com.jopdesign.wcet.uppaal.UppAalConfig;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.log4j.Logger;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* <p>Purpose: This class provides the interface to JOP's WCET tool</p>
* <p>
* <p>Note that it is currently not possible to create multiple instances of this tool, because the
* annotation loader would be registered more than once in this case. To analyze multiple WCA targets,
* the WCA tool must support this feature itself (e.g. by creating one callgraph per target internally).</p>
*
* For a typical usage, you would
* <ul>
* <li/> Create a tool instance:
* <pre>WCETTool wcetTool = new WCETTool();</pre>
* <li/> Register and Initialize the WCET tool:
* <pre>setup.registerTool("wcet", wcetTool);</pre>
* <pre>wcetTool.initialize(); </pre>
* <li/> Create analysis instance:
* <pre> RecursiveWcetAnalysis an =
* new RecursiveWcetAnalysis(wcetTool,ipetConfig,strategy);</pre>
* <li/> Run Analysis:
* <pre> wcet = an.computeCost(wcetTool.getTargetMethod(), analysisContex); </pre>
* </ul>
* </p>
*
* @see WCETAnalysis
* @author Stefan Hepp (stefan@stefant.org)
* @author Benedikt Huber (benedikt.huber@gmail.com)
*/
public class WCETTool extends EmptyTool<WCETEventHandler> implements CFGProvider, InfeasibleEdgeProvider {
public static final String VERSION = "1.0.1";
// root logger for wcet tool
public static final String LOG_WCET = "wcet";
// logger for project related stuff (main, config, eventhandler,..)
public static final String LOG_WCET_PROJECT = "wcet.project";
// logger for various sub-packages
public static final String LOG_WCET_ANALYSIS = "wcet.analysis";
public static final String LOG_WCET_CACHE = "wcet.analysis.cache";
public static final String LOG_WCET_ANNOTATIONS = "wcet.annotations";
public static final String LOG_WCET_REPORT = "wcet.report";
public static final String LOG_WCET_IPET = "wcet.ipet";
public static final String LOG_WCET_UPPAAL = "wcet.uppaal";
public static final Logger logger = Logger.getLogger(LOG_WCET_PROJECT + ".WCETTool");
/* special logger */
// TODO we could set this to something like 'console.wcet', and setup the console logger to
// print INFO for 'console.*' and WARN/ERROR only for the rest if '-q' and '-d' are not given
private Logger topLevelLogger = Logger.getLogger(LOG_WCET);
private WCETEventHandler eventHandler;
private ProjectConfig projectConfig;
private String projectName;
private AppInfo appInfo;
private CallGraph callGraph;
private DFATool dfaTool;
private boolean genWCETReport;
private Report results;
private WCETProcessorModel processor;
private File resultRecord;
private LinkerInfo linkerInfo;
private boolean hasDfaResults;
private boolean standaloneOptions = true;
private boolean ipetOptions = true;
private boolean uppaalOptions = true;
private boolean reportOptions = true;
public WCETTool() {
super(VERSION);
eventHandler = new WCETEventHandler(this);
}
/**
* Set which options should be exposed to the user.
* @param standalone if true, add options to set target method and to enable report generation
* @param ipet if true, add IPET options
* @param uppaal if true, add UPPAAL options
* @param reports if true, add report generation options
*/
public void setAvailableOptions(boolean standalone, boolean ipet, boolean uppaal, boolean reports) {
standaloneOptions = standalone;
ipetOptions = ipet;
uppaalOptions = uppaal;
reportOptions = reports;
}
@Override
public WCETEventHandler getEventHandler() {
return eventHandler;
}
@Override
public void registerOptions(Config config) {
CallGraph.registerOptions(config);
// TODO maybe put some of the options into OptionGroups to make '--help' a bit clearer
ProjectConfig.registerOptions(config, standaloneOptions, uppaalOptions, reportOptions);
config.addOptions(IPETConfig.ipetOptions, ipetOptions);
config.addOptions(UppAalConfig.uppaalOptions, uppaalOptions);
config.addOptions(ReportConfig.reportOptions, reportOptions);
}
@Override
public void onSetupConfig(AppSetup setup) throws BadConfigurationException {
appInfo = setup.getAppInfo();
Config config = setup.getConfig();
projectConfig = new ProjectConfig(config);
projectConfig.initConfig(setup.getMainMethodID());
this.projectName = projectConfig.getProjectName();
if (projectConfig.doGenerateReport()) {
this.results = new Report(this, setup.getLoggerConfig());
try {
this.results.initVelocity();
} catch (Exception e) {
throw new BadConfigurationException("Error initializing Velocity: "+e, e);
}
this.genWCETReport = true;
} else {
this.genWCETReport = false;
}
}
@SuppressWarnings({"LiteralAsArgToStringEquals"})
@Override
public void onSetupAppInfo(AppSetup setup, AppInfo appInfo) throws BadConfigurationException {
// We only do setup stuff here, but do not perform any preprocessing (this is done in initialize())
if (projectConfig.getProcessorName().equals("allocObjs")) {
this.processor = new ObjectAllocationModel(this);
} else if (projectConfig.getProcessorName().equals("allocHandles")) {
this.processor = new HandleAllocationModel(this);
} else if (projectConfig.getProcessorName().equals("allocHeaders")) {
this.processor = new HeaderAllocationModel(this);
} else if (projectConfig.getProcessorName().equals("allocBlocks")) {
this.processor = new BlockAllocationModel(this);
} else if (projectConfig.getProcessorName().equals("jamuth")) {
this.processor = new JamuthWCETModel(this);
} else if (projectConfig.getProcessorName().equals("JOP")) {
try {
this.processor = new JOPWcetModel(this);
} catch (IOException e) {
throw new BadConfigurationException("Unable to initialize JopWcetModel: " + e.getMessage(), e);
}
} else {
throw new BadConfigurationException("Unknown WCET model: " + projectConfig.getProcessorName());
}
// create output dir only after initialization is successful
File outDir = projectConfig.getProjectDir();
Config.checkDir(outDir, true);
}
public void initialize(boolean loadLinkInfo, boolean initDFA) throws BadConfigurationException {
if (projectConfig.saveResults()) {
this.resultRecord = projectConfig.getResultFile();
if (!projectConfig.appendResults()) {
// TODO remove existing file if we do not append?
//resultRecord.delete();
recordMetric("problem", this.getProjectName());
if (projectConfig.addPerformanceResults()) {
recordMetric("date", new Date());
}
}
}
if (loadLinkInfo) {
linkerInfo = new LinkerInfo(this);
try {
linkerInfo.loadLinkInfo();
} catch (IOException e) {
throw new BadConfigurationException("Could not load link infos", e);
} catch (ClassNotFoundException e) {
throw new BadConfigurationException("Could not load link infos", e);
}
}
/* run dataflow analysis */
if (doDataflowAnalysis() && initDFA) {
topLevelLogger.info("Starting DFA analysis");
dataflowAnalysis();
topLevelLogger.info("DFA analysis finished");
}
if (!appInfo.hasCallGraph()) {
DefaultCallgraphBuilder callGraphBuilder;
/* build callgraph for the whole program */
if (doDataflowAnalysis()) {
// build the callgraph using DFA results
callGraphBuilder = new DFACallgraphBuilder(getDfaTool(), appInfo.getCallstringLength());
} else {
callGraphBuilder = new DefaultCallgraphBuilder();
}
callGraphBuilder.setSkipNatives(true); // we do not want natives in the callgraph
appInfo.buildCallGraph(callGraphBuilder);
}
/* build callgraph for target method */
rebuildCallGraph();
if (projectConfig.doPreprocess()) {
WCETPreprocess.preprocess(appInfo);
}
dumpCallGraph("callgraph");
}
public AppInfo getAppInfo() {
return appInfo;
}
public ProcessorModel getProcessorModel() {
return appInfo.getProcessorModel();
}
public WCETProcessorModel getWCETProcessorModel() {
return processor;
}
public ProjectConfig getProjectConfig() {
return projectConfig;
}
public Config getConfig() {
return projectConfig.getConfig();
}
public int getCallstringLength() {
return appInfo.getCallstringLength();
}
/**
* Rebuild the WCET callgraph, starting at the target method.
* The new callgraph will be based on (but not backed by) the AppInfo callgraph, if available.
*
* @return the new callgraph.
*/
public CallGraph rebuildCallGraph() {
/* This would be the ideal solution, but this way the root
* does NOT have an empty callstring
*/
// callGraph = appInfo.getCallGraph().getSubGraph(projectConfig.getTargetMethodInfo());
/* Instead, we create a new "subgraph" based on the appInfo callgraph (which has been created using
* DFA results if available in initialize() or by some other tool), where the target method has an empty
* callstring, using the callstring length configured for the WCET tool (which is currently the same
* as the global setting).
*/
DefaultCallgraphBuilder callGraphBuilder = new CFGCallgraphBuilder(getCallstringLength());
callGraphBuilder.setSkipNatives(true); // we do not want natives in the callgraph
callGraph = CallGraph.buildCallGraph(getTargetMethod(), callGraphBuilder);
try {
callGraph.checkAcyclicity();
} catch (AppInfoException e) {
throw new AssertionError(e);
}
return callGraph;
}
public void dumpCallGraph(String graphName) {
if (callGraph == null) return;
Config config = projectConfig.getConfig();
try {
callGraph.dumpCallgraph(config, graphName, "target", null,
config.getDebugGroup().getOption(ProjectConfig.DUMP_TARGET_CALLGRAPH), false);
} catch (IOException e) {
logger.warn("Unable to dump the target method callgraph", e);
}
}
/** @return the precomputed callgraph for WCET analysis */
public CallGraph getCallGraph() {
return callGraph;
}
public DFATool getDfaTool() {
return dfaTool;
}
public void setTopLevelLogger(Logger topLevelLogger) {
this.topLevelLogger = topLevelLogger;
}
public void setDfaTool(DFATool dfaTool) {
this.dfaTool = dfaTool;
}
public ClassInfo getTargetClass() {
return appInfo.getClassInfo(projectConfig.getTargetClass());
}
public String getTargetName() {
return MiscUtils.sanitizeFileName(projectConfig.getAppClassName()
+ "_" + projectConfig.getTargetMethodName());
}
public MethodInfo getTargetMethod() {
try {
return appInfo.getMethodInfo(projectConfig.getTargetClass(),
projectConfig.getTargetMethod());
} catch (MethodNotFoundException e) {
throw new AssertionError("Target method not found: " + e);
}
}
public String getProjectName() {
return this.projectName;
}
public boolean reportGenerationActive() {
return this.genWCETReport;
}
public void setGenerateWCETReport(boolean generateReport) {
this.genWCETReport = generateReport;
}
public Report getReport() {
return results;
}
/**
* Get link info for a given class
*
* @param cli the class
* @return the linker info
*/
public LinkInfo getLinkInfo(ClassInfo cli) {
return this.linkerInfo.getLinkInfo(cli);
}
public LinkerInfo getLinkerInfo() {
return this.linkerInfo;
}
/**
* Get the flowgraph of the given method.
* <p>
* A new callgraph is constructed when this method is called, changes to this graph are not
* automatically stored back to MethodCode. If you want to keep changes to the graph you need to keep a
* reference to this graph yourself.
* </p>
*
* @param mi the method to get the CFG for
* @return the CFG for the method.
*/
public ControlFlowGraph getFlowGraph(MethodInfo mi) {
if (!mi.hasCode()) {
throw new AssertionError("No CFG for MethodInfo "+mi);
}
ControlFlowGraph cfg;
try {
/* TODO We need to make sure that changes to the CFG are not compiled back automatically
* but CFG#compile() is not yet fully implemented anyways.
* We could add an option to MethodCode#getControlFlowGraph() to get a graph which will not be compiled back.
*
* We could also create a new graph instead, would allow us to create CFGs per callstring and be consistent
* with this.callgraph. But as long as neither this.callgraph and appInfo.callgraph are not modified
* after rebuildCallGraph() has been called, there is no difference.
*
* However, if we create a new graph we have to
* - keep the new graph, if we just recreate them modifications by analyses will be lost and some things
* like SuperGraph will break if we return new graphs every time.
* - provide a way to rebuild the CFGs if the code is modified, either by implementing WCETEventHandler.onMethodModified
* or by providing a dispose() method which must be called before the analysis starts after any code modifications.
* - when we do not use a CFG anymore (e.g. because we recreate it) we need to call CFG.dispose() so that it
* is not attached to the instruction handles anymore.
*/
//cfg = new ControlFlowGraph(mi, CallString.EMPTY, callGraph);
cfg = mi.getCode().getControlFlowGraph(false);
cfg.resolveVirtualInvokes();
cfg.insertReturnNodes();
cfg.insertContinueLoopNodes();
// cfg.insertSplitNodes();
// cfg.insertSummaryNodes();
} catch (BadGraphException e) {
// TODO handle this somehow??
throw new BadGraphError(e.getMessage(), e);
}
return cfg;
}
// TODO move somewhere else?
public LoopBound getLoopBound(CFGNode node, CallString cs) {
LoopBound globalBound = node.getLoopBound();
ExecutionContext eCtx = new ExecutionContext(node.getControlFlowGraph().getMethodInfo(), cs);
if (node.getBasicBlock() != null) {
return this.getEventHandler().dfaLoopBound(node.getBasicBlock(), eCtx, globalBound);
} else {
return globalBound;
}
}
/**
* Convenience delegator to get the size of the given method
* @param mi method to get the size of
* @return size of the method in words, or 0 if abstract
*/
public int getSizeInWords(MethodInfo mi) {
if (!mi.hasCode()) return 0;
return mi.getCode().getNumberOfWords();
}
public void writeReport() throws Exception {
this.results.addStat("classpath", getAppInfo().getClassPath());
this.results.addStat("application", projectConfig.getAppClassName());
this.results.addStat("class", projectConfig.getTargetClass());
this.results.addStat("method", projectConfig.getTargetMethod());
this.results.writeReport();
}
public File getOutDir(String sub) {
return projectConfig.getOutDir(sub);
}
// public File getOutFile(String file) {
// return new File(projectConfig.getOutDir(), file);
// }
/* FIXME: Slow, caching is missing */
public int computeCyclomaticComplexity(MethodInfo m) {
ControlFlowGraph g = getFlowGraph(m);
int nLocal = g.vertexSet().size();
int eLocal = g.edgeSet().size();
int pLocal = g.buildLoopBoundMap().size();
int ccLocal = eLocal - nLocal + 2 * pLocal;
int ccGlobal = 0;
for (ExecutionContext n : this.getCallGraph().getReferencedMethods(m)) {
MethodInfo impl = n.getMethodInfo();
ccGlobal += 2 + computeCyclomaticComplexity(impl);
}
return ccLocal + ccGlobal;
}
/* recording for scripted evaluation */
public void recordResult(WcetCost wcet, double timeDiff, double solverTime) {
if (resultRecord == null) return;
Config c = projectConfig.getConfig();
OptionGroup o = JOPConfig.getOptions(c);
if (projectConfig.addPerformanceResults()) {
recordCVS("wcet", "ipet", wcet, timeDiff, solverTime,
o.getOption(JOPConfig.CACHE_IMPL),
o.getOption(JOPConfig.CACHE_SIZE_WORDS),
o.getOption(JOPConfig.CACHE_BLOCKS),
c.getOption(IPETConfig.STATIC_CACHE_APPROX),
c.getOption(IPETConfig.ASSUME_MISS_ONCE_ON_INVOKE));
} else {
recordCVS("wcet", "ipet", wcet,
o.getOption(JOPConfig.CACHE_IMPL),
o.getOption(JOPConfig.CACHE_SIZE_WORDS),
o.getOption(JOPConfig.CACHE_BLOCKS),
c.getOption(IPETConfig.STATIC_CACHE_APPROX),
c.getOption(IPETConfig.ASSUME_MISS_ONCE_ON_INVOKE));
}
}
public void recordResultUppaal(WcetCost wcet,
double timeDiff, double searchtime, double solvertimemax) {
if (resultRecord == null) return;
Config c = projectConfig.getConfig();
OptionGroup o = JOPConfig.getOptions(c);
if (projectConfig.addPerformanceResults()) {
recordCVS("wcet", "uppaal", wcet, timeDiff, searchtime, solvertimemax,
o.getOption(JOPConfig.CACHE_IMPL),
o.getOption(JOPConfig.CACHE_SIZE_WORDS),
o.getOption(JOPConfig.CACHE_BLOCKS),
c.getOption(UppAalConfig.UPPAAL_CACHE_APPROX),
c.getOption(UppAalConfig.UPPAAL_COMPLEXITY_TRESHOLD),
c.getOption(UppAalConfig.UPPAAL_COLLAPSE_LEAVES),
c.getOption(UppAalConfig.UPPAAL_CONVEX_HULL),
c.getOption(UppAalConfig.UPPAAL_TIGHT_BOUNDS),
c.getOption(UppAalConfig.UPPAAL_PROGRESS_MEASURE),
c.getOption(UppAalConfig.UPPAAL_SUPERGRAPH_TEMPLATE),
c.getOption(UppAalConfig.UPPAAL_EMPTY_INITIAL_CACHE)
);
} else {
recordCVS("wcet", "uppaal", wcet,
o.getOption(JOPConfig.CACHE_IMPL),
o.getOption(JOPConfig.CACHE_SIZE_WORDS),
o.getOption(JOPConfig.CACHE_BLOCKS),
c.getOption(UppAalConfig.UPPAAL_CACHE_APPROX),
c.getOption(UppAalConfig.UPPAAL_COMPLEXITY_TRESHOLD),
c.getOption(UppAalConfig.UPPAAL_COLLAPSE_LEAVES),
c.getOption(UppAalConfig.UPPAAL_CONVEX_HULL),
c.getOption(UppAalConfig.UPPAAL_TIGHT_BOUNDS),
c.getOption(UppAalConfig.UPPAAL_PROGRESS_MEASURE),
c.getOption(UppAalConfig.UPPAAL_SUPERGRAPH_TEMPLATE),
c.getOption(UppAalConfig.UPPAAL_EMPTY_INITIAL_CACHE)
);
}
}
public void recordSpecialResult(String metric, WcetCost cost) {
if (resultRecord == null) return;
if (projectConfig.appendResults()) return;
recordCVS("metric", metric, cost);
}
public void recordMetric(String metric, Object... params) {
if (resultRecord == null) return;
if (projectConfig.appendResults()) return;
recordCVS("metric", metric, null, params);
}
private void recordCVS(String key, String subkey, WcetCost cost, Object... params) {
Object[] fixedCols = {key, subkey};
try {
FileWriter fw = new FileWriter(resultRecord, true);
fw.write(MiscUtils.joinStrings(fixedCols, ";"));
if (cost != null) {
Object[] costCols = {cost.getCost(), cost.getNonCacheCost(), cost.getCacheCost()};
fw.write(";");
fw.write(MiscUtils.joinStrings(costCols, ";"));
}
if (params.length > 0) {
fw.write(";");
fw.write(MiscUtils.joinStrings(params, ";"));
}
fw.write("\n");
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public File getSourceFile(ClassInfo ci) throws FileNotFoundException {
// TODO maybe move to AppInfo?
List<File> dirs = projectConfig.getSourceSearchDirs(ci);
for (File sourceDir : dirs) {
File sourceFile = new File(sourceDir, ci.getSourceFileName());
if (sourceFile.exists()) return sourceFile;
}
throw new FileNotFoundException("Source for " + ci.getClassName() + " not found in " + dirs);
}
/**
* Get flow fact annotations for a class, lazily.
*
* @param cli
* @return
* @throws IOException
* @throws BadAnnotationException
*/
public SourceAnnotations getAnnotations(ClassInfo cli) throws IOException, BadAnnotationException {
return eventHandler.getAnnotations(cli);
}
/* Data flow analysis
* ------------------
*/
public boolean doDataflowAnalysis() {
return dfaTool != null;
}
public void dataflowAnalysis() {
// Moved DFA tool cache config to the DFA tool, but still ...
// FIXME: At the moment, we do not have a nice directory structure respecting
// the fact that we perform many WCET analyses for one Application
dfaTool.setAnalyzeBootMethod(projectConfig.doAnalyzeBootMethod());
// TODO this is the same code as in JCopter PhaseExecutor
dfaTool.load();
topLevelLogger.info("Receiver analysis");
dfaTool.runReceiverAnalysis(getCallstringLength());
topLevelLogger.info("Loop bound analysis");
dfaTool.runLoopboundAnalysis(getCallstringLength());
this.hasDfaResults = true;
}
public void setHasDfaResults(boolean hasDfaResults) {
this.hasDfaResults = hasDfaResults;
}
/**
* @return the loop bounds found by dataflow analysis
*/
public LoopBounds getDfaLoopBounds() {
if (!hasDfaResults) return null;
return dfaTool.getLoopBounds();
}
/**
* Find Implementations of the method called with the given instruction handle
* Uses receiver type analysis to refine the results
*
* @see #findImplementations(MethodInfo, InstructionHandle, CallString)
* @param invokerM invoking method
* @param ih the invoke instruction
* @return list of possibly invoked methods
*/
public Collection<MethodInfo> findImplementations(MethodInfo invokerM, InstructionHandle ih) {
return findImplementations(invokerM, ih, CallString.EMPTY);
}
/**
* Find Implementations of the method called with the given instruction handle
* Uses receiver type analysis to refine the results
*
* @param invokerM invoking method
* @param ih the invoke instruction
* @param ctx the call context
* @return list of possibly invoked methods
*/
public Collection<MethodInfo> findImplementations(MethodInfo invokerM, InstructionHandle ih, CallString ctx) {
InvokeSite is = invokerM.getCode().getInvokeSite(ih);
// We do not use appInfo.findImplementations here so that the result is always consistent with the callgraph
return callGraph.findImplementations(ctx.push(is));
}
/**
* Get infeasible edges for certain call string
*
* @param cfg the controlflowgraph of the method
* @param cs the callstring of the method
* @return The infeasible edges
*/
public List<CFGEdge> getInfeasibleEdges(ControlFlowGraph cfg, CallString cs) {
List<CFGEdge> edges = new ArrayList<CFGEdge>();
for (BasicBlock b : cfg.getBlocks()) {
List<CFGEdge> edge = dfaInfeasibleEdge(cfg, b, cs);
edges.addAll(edge);
}
return edges;
}
@Override
public boolean isInfeasibleReceiver(MethodInfo method, CallString cs) {
return ! this.getCallGraph().hasNode(method, cs);
}
/**
* Get infeasible edges for certain basic block call string
* @param cfg the CFG containing the block
* @param block get infeasible outgoing edges for the block
* @param cs the callstring
* @return The infeasible edges for this basic block
*/
private List<CFGEdge> dfaInfeasibleEdge(ControlFlowGraph cfg, BasicBlock block, CallString cs) {
List<CFGEdge> retval = new LinkedList<CFGEdge>();
if (getDfaLoopBounds() != null) {
LoopBounds lbs = getDfaLoopBounds();
Set<FlowEdge> edges = lbs.getInfeasibleEdges(block.getLastInstruction(), cs);
for (FlowEdge e : edges) {
BasicBlockNode head = cfg.getHandleNode(e.getHead());
BasicBlockNode tail = cfg.getHandleNode(e.getTail());
CFGEdge edge = cfg.getEdge(tail, head);
if (edge != null) {
retval.add(edge);
} else {
// edge does was removed from the CFG
// logger.warn("The infeasible edge between "+head+" and "+tail+" does not exist");
}
}
}
return retval;
}
}