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

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

package it.unive.lisa;

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

import it.unive.lisa.analysis.AbstractState;
import it.unive.lisa.analysis.heap.HeapDomain;
import it.unive.lisa.analysis.nonrelational.inference.InferenceSystem;
import it.unive.lisa.analysis.types.InferredTypes;
import it.unive.lisa.checks.warnings.Warning;
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
import it.unive.lisa.interprocedural.callgraph.CallGraph;
import it.unive.lisa.logging.TimerLogger;
import it.unive.lisa.outputs.JsonReport;
import it.unive.lisa.program.Program;
import it.unive.lisa.type.Type;
import it.unive.lisa.util.collections.externalSet.ExternalSet;
import it.unive.lisa.util.file.FileManager;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Function;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * This is the central class of the LiSA library. While LiSA's functionalities
 * can be extended by providing additional implementations for each component,
 * code executing LiSA should rely solely on this class to engage the analysis,
 * provide inputs to it and retrieve its results.
 * 
 * @author Luca Negrini
 */
public class LiSA {

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

	/**
	 * The collection of warnings that will be filled with the results of all
	 * the executed checks
	 */
	private final Collection warnings;

	/**
	 * The {@link FileManager} instance that will be used during analyses
	 */
	private final FileManager fileManager;

	/**
	 * The {@link LiSAConfiguration} containing the settings of the analysis to
	 * run
	 */
	private final LiSAConfiguration conf;

	/**
	 * Builds a new LiSA instance.
	 * 
	 * @param conf the configuration of the analysis to run
	 */
	public LiSA(LiSAConfiguration conf) {
		// since the warnings collection will be filled AFTER the execution of
		// every concurrent bit has completed its execution, it is fine to use a
		// non thread-safe one
		this.warnings = new ArrayList<>();
		this.conf = conf;
		this.fileManager = new FileManager(conf.getWorkdir());
	}

	/**
	 * Runs LiSA, executing all the checks that have been added.
	 * 
	 * @param program the program to analyze
	 * 
	 * @throws AnalysisException if anything goes wrong during the analysis
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void run(Program program) throws AnalysisException {
		printConfig();

		CallGraph callGraph;
		try {
			callGraph = conf.getCallGraph() == null ? getDefaultFor(CallGraph.class) : conf.getCallGraph();
			if (conf.getCallGraph() == null)
				LOG.warn("No call graph set for this analysis, defaulting to {}", callGraph.getClass().getSimpleName());
		} catch (AnalysisSetupException e) {
			throw new AnalysisExecutionException("Unable to create default call graph", e);
		}

		InterproceduralAnalysis interproc;
		try {
			interproc = conf.getInterproceduralAnalysis() == null ? getDefaultFor(InterproceduralAnalysis.class)
					: conf.getInterproceduralAnalysis();
			if (conf.getInterproceduralAnalysis() == null)
				LOG.warn("No interprocedural analysis set for this analysis, defaulting to {}",
						interproc.getClass().getSimpleName());
		} catch (AnalysisSetupException e) {
			throw new AnalysisExecutionException("Unable to create default interprocedural analysis", e);
		}

		AbstractState inferenceState = conf.getTypeInferenceState();
		Function, ExternalSet> typeExtractor = conf.getTypeExtractor();
		if (conf.isInferTypes() && inferenceState == null) {
			// these are used only if type inference is requested
			// we can skip configuration otherwise
			inferenceState = getDefaultFor(AbstractState.class, getDefaultFor(HeapDomain.class),
					new InferenceSystem<>(new InferredTypes()));
			typeExtractor = s -> ((InferenceSystem) s.getValueState()).getInferredValue()
					.getRuntimeTypes();
		}

		LiSARunner runner = new LiSARunner(conf, interproc, callGraph, conf.getAbstractState(),
				inferenceState, typeExtractor);

		try {
			warnings.addAll(TimerLogger.execSupplier(LOG, "Analysis time", () -> runner.run(program, fileManager)));
		} catch (AnalysisExecutionException e) {
			throw new AnalysisException("LiSA has encountered an exception while executing the analysis", e);
		}

		printStats();

		if (conf.isJsonOutput()) {
			LOG.info("Dumping reported warnings to 'report.json'");
			JsonReport report = new JsonReport(warnings, fileManager.createdFiles());
			try {
				fileManager.mkOutputFile("report.json", writer -> {
					report.dump(writer);
					LOG.info("Report file dumped to report.json");
				});
			} catch (IOException e) {
				LOG.error("Unable to dump report file", e);
			}
		}
	}

	private void printConfig() {
		LOG.info(conf.toString());
	}

	private void printStats() {
		LOG.info("LiSA statistics:");
		LOG.info("  {} warnings generated", warnings.size());
	}

	/**
	 * Yields an unmodifiable view of the warnings that have been generated
	 * during the analysis. Invoking this method before invoking
	 * {@link #run(Program)} will return an empty collection.
	 * 
	 * @return a view of the generated warnings
	 */
	public Collection getWarnings() {
		return Collections.unmodifiableCollection(warnings);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy