/// <summary> /// Log a race condition. /// </summary> /// <param name="trap">Trap details.</param> /// <param name="trappedPoint">InterceptionPoint that got trapped.</param> /// <returns><see langword="true" /> if XXXX, <see langword="false" /> otherwise.</returns> public static bool WriteBug(Trap trap, InterceptionPoint trappedPoint) { if (bugLogger == null) { return(false); } string bug = trap.InterceptionPoint.Method + ";" + trap.InterceptionPoint.API + ";" + trap.InterceptionPoint.ILOffset + ";" + trap.InterceptionPoint.StackTrace + "\n" + trappedPoint.Method + ";" + trappedPoint.API + ";" + trappedPoint.ILOffset + ";" + trappedPoint.StackTrace; if (!KnownBugs.Contains(bug)) { KnownBugs.Add(bug); List <string> lines = new List <string>(); lines.Add("---Race Condition---"); lines.Add("---Trap---"); lines.AddRange(LogHelper.GetInterceptionPointLogLines(trap.InterceptionPoint, trap.ObjectId, true)); lines.Add(string.Format("Delay: {0}", trap.Delay)); lines.Add("---Trapped---"); lines.AddRange(LogHelper.GetInterceptionPointLogLines(trappedPoint, trap.ObjectId, true)); bugLogger.Log(lines); return(true); } return(false); }
/// <summary> /// Callback invoked before a method is called. /// </summary> /// <param name="instance">Instance object on which the method is called. Null if the method is static.</param> /// <param name="caller">Parent method that calls the method.</param> /// <param name="ilOffset">ILOffset where the method is invoked.</param> /// <param name="callee">Name of the called method.</param> /// <returns>A context given to AfterMethodCall callback.</returns> public static MethodCallbackContext BeforeMethodCall(object instance, string caller, int ilOffset, string callee) { // string uniqueFieldName = GetUniqueFieldId(parentObject, fieldName); // Guid objId = ObjectId.GetRefId(instance); InterceptionPoint interceptionPoint = new InterceptionPoint() { Method = caller, API = "Read " + MethodSignatureWithoutReturnType(callee), ILOffset = ilOffset, ThreadId = Thread.CurrentThread.ManagedThreadId, ObjID = ObjectId.GetRefId(instance).ToString(), GroupName = "No-Group", IsWrite = false, }; var objId = ObjectId.GetRefId(instance); string fieldId = "null"; if (FieldNameDict.ContainsKey(objId)) { fieldId = FieldNameDict[objId]; } Configuration.TrapController.InterceptionPointStart(interceptionPoint, instance); return(new MethodCallbackContext() { Instance = instance, FieldId = fieldId, Caller = caller, ILOffset = ilOffset, Callee = callee }); }
/// <summary> /// Updates the tp history. /// </summary> /// <param name="interceptionPoint">The InterceptionPoint.</param> private void UpdateTPHistory(InterceptionPoint interceptionPoint) { // update the last operation for each thread lock (this.perThreadLastTP) { this.perThreadLastTP[interceptionPoint.ThreadId] = interceptionPoint; } // this block monitor the history of InterceptionPoints to infer the concurrent threads lock (this.globalTPHistory) { int thdid = interceptionPoint.ThreadId; this.globalTPHistory.Enqueue(interceptionPoint); if (this.perThreadTPCount.ContainsKey(thdid)) { this.perThreadTPCount[thdid]++; } else { this.perThreadTPCount[thdid] = 1; } if (this.globalTPHistory.Count > this.HistoryWindowSize) { InterceptionPoint tp = this.globalTPHistory.Dequeue(); this.perThreadTPCount[tp.ThreadId]--; if (this.perThreadTPCount[tp.ThreadId] == 0) { this.perThreadTPCount.Remove(tp.ThreadId); } } interceptionPoint.ActiveThdNum = this.perThreadTPCount.Keys.Count; } }
/// <summary> /// Locks the related op. /// </summary> /// <param name="tp">The tp.</param> /// <returns><see langword="true" /> if XXXX, <see langword="false" /> otherwise.</returns> private bool LockRelatedOP(InterceptionPoint tp) { if (tp.API.StartsWith("System.Threading.Monitor.Enter") || tp.API.StartsWith("System.Threading.Monitor.Exit")) { return(true); } return(false); }
/// <summary> /// Logs the bug for next run. /// </summary> /// <param name="tp1">The TP1.</param> /// <param name="tp2">The TP2.</param> private void LogBugForNextRun(InterceptionPoint tp1, InterceptionPoint tp2) { string bugSignature = this.GetPairID(tp1.Location, tp2.Location); if (!this.knownBugs.ContainsKey(bugSignature) && !this.blacklist.Contains(bugSignature)) { this.knownBugs[bugSignature] = 1; this.lastRunBugLogger.Log(bugSignature); } }
/// <summary> /// Get InterceptionPoint Log lines. /// </summary> /// <param name="interceptionPoint">InterceptionPoint information.</param> /// <returns>Log lines for InterceptionPoint.</returns> public static List <string> GetInterceptionPointLogLines(InterceptionPoint interceptionPoint) { List <string> lines = new List <string>(); lines.Add(string.Format("Id: {0}", interceptionPoint.GLobalHitCount)); lines.Add(string.Format("ThreadId: {0}", interceptionPoint.ThreadId)); lines.Add(string.Format("Timestamp: {0}", DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fff", CultureInfo.InvariantCulture))); lines.Add(string.Format("API: {0}", interceptionPoint.API)); lines.Add(string.Format("Method: {0}", interceptionPoint.Method)); lines.Add(string.Format("ILOffset: {0}", interceptionPoint.ILOffset)); return(lines); }
/// <summary> /// Method called right before an intercepted api. /// </summary> /// <param name="instance">Object instance of the called API.</param> /// <param name="method">Name of the method calling the api.</param> /// <param name="api">intercepeted API name.</param> /// <param name="ilOffset">ILOffset of the API call.</param> public static void OnStart(object instance, string method, string api, int ilOffset) { InterceptionPoint interceptionPoint = new InterceptionPoint() { Method = method, API = MethodSignatureWithoutReturnType(api), ILOffset = ilOffset, ThreadId = Thread.CurrentThread.ManagedThreadId, }; Configuration.TrapController.InterceptionPointStart(interceptionPoint, instance); }
/// <summary> /// Get InterceptionPoint Log lines. /// </summary> /// <param name="interceptionPoint">InterceptionPoint information.</param> /// <param name="objId">Id of the instance object.</param> /// <param name="includeStackTrace">A value indicating whether stack trace should be included as a log line.</param> /// <returns>Log lines for InterceptionPoint.</returns> public static List <string> GetInterceptionPointLogLines(InterceptionPoint interceptionPoint, int objId, bool includeStackTrace) { List <string> lines = new List <string>(); lines.AddRange(GetInterceptionPointLogLines(interceptionPoint)); lines.Add(string.Format("ObjectId: {0}", objId)); if (includeStackTrace) { lines.Add(string.Format("StackTrace: {0}", interceptionPoint.StackTrace)); } return(lines); }
/// <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> /// Updates the tp hit counts. /// </summary> /// <param name="interceptionPoint">The InterceptionPoint.</param> private void UpdateTPHitCounts(InterceptionPoint interceptionPoint) { string location = interceptionPoint.API + "|" + interceptionPoint.Method + "|" + interceptionPoint.ILOffset.ToString(); interceptionPoint.Location = location; lock (this.perTPHitCount) { if (!this.perTPHitCount.ContainsKey(location)) { this.perTPHitCount[location] = 0; } interceptionPoint.LocalHitcount = this.perTPHitCount[location]; this.perTPHitCount[location] = interceptionPoint.LocalHitcount + 1; } }
/// <summary> /// Finishes the trap. /// </summary> /// <param name="trapObjects">The trap objects.</param> /// <param name="trap">The trap.</param> /// <param name="interceptionPoint">The InterceptionPoint.</param> private void FinishTrap(Dictionary <string, HashSet <Trap> > trapObjects, Trap trap, InterceptionPoint interceptionPoint) { // execute the sleep if (trap != null) { try { } finally { Thread.BeginCriticalRegion(); trap.Semaphore.WaitOne(trap.Delay); ControllerHelper.Debug("Sleep : " + interceptionPoint.Tostring()); lock (trapObjects) { trapObjects.Remove(trap.ObjectId); } Thread.EndCriticalRegion(); } interceptionPoint.DelayCredit = this.InferSize; // figure out the which thread is blocked by this trap. { lock (this.perThreadLastTP) { foreach (var x in this.perThreadLastTP.Keys) { InterceptionPoint tp2 = this.perThreadLastTP[x]; if (interceptionPoint.Timestamp.AddMilliseconds(this.DelayPerDangerousInterceptionPoint * this.InferLimit) > tp2.Timestamp) { this.perThreadBlockedTP[x] = interceptionPoint; } } } } interceptionPoint.Trapped = true; this.isTrapActive = false; } }
/// <summary> /// Callback invoked before a field is read. /// </summary> /// <param name="parentObject">Object owning the field.</param> /// <param name="fieldName">Field name.</param> /// <param name="fieldValue">Field value.</param> /// <param name="caller">Method that writes the field.</param> /// <param name="ilOffset">ILOffset where the write happens.</param> public static void BeforeFieldRead(object parentObject, string fieldName, object fieldValue, string caller, int ilOffset) { string uniqueFieldName = GetUniqueFieldId(parentObject, fieldName); Guid objId = ObjectId.GetRefId(fieldValue); InterceptionPoint interceptionPoint = new InterceptionPoint() { Method = caller, API = "Read " + MethodSignatureWithoutReturnType(fieldName), ILOffset = ilOffset, ThreadId = Thread.CurrentThread.ManagedThreadId, ObjID = uniqueFieldName, GroupName = fieldName, IsWrite = false, }; Configuration.TrapController.InterceptionPointStart(interceptionPoint, parentObject); //Logger.Log($"BeforeFieldRead\t{uniqueFieldName}\t{objId}\t{caller}\t{ilOffset}"); }
/// <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> /// Removes the dependent InterceptionPoints. /// </summary> /// <param name="tp">The tp.</param> private void RemoveDependentInterceptionPoints(InterceptionPoint tp) { lock (this.perThreadLastTP) { if (this.perThreadBlockedTP.ContainsKey(tp.ThreadId)) { // find the InterceptionPoint that blocked this thread if exists. InterceptionPoint tp2 = this.perThreadBlockedTP[tp.ThreadId]; // delaycredit is k means a InterceptionPoint block the next-k operation in other thread if (tp2.DelayCredit > 0) { this.RemoveDangerItem(tp.Location, tp2.Location); // DebugLog.Log("HB Order " + tp.Tostring() + " " + tp2.Tostring()); tp2.DelayCredit--; } else { this.perThreadBlockedTP.Remove(tp.ThreadId); } } } }
/// <summary> /// Finishes the trap. /// </summary> /// <param name="trapObjects">The trap objects.</param> /// <param name="trap">The trap.</param> /// <param name="interceptionPoint">The InterceptionPoint.</param> private void FinishTrap(Dictionary <Guid, HashSet <Trap> > trapObjects, Trap trap, InterceptionPoint interceptionPoint) { // execute the sleep if (trap != null) { // DebugLog.Log("Start Sleeping for tp " + InterceptionPoint.Tostring()); trap.Semaphore.WaitOne(trap.Delay); interceptionPoint.DelayCredit = this.InferSize; // figure out the which thread is blocked by this trap. { lock (this.perThreadLastTP) { foreach (var x in this.perThreadLastTP.Keys) { InterceptionPoint tp2 = this.perThreadLastTP[x]; if (interceptionPoint.Timestamp.AddMilliseconds(this.DelayPerDangerousInterceptionPoint * this.InferLimit) > tp2.Timestamp) { this.perThreadBlockedTP[x] = interceptionPoint; // DebugLog.Log(x + " is blocked by " + InterceptionPoint.Tostring()); } } } } interceptionPoint.Trapped = true; lock (trapObjects) { trapObjects.Remove(trap.ObjectId); } this.isTrapActive = false; } }
/// <summary> /// Get thread safety group details of the InterceptionPoint. /// </summary> /// <param name="threadSafetyGroups">The thread safety groups.</param> /// <param name="interceptionPoint">InterceptionPoint.</param> /// <returns>Thread safety InterceptionPoint.</returns> public static ThreadSafetyInterceptionPoint GetThreadSafetyInterceptionPoint(List <ThreadSafetyGroup> threadSafetyGroups, InterceptionPoint interceptionPoint) { if (threadSafetyGroups == null) { return(null); } foreach (ThreadSafetyGroup threadSafetyGroup in threadSafetyGroups) { foreach (string writeAPI in threadSafetyGroup.WriteAPIs) { if (SignatureMatchUtils.IsWildcardMatch(interceptionPoint.API, writeAPI)) { return(new ThreadSafetyInterceptionPoint() { ThreadSafetyGroup = threadSafetyGroup, IsWriteAPI = true, }); } } foreach (string readAPI in threadSafetyGroup.ReadAPIs) { if (SignatureMatchUtils.IsWildcardMatch(interceptionPoint.API, readAPI)) { return(new ThreadSafetyInterceptionPoint() { ThreadSafetyGroup = threadSafetyGroup, IsWriteAPI = false, }); } } } return(null); }
/// <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) { // DebugLog.Log("AddDangerList : " + st); this.LogPlanForNextRun(st); } } return(s); }
/// <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; }