All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.unive.lisa.LiSARunner Maven / Gradle / Ivy

package it.unive.lisa;

import static it.unive.lisa.LiSAFactory.getInstance;

import it.unive.lisa.analysis.AbstractState;
import it.unive.lisa.analysis.AnalysisState;
import it.unive.lisa.analysis.CFGWithAnalysisResults;
import it.unive.lisa.analysis.SemanticException;
import it.unive.lisa.analysis.heap.HeapDomain;
import it.unive.lisa.analysis.value.ValueDomain;
import it.unive.lisa.caches.Caches;
import it.unive.lisa.checks.ChecksExecutor;
import it.unive.lisa.checks.semantic.CheckToolWithAnalysisResults;
import it.unive.lisa.checks.semantic.SemanticCheck;
import it.unive.lisa.checks.syntactic.CheckTool;
import it.unive.lisa.checks.warnings.Warning;
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
import it.unive.lisa.interprocedural.InterproceduralAnalysisException;
import it.unive.lisa.interprocedural.OpenCallPolicy;
import it.unive.lisa.interprocedural.callgraph.CallGraph;
import it.unive.lisa.interprocedural.callgraph.CallGraphConstructionException;
import it.unive.lisa.logging.IterationLogger;
import it.unive.lisa.logging.TimerLogger;
import it.unive.lisa.program.Program;
import it.unive.lisa.program.ProgramValidationException;
import it.unive.lisa.program.SyntheticLocation;
import it.unive.lisa.program.cfg.CFG;
import it.unive.lisa.program.cfg.edge.Edge;
import it.unive.lisa.program.cfg.statement.Expression;
import it.unive.lisa.program.cfg.statement.Statement;
import it.unive.lisa.symbolic.value.Skip;
import it.unive.lisa.type.Type;
import it.unive.lisa.util.collections.externalSet.ExternalSet;
import it.unive.lisa.util.datastructures.graph.GraphVisitor;
import it.unive.lisa.util.datastructures.graph.algorithms.FixpointException;
import it.unive.lisa.util.file.FileManager;
import java.io.IOException;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.Function;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * An auxiliary analysis runner for executing LiSA analysis.
 * 
 * @author Luca Negrini
 * 
 * @param   the type of {@link AbstractState} contained into the analysis
 *                 state that will be used in the analysis fixpoint
 * @param   the type of {@link HeapDomain} contained into the abstract state
 *                 that will be used in the analysis fixpoint
 * @param   the type of {@link ValueDomain} contained into the abstract state
 *                 that will be used in the analysis fixpoint
 * @param   the type of {@link AbstractState} contained into the analysis
 *                 state that will be used in the type inference fixpoint
 * @param  the type of {@link HeapDomain} contained into the abstract state
 *                 that will be used in the type inference fixpoint
 * @param  the type of {@link ValueDomain} contained into the abstract state
 *                 that will be used in the type inference fixpoint
 */
public class LiSARunner,
		H extends HeapDomain,
		V extends ValueDomain,
		T extends AbstractState,
		HT extends HeapDomain,
		VT extends ValueDomain> {

	private static final String FIXPOINT_EXCEPTION_MESSAGE = "Exception during fixpoint computation";

	private static final Logger LOG = LogManager.getLogger(LiSARunner.class);

	private final LiSAConfiguration conf;

	private final InterproceduralAnalysis interproc;

	private final CallGraph callGraph;

	private final A state;

	private final T typeState;

	private final Function> typeExtractor;

	/**
	 * Builds the runner.
	 * 
	 * @param conf          the configuration of the analysis
	 * @param interproc     the interprocedural analysis to use
	 * @param callGraph     the call graph to use
	 * @param state         the abstract state to use for the analysis
	 * @param typeState     the abstract state to use for type inference
	 * @param typeExtractor the abstract state to use the function that can
	 *                          extract runtime types from {@code typeState}
	 *                          instances
	 */
	LiSARunner(LiSAConfiguration conf, InterproceduralAnalysis interproc, CallGraph callGraph,
			A state, T typeState, Function> typeExtractor) {
		this.conf = conf;
		this.interproc = interproc;
		this.callGraph = callGraph;
		this.state = state;
		this.typeState = typeState;
		this.typeExtractor = typeExtractor;
	}

	/**
	 * Executes the runner on the target program.
	 * 
	 * @param program     the program to analyze
	 * @param fileManager the file manager for the analysis
	 * 
	 * @return the warnings generated by the analysis
	 */
	Collection run(Program program, FileManager fileManager) {
		finalizeProgram(program);

		Collection allCFGs = program.getAllCFGs();

		if (conf.isDumpCFGs())
			for (CFG cfg : IterationLogger.iterate(LOG, allCFGs, "Dumping input CFGs", "cfgs"))
				dumpCFG(fileManager, "", cfg, st -> "");

		CheckTool tool = new CheckTool();
		if (!conf.getSyntacticChecks().isEmpty())
			ChecksExecutor.executeAll(tool, program, conf.getSyntacticChecks());
		else
			LOG.warn("Skipping syntactic checks execution since none have been provided");

		try {
			callGraph.init(program);
		} catch (CallGraphConstructionException e) {
			LOG.fatal("Exception while building the call graph for the input program", e);
			throw new AnalysisExecutionException("Exception while building the call graph for the input program", e);
		}

		try {
			interproc.init(program, callGraph, conf.getOpenCallPolicy());
		} catch (InterproceduralAnalysisException e) {
			LOG.fatal("Exception while building the interprocedural analysis for the input program", e);
			throw new AnalysisExecutionException(
					"Exception while building the interprocedural analysis for the input program", e);
		}

		if (conf.isInferTypes())
			inferTypes(fileManager, program, allCFGs, conf.getOpenCallPolicy());
		else
			LOG.warn("Type inference disabled: dynamic type information will not be available for following analysis");

		if (state != null) {
			analyze(allCFGs, fileManager);
			Map>> results = new IdentityHashMap<>(allCFGs.size());
			for (CFG cfg : allCFGs)
				results.put(cfg, interproc.getAnalysisResultsOf(cfg));

			@SuppressWarnings({ "rawtypes", "unchecked" })
			Collection> semanticChecks = (Collection) conf.getSemanticChecks();
			if (!semanticChecks.isEmpty()) {
				CheckToolWithAnalysisResults tool2 = new CheckToolWithAnalysisResults<>(tool, results, callGraph);
				tool = tool2;
				ChecksExecutor.executeAll(tool2, program, semanticChecks);
			} else
				LOG.warn("Skipping semantic checks execution since none have been provided");
		} else
			LOG.warn("Skipping analysis execution since no abstract sate has been provided");

		return tool.getWarnings();
	}

	private void analyze(Collection allCFGs, FileManager fileManager) {
		A state = this.state.top();
		TimerLogger.execAction(LOG, "Computing fixpoint over the whole program",
				() -> {
					try {
						interproc.fixpoint(new AnalysisState<>(state, new Skip(SyntheticLocation.INSTANCE)),
								conf.getFixpointWorkingSet(), conf.getWideningThreshold());
					} catch (FixpointException e) {
						LOG.fatal(FIXPOINT_EXCEPTION_MESSAGE, e);
						throw new AnalysisExecutionException(FIXPOINT_EXCEPTION_MESSAGE, e);
					}
				});

		if (conf.isDumpAnalysis())
			for (CFG cfg : IterationLogger.iterate(LOG, allCFGs, "Dumping analysis results", "cfgs")) {
				for (CFGWithAnalysisResults result : interproc.getAnalysisResultsOf(cfg))
					dumpCFG(fileManager,
							"analysis___" + (result.getId() == null ? "" : result.getId().hashCode() + "_"), result,
							st -> result.getAnalysisStateAfter(st).toString());
			}
	}

	@SuppressWarnings("unchecked")
	private void inferTypes(FileManager fileManager, Program program, Collection allCFGs, OpenCallPolicy policy) {
		T typesState = this.typeState.top();
		InterproceduralAnalysis typesInterproc;
		CallGraph typesCg;
		try {
			typesCg = getInstance(callGraph.getClass());
			typesInterproc = getInstance(interproc.getClass());
			typesCg.init(program);
			typesInterproc.init(program, typesCg, policy);
		} catch (AnalysisSetupException | InterproceduralAnalysisException | CallGraphConstructionException e) {
			throw new AnalysisExecutionException("Unable to initialize type inference", e);
		}

		TimerLogger.execAction(LOG, "Computing type information",
				() -> {
					try {
						typesInterproc.fixpoint(new AnalysisState<>(typesState, new Skip(SyntheticLocation.INSTANCE)),
								conf.getFixpointWorkingSet(), conf.getWideningThreshold());
					} catch (FixpointException e) {
						LOG.fatal(FIXPOINT_EXCEPTION_MESSAGE, e);
						throw new AnalysisExecutionException(FIXPOINT_EXCEPTION_MESSAGE, e);
					}
				});

		String message = conf.isDumpTypeInference()
				? "Dumping type analysis and propagating it to cfgs"
				: "Propagating type information to cfgs";
		for (CFG cfg : IterationLogger.iterate(LOG, allCFGs, message, "cfgs")) {
			Collection> results = typesInterproc.getAnalysisResultsOf(cfg);
			if (results.isEmpty()) {
				LOG.warn("No type information computed for '{}': it is unreachable", cfg);
				continue;
			}

			CFGWithAnalysisResults result = null;
			try {
				for (CFGWithAnalysisResults res : results)
					if (result == null)
						result = res;
					else
						result = result.join(res);
			} catch (SemanticException e) {
				throw new AnalysisExecutionException("Unable to compute type information for " + cfg, e);
			}

			cfg.accept(new TypesPropagator(), result);

			CFGWithAnalysisResults r = result;
			if (conf.isDumpTypeInference())
				dumpCFG(fileManager, "typing___", r, st -> r.getAnalysisStateAfter(st).toString());
		}
	}

	private class TypesPropagator
			implements
			GraphVisitor> {

		@Override
		public boolean visit(CFGWithAnalysisResults tool, CFG graph) {
			return true;
		}

		@Override
		public boolean visit(CFGWithAnalysisResults tool, CFG graph, Edge edge) {
			return true;
		}

		@Override
		public boolean visit(CFGWithAnalysisResults tool, CFG graph,
				Statement node) {
			if (tool != null && node instanceof Expression)
				((Expression) node).setRuntimeTypes(typeExtractor.apply(tool.getAnalysisStateAfter(node).getState()));
			return true;
		}
	}

	private static void finalizeProgram(Program program) {
		// fill up the types cache by side effect on an external set
		Caches.types().clear();
		ExternalSet types = Caches.types().mkEmptySet();
		program.getRegisteredTypes().forEach(types::add);
		types = null;

		TimerLogger.execAction(LOG, "Finalizing input program", () -> {
			try {
				program.validateAndFinalize();
			} catch (ProgramValidationException e) {
				throw new AnalysisExecutionException("Unable to finalize target program", e);
			}
		});
	}

	private static void dumpCFG(FileManager fileManager, String filePrefix, CFG cfg,
			Function labelGenerator) {
		try {
			fileManager.mkDotFile(filePrefix + cfg.getDescriptor().getFullSignatureWithParNames(),
					writer -> cfg.dump(writer, labelGenerator::apply));
		} catch (IOException e) {
			LOG.error("Exception while dumping the analysis results on {}", cfg.getDescriptor().getFullSignature());
			LOG.error(e);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy