⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 findinconsistentsync2.java

📁 A static analysis tool to find bugs in Java programs
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/* * FindBugs - Find bugs in Java programs * Copyright (C) 2003-2005, University of Maryland *  * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. *  * This library 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 * Lesser General Public License for more details. *  * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */package edu.umd.cs.findbugs.detect;import edu.umd.cs.findbugs.*;import edu.umd.cs.findbugs.ba.*;import edu.umd.cs.findbugs.ba.type.*;import edu.umd.cs.findbugs.ba.vna.*;import edu.umd.cs.findbugs.props.WarningPropertySet;import java.util.*;import org.apache.bcel.Constants;import org.apache.bcel.classfile.*;import org.apache.bcel.generic.*;/** * Find instance fields which are sometimes accessed (read or written) * with the receiver lock held and sometimes without. * These are candidates to be data races. *  * @author David Hovemeyer * @author Bill Pugh */public class FindInconsistentSync2 implements Detector {	private static final boolean DEBUG = SystemProperties.getBoolean("fis.debug");	private static final boolean SYNC_ACCESS = true;	// Boolean.getBoolean("fis.syncAccess");	private static final boolean ADJUST_SUBCLASS_ACCESSES = !SystemProperties.getBoolean("fis.noAdjustSubclass");	private static final boolean EVAL = SystemProperties.getBoolean("fis.eval");	/* ----------------------------------------------------------------------	 * Tuning parameters	 * ---------------------------------------------------------------------- */	/**	 * Minimum percent of unbiased field accesses that must be synchronized in	 * order to report a field as inconsistently synchronized.	 * This is meant to distinguish incidental synchronization from	 * intentional synchronization.	 */	private static final int MIN_SYNC_PERCENT =			SystemProperties.getInteger("findbugs.fis.minSyncPercent", 50).intValue();	/**	 * Bias that writes are given with respect to reads.	 * The idea is that this should be above 1.0, because unsynchronized	 * writes are more dangerous than unsynchronized reads.	 */	private static final double WRITE_BIAS =			Double.parseDouble(SystemProperties.getProperty("findbugs.fis.writeBias", "2.0"));	/**	 * Factor which the biased number of unsynchronized accesses is multiplied by.	 * I.e., for factor <i>f</i>, if <i>nUnsync</i> is the biased number of unsynchronized	 * accesses, and <i>nSync</i> is the biased number of synchronized accesses,	 * and	 * <pre>	 *      <i>f</i>(<i>nUnsync</i>) &gt; <i>nSync</i>	 * </pre>	 * then we report a bug.  Default value is 2.0, which means that we	 * report a bug if more than 1/3 of accesses are unsynchronized.	 * <p/>	 * <p> Note that <code>MIN_SYNC_PERCENT</code> also influences	 * whether we report a bug: it specifies the minimum unbiased percentage	 * of synchronized accesses.	 */	private static final double UNSYNC_FACTOR =			Double.parseDouble(SystemProperties.getProperty("findbugs.fis.unsyncFactor", "2.0"));	/* ----------------------------------------------------------------------	 * Helper classes	 * ---------------------------------------------------------------------- */	private static final int UNLOCKED = 0;	private static final int LOCKED = 1;	private static final int READ = 0;	private static final int WRITE = 2;	private static final int READ_UNLOCKED = READ | UNLOCKED;	private static final int WRITE_UNLOCKED = WRITE | UNLOCKED;	private static final int READ_LOCKED = READ | LOCKED;	private static final int WRITE_LOCKED = WRITE | LOCKED;	/**	 * The access statistics for a field.	 * Stores the number of locked and unlocked reads and writes,	 * as well as the number of accesses made with a lock held.	 */	private static class FieldStats {		private int[] countList = new int[4];		private int numLocalLocks = 0;		private int numGetterMethodAccesses = 0;		private LinkedList<SourceLineAnnotation> unsyncAccessList = new LinkedList<SourceLineAnnotation>();		private LinkedList<SourceLineAnnotation> syncAccessList = new LinkedList<SourceLineAnnotation>();		public void addAccess(int kind) {			countList[kind]++;		}		public int getNumAccesses(int kind) {			return countList[kind];		}		public void addLocalLock() {			numLocalLocks++;		}		public int getNumLocalLocks() {			return numLocalLocks;		}		public void addGetterMethodAccess() {			numGetterMethodAccesses++;		}		public int getNumGetterMethodAccesses() {			return numGetterMethodAccesses;		}		public void addAccess(ClassContext classContext, Method method, InstructionHandle handle, boolean isLocked) {			if (!SYNC_ACCESS && isLocked)				return;			SourceLineAnnotation accessSourceLine = SourceLineAnnotation.fromVisitedInstruction(classContext, method, handle.getPosition());			if (accessSourceLine != null)				(isLocked ? syncAccessList : unsyncAccessList).add(accessSourceLine);		}		public Iterator<SourceLineAnnotation> unsyncAccessIterator() {			return unsyncAccessList.iterator();		}		public Iterator<SourceLineAnnotation> syncAccessIterator() {			return syncAccessList.iterator();		}	}	/* ----------------------------------------------------------------------	 * Fields	 * ---------------------------------------------------------------------- */	private BugReporter bugReporter;	private Map<XField, FieldStats> statMap = new HashMap<XField, FieldStats>();	/* ----------------------------------------------------------------------	 * Public methods	 * ---------------------------------------------------------------------- */	public FindInconsistentSync2(BugReporter bugReporter) {		this.bugReporter = bugReporter;	}	public void visitClassContext(ClassContext classContext) {		JavaClass javaClass = classContext.getJavaClass();		if (DEBUG) System.out.println("******** Analyzing class " + javaClass.getClassName());		// Build self-call graph		SelfCalls selfCalls = new SelfCalls(classContext) {			@Override						 public boolean wantCallsFor(Method method) {				return !method.isPublic();			}		};		Set<Method> lockedMethodSet;		Set<Method> publicReachableMethods;		try {			selfCalls.execute();			CallGraph callGraph = selfCalls.getCallGraph();			if (DEBUG)				System.out.println("Call graph (not unlocked methods): " + callGraph.getNumVertices() + " nodes, " +						callGraph.getNumEdges() + " edges");			// Find call edges that are obviously locked			Set<CallSite> obviouslyLockedSites = findObviouslyLockedCallSites(classContext, selfCalls);			lockedMethodSet = findNotUnlockedMethods(classContext, selfCalls, obviouslyLockedSites);			lockedMethodSet.retainAll(findLockedMethods(classContext, selfCalls, obviouslyLockedSites));			publicReachableMethods = findPublicReachableMethods(classContext, selfCalls);		} catch (CFGBuilderException e) {			bugReporter.logError("Error finding locked call sites", e);			return;		} catch (DataflowAnalysisException e) {			bugReporter.logError("Error finding locked call sites", e);			return;		}		for (Method method : publicReachableMethods) {			if (classContext.getMethodGen(method) == null)				continue;			/*											 if (isConstructor(method.getName()))											 continue;											 */			if (method.getName().startsWith("access$"))				// Ignore inner class access methods;				// we will treat calls to them as field accesses				continue;			try {				analyzeMethod(classContext, method, lockedMethodSet);			} catch (CFGBuilderException e) {				bugReporter.logError("Error analyzing method", e);			} catch (DataflowAnalysisException e) {				bugReporter.logError("Error analyzing method", e);			}		}	}	public void report() {		for (XField xfield : statMap.keySet()) {			FieldStats stats = statMap.get(xfield);			JCIPAnnotationDatabase jcipAnotationDatabase = AnalysisContext.currentAnalysisContext()								.getJCIPAnnotationDatabase();			boolean guardedByThis = "this".equals(jcipAnotationDatabase.getFieldAnnotation(xfield, "GuardedBy"));			boolean notThreadSafe = jcipAnotationDatabase.hasClassAnnotation(xfield.getClassName(), "NotThreadSafe");			boolean threadSafe = jcipAnotationDatabase.hasClassAnnotation(xfield.getClassName().replace('/','.'), "ThreadSafe");			if (notThreadSafe) continue;			WarningPropertySet propertySet = new WarningPropertySet();			int numReadUnlocked = stats.getNumAccesses(READ_UNLOCKED);			int numWriteUnlocked = stats.getNumAccesses(WRITE_UNLOCKED);			int numReadLocked = stats.getNumAccesses(READ_LOCKED);			int numWriteLocked = stats.getNumAccesses(WRITE_LOCKED);			int locked = numReadLocked + numWriteLocked;			int biasedLocked = numReadLocked + (int) (WRITE_BIAS * numWriteLocked);			int unlocked = numReadUnlocked + numWriteUnlocked;			int biasedUnlocked = numReadUnlocked + (int) (WRITE_BIAS * numWriteUnlocked);			int writes = numWriteLocked + numWriteUnlocked;			if (unlocked == 0) {				continue;//				propertySet.addProperty(InconsistentSyncWarningProperty.NEVER_UNLOCKED);			}			if (guardedByThis) {					propertySet.addProperty(InconsistentSyncWarningProperty.ANNOTATED_AS_GUARDED_BY_THIS);			}			if (threadSafe) {				propertySet.addProperty(InconsistentSyncWarningProperty.ANNOTATED_AS_THREAD_SAFE);		}			if (!guardedByThis && locked == 0) {				continue;//				propertySet.addProperty(InconsistentSyncWarningProperty.NEVER_LOCKED);			}			if (DEBUG) {				System.out.println("IS2: " + xfield);				if (guardedByThis) System.out.println("Guarded by this");				System.out.println("  RL: " + numReadLocked);				System.out.println("  WL: " + numWriteLocked);				System.out.println("  RU: " + numReadUnlocked);				System.out.println("  WU: " + numWriteUnlocked);			}			if (!EVAL && numReadUnlocked > 0 && ((int) (UNSYNC_FACTOR * biasedUnlocked)) > biasedLocked) {//				continue;				propertySet.addProperty(InconsistentSyncWarningProperty.MANY_BIASED_UNLOCKED);			}			// NOTE: we ignore access to public, volatile, and final fields			if (numWriteUnlocked + numWriteLocked == 0) {				// No writes outside of constructor				if (DEBUG) System.out.println("  No writes outside of constructor");				propertySet.addProperty(InconsistentSyncWarningProperty.NEVER_WRITTEN);//				continue;			}			if (numReadUnlocked + numReadLocked == 0) {				// No reads outside of constructor				if (DEBUG) System.out.println("  No reads outside of constructor");//				continue;				propertySet.addProperty(InconsistentSyncWarningProperty.NEVER_READ);			}			if (stats.getNumLocalLocks() == 0) {				if (DEBUG) System.out.println("  No local locks");//				continue;				propertySet.addProperty(InconsistentSyncWarningProperty.NO_LOCAL_LOCKS);			}			int freq;			if (locked + unlocked > 0) {				freq = (100 * locked) / (locked + unlocked);			} else {				freq = 0;			}			if (freq < MIN_SYNC_PERCENT) {//				continue;				propertySet.addProperty(InconsistentSyncWarningProperty.BELOW_MIN_SYNC_PERCENT);			}			if (DEBUG) System.out.println("  Sync %: " + freq);			if (stats.getNumGetterMethodAccesses() >= unlocked) {				// Unlocked accesses are only in getter method(s).				propertySet.addProperty(InconsistentSyncWarningProperty.ONLY_UNSYNC_IN_GETTERS);			}			// At this point, we report the field as being inconsistently synchronized			int priority = propertySet.computePriority(NORMAL_PRIORITY);			if (!propertySet.isFalsePositive(priority)) {				BugInstance bugInstance = new BugInstance(this, guardedByThis? "IS_FIELD_NOT_GUARDED" : "IS2_INCONSISTENT_SYNC", priority)						.addClass(xfield.getClassName())						.addField(xfield)						.addInt(freq).describe(IntAnnotation.INT_SYNC_PERCENT);				if (FindBugsAnalysisFeatures.isRelaxedMode()) {					propertySet.decorateBugInstance(bugInstance);				}				// Add source lines for unsynchronized accesses				for (Iterator<SourceLineAnnotation> j = stats.unsyncAccessIterator(); j.hasNext();) {					SourceLineAnnotation accessSourceLine = j.next();					bugInstance.addSourceLine(accessSourceLine).describe("SOURCE_LINE_UNSYNC_ACCESS");				}				if (SYNC_ACCESS) {					// Add source line for synchronized accesses;					// useful for figuring out what the detector is doing					for (Iterator<SourceLineAnnotation> j = stats.syncAccessIterator(); j.hasNext();) {						SourceLineAnnotation accessSourceLine = j.next();						bugInstance.addSourceLine(accessSourceLine).describe("SOURCE_LINE_SYNC_ACCESS");					}				}				if (EVAL) {					bugInstance.addInt(biasedLocked).describe("INT_BIASED_LOCKED");					bugInstance.addInt(biasedUnlocked).describe("INT_BIASED_UNLOCKED");				}				bugReporter.reportBug(bugInstance);			}		}	}	/* ----------------------------------------------------------------------	 * Implementation	 * ---------------------------------------------------------------------- */	private static boolean isConstructor(String methodName) {		return methodName.equals("<init>")				|| methodName.equals("<clinit>")				|| methodName.equals("readObject")				|| methodName.equals("clone")				|| methodName.equals("close")				|| methodName.equals("writeObject")				|| methodName.equals("toString")				|| methodName.equals("init")				|| methodName.equals("initialize")				|| methodName.equals("dispose")				|| methodName.equals("finalize")				|| methodName.equals("this");	}	private void analyzeMethod(ClassContext classContext, Method method, Set<Method> lockedMethodSet)			throws CFGBuilderException, DataflowAnalysisException {		InnerClassAccessMap icam = AnalysisContext.currentAnalysisContext().getInnerClassAccessMap();		ConstantPoolGen cpg = classContext.getConstantPoolGen();		MethodGen methodGen = classContext.getMethodGen(method);		if (methodGen == null) return;		CFG cfg = classContext.getCFG(method);		LockChecker lockChecker = classContext.getLockChecker(method);		ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method);		boolean isGetterMethod = isGetterMethod(classContext, method);		if (DEBUG)			System.out.println("**** Analyzing method " +					SignatureConverter.convertMethodSignature(methodGen));		for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {			Location location = i.next();			try {				Instruction ins = location.getHandle().getInstruction();				XField xfield = null;				boolean isWrite = false;				boolean isLocal = false;				if (ins instanceof FieldInstruction) {					FieldInstruction fins = (FieldInstruction) ins;					xfield = Hierarchy.findXField(fins, cpg);					isWrite = ins.getOpcode() == Constants.PUTFIELD;					isLocal = fins.getClassName(cpg).equals(classContext.getJavaClass().getClassName());					if (DEBUG)						System.out.println("Handling field access: " + location.getHandle() +								" (frame=" + vnaDataflow.getFactAtLocation(location) + ")");				} else if (ins instanceof INVOKESTATIC) {					INVOKESTATIC inv = (INVOKESTATIC) ins;					InnerClassAccess access = icam.getInnerClassAccess(inv, cpg);					if (access != null && access.getMethodSignature().equals(inv.getSignature(cpg))) {						xfield = access.getField();						isWrite = !access.isLoad();						isLocal = false;						if (DEBUG)							System.out.println("Handling inner class access: " + location.getHandle() +

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -