/* This file is part of JOP, the Java Optimized Processor see <http://www.jopdesign.com/> Copyright (C) 2006, Rasmus Ulslev Pedersen Copyright (C) 2006-2008, Martin Schoeberl (martin@jopdesign.com) Copyright (C) 2008-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.annotations; import com.jopdesign.common.ClassInfo; import com.jopdesign.common.code.LoopBound; import com.jopdesign.wcet.WCETTool; import org.apache.bcel.util.ClassPath.ClassFile; import org.apache.log4j.Logger; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Parsing source annotations for WCET analysis. * <p/> * We currently support the following source code annotations: * <ul> * <li/>{@code "loop" ("<=" | "=") execution-frequency-expression} * This annotations asserts that the loop associated with the source code line * is executed at most or exactly as often as {@code execution-frequency-expression}. * </ul> * <h2>Execution Frequency Expressions</h2> * An execution frequency expression is currently of the form * {@code integer-expression [context]}. * <ul> * <li/>{@code integer-expression} is a plain integer. * <li/>{@code context is one of} * <ul> * <li/> No context: The default context is the execution frequency of the entry edges of the loop * itself({@code outer(0)}) * <li/> {@code "outer" ["(" i ")"]}: The execution frequency of the entry edges of the i-th predecessor * in the loop-nest-tree. {@code i} defaults to 1 (the outer loop) if left out. * <li/> {@code "method" ["("name")"]}: The execution frequency of the entry edges of the method with the * given name. {@code name} defaults to the method containing the loop if no name is given.<br/> * TODO: Currently, we require that the method dominates the method containing * the loop. What we really want is: The constraint only applies to those call contexts, where * the method is in the callstring. * </ul> * </ul> * <p/> * TODO: Many more features :) Would be a nice internship. * * @author Benedikt Huber (benedikt.huber@gmail.com) */ public class SourceAnnotationReader { private static final Logger logger = Logger.getLogger(WCETTool.LOG_WCET_ANNOTATIONS+".SourceAnnotationReader"); private WCETTool project; public SourceAnnotationReader(WCETTool p) { this.project = p; } /** * Extract loop bound annotations for one class * <p/> * All annotations start with {@code // @WCA }, and belong to the last source code line encountered. * A source code line is a line which has at least one token in it. * * @return a FlowFacts object encapsulating the annotations found in the source file of the given class * @throws IOException * @throws BadAnnotationException */ public SourceAnnotations readAnnotations(ClassInfo ci) throws IOException, BadAnnotationException { SourceAnnotations flowFacts = new SourceAnnotations(); File fileName = getSourceFile(ci); BufferedReader reader = new BufferedReader(new FileReader(fileName)); String line = null; int lineNr = 1; int sourceLineNr = 1; while ((line = reader.readLine()) != null) { if (!SourceAnnotationReader.isCommentLine(line)) { sourceLineNr = lineNr; } LoopBound loopBound = SourceAnnotationReader.extractAnnotation(line); if (loopBound != null) { logger.debug("Adding loop bound @ " + sourceLineNr + ": " + loopBound); flowFacts.addLoopBound(sourceLineNr, loopBound); } lineNr++; } logger.debug("Read WCA annotations for " + fileName); return flowFacts; } private static boolean isCommentLine(String line) { Pattern pattern = Pattern.compile("^\\s*(//.*)?$"); Matcher matcher = pattern.matcher(line); return matcher.matches(); } private File getSourceFile(ClassInfo ci) throws FileNotFoundException, BadAnnotationException { File src = project.getSourceFile(ci); try { ClassFile klazz = project.getAppInfo().getClassFile(ci); if (src.lastModified() > klazz.getTime()) { throw new BadAnnotationException("Timestamp error: source file modified after compilation"); } } catch (IOException ex) { WCETTool.logger.warn("Could not find class file for " + ci, ex); } return src; } /** * Return the loop bound for a java source lines, if any. * <p> * Loop bounds annotations are of the form * <br/> * {@code // @WCA "loop" ("<=" | "=") execution-frequency-expression} * <br/> * Examples: * <ul><li/>{@code // @WCA loop <= 10} * <li/>{@code // @WCA loop = 45 outer} * <li/>{@code // @WCA loop <= 135 outer(2)} * <li/>{@code // @WCA loop <= 135 method} * <li/>{@code // @WCA loop = 423 method("main")} * </ul> * For details see the documentation of {@link SourceAnnotationReader}. * <br/> * NOTE: For backward compatibility reasons, we also support the notation:<br/> * {@code lower-bound "<=" "loop" "<=" upper-bound}<br/> * It is currently open if there will be a replacement for this rarely used feature. * </p> * * @param sourceLine a java source code line (possibly annotated) * @return the loop bound or null if no annotation was found * @throws BadAnnotationException if the loop bound annotation has syntax errors or is invalid */ public static LoopBound extractAnnotation(String sourceLine) throws BadAnnotationException { int ai = sourceLine.indexOf("@WCA"); if (ai != -1) { String annotString = sourceLine.substring(ai + "@WCA".length()); if (annotString.indexOf("loop") < 0) return null; // Backward compatibility Pattern pattern2 = Pattern.compile(" *([0-9]+) *<= *loop *<= *([0-9]+) *"); Matcher matcher2 = pattern2.matcher(annotString); if (matcher2.matches()) { logger.warn("Deprecated loop bound notation: X <= loop <= Y"); LoopBoundExpr expr = LoopBoundExpr.numericBound(matcher2.group(1),matcher2.group(2)); return LoopBound.simpleBound(expr); } // New loop bound InputStream is = new ByteArrayInputStream(annotString.getBytes()); Parser parser = new Parser(new Scanner(is)); parser.Parse(); if (parser.errors.count > 0) { throw new BadAnnotationException("Parse Error in Annotation: " + annotString); } return parser.getResult(); } return null; } }