/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2006-2008, Martin Schoeberl (martin@jopdesign.com)
Copyright (C) 2008-2009, 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/>.
*/
/* Notes: WCET times reported by JOP (noted if JopSIM+cache-timing differs)
* Method.java: 12039
* StartKfl.java: 3048-11200
* StartLift.java: 4638-4772 (JopSIM: 4636-4774)
*/
package com.jopdesign.wcet;
import com.jopdesign.common.AppSetup;
import com.jopdesign.common.MethodInfo;
import com.jopdesign.common.code.BasicBlock;
import com.jopdesign.common.code.CallString;
import com.jopdesign.common.code.ControlFlowGraph;
import com.jopdesign.common.code.ControlFlowGraph.CFGNode;
import com.jopdesign.common.code.ExecutionContext;
import com.jopdesign.common.code.Segment;
import com.jopdesign.common.code.CallGraph.ContextEdge;
import com.jopdesign.common.code.SuperGraph.ContextCFG;
import com.jopdesign.common.code.SuperGraph.SuperGraphEdge;
import com.jopdesign.common.code.SuperGraph.SuperGraphNode;
import com.jopdesign.common.config.Config;
import com.jopdesign.common.config.Config.BadConfigurationException;
import com.jopdesign.common.misc.MiscUtils;
import com.jopdesign.dfa.DFATool;
import com.jopdesign.wcet.analysis.AnalysisContextLocal;
import com.jopdesign.wcet.analysis.GlobalAnalysis;
import com.jopdesign.wcet.analysis.LocalAnalysis;
import com.jopdesign.wcet.analysis.cache.CacheAnalysis.UnsupportedCacheModelException;
import com.jopdesign.wcet.analysis.cache.MethodCacheAnalysis;
import com.jopdesign.wcet.analysis.InvalidFlowFactException;
import com.jopdesign.wcet.analysis.RecursiveWcetAnalysis;
import com.jopdesign.wcet.analysis.TreeAnalysis;
import com.jopdesign.wcet.analysis.UppaalAnalysis;
import com.jopdesign.wcet.analysis.WcetCost;
import com.jopdesign.wcet.ipet.IPETConfig;
import com.jopdesign.wcet.ipet.IPETConfig.CacheCostCalculationMethod;
import com.jopdesign.wcet.ipet.LpSolveWrapper;
import com.jopdesign.wcet.uppaal.UppAalConfig;
import com.jopdesign.wcet.uppaal.model.DuplicateKeyException;
import com.jopdesign.wcet.uppaal.model.XmlSerializationException;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.MONITORENTER;
import org.apache.bcel.generic.MONITOREXIT;
import org.apache.log4j.Logger;
import org.jgrapht.traverse.TopologicalOrderIterator;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import lpsolve.LpSolveException;
import static com.jopdesign.wcet.ExecHelper.timeDiff;
/**
* WCET Analysis for JOP - Executable
*/
public class WCETAnalysis {
public static void main(String[] args) {
// We set a different output path for this tool if invoked by cmdline
// Note that WCETTool could also override defaults, but we do not want to change the
// default value of outdir if WCETTool is invoked from another tool
Properties defaultProps = new Properties();
defaultProps.put("outdir", "java/target/wcet/${projectname}");
AppSetup setup = new AppSetup(defaultProps, false);
setup.setVersionInfo("1.0.1");
// We do not load a config file automatically, user has to specify it explicitly to avoid
// unintentional misconfiguration
//setup.setConfigFilename(CONFIG_FILE_NAME);
setup.setUsageInfo("WCETAnalysis", "WCET Analysis tool");
WCETTool wcetTool = new WCETTool();
DFATool dfaTool = new DFATool();
setup.registerTool("dfa", dfaTool, true, false);
setup.registerTool("wcet", wcetTool);
setup.addSourceLineOptions(false);
setup.initAndLoad(args, true, false, false);
if (setup.useTool("dfa")) {
wcetTool.setDfaTool(dfaTool);
}
ExecHelper exec = new ExecHelper(setup.getConfig(), Logger.getLogger(WCETTool.LOG_WCET+".WCETAnalysis"));
exec.dumpConfig(); /* Load config */
exec.checkLibs(); /* check environment */
WCETAnalysis inst = new WCETAnalysis(wcetTool, exec);
try {
inst.run();
exec.info("Worst Case Analysis finished");
exec.info("Cumulative LP/ILP solver time: "+LpSolveWrapper.getTotalSolverTime());
exec.info("Cumulative WCET Calculation time: "+inst.totalWCETCalculationTime);
} catch (Exception e) {
exec.bail("Worst Case Analysis failed: " + e);
}
}
private Config config;
private WCETTool wcetTool;
// TODO: Maybe exec can be replaced by something stefan's framework?
private ExecHelper exec;
private WcetCost wcet;
private WcetCost alwaysMissCost;
private WcetCost alwaysHitCost;
private WcetCost approxCost;
private IPETConfig ipetConfig;
private double totalWCETCalculationTime = 0.0;
public WCETAnalysis(WCETTool wcetTool, ExecHelper e) {
this.wcetTool = wcetTool;
this.config = wcetTool.getConfig();
this.exec = e;
}
private boolean run() {
/* Initialize */
try {
wcetTool.setTopLevelLogger(exec.getExecLogger());
exec.info("Loading project");
wcetTool.initialize(wcetTool.getProjectConfig().doLoadLinkInfo(), true);
} catch (Exception e) {
exec.logException("Loading project", e);
return false;
}
// exploreCacheAnalysis();
// project.getLinkerInfo().dump(System.out);
// new ConstantCache(project).build().dumpStats();
/* Perf-Test */
// for(int i = 0; i < 50; i++) {
// RecursiveAnalysis<StaticCacheApproximation> an =
// new RecursiveAnalysis<StaticCacheApproximation>(project,new RecursiveAnalysis.LocalIPETStrategy());
// an.computeWCET(project.getTargetMethod(),StaticCacheApproximation.ALWAYS_HIT);
// }
// System.err.println("Total solver time (50): "+LpSolveWrapper.getSolverTime());
// System.exit(1);
MethodInfo targetMethod = wcetTool.getTargetMethod();
if(wcetTool.getProjectConfig().doObjectCacheAnalysis()) {
ObjectCacheEvaluation oca = new ObjectCacheEvaluation(wcetTool);
return oca.run(targetMethod);
} else {
return runWCETAnalysis(targetMethod);
}
}
private boolean runWCETAnalysis(MethodInfo targetMethod) {
/* Run */
ipetConfig = new IPETConfig(config);
try {
/* Analysis */
/* some metrics and some cheap analyses for comparison and
* report logging (not supported by global/uppaal at the moment) */
runMetrics(targetMethod);
if(wcetTool.getProjectConfig().doBlockingTimeAnalysis()) {
runBlockingTimeAnalysis(targetMethod);
}
exec.info("Starting precise WCET analysis");
/* uppaal */
if(wcetTool.getProjectConfig().useUppaal()) {
runUppaal(targetMethod);
}
/* global IPET */
else {
runGlobal(targetMethod);
}
exec.info("WCET analysis finished: "+wcet);
/* report generation */
return generateReport();
} catch (Exception e) {
exec.logException("analysis", e);
return false;
}
}
private void runMetrics(MethodInfo targetMethod) throws Exception {
/* generate reports later for simple_fit */
wcetTool.setGenerateWCETReport(false);
/* check whether the largest method fits into the cache */
List<MethodInfo> allMethods = wcetTool.getCallGraph().getReachableImplementations(targetMethod);
MethodInfo largestMethod = MethodCacheAnalysis.checkCache(wcetTool, allMethods);
int minWords = MiscUtils.bytesToWords(largestMethod.getCode().getNumberOfBytes());
reportMetric("min-cache-size",largestMethod.getFQMethodName(),minWords);
/* Compute cyclomatic complexity */
exec.info("Cyclomatic complexity: " + wcetTool.computeCyclomaticComplexity(targetMethod));
/* Fast, useful cache approximationx */
CacheCostCalculationMethod cacheApprox = CacheCostCalculationMethod.ALL_FIT_SIMPLE;
/* Perform a few standard analysis (MIN_CACHE_COSdT, ALWAYS_HIT, ALWAYS_MISS) without call strings */
long start,stop;
/* Tree based WCET analysis - has to be equal to ALWAYS_MISS if no flow facts are used */
{
start = System.nanoTime();
TreeAnalysis treeAna = new TreeAnalysis(wcetTool, false);
long treeWCET = treeAna.computeWCET(targetMethod);
stop = System.nanoTime();
reportMetric("progress-measure",treeAna.getMaxProgress(targetMethod));
reportSpecial("wcet.tree",WcetCost.totalCost(treeWCET),start,stop,0.0);
}
RecursiveWcetAnalysis<AnalysisContextLocal> an =
new RecursiveWcetAnalysis<AnalysisContextLocal>(
wcetTool, ipetConfig,
new LocalAnalysis(wcetTool,ipetConfig));
/* always miss */
start = System.nanoTime();
alwaysMissCost = an.computeCost(targetMethod,new AnalysisContextLocal(CacheCostCalculationMethod.ALWAYS_MISS));
stop = System.nanoTime();
reportSpecial("always-miss",alwaysMissCost,start,stop,LpSolveWrapper.getSolverTime());
/* always hit */
LpSolveWrapper.resetSolverTime();
start = System.nanoTime();
alwaysHitCost = an.computeCost(targetMethod, new AnalysisContextLocal(CacheCostCalculationMethod.ALWAYS_HIT));
stop = System.nanoTime();
reportSpecial("always-hit",alwaysHitCost,start,stop,LpSolveWrapper.getSolverTime());
/* simple approx */
wcetTool.setGenerateWCETReport(true);
start = System.nanoTime();
approxCost = an.computeCost(targetMethod,new AnalysisContextLocal(cacheApprox));
stop = System.nanoTime();
reportSpecial("recursive-report",approxCost,start,stop,LpSolveWrapper.getSolverTime());
wcetTool.setGenerateWCETReport(false);
}
private static class SynchronizedBlockResult {
int id;
Segment synchronizedSegment;
CFGNode node;
InstructionHandle ih;
WcetCost cost;
List<SynchronizedBlockResult> nested = new ArrayList<SynchronizedBlockResult>();
public SynchronizedBlockResult(int id, Segment synchronizedSegment, CFGNode node,
InstructionHandle ih, WcetCost wcet) {
this.id = id;
this.synchronizedSegment = synchronizedSegment;
this.node = node;
this.ih = ih;
this.cost = wcet;
}
public void dump(PrintStream ps) {
ps.println("[" + id + "] " + this.getPosDescr()+" "+cost);
if(nested.size() > 0) {
ps.println(" Nested Blocks: ");
for(SynchronizedBlockResult r : nested) {
ps.println(" "+r.getPosDescr());
}
}
}
private String getPosDescr() {
return node.getBasicBlock().getStartLine();
}
}
private void runBlockingTimeAnalysis(MethodInfo targetMethod)
throws InvalidFlowFactException, LpSolveException, UnsupportedCacheModelException {
GlobalAnalysis an = new GlobalAnalysis(wcetTool, ipetConfig);
CacheCostCalculationMethod requestedCacheApprox = IPETConfig.getRequestedCacheApprox(config);
/* Find all synchronized segments */
Segment target = Segment.methodSegment(targetMethod, CallString.EMPTY,
wcetTool, wcetTool.getCallstringLength(), wcetTool);
ArrayList<SynchronizedBlockResult> sBlocks = new ArrayList<SynchronizedBlockResult>();
for(ContextCFG ccfg : target.getCallGraphNodes()) {
for (CFGNode cfgNode : ccfg.getCfg().vertexSet()) {
if(cfgNode.getBasicBlock() == null) continue;
for (InstructionHandle ih : cfgNode.getBasicBlock().getInstructions()) {
if (ih.getInstruction() instanceof MONITORENTER) {
/* compute synchronized block WCET */
Segment synchronizedSegment = Segment.synchronizedSegment(ccfg, cfgNode, ih,
wcetTool,wcetTool.getCallstringLength(), wcetTool);
wcet = an.computeWCET(targetMethod.getShortName(), synchronizedSegment, requestedCacheApprox);
sBlocks.add(new SynchronizedBlockResult(sBlocks.size(), synchronizedSegment, cfgNode, ih, wcet));
}
}
}
}
/* check nested synchronized blocks */
for(SynchronizedBlockResult sBlock : sBlocks) {
for(SynchronizedBlockResult otherBlock : sBlocks) {
if(sBlock == otherBlock) continue;
for(SuperGraphEdge entryEdge : otherBlock.synchronizedSegment.getEntryEdges()) {
if(sBlock.synchronizedSegment.includesEdge(entryEdge)) {
sBlock.nested.add(otherBlock);
break;
}
}
}
}
System.out.println("=== Synchronized Blocks ===");
for(SynchronizedBlockResult sBlock : sBlocks) {
sBlock.dump(System.out);
}
}
private void runGlobal(MethodInfo targetMethod)
throws InvalidFlowFactException, LpSolveException, UnsupportedCacheModelException {
CacheCostCalculationMethod requestedCacheApprox = IPETConfig.getRequestedCacheApprox(config);
GlobalAnalysis an = new GlobalAnalysis(wcetTool, ipetConfig);
Segment target = Segment.methodSegment(targetMethod, CallString.EMPTY,
wcetTool, wcetTool.getCallstringLength(), wcetTool);
/* Run global analysis */
// for(CacheCostCalculationMethod cacheApprox : CacheCostCalculationMethod.values()) {
LpSolveWrapper.resetSolverTime();
long start = System.nanoTime();
wcet = an.computeWCET(targetMethod.getShortName(), target, requestedCacheApprox);
long stop = System.nanoTime();
report(wcet, start, stop, LpSolveWrapper.getSolverTime());
}
/**
* @throws BadConfigurationException
* @throws XmlSerializationException
* @throws DuplicateKeyException
* @throws IOException
*/
private void runUppaal(MethodInfo targetMethod)
throws BadConfigurationException, IOException, DuplicateKeyException, XmlSerializationException {
UppaalAnalysis an = new UppaalAnalysis(exec.getExecLogger(),wcetTool,wcetTool.getOutDir("uppaal"));
config.checkPresent(UppAalConfig.UPPAAL_VERIFYTA_BINARY);
/* Run uppaal analysis */
long start = System.nanoTime();
wcet = an.computeWCET(targetMethod,alwaysMissCost.getCost());
long stop = System.nanoTime();
reportUppaal(wcet,start,stop,an.getSearchtime(),an.getSolvertimemax());
}
/**
* @param mca
* @param iter
* @throws InvalidFlowFactException
*/
@SuppressWarnings("unused")
private void exploreCacheAnalysis()
throws InvalidFlowFactException {
// Segment Cache Analysis: Experiments
MethodCacheAnalysis mca = new MethodCacheAnalysis(wcetTool);
/* iterate top down the scope graph (currently: the call graph) */
TopologicalOrderIterator<ExecutionContext, ContextEdge> iter =
wcetTool.getCallGraph().reverseTopologicalOrder();
LpSolveWrapper.resetSolverTime();
long blocks = 0;
long start = System.nanoTime();
while (iter.hasNext()) {
ExecutionContext scope = iter.next();
Segment segment = Segment.methodSegment(scope.getMethodInfo(), scope.getCallString(), wcetTool,
wcetTool.getCallstringLength(), wcetTool);
int availBlocks = wcetTool.getWCETProcessorModel().getMethodCache().getNumBlocks();
long total, distinctApprox = -1, distinct = -1;
blocks = total = mca.countDistinctBlocksUsed(segment);
if(total > availBlocks || true) {
try {
blocks = distinctApprox = mca.countDistinctBlocksAccessed(segment, false);
if(blocks > availBlocks && blocks < availBlocks*2 || true) {
blocks = distinct = mca.countDistinctBlocksAccessed(segment, true);
}
} catch (LpSolveException e) {
System.err.println((distinctApprox>=0 ? "I" : "Relaxed ")+"LP Problem too difficult, giving up: "+e);
}
}
System.out.println(String.format("block-count < %2d [%2d,%2d,%2d] for %-30s @ %s", blocks,
total, distinctApprox, distinct,
scope.getMethodInfo().getFQMethodName(), scope.getCallString().toStringVerbose(false)));
}
long stop = System.nanoTime();
reportSpecial("block-count",WcetCost.totalCost(blocks),start,stop,LpSolveWrapper.getSolverTime());
System.out.println("solver-time: "+LpSolveWrapper.getSolverTime());
}
/**
* @return
*/
private boolean generateReport() {
if (!wcetTool.getProjectConfig().doGenerateReport()) {
exec.info("Ommiting HTML report");
return true;
}
try {
/* Report */
exec.info("Generating info pages");
wcetTool.getReport().generateInfoPages();
exec.info("Generating result document");
wcetTool.writeReport();
exec.info("Generated files are in " + wcetTool.getProjectConfig().getProjectDir());
} catch (Exception e) {
exec.logException("Report generation", e);
return false;
}
return true;
}
private void reportMetric(String metric, Object... args) {
wcetTool.recordMetric(metric, args);
System.out.print(metric+":");
for(Object o : args) System.out.print(" "+o);
System.out.println("");
}
private void report(WcetCost wcet, long start, long stop,double solverTime) {
String key = "wcet";
System.out.println(key+": "+wcet);
System.out.println(key+".time: " + timeDiff(start,stop));
totalWCETCalculationTime += timeDiff(start,stop);
System.out.println(key+".solvertime: " + solverTime);
wcetTool.recordResult(wcet,timeDiff(start,stop),solverTime);
wcetTool.getReport().addStat(key, wcet.toString());
}
private void reportUppaal(WcetCost wcet, long start, long stop, double searchtime, double solvertimemax) {
String key = "wcet";
System.out.println(key+": "+wcet);
System.out.println(key+".time: " + timeDiff(start,stop));
totalWCETCalculationTime += timeDiff(start,stop);
System.out.println(key+".searchtime: " + searchtime);
System.out.println(key+".solvertimemax: " + solvertimemax);
wcetTool.recordResultUppaal(wcet,timeDiff(start,stop),searchtime,solvertimemax);
wcetTool.getReport().addStat(key, wcet.toString());
}
private void reportSpecial(String name, WcetCost cost) {
String key = "wcet."+name;
System.out.println(key+": "+cost);
wcetTool.recordSpecialResult(name,cost);
if (wcetTool.reportGenerationActive()) {
wcetTool.getReport().addStat(key, cost.toString());
}
}
private void reportSpecial(String metric, WcetCost cost, long start, long stop, double solverTime) {
reportSpecial(metric,cost);
String key = "wcet."+metric;
System.out.println(key+".time: " + timeDiff(start,stop));
totalWCETCalculationTime += timeDiff(start,stop);
if(solverTime != 0) System.out.println(key+".solvertime: " + solverTime);
}
}