/// <summary> /// Initialize the controller. /// </summary> /// <param name="configuration">The configuration.</param> public void Initialize(TSVDRuntimeConfiguration configuration) { this.configuration = configuration; if (this.LogDirectory != null && Directory.Exists(this.LogDirectory)) { ControllerHelper.SetLoggingDirectory(this.LogDirectory); this.LastRunPlanFile = Path.Combine(this.LogDirectory, Path.GetFileName(this.LastRunPlanFile)); this.LastRunBugFile = Path.Combine(this.LogDirectory, Path.GetFileName(this.LastRunBugFile)); } this.isTrapActive = false; int seed = this.RandomSeed != 0 ? this.RandomSeed : Guid.NewGuid().GetHashCode(); this.random = new Random(seed); ControllerHelper.Debug(string.Format("RandomSeed: {0}", seed)); if (this.LastRunPlanFile != null) { if (File.Exists(this.LastRunPlanFile)) { this.LoadPlansFromLastRun(this.LastRunPlanFile); File.Delete(this.LastRunPlanFile); } this.lastRunPlanLogger = new FileLogger(this.LastRunPlanFile); } if (this.LastRunBugFile != null) { if (File.Exists(this.LastRunBugFile)) { this.LoadKnownBuggyInterceptionPoints(this.LastRunBugFile); } this.lastRunBugLogger = new FileLogger(this.LastRunBugFile); } }
/// <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); }
/// <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; }
/// <summary> /// Finds the racing tp. /// </summary> /// <param name="tp">The tp.</param> /// <returns>System.String.</returns> private string FindRacingTP(InterceptionPoint tp) { List <InterceptionPoint> list = new List <InterceptionPoint>(); // this block goes through the last-k tps that access the same object to find the dangerous list.co lock (this.lastInterceptionPointForOBJ) { if (this.lastInterceptionPointForOBJ.ContainsKey(tp.ObjID)) { Queue <InterceptionPoint> q = this.lastInterceptionPointForOBJ[tp.ObjID]; foreach (var tp2 in q) { // from different thread and at least one is write if ((tp2.ThreadId != tp.ThreadId) && (tp.IsWrite || tp2.IsWrite)) { // close enough from timing prespective if (tp2.Timestamp.AddMilliseconds(this.planDistance) > tp.Timestamp) { // the next condition is always true as we ignore the lock. Every LockState is false. if ((tp2.LockState == false) || (tp.LockState == false)) { if ((tp2.ActiveThdNum > 1) || (tp.ActiveThdNum > 1)) { list.Add(tp2); } } } } } // update the last access for this object q.Enqueue(tp); if (q.Count > this.LastTPWindow) { q.Dequeue(); } } else { this.lastInterceptionPointForOBJ[tp.ObjID] = new Queue <InterceptionPoint>(); this.lastInterceptionPointForOBJ[tp.ObjID].Enqueue(tp); } } // update the dangerous pair list string s = string.Empty; HashSet <string> temp = new HashSet <string>(); foreach (InterceptionPoint tp2 in list) { string st = tp.Tostring() + " ! " + tp2.Tostring(); string st2 = tp2.Tostring() + " ! " + tp.Tostring(); Tuple <string, string> tuple = new Tuple <string, string>(tp.Tostring(), tp2.Tostring()); string shortst = this.GetPairID(st, st2); string pairname = this.GetPairID(tp.Location, tp2.Location); if (this.blacklist.Contains(pairname)) { // the blacklist contains the bugs found in previous ROUNDS. DebugLog.Log("Skip blacked pair " + shortst); continue; } bool flag = false; lock (this.dangerousTPPairs) { if (this.hitTime.ContainsKey(tp.Location) && this.hitTime.ContainsKey(tp2.Location) && (this.hitTime[tp.Location] + this.hitTime[tp2.Location] >= 10) && this.dangerousTPPairs.Contains(shortst)) { continue; } // the hittime is how many time this dangerous pair has caused trap. It is used for decay. this.hitTime[tp.Location] = 0; this.hitTime[tp2.Location] = 0; this.dangerousTPPairs.Add(shortst); flag = true; } if (flag) { ControllerHelper.Debug("AddDangerList : " + st); this.LogPlanForNextRun(st); } } return(s); }