/// <summary> /// Gets the trap objects. /// </summary> /// <param name="threadSafetyInterceptionPoint">The thread safety InterceptionPoint.</param> /// <returns>Dictionary<Guid, HashSet<Trap>>.</returns> private Dictionary <Guid, HashSet <Trap> > GetTrapObjects(ThreadSafetyInterceptionPoint threadSafetyInterceptionPoint) { string groupName = threadSafetyInterceptionPoint.ThreadSafetyGroup.Name; Dictionary <Guid, HashSet <Trap> > trapObjects = null; lock (this.trapPoints) { if (!this.trapPoints.ContainsKey(groupName)) { this.trapPoints.Add(groupName, new Dictionary <Guid, HashSet <Trap> >()); } trapObjects = this.trapPoints[groupName]; } return(trapObjects); }
/// <summary> /// Get thread safety group details of the InterceptionPoint. /// </summary> /// <param name="interceptionPoint">InterceptionPoint.</param> /// <returns>Thread safety InterceptionPoint.</returns> private ThreadSafetyInterceptionPoint GetThreadSafetyInterceptionPoint(InterceptionPoint interceptionPoint) { if (this.threadSafetyInterceptionPointCache.ContainsKey(interceptionPoint.API)) { return(this.threadSafetyInterceptionPointCache[interceptionPoint.API]); } foreach (ThreadSafetyGroup threadSafetyGroup in this.configuration.ThreadSafetyGroups) { foreach (string writeAPI in threadSafetyGroup.WriteAPIs) { if (SignatureMatchUtils.IsWildcardMatch(interceptionPoint.API, writeAPI)) { var tsInterceptionPoint = new ThreadSafetyInterceptionPoint() { ThreadSafetyGroup = threadSafetyGroup, IsWriteAPI = true, }; this.threadSafetyInterceptionPointCache[interceptionPoint.API] = tsInterceptionPoint; return(tsInterceptionPoint); } } foreach (string readAPI in threadSafetyGroup.ReadAPIs) { if (SignatureMatchUtils.IsWildcardMatch(interceptionPoint.API, readAPI)) { var tsInterceptionPoint = new ThreadSafetyInterceptionPoint() { ThreadSafetyGroup = threadSafetyGroup, IsWriteAPI = false, }; this.threadSafetyInterceptionPointCache[interceptionPoint.API] = tsInterceptionPoint; return(tsInterceptionPoint); } } } return(null); }
/// <summary> /// Shoulds the return without trap. /// </summary> /// <param name="interceptionPoint">The InterceptionPoint.</param> /// <param name="instance">The instance.</param> /// <returns><see langword="true" /> if XXXX, <see langword="false" /> otherwise.</returns> private bool ShouldReturnWithoutTrap(InterceptionPoint interceptionPoint, object instance) { // this.Updatelockinformation(InterceptionPoint); if (this.LockRelatedOP(interceptionPoint)) { return(true); } // Locate the APIs bypass the lock operation ThreadSafetyInterceptionPoint threadSafetyInterceptionPoint = this.GetThreadSafetyInterceptionPoint(interceptionPoint); if (threadSafetyInterceptionPoint == null) { return(true); } if (!threadSafetyInterceptionPoint.ThreadSafetyGroup.IsStatic && instance == null) { return(true); } return(false); }
/// <summary> /// Checks for bugs and start trap. /// </summary> /// <param name="interceptionPoint">The InterceptionPoint.</param> /// <param name="threadSafetyInterceptionPoint">The thread safety InterceptionPoint.</param> /// <param name="trapObjects">The trap objects.</param> /// <param name="trapPlan">The trap plan.</param> /// <param name="instance">The instance.</param> /// <param name="bugFound">If set to <see langword="true" /> then a bug has been found.</param> /// <returns>Trap.</returns> private Trap CheckForBugsAndStartTrap(InterceptionPoint interceptionPoint, ThreadSafetyInterceptionPoint threadSafetyInterceptionPoint, Dictionary <Guid, HashSet <Trap> > trapObjects, TrapPlan trapPlan, object instance, out bool bugFound) { int danger = -1; Semaphore semaphore = null; double prob = 1.0; int sleepDuration = 0; Guid objectId = interceptionPoint.ObjID; bool continueAfterBug = false; bugFound = false; Trap newTrap = null; lock (trapObjects) { // Check if a thread-safety bug is found if (trapObjects.ContainsKey(objectId)) { foreach (Trap trap in trapObjects[objectId]) { ThreadSafetyInterceptionPoint runningTP = this.GetThreadSafetyInterceptionPoint(trap.InterceptionPoint); if (threadSafetyInterceptionPoint.IsWriteAPI || runningTP.IsWriteAPI) { bugFound = true; interceptionPoint.StackTrace = Environment.StackTrace; // DebugLog.Log("HitBug " + InterceptionPoint.Tostring() + " " + runningt.InterceptionPoint.Tostring()); ControllerHelper.WriteBug(trap, interceptionPoint); this.LogBugForNextRun(interceptionPoint, trap.InterceptionPoint); // For some InterceptionPoints, keep execution even hitting a bug. It may expose more bugs. if ((trapPlan != null) && (!trapPlan.Repeat)) { continueAfterBug = true; } } } } if (bugFound && (!continueAfterBug)) { // DebugLog.Log("HIt Bug and exit " + InterceptionPoint.Tostring()); return(null); } // else { // check if the InterceptionPoint match with dangerous list danger = this.IsInsideDangerList(interceptionPoint.Location); if ((danger < 0) && (!interceptionPoint.IsInPlan)) { // exit when not matching and not in the plan return(null); } // prepare to set up a trap if (threadSafetyInterceptionPoint.IsWriteAPI && !this.isTrapActive) if ((!this.isTrapActive || (danger >= 0) || interceptionPoint.IsInPlan) && ((trapPlan != null) || threadSafetyInterceptionPoint.IsWriteAPI)) { prob = trapPlan != null ? trapPlan.DelayProbability : this.DelayProbability; if (danger >= 0) { prob = this.DelayProbabilityAtDangerousInterceptionPoint - (this.DecayFactor * danger); } if (this.random.NextDouble() <= prob) { // Get stack trace only if setting up a trap. interceptionPoint.StackTrace = Environment.StackTrace; semaphore = new Semaphore(0, 1); sleepDuration = trapPlan == null? this.random.Next(this.MaxDelayPerInterceptionPoint) : trapPlan.FixedDelayMs; if (danger >= 0) { sleepDuration = this.DelayPerDangerousInterceptionPoint; } newTrap = new Trap { ObjectId = objectId, Semaphore = semaphore, InterceptionPoint = interceptionPoint, Delay = sleepDuration, }; ControllerHelper.WriteTrap(newTrap); if (!trapObjects.ContainsKey(objectId)) { trapObjects.Add(objectId, new HashSet <Trap>()); } trapObjects[objectId].Add(newTrap); this.isTrapActive = true; } } } } return(newTrap); }
/// <summary> /// Converts to rchpointstart. /// </summary> /// <param name="interceptionPoint">The InterceptionPoint.</param> /// <param name="instance">The instance.</param> /// <param name="parameters">The parameters.</param> /// </summary> public void InterceptionPointStart(InterceptionPoint interceptionPoint, object instance = null, object[] parameters = null) { /* * I add more field variables to help the analysis. They are initialized in the constructor. * when hitting a new InterceptionPoint here are the procedures in the implementation: * 1.assign the hit-seq for InterceptionPoint * 2.assign the global id for InterceptionPoint. It is duplicated with InterceptionPoint.id. * 3.update the InterceptionPoints history to infer the concurrent threads. * 4.find the dangerous pair for this InterceptionPoint * 5.find the the opeartion set that happens-before this InterceptionPoint * 6.check if find a bug * 7.construct the trap * 8.execute the trap * 9.locate the threads that are blocked by this trap */ var newBugFound = false; if (this.configuration == null || this.configuration.ThreadSafetyGroups == null) { return; } this.UpdateTPHitCounts(interceptionPoint); TrapPlan trapPlan = this.TrapPlans.FindMatchingPlan(interceptionPoint, this.GlobalHitcountWindow, this.LocalHitcountWindow); interceptionPoint.IsInPlan = trapPlan != null; this.UpdateTPHistory(interceptionPoint); // This block is used for monitoring the lock.Now we can just skip the lock operations. this.Updatelockinformation(InterceptionPoint); if (this.LockRelatedOP(interceptionPoint)) { return; } // Locate the APIs bypass the lock operation ThreadSafetyInterceptionPoint threadSafetyInterceptionPoint = this.GetThreadSafetyInterceptionPoint(interceptionPoint); if (threadSafetyInterceptionPoint == null || (!threadSafetyInterceptionPoint.ThreadSafetyGroup.IsStatic && instance == null)) { return; } // update the write/read InterceptionPoint interceptionPoint.IsWrite = threadSafetyInterceptionPoint.IsWriteAPI; interceptionPoint.ObjID = threadSafetyInterceptionPoint.ThreadSafetyGroup.IsStatic ? ObjectId.GetRefId(null) : ObjectId.GetRefId(instance); // this block will looking for the near-miss pair and learn the dependency. if ((interceptionPoint.IsInPlan == false) && (this.DetectDangerousPairs == true)) { this.FindRacingTP(interceptionPoint); this.RemoveDependentInterceptionPoints(interceptionPoint); } var trapObjects = this.GetTrapObjects(threadSafetyInterceptionPoint); Trap trap = this.CheckForBugsAndStartTrap(interceptionPoint, threadSafetyInterceptionPoint, trapObjects, trapPlan, instance, out newBugFound); if (trap == null) { return; } this.FinishTrap(trapObjects, trap, interceptionPoint); if (this.configuration.ThrowExceptionOnRace & newBugFound) { throw new Exception("ThreadSafety bugs detected by TSVD"); } return; }
/// <inheritdoc/> public void InterceptionPointStart(InterceptionPoint interceptionPoint, object instance = null, object[] parameters = null) { /* * if (!this.isTrapActive && bugFound) * throw new Exception("ThreadSafety bugs detected by Torch"); */ bool newTrapsAllowed = true; bool newBugFound = false; if (this.configuration == null || this.configuration.ThreadSafetyGroups == null) { return; } ThreadSafetyInterceptionPoint threadSafetyInterceptionPoint = ControllerHelper.GetThreadSafetyInterceptionPoint(this.configuration.ThreadSafetyGroups, interceptionPoint); if (threadSafetyInterceptionPoint == null) { return; } if (!threadSafetyInterceptionPoint.ThreadSafetyGroup.IsStatic && instance == null) { return; } string groupName = threadSafetyInterceptionPoint.ThreadSafetyGroup.Name; Dictionary <Guid, Trap> trapObjects = null; lock (this.trapPoints) { if (!this.trapPoints.ContainsKey(groupName)) { this.trapPoints.Add(groupName, new Dictionary <Guid, Trap>()); if (threadSafetyInterceptionPoint.IsWriteAPI) { this.writeTrapPoints.Add(groupName); } } trapObjects = this.trapPoints[groupName]; } Semaphore semaphore = null; int sleepDuration = 0; Guid keyInstance = ObjectId.GetRefId(null); lock (trapObjects) { if (!threadSafetyInterceptionPoint.ThreadSafetyGroup.IsStatic) { keyInstance = ObjectId.GetRefId(instance); } // Check if a trap on this object is live Trap trap = null; trapObjects.TryGetValue(keyInstance, out trap); // A bug is found if there is a trap on this object and // either this access or trapped access is a write if (trap != null && (threadSafetyInterceptionPoint.IsWriteAPI || trap.ThreadSafetyInterceptionPoint.IsWriteAPI)) { // Get stack track when a bug is found. interceptionPoint.StackTrace = Environment.StackTrace; ControllerHelper.WriteBug(trap, interceptionPoint); newBugFound = true; try { trap.Semaphore.Release(); } catch (SemaphoreFullException) { } } else if (newTrapsAllowed) { // prepare to set up a trap if (!this.isTrapActive) { if (threadSafetyInterceptionPoint.IsWriteAPI && this.random.NextDouble() <= this.DelayProbability) { // Get stack trace only if setting up a trap. interceptionPoint.StackTrace = Environment.StackTrace; semaphore = new Semaphore(0, 1); sleepDuration = this.random.Next(this.MaxDelayPerInterceptionPoint); trap = new Trap { ObjectId = keyInstance.ToString(), Semaphore = semaphore, InterceptionPoint = interceptionPoint, ThreadSafetyInterceptionPoint = threadSafetyInterceptionPoint, Delay = sleepDuration, }; ControllerHelper.WriteTrap(trap); trapObjects.Add(keyInstance, trap); this.isTrapActive = true; } } } } if (semaphore != null) { semaphore.WaitOne(sleepDuration); lock (trapObjects) { trapObjects.Remove(keyInstance); } this.isTrapActive = false; } if (this.configuration.ThrowExceptionOnRace && newBugFound) { throw new Exception("ThreadSafety bugs detected by Torch"); } return; }