/// <summary> /// Loads a plan into the lookup dictionary. /// </summary> /// <param name="plan">A <see cref="TrapPlan" />.</param> public void AddPlan(TrapPlan plan) { if (plan != null) { if (plan.API.Contains("*") || plan.Method.Contains("*")) { this.wildCardPlans.Add(plan); } else { string planId = plan.GetID(); if (this.nonwildCardPlans.ContainsKey(planId)) { if (plan.HitCounts != null) { this.nonwildCardPlans[planId].HitCounts.AddRange(plan.HitCounts); } } else { this.nonwildCardPlans[planId] = plan; } } } }
/// <summary> /// Finds a matching <see cref="TrapPlan" />. /// </summary> /// <param name="interceptionPoint">CUrrent InterceptionPoint.</param> /// <param name="globalHitcountWindow">Global hitcount window.</param> /// <param name="localHitcountWindow">Local hitcount window.</param> /// <returns>A matching <see cref="TrapPlan" />.</returns> public TrapPlan FindMatchingPlan(InterceptionPoint interceptionPoint, int globalHitcountWindow = 20, int localHitcountWindow = 5) { if (interceptionPoint == null || interceptionPoint.API == null) { return(null); } var planId = TrapPlan.GetID(interceptionPoint.API, interceptionPoint.Method, interceptionPoint.ILOffset); if (this.nonwildCardPlans.ContainsKey(planId)) { var plan = this.nonwildCardPlans[planId]; bool isMatch = (plan.ThreadId == null || string.Equals(plan.ThreadId, interceptionPoint.ThreadId)) && (plan.HitCounts == null || plan.HitCounts.Count(x => x.IsMatch(interceptionPoint.LocalHitcount, localHitcountWindow, interceptionPoint.GLobalHitCount, globalHitcountWindow)) > 0); return(isMatch ? plan : null); } foreach (var plan in this.wildCardPlans) { if (!SignatureMatchUtils.IsWildcardMatch(interceptionPoint.API, plan.API)) { continue; } if (plan.Method != null && !SignatureMatchUtils.IsWildcardMatch(interceptionPoint.Method, plan.Method)) { continue; } if (plan.ILOffset >= 0 && plan.ILOffset != interceptionPoint.ILOffset) { continue; } if (plan.ThreadId != null && !string.Equals(plan.ThreadId, interceptionPoint.ThreadId)) { continue; } if (plan.HitCounts != null && plan.HitCounts.Count(x => x.IsMatch(interceptionPoint.LocalHitcount, localHitcountWindow, interceptionPoint.GLobalHitCount, globalHitcountWindow)) > 0) { continue; } return(plan); } return(null); }
/// <summary> /// Adds the new plan. /// </summary> /// <param name="interceptionPointInfo">The InterceptionPoint information.</param> /// <param name="frequency">The frequency.</param> private void AddNewPlan(string interceptionPointInfo, int frequency) { TrapPlan trapPlan = new TrapPlan(); string[] tokens = interceptionPointInfo.Split('|'); trapPlan.API = tokens[0]; trapPlan.Method = tokens[1]; trapPlan.ILOffset = int.Parse(tokens[2]); trapPlan.AddHitCount(int.Parse(tokens[4]), int.Parse(tokens[3])); trapPlan.FixedDelayMs = this.DelayPerDangerousInterceptionPoint; trapPlan.Repeat = frequency > 1; lock (this.TrapPlans) { this.TrapPlans.AddPlan(trapPlan); } }
/// <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; }
/// <summary> /// Gets the identifier. /// </summary> /// <returns>System.String.</returns> public string GetID() { return(TrapPlan.GetID(this.API, this.Method, this.ILOffset)); }