Mercurial > hg > Members > kono > jpf-core
view src/main/gov/nasa/jpf/listener/PreciseRaceDetector.java @ 15:cb7500a46eab
some minor syntax fixes to make IntelliJ happy
author | Peter Mehlitz <pcmehlitz@gmail.com> |
---|---|
date | Wed, 18 Mar 2015 11:52:11 -0700 |
parents | 61d41facf527 |
children |
line wrap: on
line source
/* * Copyright (C) 2014, United States Government, as represented by the * Administrator of the National Aeronautics and Space Administration. * All rights reserved. * * The Java Pathfinder core (jpf-core) platform is licensed under the * Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package gov.nasa.jpf.listener; import gov.nasa.jpf.Config; import gov.nasa.jpf.PropertyListenerAdapter; import gov.nasa.jpf.vm.bytecode.ArrayElementInstruction; import gov.nasa.jpf.vm.bytecode.FieldInstruction; import gov.nasa.jpf.search.Search; import gov.nasa.jpf.util.StringSetMatcher; import gov.nasa.jpf.vm.ChoiceGenerator; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.FieldInfo; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.VM; import gov.nasa.jpf.vm.MethodInfo; import gov.nasa.jpf.vm.bytecode.ReadOrWriteInstruction; import gov.nasa.jpf.vm.ThreadInfo; import gov.nasa.jpf.vm.choice.ThreadChoiceFromSet; import java.io.PrintWriter; import java.io.StringWriter; /** * This is a Race Detection Algorithm that is precise in its calculation of races, i.e. no false warnings. * It exploits the fact that every thread choice selection point could be due to a possible race. It just runs * through all the thread choices and checks whether there are more than one thread trying to read & write to the * same field of an object. * * Current limitation is that it is only sound, i.e. will not miss a race, if the sync-detection is switched off * during model checking. This is due to the fact that the sync-detection guesses that an acess is lock-protected * when it in reality might not be. * * The listener also checks races for array elements, but in order to do so you have to set * "cg.threads.break_arrays=true" (note that it is false by default because this can cause serious state * explosion) * * This algorithm came out of a discussion with Franck van Breugel and Sergey Kulikov from the University of York. * All credits for it goes to Franck and Sergey, all the bugs are mine. * * NOTE - the PreciseRaceDetector is machine type agnostic * * Author: Willem Visser * */ public class PreciseRaceDetector extends PropertyListenerAdapter { static class Race { Race prev; // linked list ThreadInfo ti1, ti2; ReadOrWriteInstruction insn1, insn2; ElementInfo ei; boolean isRead1, isRead2; boolean isRace() { return insn2 != null && ti1 != null && ti2 != null && ( ! ti1.equals(ti2) ); } void printOn(PrintWriter pw){ pw.print(" "); pw.print( ti1.getName()); pw.print(" at "); pw.println(insn1.getSourceLocation()); String line = insn1.getSourceLine(); if (line != null){ pw.print("\t\t\"" + line.trim()); } pw.print("\" "); pw.print( insn1.isRead() ? "READ: " : "WRITE: "); pw.println(insn1); if (insn2 != null){ pw.print(" "); pw.print(ti2.getName()); pw.print(" at "); pw.println(insn2.getSourceLocation()); line = insn2.getSourceLine(); if (line != null){ pw.print("\t\t\"" + line.trim()); } pw.print("\" "); pw.print( insn2.isRead() ? "READ: " : "WRITE: "); pw.println(insn2); } } } static class FieldRace extends Race { FieldInfo fi; static Race check (Race prev, ThreadInfo ti, ReadOrWriteInstruction insn, ElementInfo ei, FieldInfo fi){ for (Race r = prev; r != null; r = r.prev){ if (r instanceof FieldRace){ FieldRace fr = (FieldRace)r; if (fr.ei == ei && fr.fi == fi){ if (!((FieldInstruction)fr.insn1).isRead() || !((FieldInstruction)insn).isRead()){ fr.ti2 = ti; fr.insn2 = insn; return fr; } } } } FieldRace fr = new FieldRace(); fr.ei = ei; fr.ti1 = ti; fr.insn1 = insn; fr.fi = fi; fr.prev = prev; return fr; } @Override void printOn(PrintWriter pw){ pw.print("race for field "); pw.print(ei); pw.print('.'); pw.println(fi.getName()); super.printOn(pw); } } static class ArrayElementRace extends Race { int idx; static Race check (Race prev, ThreadInfo ti, ReadOrWriteInstruction insn, ElementInfo ei, int idx){ for (Race r = prev; r != null; r = r.prev){ if (r instanceof ArrayElementRace){ ArrayElementRace ar = (ArrayElementRace)r; if (ar.ei == ei && ar.idx == idx){ if (!((ArrayElementInstruction)ar.insn1).isRead() || !((ArrayElementInstruction)insn).isRead()){ ar.ti2 = ti; ar.insn2 = insn; return ar; } } } } ArrayElementRace ar = new ArrayElementRace(); ar.ei = ei; ar.ti1 = ti; ar.insn1 = insn; ar.idx = idx; ar.prev = prev; return ar; } @Override void printOn(PrintWriter pw){ pw.print("race for array element "); pw.print(ei); pw.print('['); pw.print(idx); pw.println(']'); super.printOn(pw); } } // this is where we store if we detect one protected Race race; // our matchers to determine which code we have to check protected StringSetMatcher includes = null; // means all protected StringSetMatcher excludes = null; // means none public PreciseRaceDetector (Config conf) { includes = StringSetMatcher.getNonEmpty(conf.getStringArray("race.include")); excludes = StringSetMatcher.getNonEmpty(conf.getStringArray("race.exclude")); } @Override public boolean check(Search search, VM vm) { return (race == null); } @Override public void reset() { race = null; } @Override public String getErrorMessage () { if (race != null){ StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); race.printOn(pw); pw.flush(); return sw.toString(); } else { return null; } } protected boolean checkRace (ThreadInfo[] threads){ Race candidate = null; for (int i = 0; i < threads.length; i++) { ThreadInfo ti = threads[i]; Instruction insn = ti.getPC(); MethodInfo mi = insn.getMethodInfo(); if (StringSetMatcher.isMatch(mi.getBaseName(), includes, excludes)) { if (insn instanceof FieldInstruction) { FieldInstruction finsn = (FieldInstruction) insn; FieldInfo fi = finsn.getFieldInfo(); ElementInfo ei = finsn.peekElementInfo(ti); candidate = FieldRace.check(candidate, ti, finsn, ei, fi); } else if (insn instanceof ArrayElementInstruction) { ArrayElementInstruction ainsn = (ArrayElementInstruction) insn; ElementInfo ei = ainsn.peekArrayElementInfo(ti); // these insns have been through their top half since they created CGs, but they haven't // removed the operands from the stack int idx = ainsn.peekIndex(ti); candidate = ArrayElementRace.check(candidate, ti, ainsn, ei, idx); } } if (candidate != null && candidate.isRace()){ race = candidate; return true; } } return false; } //----------- our VMListener interface // All we rely on here is that the scheduler breaks transitions at all // insns that could be races. We then just have to look at all currently // executed insns and don't rely on any past-exec info, PROVIDED that we only // use execution parameters (index or reference values) that are retrieved // from the operand stack, and not cached in the insn from a previous exec // (all the insns we look at are pre-exec, i.e. don't have their caches // updated yet) @Override public void choiceGeneratorSet(VM vm, ChoiceGenerator<?> newCG) { if (newCG instanceof ThreadChoiceFromSet) { ThreadInfo[] threads = ((ThreadChoiceFromSet)newCG).getAllThreadChoices(); checkRace(threads); } } @Override public void executeInstruction (VM vm, ThreadInfo ti, Instruction insnToExecute) { if (race != null) { // we're done, report as quickly as possible //ti.skipInstruction(); ti.breakTransition("dataRace"); } } }