Methods and systems to identify and reproduce concurrency bugs in multi-threaded programs are disclosed. An example method disclosed herein includes defining a data type. The data type includes a first predicate associated with a first thread of a multi-threaded program that is associated with a first condition, a second predicate that is associated with a second thread of the multi-threaded program, the second predicate being associated with a second condition, and an expression that defines a relationship between the first predicate and the second predicate. The relationship, when satisfied, causes the concurrency bug to be detected. A concurrency bug detector conforming to the data type is used to detect the concurrency bug in the multi-threaded program.
|
1. A method comprising:
defining, with a processor, a data type comprising:
a first predicate associated with a first thread of a multi-threaded program, the first predicate being associated with a first condition;
a second predicate associated with a second thread of the multi-threaded program, the second predicate being associated with a second condition; and
an expression, the expression defining a relationship between the first predicate and the second predicate, the expression to be used to determine whether the relationship between the first predicate and the second predicate is present, and to cause a concurrency bug to be detected when the relationship is present.
11. An apparatus to detect a concurrency bug in a multi-threaded program, the apparatus comprising:
a processor;
a memory to store machine readable instructions and a data type, the data type comprising:
a first program instruction invoking a first predicate, the first predicate specifying a first condition;
a second program instruction invoking a second predicate, the second predicate specifying a second condition; and
an expression defining a relationship between the first predicate and the second predicate, the expression to be used to determine whether the relationship between the first predicate and the second predicate is present, and to cause a concurrency bug to be detected when the relationship is present.
18. A machine readable storage medium comprising instructions which, when executed, cause a machine to at least:
recognize a concurrency bug detector conforming to a data type, the data type being defined to include:
a first predicate associated with a first thread of a multi-threaded program, the first predicate being associated with a first condition;
a second predicate associated with a second thread of the multi-threaded program, the second predicate being associated with a second condition; and
an expression, the expression defining a relationship between the first predicate and the second predicate, the expression to be used to determine whether the relationship between the first predicate and the second predicate is present, and to cause a concurrency bug to be detected when the relationship is present.
2. A method as defined in
defining a concurrency bug detector conforming to the data type, wherein defining the concurrency bug detector comprises defining the first condition and the second condition;
inserting a first program instruction invoking the first predicate into the first thread; and
inserting a second program instruction invoking the second predicate into the second thread.
3. A method as defined in
4. A method as defined in
5. A method as defined in
6. A method as defined in
7. A method as defined in
8. A method as defined in
9. A method as defined in
10. A method as defined in
12. An apparatus as defined in
13. An apparatus as defined in
an invoker to invoke the first program instruction and the second program instruction; and
a tester to determine whether the first predicate and the second predicate are satisfied when invoked by the invoker.
14. An apparatus as defined in
a tester to determine whether the expression is satisfied.
15. An apparatus as defined in
a thread controller to control operations of a first thread of the multi-threaded program relative to the operations of a second thread of the multi-threaded program.
16. An apparatus as defined in
17. An apparatus as defined in
a message generator to generate an error message if the tester determines that the expression is satisfied, the error message identifying at least one of a first location in the first thread or a second location in the second thread.
19. A machine readable storage medium as defined in
determine whether the first predicate is satisfied;
determine whether the second predicate is satisfied; and
determine whether the expression is satisfied.
20. A machine readable storage medium as defined in
generate the error message when the expression is satisfied.
|
This disclosure relates generally to multi-threaded programs and more particularly to debugging concurrency violations occurring in multi-threaded programs.
In recent years, concurrent/parallel programming, in which different computer programs execute simultaneously and access shared memory, has become quite popular. Concurrently executing programs that operate within the context of a single computer program are commonly referred to as threads of a multi-threaded program. Multi-threaded programming has inherent challenges both because of the simultaneous nature of the execution of the threads and because the different threads of the program have access to shared memory. As a result of the simultaneous executions of the threads, operations performed by the threads do not necessarily occur in any particular order. However, there are instances in which proper operation of the multi-threaded program requires that one or more of the operations performed in one or more threads occur in a specific order relative to the operations of one or more other threads. For example, two or more threads that access the same memory location may rely on a specific value or values being present in the shared memory location when that shared memory location is accessed. However, the specific value or values in the shared memory location when that memory location is accessed by any given thread may depend on the order in which one or more of the threads access the shared memory location. Further, the simultaneous operation of the various threads makes it difficult to control the order in which a shared memory location is accessed by any given thread in a multi-threaded program.
Fixing any computer error, or bug, including those caused by a concurrency violation generally involves reproducing the bug so that the bug can be properly identified and so that the responsible portions of the program can be modified to correct the bug. Debugging in the context of a serial program is fairly straightforward as it involves sequentially stepping through the instructions of the code until the bug occurs and then revising the instructions responsible for the bug. However, this method of debugging does not work well in a multi-threaded program because the order in which the instructions of the various threads of a multi-threaded program are executed is unknown and, indeed, may vary from execution to execution. In fact, a particular bug present in a multi-threaded program may not occur upon every execution of the program (i.e., the bug may be non-deterministic) making the bug extremely difficult to detect and debug.
Methods and systems to identify, reproduce and/or repair concurrency violations in multi-threaded programs are disclosed herein. In some example methods, one or more operations performed by a first thread, thread1, of a multi-threaded program are serialized relative to one or more operations performed by a second thread, thread2, of the example multi-threaded program when one or more conditions have been met. In at least some examples, the serialization of the operations of thread1 and thread2 causes a concurrency violation to occur.
Unlike existing debugging tools which are only able to identify that the execution of a multi-threaded program has resulted in one or more concurrency violations, debugging tools disclosed here are able to reproduce a concurrency violation so that the operations responsible for the violation are identified and can be revised. In addition, debugging tools disclosed here can be used after the program has been revised to determine whether the revisions have effectively removed the bug. Also, debugging tools disclosed here do not produce the false positives which may result from using existing debugging tools.
Example methods and systems disclosed herein include, detecting a bug in a multi-threaded program, defining a data type that includes a name of a bug, a first predicate associated with a first thread of the multi-threaded program, the first predicate specifying a condition, a second predicate associated with a second thread of the multi-threaded program, the second predicate specifying the condition and a relationship between the first predicate and the second predicate, the relationship, when present, causing the bug to be detected.
Some example methods and systems disclosed herein include defining an expression conforming to the data type, inserting the expression into the multi-threaded program, inserting the first predicate into the first thread, and inserting the second predicate into the second thread. Example methods and system disclosed can identify one or more bugs in the multi-threaded program by defining condition(s) (e.g., predicates) that, when concurrently satisfied by one or more of the threads of a multi-threaded program, will result in a specific type of bug called a concurrency violation. After being identified and defined, these concurrently satisfied predicates (e.g., concurrent predicates) are placed in the source code or instructions of one or more of the threads of the multi-threaded program. The order in which the concurrent predicates must be met (i.e., satisfied) is defined in an expression referred to as a Concurrent Predicate Expression (CPEs) that is converted by a compiler into executable code/instructions and used to control the scheduling of the threads of a multi-threaded program to pin-point and/or reproduce the concurrency violation/bug.
Using CPEs allows a programmer to consider and comprehend the thread interactions at a high level, without requiring the programmer to analyze the code/instructions in which the interaction occurs. This frees the programmer from the confines of the program's structure to allow reasoning directly about the cause of the concurrency violation. CPEs also reduce and even prevent the bug from being inadvertently hidden by way of an incorrect repair of the concurrency violation, a situation that commonly occurs when introducing code that changes thread scheduling (known as the “probe effect”). When a CPE is properly used, the programmer formally defines the bug and then uses that formal definition to attempt to reproduce the bug after a repair has been applied. If the bug can no longer be reproduced after the repair has been applied, the programmer has some degree of confidence, and in some cases proof, that the repair has removed the bug.
An assertion is one technique used to effectively identify and/or prevent a concurrency violation in serial programs. An assertion is a condition (also called a predicate) placed in a program at a specific location and identifies a predicate that is assumed to be true at that specific location. If an assertion evaluates to false at run time, an assertion failure occurs, which typically causes execution of the computer program to abort. This draws attention to the location at which the logical inconsistency caused by the unsatisfied condition/predicate is detected. Such an assertion is helpful in detecting bugs in sequential programs, because the program code preceding the assertion can be reviewed to find the code that is responsible for triggering the assertion failure. However, for parallel programs, the location of the assertion is insufficient to pin-point a bug because the assertion only identifies errors in the thread in which the assertion was placed but does not identify other threads that may be responsible for causing the bug, also known as the root cause. At least one example bug detector disclosed herein uses predicates that operate in a manner similar to an assertion to reproduce and identify bugs in a multi-threaded program.
A block diagram of an example debugging tool (bug detector) 10 to detect a bug in an example multi-threaded program 11 is illustrated in
In some examples, the example controller 12 causes the example thread monitor 14 to observe the execution of thread1 and thread2. When the execution of thread1 has progressed to a first program code statement at a predefined example first location, L1, the example thread monitor 14 notifies the example controller 12 which causes the example tester 20 to determine whether a first condition associated with a first predicate is present. In some examples, the condition is associated with a first variable stored in the example second memory 27 and the example tester 20 determines whether the first condition is present by accessing the example second memory 27 and determining whether the first stored variable is equal to a first predefined value. If the first stored variable is equal to the first predefined value, the example tester 20 notifies the example controller 12 that the first predicate is present (“satisfied”). When thread2 has progressed to a second program code statement at a predefined example second location, L2, the example thread monitor 14 notifies the example controller 12 which causes the example tester 20 to determine whether a second condition associated with a second predicate is satisfied. In some examples, the second condition is associated with a second variable stored in the example second memory 27 and the example tester 20 determines whether the second condition is satisfied by accessing the example second memory 27 and determining whether the second stored variable is equal to a second predefined value. If the second stored variable is equal to the second predefined value, the example tester 20 notifies the example controller 12 that the second predicate is satisfied. The example controller 12 then causes the example tester 20 to test for the presence of a predefined relationship (“predicate relationship”) between the first predicate, P1, and the second predicate, P2. If the predicate relationship is present, the example controller 12 causes an example message generator 22 to generate and transmit an error message to an example output device 28 and then the multi-threaded program 11 crashes. The example output device 28 may be a visual display, a speaker, a printer or any other device capable of informing a user that the bug has been detected. In some examples, the predicate relationship specifies that both the first and the second predicates are to be concurrently satisfied. In other examples, the first condition and the second condition are the same condition. In other examples, the first predicate can be associated with a first plurality of conditions and the example controller 12 causes the example tester 20 to test for all of the first conditions upon reaching the first location, L1. If all of the conditions are satisfied, then the example tester 20 notifies the example controller 12 that the first predicate, P1, has been satisfied. Likewise, the second predicate, P2, can be associated with a plurality of example second conditions and the example tester 20 tests for all of the second conditions upon reaching the example second predefined location, L2. If all of the second conditions are satisfied, the example controller 12 causes the example tester 20 to test for the presence of the predicate relationship between the first predicate, P1, and the second predicate, P2. In other examples, the predicate relationship can also/instead specify that the first predicate, P1, and the second predicate, P2, be satisfied in a specific order (e.g., that P1 be satisfied before P2 or vice versa). In still other examples, the predicate relationship can specify that either P1 or P2 be satisfied. Other example predicate relationships are disclosed below.
The first location, L1, in thread1 may represent a location contained in a portion of thread1 that may be at least partially responsible for causing the bug. The second location, L2, in thread2 may represent a location contained in a portion of the second thread that may be at least partially responsible for causing the bug. If the predicate relationship is satisfied, the portion of thread1 containing the first location, L1, and the portion of the second thread containing the second location, L2, are at least partially responsible for causing the bug.
In a debugging environment, the programmer may not know exactly which portions of thread1 and thread2 are responsible for causing a concurrency violation, (e.g., the programmer may not know where the bug is located). Thus, the programmer is assumed to have made an educated guess about where the locations, L1 and L2, are located and what conditions must be met for the concurrency violation to occur. Provided that the programmer's educated guess is accurate, the concurrency violation is reproduced (e.g., the bug occurs), and the programmer is able to identify the responsible, defective operations of thread1 and thread2. If the concurrency violation does not occur when thread1 and thread2 are executed with the bug detector in place, the programmer is able to identify the corresponding portions of thread1 and thread2 as not being responsible for the bug. The programmer may then make another educated guess about what other portions of thread1 and thread2 might be causing the concurrency violation. The programmer then reconfigures the bug detector 10 and identifies a new first location and a new second location at which the bug may occur in an attempt to reproduce the concurrency violation. This process of locating the portions of thread1 and thread2 responsible for the concurrency violation is sometimes referred to as a divide-and-conquer technique.
Upon isolating the defective operations of thread1 and thread2 responsible for the bug, the programmer may revise those operations in an attempt to remove the bug. The programmer may then use the bug detector 10 to determine whether the bug has been successfully removed. If the bug occurs again, then the programmer may further revise the defective thread operations. If the bug does not occur again, and provided that the example bug detector 10 has been properly configured, the programmer has a greater degree of confidence that the bug has been removed and/or repaired.
In some examples, the example controller 12 causes the example thread monitor 14 to monitor the forward progress of thread1. Upon observing that thread1 has reached the first location, L1, the example thread monitor 14 provides an indication to the example controller 12 which responds to the indication by causing the example tester 20 to test for the first condition. If the first condition is satisfied, the example thread monitor 14 indicates to the example controller 12 that the first condition has been satisfied and the example controller 12 causes the example thread controller 16 to control the forward progress of thread1 for a threshold amount of time. While the forward progress of thread1 is being controlled, the example controller 12 causes the example thread monitor 14 to begin monitoring thread2. Upon observing that the second thread has reached the second location, L2, the example thread monitor 14 provides an indication to the example controller 12 which responds to the indication by causing the example tester 20 to test for the second condition. If the second condition is satisfied, the example thread monitor 14 indicates to the example controller 12 that the second condition has been satisfied and the example controller 12 tests to determine whether a relationship between/among the first and second condition is satisfied. If satisfied, a bug is detected and the example controller 12 causes the example message generator 22 to output an error message. If the second condition is not satisfied within a threshold amount of time as measured by the example timer 18, the bug has not been detected and the bug detector 10 ceases operation. In some examples, the example thread controller 16 controls the forward progress of thread1 by stalling thread1 for the threshold amount of time.
By controlling the forward progress of thread1 relative to thread2, or vice versa, the example bug detector 10 controls the order in which the operations of thread1 are interleaved with the operations of thread2, (e.g., serializes the operations of thread1 relative to the operations of thread2). The example controlled interleaving of such operations is performed in a manner intended to increase the likelihood that the predicates are satisfied in the manner specified by the predicate relationship so that the bug, if present, is detected.
In some examples, the first predicate, P1, specifies a first condition relationship between the plurality of first conditions and/or the second predicate, P2, specifies a second condition relationship between the plurality of second conditions. In this example, P1 is not satisfied until the first conditions associated with P1 are satisfied in accordance with the first condition relationship specified by P1. Likewise, P2 is not satisfied until the second conditions associated with P2 are satisfied in accordance with the second condition relationship specified by P2. In some examples, the predicates include a plurality of predicates (e.g., P1, P2, P3 . . . Pn) and the predicate relationship specifies a manner in which all of or a subset of the plurality of predicates must be satisfied before the bug will be detected.
In the illustrated example of
A data type 212, represented as pseudocode, is illustrated
To use a bug detector conforming to the example abstract data type 212 (see
When globally defining the example CPE 300 within the example multi-threaded program 11, the predicates, P1, P2 . . . Pk, are declared as being associated within one or more undefined conditions. The undefined condition(s) associated with any given predicate is then defined with specificity in the program code that invokes the associated predicate. In operation, the example multi-threaded program 11 having the global definition of the CPE and the predicate-invoking program codes inserted therein is supplied to the processor 26. The implementation generator 202 of the processor 26 uses the example globally defined CPE 300 and the predicate-invoking program codes to automatically generate a software implementation of the CPE 300. The integrator 206 of the processor 26 integrates the software implementation of the example CPE 300 with the example multi-threaded program 11 to form the integrated program code 210 which is then executed by the thread executor 208 of the processor 26.
As described above, when the example integrated program code 210 is executed, thread1 is executed and P1 is invoked upon reaching L1, and thread2 is executed and P2 is invoked upon reaching L2. If both P1 and P2 are satisfied in the manner specified by the predicate relationship during execution of the example integrated program code 210, the example multi-threaded program 11 crashes and the error message is generated.
In some examples, the conditions associated with the predicates are based on the declared variables defined at line CPE2 and can be based on any program variables that are accessible to the programmer at the locations, L1 and L2. Further, any variables specified in the global definition of the CPE may be modified and used only by the predicates in which they are specified.
In some examples, each predicate can only be invoked in a single thread of the example multi-threaded program 11. In this example, any predicate invoked in a first thread can be renamed and a renamed version of the same predicate can be invoked in a different thread of the example multi-threaded program 11. In other examples, two differently named predicates can specify that the same conditions be satisfied in accordance with the same condition relationship. In still other examples, a STALL (t) condition is defined to allow the execution of a predicate to be delayed for an amount of time that is proportional to the value of “t.” Accordingly, a predicate containing the STALL(t) condition will not return true until the specified amount of time, “t,” has elapsed (and provided that any other conditions specified in the predicate are satisfied in accordance with the condition relationship specified by the predicate). In other examples, more than one predicate can be invoked in a single thread of a multi-threaded program 11.
In the illustrated example of
In the illustrated example of
Referring still to the illustrated example of
Example thread2 begins, as reflected in line T21 of
Referring still to the illustrated example of
In the illustrated example of
Example thread2 is an infinite loop that causes the processor 26 to obtain the lock on the container, set the variable “id” equal to a randomly generated number, erase or remove the element, “id,” from the container, and release the lock (lines T21-T26). However, the portion of thread1 represented by lines T17-T110 does not perform a validity check on the iterator or pointer after thread2 has executed and before thread1 proceeds to access the container location referenced by the pointer. Instead, thread1 is designed to operate as though an element is stored at the location referenced by the pointer because thread1 placed the element into that location at the lines T21-T26 (before thread2 executed). However, thread2, as described above, may have removed the element inserted into that location by thread1. Thus, after the execution of thread2, the iterator or pointer may point to a location in the container that no longer contains the element. When thread1 then attempts to access the element contained at the location referenced by the pointer (see line T17), thread1 will find the location empty and the program crashes.
Referring still to the illustrated example of
In this example, P2 is invoked after thread2 has removed the element, “id,” from the container (line T24) and after thread2 has released access control over the container (line T25). P2, when invoked, causes the example controller 12 to set the local variable “removed_id” equal to “id” to indicate that the element “id” has been removed from the container. In this example, P2 specifies an operation to be performed such that P2 is satisfied when the operation is completed. After the operation specified in P2 is performed, the example controller 12 stalls the execution of thread2 and begins monitoring thread1 to determine whether P1 has been satisfied as specified by the expression (P2>&P1).
In the illustrated example of
In the illustrated example of
In this example, thread2 performs the same operations as thread1 (compare the statements at lines T11-T117 to statements T21-T217). Thus, thread2 acquires a lock on the variable B before acquiring a lock on the variable A if the rand statement at line T25 returns true. Conversely, thread2 acquires a lock on the variable A before acquiring a lock on the variable B if the rand statement at line T25 returns false.
When both thread1 and thread2 have acquired lock_one, the other of the threads will be prevented from accessing the locked variable until the lock-possessing thread releases the lock. Thus, if the statements at lines T15 and T25 have evaluated to true in each of the threads (i.e., both threads have set lock_one equal to the variable B), then whichever thread acquires lock_one first will proceed to execute the statements at lines T114-T117 or lines T214-T217 (depending on which thread acquires lock_one first) and the other thread will be unable to proceed until the lock-possessing thread has released lock_one at the statement T116 or T216 (depending, again, on which thread acquired lock_one first). Upon release of the lock on the variable B by the lock-possessing thread, the halted thread will again be able to operate and, having also set lock_one equal to the variable B, will acquire the lock on the variable B.
Likewise, if the statements at lines T15 and T25 have evaluated to false in each of the threads (i.e., both threads have set lock_one equal to the variable A), then whichever thread acquires lock_one first will proceed to execute the statements at lines T114-T117 or lines T214-T217 (depending on which thread acquires lock_one first) and the other thread will be unable to proceed until the lock-possessing thread has released lock_one at the statement T116 or T216 (depending, again, on which thread acquired lock_one first). Upon release of the lock on the variable A by the lock-possessing thread, the halted thread will again be able to operate and, having also set lock_one equal to the variable A, will acquire the lock on the variable A.
If, instead, the rand statements at line T15 and line T25 do not both return true, or the rand statements at line T15 and line T25 do not both return false and provided that both threads have obtained lock_one (at lines T113 or T213) before either thread reaches for lock—2 (at lines T115 or T215), then a deadlock will occur as described below. Consider the example in which the rand statement at line T15 in thread1 evaluates to false and the rand statement at line T25 in thread2 evaluates to true. Here, thread1 sets lock_one equal to A and thread2 sets lock_one equal to B. When thread1 acquires the lock on lock_one at line T113, the variable A is locked to prevent access by thread2. When thread2 acquires the lock on lock_one at line T213, the variable B is locked to prevent access by thread1. Thread1 then proceeds to attempt to acquire a lock on lock_two at line T115, which, in this instance has been set equal to B by thread1. Given that the variable B has been locked by thread2 at the line T213, thread1 halts until the lock on variable B has been released. Likewise, thread2 proceeds to attempt to acquire a lock on lock_two at line T215 which has been set equal to A by thread2. However, thread2 is unable to proceed because thread1 has locked the variable A at line T113. Thus, neither of thread1 nor thread2 is able to proceed and the example multi-threaded program 11 becomes deadlocked.
Thus, due to the shared locks, lock_one and lock_two, when the rand statements in thread1 and thread2 both return true or both return false, the threads will obtain the locks on the variables A and B in the same order and the example multi-threaded program will operate properly. If the rand statements evaluate differently in thread1 and thread2 and provided that both threads have obtained lock_one (at lines T113 or T213) before either thread reaches for lock—2 (at lines T115 or T215) then the threads will attempt to acquire the locks on the variables A and B in an order and the program will be deadlocked as described above.
There are at least four examples in which thread1 and thread2 may acquire the locks on the variables A and B in an order that will cause a deadlock including, 1) when thread2 acquires a lock on A first and thread1 acquires a lock on B first; 2) when thread1 acquires a lock on A first and thread2 acquires a lock on B first, 3) when thread2 acquires a lock on B first and thread1 acquires a lock on A first; and 4) when thread1 acquires a lock on B first and thread2 acquires a lock on A first. In order to cause a deadlock in each of the examples 1)-4), both threads must have obtained lock_one before either thread has reached for lock_two.
Referring still to
In operation, thread1 executes in the same manner described above until the example thread monitor 14 observes L1 is reached at which time P11 and then P12 are invoked. Likewise, thread2 executes in the same manner described with respect to above until the example thread monitor 14 observes that L2 is reached at which time P21 and then P22 are invoked. In this example, P11 and P12 are invoked after thread1 has acquired the lock on lock_one. P11 when invoked, causes the example controller 12 to determine whether lock_one is equal to the variable B and P12, when invoked, causes the example controller 12 to determine whether lock_one is equal to the variable A. If either predicate P11 or P12 is satisfied, the example tester 20 identifies the satisfied predicate to the example controller 12. P21, when invoked, causes the example controller 12 to determine whether lock_one is equal to the variable B and P22, when invoked, causes the example controller 12 to determine whether lock_one is equal to the variable A. If either predicate P21 or P22 is satisfied, the example tester 20 identifies the satisfied predicate to the example controller 12. After P11 or P22 is satisfied, thread1 is stalled in accordance with the expression declared in the CPE and after P12 or P21 is satisfied, thread2 is also stalled in accordance with the expression declared in the CPE. Stalling the threads in this manner prevents either thread from reaching for lock_two when one thread has locked A upon obtaining lock_one and the other thread has locked B upon obtaining lock_one.
As described above, there are, at least, four examples in which thread1 and thread2 may deadlock by acquiring the locks A and B in different order including, 1) when thread2 acquires A first and thread1 acquires B first, (i.e., when (P11>&P22) is satisfied); 2) when thread1 acquires A first and thread2 acquires B first, (i.e., when (P21>&P12) is satisfied, 3) when thread2 acquires B first and thread1 acquires A first, (i.e., when (P22>&P11) is satisfied; 4) when thread1 acquires B first and thread2 acquires A first (i.e., when (P12>&P21) is satisfied. Thus, the deadlock bug is reproduced/detected when the predicates, P11, P12, P21 and P22 satisfy the expression (P11 && P22∥P21 && P12). As described above, the expression is formulated to ensure that the deadlock will only be detected/reproduced when both threads have obtained lock_one in a manner that causes one thread to lock the variable A and the other thread to lock the variable B and provided that neither thread has yet obtained lock_two.
In some examples, an alternative expression (P11>& P22∥P21>& P12) may be substituted for the original expression (P11 && P22∥P21 && P12). This alternative expression, when used, will fail to identify bugs occurring when the expressions (P22>&P21) and (P12>&P21) are satisfied. However, using this alternative expression will allow the bug detector to operate faster because the alternative expression is likely to occur sooner and with fewer stalls. The alternative expression is likely to occur sooner than the original expression because P11 and P21 are satisfied when the rand statement evaluates to true which is likely to occur sooner than P12 and P22 which are satisfied when the rand statement evaluates to false. Thus, the alternative relationship can be used when it is desirable to sacrifice accuracy in favor of speed.
In the illustrated example of
In this example, thread2 is an infinite loop (lines T21-T211). Upon entering the loop, thread2 locks the container at line T23 to restrict thread1 from accessing the container and sets a variable “total” equal to zero at a line T24. Next, thread2 assigns a pointer, “i,” to point to a first element in the container. Provided that the pointer “i” is not pointing to a last element in the container, the value of “i” is incremented by one to point to a next element in the container at lines T25. Thread2 then adds the ith container element to the value of the variable “total” at line T27 and releases the lock on the container at line T29. Finally, thread2 calculates a variable “ave” by dividing the value of “total” by the number of elements in the container at line T210.
In the example multi-threaded program 11 represented by thread1 and thread2 of
Referring still to the example of
In operation, thread1 executes as described above until the example thread monitor 14 observes that L1 is reached at which time P1 is invoked. When P1 is invoked, the example controller 12 causes the example tester 20 to determine whether P1 has been satisfied, (i.e., whether the container is empty). Likewise, thread2 executes as described above until the example thread monitor 14 observes that line L2 is reached at which time P2 is invoked. When P2 is invoked, the example controller 12 causes the example tester 20 to ensure that the value of the variable “total” is not equal to zero. If the variable “total” is equal to zero, then the dividend of the “ave” calculation performed at the line T210 will be equal to zero such that the calculation of “ave” will not result in a divide by zero exception even though the container may be empty. After P2 is invoked, P3 is invoked in thread2. When P3 is invoked, the example controller 12 causes the example tester 20 to determine whether P3 has been satisfied. P3 is satisfied if the container becomes empty provided that “total” is a non zero value. The timing at which the conditions of P3 are satisfied is controlled by stalling thread2 according to the relationship specified by P3. When P1, P2 and P3 are all satisfied in accordance with the expression (P2>(P1>&P3)), the bug is detected/reproduced and the example controller 12 causes the example message generator 22 to output the error message. In this example, the bug can be detected solely through the use of P3, though using the predicates P1 and P2 helps to pin point where the undesired interleaving originated in thread1 and thread2 which is useful when repairing the bug.
In the illustrated example of
When performing the operations at lines I5-I9, the processor 26 determines whether the condition P1 is satisfied. If P1 is not satisfied, then (P1>&P2) is not satisfied and the processor 26 proceeds to execute the operations at lines I10-I13. If P1 is satisfied, the processor 26 stalls thread1 and repeatedly tests to determine whether P2 is satisfied until either P2 is satisfied or until a threshold amount of time has elapsed at line I8. If P2 is satisfied before the threshold amount of time has elapsed, the portion of the expression (P1&&P2) represented as (P1>&P2) has been satisfied such that the processor 26 will indicate that the bug has been detected in connection with the operation performed at line I15 as described below. If P2 is not satisfied before the threshold amount of time has elapsed, then (P1&&P2) is not satisfied and the processor 26 performs the operations at I10-I13 to determine whether (P2>&P1) is satisfied.
When performing the operations at lines I10-I13, the processor 26 determines whether the condition P2 is satisfied. If P2 is not satisfied, the processor 26 executes the operations at lines I14-I17. If P2 is satisfied, the processor 26 stalls thread2 and repeatedly tests to determine whether P1 is satisfied until either P1 is satisfied or until a threshold amount of time has elapsed at line I13. If P1 is satisfied before the threshold amount of time has elapsed, the portion of the expression (P1&&P2) represented as (P2>&P1) has been satisfied such that the processor 26 will indicate that the bug has been detected in connection with the operation performed at line I15. If P1 is not satisfied, the processor 26 within the threshold amount of time, then (P2>&P1) is not satisfied and the processor 26 performs the operations at lines I14 through I17.
When performing the operations at lines I14-I17, the processor 26 determines whether either of (P1>&P2) or (P2>&P1) are satisfied based on the outcome of the operations performed at lines I5-I9 and at lines I10-I13. If either or both of (P1>&P2) or (P2>&P1) are satisfied, the expression (P1&&P2) has been satisfied and the processor 26 sets a variable “bug_found” to true at line I15 to indicate that the bug has been detected. If neither (P1>&P2) nor (P2>&P1) are satisfied, the processor 26 sets the variable “bug_found” to false at line I16 to indicate that the bug has been detected.
The processor 26 resets the status of both P1 and P2 to not satisfied at the lines I18 and I19 and the processor 26 either reports the bug as being found or not, depending on the outcome of the operations performed at lines I14 through I16.
In the illustrated example of
As described above, the example of
At the lines I2-I5, the processor 26 sets the status of the predicate P0 to not satisfied, sets the number of times that each of P1 and P2 has been satisfied equal to zero and sets a variable, “num_repeats,” to zero. Here, the variable “num_repeats” represents the number of times that the expression (P1^2&P2^3) has been satisfied.
At the lines I6-I8, the processor 26 determines whether P0 has been satisfied by evaluating whether the condition associated with P0 has been satisfied.
At the lines I9-I13, provided that P0 has been satisfied, the processor 26 increments the number of times P1 has been satisfied by one each time the condition associated with P1 has been satisfied. At the line I14, the processor 26 waits a threshold amount of time for either (P1^2>&P2^3) to be satisfied or for (P2^3>&P1^2) to be satisfied. Each time P1 is satisfied in any given thread, that thread is stalled to allow time for P1 to be concurrently satisfied in one or more other threads. Likewise, each time P2 is satisfied in any given thread, that thread is stalled to allow time for P2 to be concurrently satisfied in one or more other threads.
At the lines I16-I20, provided that P0 has been satisfied, the processor 26 increments the number of times P2 has been satisfied by one each time the condition associated with P2 has been satisfied. At the line I21, the processor 26 waits a threshold amount of time for either (P1^2>&P2^3) to be satisfied or for (P2^3>&P1^2) to be satisfied. Each time P1 is satisfied in any given thread, that thread is stalled to allow time for P1 to be concurrently satisfied in one or more other threads. Likewise, each time P2 is satisfied in any given thread, that thread is stalled to allow time for P2 to be concurrently satisfied in one or more other threads.
At the lines I22-I24, the processor 26 increments the variable “num_repeats” by one each time the expression (P1^2 && P2^3) is satisfied. At the lines I25-I26, the processor 26 resets the status of P1 and P2 to indicate that they are not satisfied. At the line I27-I29, provided that the variable “num_repeats” is equal to three (i.e., the expression (P1^2 && P2^3) has been repeatedly satisfied three times), the processor 26 sets the status of P0 to indicate P0 has not been satisfied and also reports the bug as being detected.
While examples of the bug detector 10 have been illustrated in
Flowcharts representative of example machine readable instructions that may be executed to implement the bug detector 10 of
As mentioned above, the example processes of
Example machine readable instructions 1000 that may be executed to implement the bug detector 10 of
As described above, the example thread monitor 14 begins to observe the operations of thread2 (block 1002B). The example thread monitor 14 determines whether thread2 has progressed to a second location, L2, at which a second predicate, P2, is invoked (block 1014). If L2 has not yet been reached, the example thread monitor 14 continues to observe the progress of thread2 (block 1014). If L2 has been reached, the example thread monitor 14 notifies the example controller 12 and the example controller 12 invokes P2 (block 1016). The example controller 12 causes a tester 20 to determine whether P2 has been satisfied (block 1018). If P2 has not been satisfied, the example controller 12 sets a flag “P2” to zero (block 1020) and if P2 has been satisfied, the example controller 12 sets the flag “P2” to one (block 1022).
In the illustrated example of
In some examples, P1 and/or P2 are associated with one or more conditions. In this example, the example controller 12 determines whether P1 is satisfied by determining whether the conditions specified by P1 are satisfied and the example controller 12 determines whether P2 is satisfied by determining whether the conditions specified by P2 are satisfied. In other examples, P1 and/or P2 can each specify a condition expression. The condition expression specifies a relationship among two or more conditions such that when the relationship among the conditions has been satisfied, the condition expression is satisfied. In yet other examples, P1 and/or P2 specify a manner in which the example thread controller 16 is to control thread1 and/or thread 2, respectively. In some examples, the condition expression and/or the predicate expression can each specify a manner in which the example thread controller 16 is to control thread1 and/or thread2.
Example machine readable instructions 1100 that may be executed to implement the bug detector 10 of
An example CPE designed to detect a specific type of bug in a specific multi-threaded program is globally defined in the example multi-threaded program and program code invoking each globally defined predicate is inserted at desired locations into the source code of the threads of the example multi-threaded program (block 1106). The location selected for invocation of any given predicate is optimally at or near the portion(s) of the thread that cause the bug or the invocations are located at any position that will allow the bug to be detected. Each predicate identifies specific condition(s) to be satisfied and, in some examples, also identifies a condition expression among/between the conditions that, when satisfied, causes the predicate to be satisfied. As described above, the design of the bug detector 10 and the locations at which to insert the predicates of the bug detector 10 are determined based on the operations of the specific multi-threaded program being debugged and based on the specific type of bug to be detected. The example multi-threaded program having the globally defined CPE and having predicate invocations inserted therein is supplied to the processor 26 for execution.
Prior to execution of the example multi-threaded program supplied, the processor 26 uses the extended grammar to recognize the bug detector 10 and causes the implementation generator 202 to generate a software implementation of the bug detector 10 (block 1108). Also, the processor 26 causes the software implementation of the bug detector 10 to be integrated with the example multi-threaded program to form the integrated program code (block 1108). The processor 26 executes the integrated program code (block 1110), and if the bug is present in the example multi-threaded program, and provided that the CPE has been designed properly, the bug is detected.
Example machine readable instructions 1200A, 1200B, 1200C, 1200D, 1200E, and 1200F that may be executed by a bug detector 10 to detect a bug using the example expressions illustrated in
If P1 has been satisfied (block 1206A), the example controller 12 causes a thread controller 16 to stall thread1 for a threshold amount of time (block 1210A) and causes the example thread monitor 14 to begin monitoring thread2 at a block 1212A until L2 is reached. The example controller 12 invokes P2 (block 1214A) and causes the example tester 20 to determine whether P2 has been satisfied (block 1216A). If P2 has not been satisfied, no bug has been detected and the example controller 12 causes the bug detector 10 to cease operating (block 1208A). If P2 has been satisfied, the example controller 12 causes the example message generator 22 to generate an error message indicating that the bug has been detected (block 1218A).
The CPE 402 of
The CPE 502 of
Example machine readable instructions 1200B that may be executed to implement a bug detector 10 that evaluates the example expression e2 (P1&&P2) of
If P1 is not satisfied (block 1206B), then P1>&P2 is not satisfied and the flag “P1>&P2” is set to zero (block 1208B). The example controller 12 begins to monitor thread2 (block 1222B) until L2 is reached at which time the example controller 12 invokes P2 (block 1224B). The example controller 12 causes the example tester 20 to determine whether P2 is satisfied (block 1226B). If P2 is not satisfied (block 1226B), then P2>&P1 is not satisfied and a flag “P2>&P1” is set to zero (block 1228B). If P2 is satisfied (block 1226B), the example controller 12 causes the example thread controller 16 to stall thread2 (block 1230B) and begin monitoring thread1 until L1 is reached (block 1232B). The example controller 12 invokes P1 (block 1234B) and the example controller 12 causes the example tester 20 to determine whether P1 is satisfied (block 1236B). If P1 is not satisfied, then P2>&P1 has not been satisfied and the flag “P2>&P1” is set to zero (block 1228B) as described above. If P1 is satisfied (block 1236B), then P2>&P1 has been satisfied and the flag “P2>&P1” is set to one (block 1238B).
The example controller 12 causes the example tester to use the flag “P1>&P2” and the flag “P2>&P1” to determine whether the expression (P1&&P2) has been satisfied (block 1240B). If either of the flags “P1>&P2” or “P2>&P1” are set to one then the expression (P1&&P2) is satisfied and the example controller 12 causes the example message generator 22 to generate the error message (block 1242B). If neither of the flags “P1>&P2” or “P2>&P1” are set to one when tested (block 1240B), the example controller 12 determines whether a threshold amount of time “t” has elapsed (block 1244B). If the threshold amount of time “t” has not yet elapsed (block 1244B), then the example controller 12 again tests to determine whether the expression (P1&&P2) has been satisfied (block 1240B). If the threshold amount of time “t” has elapsed (block 1244B), then the bug has not been detected and the example controller 12 causes the bug detector 10 to cease operating (block 1246B).
Example machine readable instructions 1200C that may be executed to implement the CPE evaluating the example expression e3 (P1∥P2) of
As described above, the CPE 602 of
Example machine readable instructions 1200D that may be executed to implement the example expression e4 (P1>P2) of
Example machine readable instructions 1200E that may be executed to implement the example expression e5 (P^n) of
Example machine readable instructions 1200F that may be executed to implement the example expression e6 (P#n) of
Example machine readable instructions 1300 that may be executed to implement the bug detector 10 of
Example machine readable instructions 1400 that may be executed by the processor 26 of
The machine readable instructions represented in the flowchart of
In the illustrated example of
The flowchart of
The processor 26 increments the number of times P1 has been satisfied by one each time the condition associated with P1 has been satisfied and waits a threshold amount of time for either (P1^2>&P2^3) to be satisfied or for (P2^3>&P1^2) to be satisfied as described above with reference to lines I9-I13 of
The processor 26 increments the number of times P2 has been satisfied by one each time the condition associated with P2 has been satisfied and then waits a threshold amount of time for either (P1^2>&P2^3) to be satisfied or for (P2^3>&P1^2) to be satisfied as described above with reference to the lines I14-I17 of the pseudocode of
The processor 26 increments the variable “num_repeats” by one each time the expression (P1^2 && P2^3) is satisfied as described above with reference to the lines I18-I19 of the pseudocode of
The system 1600 of the instant example includes a processor 1612 such as a general purpose programmable processor. The example processor 1612 includes a local memory 1614, and executes coded instructions 1616 present in the local memory 1614 and/or in another memory device. The example processor 1612 may execute, among other things, the machine readable instructions represented in
The example processor 1612 is in communication with a main memory including a volatile memory 1618 and a non-volatile memory 1620 via a bus 1622. The volatile memory 1618 may be implemented by Static Random Access Memory (SRAM), Synchronous Dynamic Random Access Memory (SDRAM), Dynamic Random Access Memory (DRAM), RAMBUS Dynamic Random Access Memory (RDRAM) and/or any other type of random access memory device. The non-volatile memory 1620 may be implemented by flash memory and/or any other desired type of memory device. Access to the main memory 1618, 1620 is typically controlled by a memory controller (not shown).
The processing system 1600 also includes an interface circuit 1624. The interface circuit 1624 may be implemented by any type of interface standard, such as an Ethernet interface, a universal serial bus (USB), and/or a PCI express interface.
One or more input devices 1626 are connected to the interface circuit 1624. The input device(s) 1626 permit a user to enter data and commands into the example processor 1612. The input device(s) can be implemented by, for example, a keyboard, a mouse, a touchscreen, a track-pad, a trackball, an isopoint and/or a voice recognition system.
One or more output devices 1628 are also connected to the interface circuit 1624. The output devices 1628 can be implemented, for example, by display devices (e.g., a liquid crystal display, a cathode ray tube display (CRT)), by a printer and/or by speakers. The interface circuit 1624, thus, typically includes a graphics driver card.
The interface circuit 1624 also includes a communication device such as a modem or network interface card to facilitate exchange of data with external computers via a network (e.g., an Ethernet connection, a digital subscriber line (DSL), a telephone line, coaxial cable, a cellular telephone system, etc.).
The processing system 1600 also includes one or more mass storage devices 1630 for storing machine readable instructions and data. Examples of such mass storage devices 1630 include floppy disk drives, hard drive disks, compact disk drives and digital versatile disk (DVD) drives. In some examples, the mass storage device 1630 may implement the memories 24 and 27.
The coded instructions 1632 of
As an alternative to implementing the methods and/or apparatus described herein in a system such as the processing system of
An example method to detect a concurrency bug in a multi-threaded program disclosed herein includes defining, with a processor, a data type. In some examples, the data type comprises a first predicate and a second predicate. The first predicate is associated with a first thread of the multi-threaded program and the second predicate is associated with a second thread of the multi-threaded program. In some examples, the first predicate is associated with a first condition and the second predicate being associated with a second condition. In some examples, the data type comprises an expression that defines a relationship between the first predicate and the second predicate and the relationship, when satisfied, causes the concurrency bug to be detected. In some example methods the relationship specifies an order in which the first predicate and the second predicate are to be satisfied.
Some example methods also include defining a concurrency bug detector conforming to the data type, where defining the concurrency bug detector includes defining the first condition and the second condition. Some example methods also include inserting a first program instruction invoking the first predicate into the first thread, and inserting a second program instruction invoking the second predicate into the second thread. The first program instruction can be inserted at a first location in the first thread and the second program instruction can be inserted at a second location in the second thread. In some example methods, when the first program instruction and the second program instruction are invoked, one or more operations of the first thread are interleaved relative to the one or more operations of the second thread.
Some example methods also include defining a set of parameters associated with the concurrency bug detector. In some example methods, the expression can be a predicate expression, the first condition can be a plurality of first conditions, and the first predicate can be associated with a condition expression that defines a relationship between the plurality of first conditions.
In some example methods the data type can include a name of the concurrency bug that includes an error message generated when the concurrency bug is detected. The error message can identify, at least one of a first location in the first thread or a second location in the second thread. Some example methods, also include extending a compiler to generate an implementation of a concurrency bug detector conforming to the data type and in some example methods, the first condition and the second condition are the same condition.
An example apparatus to detect a concurrency bug in a multi-threaded program disclosed herein includes a processor, and a memory to store machine readable instructions and a data type. In some examples, the data type includes a first program instruction that invokes a first predicate specifying a first condition and includes a second program instruction that invokes a second predicate specifying a second condition. In some examples, the data type can also include an expression that defines a relationship between the first predicate and the second predicate and a set of parameters associated with the data type that are globally defined within the multi-threaded program. The first program instruction can be inserted into a first thread of the multi-threaded program and the second program instruction can be inserted into a second thread of the multi-threaded program. In some examples, the example apparatus also includes an invoker to invoke the first program instruction and the second program instruction, and a tester to determine whether the first predicate and the second predicate are satisfied when invoked by the invoker.
In some examples the apparatus can include a tester to determine whether the expression is satisfied and a thread controller to control operations of a first thread of the multi-threaded program relative to the operations of a second thread of the multi-threaded program.
In some examples the apparatus cam include a thread monitor to monitor a first thread of the multi-threaded program for the first program instruction and to monitor a second thread of the multi-threaded program for the second program instruction. The apparatus can also include a message generator to generate an error message if the tester determines that the expression is satisfied. The error message can identify at least one of a first location in the first thread or a second location in the second thread.
In some examples disclosed herein a machine readable storage medium includes instructions that cause a machine to at least recognize a concurrency bug detector conforming to a data type. The data type can be defined to include a first predicate associated with a first thread of a multi-threaded program and can be associated with a first condition and a can be defined to include a second predicate associated with a second thread of the multi-threaded program and the second thread being associated with a second condition. In some examples, the data type also includes an expression that defines a relationship between the first predicate and the second predicate. In some examples, the machine readable instructions can also cause the machine to determine whether the first predicate and second predicates are satisfied and determine whether the expression is satisfied. In some examples, the data type further includes an error message and the instructions further cause the machine to generate an error message when the expression is satisfied.
Finally, although certain example methods, apparatus and articles of manufacture have been described herein, the scope of coverage of this patent is not limited thereto. On the contrary, this patent covers all methods, apparatus and articles of manufacture fairly falling within the scope of the claims of the patent either literally or under the doctrine of equivalents.
Wu, Youfeng, Gottschlich, Justin E., Adl-Tabatabai, Ali-Reza, Hu, Shiliang, Pereira, Cristiano L., Pokam, Gilles
Patent | Priority | Assignee | Title |
10191834, | Dec 21 2011 | Intel Corporation | Methods and systems to identify and reproduce concurrency violations in multi-threaded programs |
10725889, | Aug 28 2013 | MICRO FOCUS LLC | Testing multi-threaded applications |
Patent | Priority | Assignee | Title |
6009269, | Mar 10 1997 | Hewlett Packard Enterprise Development LP | Detecting concurrency errors in multi-threaded programs |
7100157, | Sep 24 2002 | Intel Corporation | Methods and apparatus to avoid dynamic micro-architectural penalties in an in-order processor |
7539979, | Mar 31 2008 | AIRBNB, INC | Method and system for forcing context-switch during mid-access to non-atomic variables |
7673181, | Jun 07 2006 | CA, INC | Detecting race conditions in computer programs |
8510722, | Jul 22 2009 | Microsoft Technology Licensing, LLC | Detecting data race and atomicity violation via typestate-guided static analysis |
8572606, | Dec 29 2005 | VMware, Inc. | System and method for avoiding synchronization bugs through virtualization |
20020035722, | |||
20030233394, | |||
20050102681, | |||
20060161897, | |||
20070168986, | |||
20080201629, | |||
20080250422, | |||
20090100432, | |||
20090113399, | |||
20090125887, | |||
20090178044, | |||
20100050161, | |||
20100070955, | |||
20100131931, | |||
20110022893, | |||
20110131550, | |||
20110161590, | |||
20110173422, | |||
20110173592, | |||
20110258421, | |||
20120017221, | |||
20120144372, | |||
20120174074, | |||
20120204062, | |||
20120204154, | |||
20130031531, | |||
20130097136, | |||
20140108860, | |||
20140115604, | |||
20140282423, |
Executed on | Assignor | Assignee | Conveyance | Frame | Reel | Doc |
Jun 27 2012 | Intel Corporation | (assignment on the face of the patent) | / | |||
Nov 27 2012 | WU, YOUFENG | Intel Corporation | ASSIGNMENT OF ASSIGNORS INTEREST SEE DOCUMENT FOR DETAILS | 030143 | /0844 | |
Nov 27 2012 | POKAM, GILLES A | Intel Corporation | ASSIGNMENT OF ASSIGNORS INTEREST SEE DOCUMENT FOR DETAILS | 030143 | /0844 | |
Nov 27 2012 | PEREIRA, CRISTIANO L | Intel Corporation | ASSIGNMENT OF ASSIGNORS INTEREST SEE DOCUMENT FOR DETAILS | 030143 | /0844 | |
Nov 28 2012 | GOTTSCHLICH, JUSTIN E | Intel Corporation | ASSIGNMENT OF ASSIGNORS INTEREST SEE DOCUMENT FOR DETAILS | 030143 | /0844 | |
Nov 29 2012 | ADL-TABATABAI, ALI-REZA | Intel Corporation | ASSIGNMENT OF ASSIGNORS INTEREST SEE DOCUMENT FOR DETAILS | 030143 | /0844 | |
Jan 15 2013 | HU, SHILIANG | Intel Corporation | ASSIGNMENT OF ASSIGNORS INTEREST SEE DOCUMENT FOR DETAILS | 030143 | /0844 |
Date | Maintenance Fee Events |
May 06 2019 | REM: Maintenance Fee Reminder Mailed. |
Oct 21 2019 | EXP: Patent Expired for Failure to Pay Maintenance Fees. |
Date | Maintenance Schedule |
Sep 15 2018 | 4 years fee payment window open |
Mar 15 2019 | 6 months grace period start (w surcharge) |
Sep 15 2019 | patent expiry (for year 4) |
Sep 15 2021 | 2 years to revive unintentionally abandoned end. (for year 4) |
Sep 15 2022 | 8 years fee payment window open |
Mar 15 2023 | 6 months grace period start (w surcharge) |
Sep 15 2023 | patent expiry (for year 8) |
Sep 15 2025 | 2 years to revive unintentionally abandoned end. (for year 8) |
Sep 15 2026 | 12 years fee payment window open |
Mar 15 2027 | 6 months grace period start (w surcharge) |
Sep 15 2027 | patent expiry (for year 12) |
Sep 15 2029 | 2 years to revive unintentionally abandoned end. (for year 12) |