Você está na página 1de 11

Runtime Model Checking of Multithreaded C/C++ Programs

Yu Yang Xiaofang Chen Ganesh Gopalakrishnan Robert M. Kirby


School of Computing, University of Utah
Salt Lake City, UT 84112, U.S.A.

ABSTRACT Eraser[2] and Helgrind[3] are two examples of data race


We present inspect, a tool for model checking safety prop- detectors that dynamically track the set of locks held by
erties of multithreaded C/C++ programs where threads in- shared objects during program execution. They use locksets
teract through shared variables and synchronization primi- to compute the intersection of all locks held when accessing
tives. The given program is mechanically transformed into shared objects. Shared object accesses that have an empty
an instrumented version that yields control to a centralized lockset intersection will be reported as being inconsistently
scheduler around each such interaction. The scheduler first protected, and as a result, potentially cause data races. Choi
enables an arbitrary execution. It then explores alternative et al.[4] improve the Eraser algorithm by avoiding redun-
interleavings of the program. It avoids redundancy explo- dant analysis. Eraser (and similar tools) have been very
ration through dynamic partial order reduction(DPOR) [1]. successful in finding potential data races in multithreaded
Our initial experience shows that inspect is effective in test- programs. However, as these tools try to detect potential
ing and debugging multithreaded C/C++ programs. We are data races by inferring them based on one feasible execution
not aware of DPOR having been implemented in such a set- path, there is no guarantee that the program is free from
ting. With inspect, we have been able to find many bugs data races if no error is reported (i.e., full coverage is not
in real applications. guaranteed). Besides, these tools can generate many false
warnings. As these tools are designed for identifying data
races only, they are not capable of detecting other safety
Categories and Subject Descriptors violations such as deadlocks.
D.2.4 [Software Engineering]: Software/Program Verifi- Tools such as RacerX[5], ESC/Java[6], and LockSmith[7]
cation; D.2.5 [Software Engineering]: Testing and De- detect potential errors in the programs by statically ana-
bugging lyzing the source code. Since they do not get the benefit
of analyzing concrete executions, the false warning rates of
these tools can be high. They also provide no guarantee of
General Terms full coverage.
Verification, Threading Traditional model checking can guarantee complete cover-
age, but on extracted finite state models (e.g., [8, 9, 10, 11])
Keywords or in the context of languages whose interpreters can be eas-
ily modified for backtracking (e.g., [12]). However, as far as
dynamic partial order reduction, multithreaded, C/C++ we know, none of these model checkers can easily check (or
be easily adapted to check) general application-level multi-
1. INTRODUCTION threaded C/C++ programs. For instance, if we want to fol-
Writing correct multithreaded programs is difficult. Many low Java PathFinder’s [12] approach to check multithreaded
“unexpected” thread interactions can only be manifested C/C++ programs, we will have to build a virtual machine
with intricate low-probability event sequences. As a result, that can handle C/C++ programs. This is very involved.
they often escape conventional testing, and manifest years Model checkers like Bogor[9], Spin[13], Zing [11], etc. im-
after code deployment. Many tools have been designed to plicitly or explicitly extract a model out of the source code
address this problem. They can be generally classified into before model checking. However, modeling library functions
three categories: dynamic detection, static analysis, and and the runtime environment of C/C++ programs is very
model checking. involved as well as error-prone: the gap between modeling
languages and programming languages is unbridgeably large
in many cases.
Blast[8] and Magic[10] use predicate abstraction and re-
finement technique to verify concurrent programs. Blast can
Permission to make digital or hard copies of all or part of this work for be used to detect data races in nesC[14] programs. Magic
personal or classroom use is granted without fee provided that copies are focuses on detecting errors in concurrent programs that com-
not made or distributed for profit or commercial advantage and that copies municate via message passing. Again, writing correct library
bear this notice and the full citation on the first page. To copy otherwise, to
republish, to post on servers or to redistribute to lists, requires prior specific
function stubs is a problem for these model checkers. Adapt-
permission and/or a fee. ing these ideas to real-world C/C++ programs is also very
Copyright 200X ACM X-XXXXX-XX-X/XX/XX ...$5.00.
re-run the program until all
thread library interleavings are explored
wrapper

executable
re q u e st
Multithreaded thread1 /p e r mit
Instrumented Scheduler
C/C++ Source Code compile with report
programs thread2
programs Transformer request/permit dynamic partial errors
... r mit order reduction
st / p e
threadn r eque

Figure 1: Inspect’s workflow

difficult. that can efficiently check multithreaded C/C++ pro-


To the best of the authors’ knowledge, Verisoft [15] is the grams. Inspect not only supports mutual exclusive
only model checker that is able to check concurrent C/C++ locks, but also wait/signal, and read/write locks. The
programs without incurring modeling overheads. Unfortu- ability to model check programs containing these con-
nately, Verisoft focuses on concurrent programs that interact structs makes inspect a unique tool.
only through inter-process communication mechanisms. In
a real-world multithreaded program, the threads can affect • The in situ model checking capability runs the actual
each other not only through explicit synchronization/mutual code, and not a formal model thereof. This eliminates
exclusion primitives, but also through read/write operations the tedium of model extraction, and lends itself to an
on shared data objects. adaptation of the DPOR algorithm. Such a DPOR
To address these problems, we designed inspect, a run- algorithm has, previously, not been implemented in
time model checker for systematically exploring all possible the context of debugging large C/C++ programs that
interleavings of a multithreaded C/C++ program under a communicate using shared memory in a general way.
specific testing scenario. In other words, the reactive pro- • We have evaluated inspect on a set of benchmarks
gram under test is closed by providing a test driver, and and confirmed its efficacy in detecting bugs.
inspect examines all thread interleavings under this driver.
An overview of inspect is shown in Figure 1. It consists • We have designed and implemented an algorithm to
of three parts: a source code transformer to instrument the automate the source code instrumentation for runtime
program at the source code level, a thread library wrapper model checking.
that helps intercept the thread library calls, and a central-
• Since inspect employs stateless search, it relies on
ized scheduler that schedules the interleaved executions of
re-execution to pursue alternate interleavings. How-
the threads. Given a multithreaded program, inspect first
ever, during re-execution, the runtime environment of
instruments the program with code that is used to commu-
the threaded code can change. This can result in the
nicate with the scheduler. Then inspect compiles the pro-
threads not being allotted the same id by the operating
gram into an executable and runs the executable repeatedly
system. Also, dynamically-created shared objects may
under the control of the scheduler until all relevant interleav-
not reside in the same physical memory address in dif-
ings among the threads are explored. Before performing any
ferent runs. We have suitably address these challenges
operation that might have side effects on other threads, the
in our work.
instrumented program sends a request to the scheduler. The
scheduler can block the requester by postponing a reply. We • We employ lock-sets, and additionally sleep-sets (the
use blocking sockets as communication channels between the latter is recommended in [1]), to eliminate redundant
threads and the scheduler. As the number of possible inter- backtrack points during DPOR.
leavings grows exponentially with the size of the program,
we implemented an adaption of the dynamic partial order 2. BACKGROUND
reduction (DPOR [1]) algorithm proposed by Flanagan and
Godefroid to reduce the search space. Such an implementa- 2.1 Multithreaded Programs in C/C++
tion of DPOR in the context of threaded C/C++ programs
Threading is not part of the C/C++ language specifica-
is our first key contribution. Demonstrating the ability of
tion. Instead, it is supported by add-on libraries. Among
inspect to find bugs in medium-sized public-domain appli-
many implementations of threading, POSIX threads [16] are
cations is our second key contribution.
perhaps the most widely used.
Inspect can check application-level C/C++ programs that
Mutex and condition variable are two common data struc-
use POSIX threads[16]. Inspect supports not only mu-
tures for communication between threads. Mutexes are used
tual exclusive lock/unlock, but operations on condition vari-
to give threads exclusive access to critical sections. Condi-
ables, including wait, signal and broadcast. The errors that
tion variables are used for synchronization between threads.
inspect can detect include data races, deadlocks, and in-
Each condition variable is always used together with an as-
correct usages of thread library routines. When an error is
sociated mutex. When a thread requires a particular condi-
located, inspect reports the error along with the trace that
tion to be true before it can proceed, it waits on the asso-
leads to the error, which facilities debugging. The key fea-
ciated condition variable. By waiting, it gives up the lock
tures of our work, and some of the challenges we overcame
and blocks itself. The operations of releasing the lock and
are as follows:
blocking the thread should be atomic. Any thread that sub-
• We design inspect, an in situ runtime model checker, sequently causes the condition to be true may then use the
condition variable to notify a thread waiting for the condi- 2.3 Runtime Model Checking
tion. A thread that has been notified regains the lock and Model checking is a technique for verifying a transition
can then proceed. 1 system by exploring its state space. Cycles in the state
The POSIX thread library also provides read-write locks space are detected by checking whether a state has been
and barriers. A read-write lock allows concurrent read ac- visited before or not. Usually the visited states information
cess to an object but requires exclusive access for write oper- is stored in a hash table. Runtime model checkers explore
ations. Barrier provides explicit synchronization for a set of the state space by executing the program concretely and
threads. As barriers seem not to be frequently used in mul- observing its visible operations. Runtime model checkers do
tithreaded programs (we did not encounter any uses, except not keep the search history because it is not easy to capture
in some tutorials), we do not consider them here. and restore the state of a program which runs concretely. As
a result, runtime model checkers are not capable of checking
2.2 Formal Model of Multithreaded Programs programs that have cyclic state spaces.
A multithreaded program can be modeled as a concurrent Inspect follows the common design principles of a run-
system, which consists of a finite set of threads, and a set time model checker, and uses a depth-first strategy to ex-
of shared objects. Shared objects include mutexes, condi- plore the state space. As a result, inspect can only handle
tion variables, read/write locks, and data objects. The state programs that can terminate in a finite number of steps.
of a mutex, M utexes, can be captured as a function from Fortunately the execution of many multithreaded programs
the mutex id(M utexId) to the thread(T id) which holds the terminates eventually. 2
mutex, and the set of threads(2T id ) that are waiting for
acquiring the mutex. A condition variable(Conds) can be 2.4 Dynamic Partial Order Reduction
modeled in terms of the associated mutexes, along with the Partial order reduction (POR) techniques[17] are those
set of threads that are waiting on the condition variable. A that avoid interleaving independent transtions during search.
read-write lock can be modeled in terms of a writer thread Given a state s and a transition t, let t.tid denote the iden-
or a set of read threads, along with a set of threads waiting tity of the thread that executes t, and next(s, t) refer to the
for read, and the other set of threads waiting for write. We state which is reached from s by executing t. Let s.enabled
denote this as Rwlocks. denote the set of transitions that are enabled from s, and
s.sleep (sleep sets [18]) the set of transitions that are en-
ObjId, T id ⊂ N abled in s but will not be executed from s (because doing so
M utexId ⊂ ObjId would only interleave independent transitions). A thread p
M utexes = M utexId → T id × 2T id is enabled in a state s if there exists transition t such that
Conds = ObjId → M utexId × 2T id t ∈ s.enabled and t.tid = p. Let s.backtrack be the back-
Rwlocks = ObjId → T id × 2T id × 2T id × 2T id track set at state s (Figure 2). {t | t.tid ∈ s.backtrack} is
the set of transitions which are enabled but have not been
Threads communicate with each other only through shared executed from s. Let s.done be the set of threads exam-
objects. Operations on shared objects are called visible oper- ined at s, and let {t | t.tid ∈ s.done} be the set of tran-
ations, while the rest are invisible operations. The execution sitions that have been executed from s. Given the set of
of an operation is said to block if it results in putting the enabled transitions from a state s, partial order reduction
calling threads into the waiting queue of a mutex, a condi- algorithms try to explore only a (proper) subset of s.enabled,
tion variable, or a read/write lock. We assume that only the and at the same time guarantee that the properties of inter-
following can block a thread: visible operations on acquir- est will be preserved. Such a subset is called persistent set,
ing a mutex; acquiring a read/write lock; or waiting for a i.e. s.persistent.
condition variable signal. In a finite transition sequence T = t1 t2 ...tn , we say ti
A state of a multithreaded program consists of the global happens before tj if i < j in every member of the equivalence
state Global of all shared objects, and the local state Local of class (Mazurkeiwitz trace set) of T obtained by permuting
each thread. Global includes M utexes, Conds, and Rwlocks. independent transitions [18].
Static POR algorithms compute the persistent set of a
Locals = T id → Local
state s immediately after reaching it. In our context, per-
State = Global × Locals
P rogram = (State, s0 , ∆) sistent sets computed statically will be excessively large be-
∆ = State → State cause of the limitations of static analysis. For instance, if
two transitions leading out of s access an array a[] by in-
A transition moves the program from one state to the dexing it at locations captured by expressions e1 and e2
next state, by performing one visible operation of a certain (i.e., a[e1] and a[e2]), a static analyzer may not be able to
thread, followed by a finite sequence of invisible operations, decide whether e1=e2. Flanagan and Godefroid introduced
ending just before the next visible operation of that thread. dynamic partial-order reduction (DPOR) [1] to dynamically
A multithreaded program as a whole is denoted as P rogram, compute smaller persistent sets (smaller persistent sets are
which is a triplet: the state space State, the initial state s0 , almost always better).
and the transition relation ∆. In DPOR, given a state s, s.persistent is not computed
1
immediately after reaching s. Instead, DPOR explores the
POSIX threads have condition wait and signal rou- states that can be reached from s with depth-first search,
tines named pthread cond wait, pthread cond signal,
pthread cond broadcast. On Microsoft Windows plat- and dynamically computes s.persistent. Assume t ∈ s.enabled
forms, the correspondent APIs of the same semantics
2
are SleepConditionVariableCS, WakeConditionVariable If termination is not guaranteed, inspect can still work by
and WakeAllConditionVariable. depth-bounding the search.
1: StateStack S; In this section we consider the following example, which
2: TransitionSequence T ; captures a common concurrent scenario in database systems:
3: Transition t; Suppose that a shared database supports two distinct classes
of operations, A and B. The semantics of the two operations
4: DPOR( ) { allow multiple operations of the same class to run concur-
5: State s = S.top; rently, but operations belongs to different classes cannot be
6: update backtrack info(s); run concurrently. Figure 3 is an implementation that at-
7: if (∃ thread p, ∃t ∈ s.enabled, t.tid = p) { tempts to solve this problem. a count and b count are the
8: s.backtrack = {p}; number of threads that are performing operations A and B
9: s.done = ∅; respectively. Here, lock is used for the mutual exclusion
10: while (∃q ∈ s.backtrack) { between threads, and mutex is used for the mutual exclu-
11: s.done = s.done ∪ {q}; sion between threads of the same class. Could this code
12: s.backtrack = s.backtrack \ {q}; deadlock?
13: let tn ∈ s.enabled, tn .tid = q;
14: T.append(tn ); shared variables among threads:
15: S.push(next(s, tn ));
pthread_mutex_t mutex, lock;
16: DPOR();
int a_count = 0, b_count = 0;
17: T.pop back();
18: S.pop(); class A operation:
19: }
20: } 1: pthread_mutex_lock(&mutex);
21: } 2: a_count++;
3: if (a_count == 1) {
22: update backtrack info(State s) { 4: pthred_mutex_lock(&lock);
23: for each thread p { 5: }
24: let tn ∈ s.enabled, tn .tid = p; 6: pthread_mutex_unlock(&mutex);
25: td = the latest transition in T that dependent 7: performing class A operation;
and may be co-enabled with tn ; 8: pthread_mutex_lock(&mutex);
26: if (td 6= null) { 9: a_count--;
27: sd = the state in S from which td is executed; 10: if (a_count == 0){
28: E = {q ∈ sd .enabled | q = p or ∃tj ∈ T, tj 11: pthread_mutex_unlock(&lock);
happened after td , and is dependent with 12: }
some transition of process p in T that is 13: pthread_mutex_unlock(&mutex);
happened after tj }
29: if (E 6= ∅) class B operation:
30: add any q ∈ E to sd .backtrack 1: pthread_mutex_lock(&mutex);
31: else 2: b_count++;
32: add all enabled threads to sd .backtrack; 3: if (b_count == 1){
33: } 4 pthred_mutex_lock(&lock);
34: } 5: }
35: } 6: pthread_mutex_unlock(&mutex);
7: performing class B operation;
8: pthread_mutex_lock(&mutex);
Figure 2: Dynamic partial-order reduction
9: b_count--;
10: if (b_count == 0){
11: pthread_mutex_unlock(&lock);
is the transition which the model checker chose to execute, 12: }
and t′ is a transition that can be enabled with DFS from s 13: pthread_mutex_unlock(&mutex);
by executing t. For each to-be-executed transition t′ , DPOR
will check whether t′ and t are dependent and can be enabled
concurrently (i.e. co-enabled). If t′ and t are dependent and Figure 3: An example on concurrent operations in
can be co-enabled, t′ .tid will be added to the s.backtrack. a shared database
Later, when backtracking during DFS, if a state s is found
with non-empty s.backtrack, DPOR will pick one transition Conventional testing might miss the error as it runs with
t such that t ∈ s.enabled and t.tid ∈ s.backtrack, and ex- random scheduling. In general it is difficult to get a spe-
plore a new branch of the state space by executing t. Figure cific scheduling that will lead to the error. To systemati-
2 recapitulates the DPOR algorithm (this is the same as the cally explore all possible interleavings, inspect needs to take
one given, as well as proved correct in [1]; we merely sim- the control of scheduling away from the operating system.
plified some notations). In Section 4 we show how to adapt We do this by instrumenting the program with code that is
the DPOR algorithm for checking multithreaded C/C++ used to communicate with a central scheduler. As only vis-
programs. ible operations in one thread can have side effects on other
threads, we only need to instrument before each visible oper-
ation is performed. The instrumented code sends a request
3. AN EXAMPLE to the scheduler. The scheduler can then decide whether
the request should be granted permission immediately, or (thread a) ⇒ a1 : acquire mutex
a2 : count a + +
be delayed. The scheduler works as an external observer.
a3 : count a == 1
In addition, it needs to be notified about the occurrences a4 : acquire lock
of the thread start, join, and exit events. Figure 4 shows a5 : release mutex
the code after instrumentation for threads that performs ← ({b}, {a})
class A operations. As shown in the figure, each call to the a6 : acquire mutex
pthread library routines is replaced with a wrapper routine. a7 : count a − −
a8 : count a == 0
As a count is a shared variable that multiple threads can
a9 : release lock
access, we insert a read/write request to the scheduler be- a10 : release mutex
fore each access of a count. A call to inspect thread start (thread b) ⇒ b1 : acquire mutex
is inserted at the entry of the thread to notify that a new b2 : count b + +
thread is started. Similarly, a call to inspect thread end is b3 : count b == 1
inserted at the end of the thread routine. b4 : acquire lock
b5 : release mutex
inspect_thread_start(); b6 : acquire mutex
... b7 : count b − −
inspect_mutex_lock(&mutex); b8 : count b == 0
b9 : release lock
inspect_obj_write( (void*)&a_count ); b10 : release mutex
a_count++;
inspect_obj_read( (void*)&a_count );
if (a_count == 1) Figure 5: A possible interleaving of one class A
inspect_mutex_lock(&lock); thread and one class B thread
inspect_mutex_unlock(&mutex);
...
inspect_mutex_lock(&mutex); the transition in the backtrack set to start exploring another
inspect_obj_write( (void*)&a_count ); branch of the state space. It, however, accomplishes this by
a_count--; marking that such a branch must be tried (the actual explo-
inspect_obj_read( (void*)&a_count ); ration is done following a re-execution from the initial state).
if (a_count == 0) In this example, (i) inspect will first re-execute the instru-
inspect_mutex_unlock(&lock); mented program and allow thread a run through a1 − a5 .
inspect_mutex_unlock(&mutex); (ii) now, since the backtrack set contains b, the scheduler
... will block thread a until thread b has performed the visible
inspect_thread_end(); operation b1 . Then it will allow thread a and thread b run
randomly until all threads ends. In our example, we will ob-
serve the following alternate sequence of visible operations
generated as a result of re-execution:
Figure 4: Instrumented code for class A threads
shown in Figure 3 ← ({b}, {a})
(thread a) ⇒ a1 : acquire mutex
After compiling the instrumented program, inspect ob- a2 : count a + +
tains an executable that can be run under the central sched- a3 : count a == 1
a4 : acquire lock
uler’s control and monitoring. Firstly inspect lets the pro-
a5 : release mutex
gram run randomly and collects a sequence of visible oper- (thread b) ⇒ b1 : acquire mutex
ations, which reflects a random interleaving of threads. If it b2 : count b + +
happens that an interleaving that can lead to errors, these b3 : count b == 1
errors will be reported immediately. (When inspect en-
counters an error, it does not stop immediately. Section 4.6 After thread b performs the visible operation b3 , thread
presents the details.) Otherwise, inspect will try to find a a is trying to acquire mutex which is held by thread b; at
backtrack point out of the trace (as described in Figure 2), the same time, thread b is waiting to acquire lock, which
and begins monitoring the executable runs, now obtained is held by thread a. Inspect will report this deadlock sce-
through another interleaving. nario at this point, and start backtracking. As for the new
Assume the program shown in Figure 3 has only one class interleaving, b1 may happen before a1 , inspect will start
A thread, and one class B thread. In the first run of the in- another backtracking by having b1 execute first. In general,
strumented program, inspect may observe the visible oper- inspect will continue the process of finding the backtrack
ation sequence shown in Figure 5, beginning at “(thread a)” point and re-running the program under test until there are
(which does not contain any errors). no backtrack points in the search stack.
While observing the random visible operation sequence,
inspect will, for each visible operation, update the back-
track set for each state in the search stack. In the above 4. ALGORITHMS
trace, as event b1 may happen before a6 , and b1 and a6 are
both lock acquire operation on shared object mutex, inspect 4.1 Identifying threads and shared objects out
will put the backtracking information after event a5 (i.e., of multiple runs
just before a6 ). Before inspect backtracks from a state, When inspect runs the program under test repeatedly, as
if its backtrack set is not empty, inspect will try to enable the runtime environment may change across re-executions,
each thread may not be allocated to the same id by the oper- inspect cond wait(cond, mutex) {
ating system. Also, dynamically-created shared objects may send pre wait request;
not reside in the same physical memory address in different receive pre wait permit;
runs. receive unblocking permit;
One observation about thread execution is this: given the send post wait request;
same external inputs, if the two runs of a thread program receive post wait permit;
generate the same sequence of visible operations, then the }
constituent threads in this program should be created in the
same order. Banking on this fact, we can identify threads
(which may be allocated different thread IDs in various re- Figure 6: Wapper function for pthread cond wait
executions) across two different runs by examining the se-
quence of thread creations. In our implementation, we make
each thread register itself in a mapping table, from system- invoked, the calling thread t first sends a pre wait request
allocated thread ids to integers. If the threads are created to the scheduler; the scheduler records that t releases the
in the same sequential order in different runs, each thread mutex, and set t’s status as blocking. The scheduler will not
will be assigned to the same id by this table. In the same send an “unblocking” permit to t until some other threads
manner, if two runs of the program have the same visible send out a related signal and t is picked out by the scheduler
operation sequence, the shared objects will also be created from the waiting queue. In t, after receiving the unblocking
with malloc, etc., in the same sequence. As a result, the permit from the scheduler, t will send a post wait request
same shared objects between multiple runs can be recog- to acquire the mutex.
nized in a similar way as threads.
4.4 Avoiding Redundant Backtracking
4.2 Communicating with the scheduler Following [1], we assume that two transitions t1 and t2 are
dependent if and only if they access the same communication
As explained before, inspect works by having the instru-
object. We treat wait and signal operations on the same
mented threads calling the scheduler before executing visible
condition variable as dependent transitions. Also, a thread
operations, and moving forward only based on the permis-
join operation is dependent with the correspondent thread
sions being granted by the scheduler. The requests that
exit.
a thread can send to the scheduler can be classified into
In runtime model checking, backtracking is an expensive
four classes: 1) thread-management related events, includ-
operation as we need to restart the program, and replay the
ing thread creation, thread destruction, thread join, etc.; 2)
program from the initial state until the backtrack point. To
mutex-events, include mutex init, destroy, acquire, release;
improve efficiency, we want to avoid backtracking as much
3) read-write lock init, destroy, reader lock, writer lock,and
as possible. Line 21 in Figure 2 is the place in DPOR where
unlock operations; 4) cond-related events, include condition
a backtrack point is identified. It treats td , which is depen-
variable creation, destroy, wait, signal, broadcast; and 5)
dent and may-be co-enabled with tn as a backtrack point.
data object related events, include the creation of the data
However, if two transitions that may be co-enabled are never
object, read and write operations.
co-enabled, we may end up exploring redundant backtrack-
ings, and reduce the efficiency of DPOR. Our two solutions
4.3 Handling wait and signal – the use of locksets, and the use of sleep sets – are now
Condition variable and the related wait/signal/broadcast discussed.
routines are a necessity in many threading libraries. The We use lockset to eliminate exploring transitions pairs
wait/signal routines usually obey the following rules: (i) the that may not be co-enabled. Take the following trace as
call to a condition wait routine shall block on a condition an example. It shows a program of two threads, both of
variable; (ii) They shall be called with a mutex locked by which are trying to acquire locks p and q, and then release
the calling thread; (iii) The condition wait function atom- them. In thread b, before it acquires the lock p, it updates
ically release mutex and causes the calling thread to block some shared variable a.
on the condition variable; and (iv) Upon successful return,
the mutex shall have been locked and shall be owned by he
(thread a) ⇒ a1 : acquire p
calling thread.
a2 : acquire q
A common problem (user bug) related to wait and signal
a3 : release p
operations is the “lost wake-up” caused by a thread execut-
a4 : release q
ing a signal operation before the waiter goes into the waiting
(thread b) ⇒ b0 : a++;
status. This can cause the waiter to block forever, since con-
b1 : acquire p
dition signals do not pend. We now explain how inspect
b2 : acquire q
takes care of this so as to not mask such bugs.
b3 : release p
Inspect handles the condition variable related events by
b4 : release q
splitting the wait routine into three sub-operations: pre wait,
wait, and post wait. Here, pre wait releases the mutex The algorithm in [1] relies on clock vectors, and the sta-
that is held by the caller thread; wait changes the thread tus of the process after taking a transition to infer whether
into blocking status and put the thread into the waiting two transitions may be co-enabled or not. Although that
queue of the correspondent condition variable; and post wait is a safe approximation, and will not affect correctness, this
tries to re-acquire the mutex again. The wrapper function may lead to the result that the state before taking a2 is a
for the pthread cond wait routine is shown in Figure 6. backtrack point because of the dependency between a2 and
In an instrumented program, when the wait routine is b2 . However, this will lead to a redundant backtracking as
a2 and b2 cannot be co-enabled. (More specifically, as in [1], shared variables :
an attempt to run thread b starting with a++ just before a2 pthread_mutex_t f1, f2;
is superfluous.) pthread_cond_t f1_free, f2_free;
To solve this problem, we associate with each transition t int f1_owner, f2_owner;
the set of locks that are held by the thread which executes
t. Testing whether the intersection of the locksets that are thread routine :
held by the threads right after a1 and b1 can help us safely
judge that the two transitions are mutually exclusive, and pthread_mutex_lock(&f1);
avoid redundant backtracking after a1 . while ( f1_owner != 0 )
pthread_cond_wait(&f1_free, &f1);
4.5 Using Sleep Sets for Further Reduction f1_owner = thread_id;
pthread_mutex_unlock(&f1);
While using locksets can avoid some redundant backtrack-
ing, conditional dependency and cond/signal make filtering
pthread_mutex_lock(&f2);
out false “may-be co-enabled” transition pairs more diffi-
while ( f2_owner != 0 )
cult. Instead, we use sleep sets to further reduce the search
pthread_cond_wait(&f2_free, &f2);
space to detect the false “may-be co-enabled” transitions at
f2_owner = thread_id;
runtime.
pthread_mutex_unlock(&f2);
Figure 7 shows an example that has redundant backtrack
points that is hard to detect by checking the transition se-
pthread_mutex_lock(&f1);
quence. It is a simplified dining philosopher problem: two
left_owner =0;
philosophers competing for forks f 1 and f 2, both follow the
pthread_mutex_unlock(&f1);
order of get f 1 first, and then f 2. Two condition variables,
pthread_cond_signal(&f1_free);
a free and b free are used for synchronization between the
philosophers. The lower part of Figure 7 shows a trace that
pthread_mutex_lock(&f2);
inspect may explore with DPOR. In this trace, thread a
right_owner = 0;
first acquires the forks, and then releases f 1, thread b takes
pthread_mutex_unlock(&f2);
f 1 right after thread a releases it, and waits on thread a
pthread_cond_signal(&f2_free);
to release f 2. After that, thread a releases f 2 and notifies
thread b that the fork is available. We only show the first
two context switches between threads in Figure 7. In this ← ({b}, {a})
trace, two lower backtrack points have been explored. The (thread a) ⇒ a1 : acquire f1
next backtrack points to be explored is right before a5 , as a2 : (f1 owner ! = 0)?
a5 and b5 are dependent transitions that appear to be co- a3 : f1 owner = thread id
enabled (actually they are not). However, enabling b1 , b2 a4 : release f1
after a4 will have thread b attaining blocking status waiting ← ({b}, {a})
a5 : acquire f2
for signal f 1 f ree, a5 will be enabled again. In this situa- a6 : (f2 owner! = 0)?
tion, stopping further depth-first search will not affect the a7 : f2 owner = thread id
correctness of model checking as the assumption that a5 and a8 : release f2
b5 may be co-enabled is wrong. We use sleep sets to achieve ← ({}, {a, b})
this. a9 : acquire f1
The sleep set is a mechanism used to avoid the interleaving a10 : f1 owner = 0
a11 : release f1
of independent transitions. It works by maintaining a set of a12 : signal f2 free
transitions sleep such that whenever a new transition new (thread b) ⇒ b1 : acquire f1
is considered, if new is in sleep, then moving new can be b2 : (f1 owner ! = 0)?
considered to be un-necessary, and hence avoided [16]. Line b3 : f1 owner = thread id
38-45 in Figure 8 shows how the sleep set are computed. In b4 : release f1
this example, while backtracking right after a4 , and executes ← ({}, {a, b})
b5 : acquire f2
b1 and b2 , we will reach a state in which thread b is blocked, b6 : (f2 owner ! = 0)?
and the transition a5 in the sleep set, which is not going to be b7 : pre wait f2 free
executed. At this point, since there is no transition available b8 : wait
for further exploration, we can backtrack immediately. (thread a) ⇒ a13 : acquire f2
a14 : f2 owner = 0
4.6 Runtime Model Checking with DPOR ...

Figure 8 shows how DPOR is adapted in the context of


model checking multithreaded C/C++ programs. The algo-
Figure 7: Simplified dining philosophers
rithm has two phases: In the first phase, inspect executes
the program for the first time under the monitoring of the
scheduler, and collects a random visible operation sequence
(lines 7-17). Any errors encountered will be reported. In multithreaded program must be able to precisely follow the
the second phase, inspect does backtrack checking until all transition sequence. After that, we choose a new transition
backtrack points are explored (lines 18-26). t from the backtrack set of the backtracking state s (line 37).
In the replay mode in the second phase, we first rerun the In lines 38-40, we update s.backtrack, s.done and initialize
program until the latest backtrack point (lines 31-36). The s.sleep. After that, we will continue the depth-first search
while updating the sleep sets associated with each state. 1: TransitionSequence T , T ′ ;
Line 45 shows how sleep sets are updated. 2: StateStack S;
In the second phase, if a data race is detected, it will be 3: State s, s′ ;
reported on the fly. If a deadlock detected, FoundDeadlock-
Exceptionwill be thrown out. When inspect catches such 4: runtime mc with DPOR( ) {
an exception, it will abort the current execution and start 5: run P , which is the program under test;
explore another backtrack point. 6: s = the initial state of the program;
The backtrack point information is updated each time a 7: try {
new transition is appended to the transition sequence. We 8: while (s.enabled = 6 ∅) {
do not show the pseudo-code here. In inspect, we use the 9: S.push(s);
clock vector, lockset, along with the thread creation/join 10: choose t ∈ s.enabled;
information to decide whether a pair of transitions may be 11: s = next(s, t);
co-enabled or not. As we divide cond wait into three sub- 12: T.append(t);
transitions, for a lock acquire, the appropriate backtrack- 13: update backtrack info(); //defined in Figure 2
ing points include not only the preceding lock acquire, but 14: }
post wait which is also a lock acquiring operation. For a 15: }
cond signal, the appropriate backtrack point is the latest 16: catch(FoundDeadlockException){ ... }
wait on the signal. 17: catch(AssertViolationException) { ... }

4.7 Automated Instrumentation 18: while (¬S.empty()) {


Inspect needs to capture every visible operation to guar- 19: s = S.pop();
antee that it is not missing any bugs in the program. In- 20: T.pop back(); // remove the last element of T
correct instrumentation can make the scheduler fail to ob- 21: if (s.backtrack 6= ∅) {
serve visible operations (viz., before execution of some visi- 22: restart the program P ;
ble operations, the program under test does not notify the 23: backtrack checking(s);
scheduler). To automate the instrumentation process, we 24: T = T ′;
designed an algorithm as shown in Figure 9. 25: }
The automated instrumentation is primarily composed of 26: }
three steps: (i) replace the call to the thread library routines 27: }
with the call to the wrapper functions; (ii) before each visible
operation on a data object, insert code to send a request to 28: backtrack checking(State sbt ) {
the scheduler; (iii) add thread start at the entry of every 29: initialize T ′ to empty;
thread, and thread end at each exit point. 30: try {
To achieve this, we need to know whether an update to 31: s = the initial state of the program;
a data object is a visible operation or not. The may-escape 32: while (s 6= sbt ) {
analysis [19] is used to discover the shared variables among 33: t = T.pop f ront(); // remove the head of T
threads. Because the result of may-escape analysis is an 34: T ′ .append(t);
over-approximation of all-possible shared variables among 35: s = next(s, t);
threads, our instrumentation is safe for intercepting all vis- 36: }
ible operations in the concrete execution. 37: choose t, t ∈ s.enabled ∧ t.tid ∈ s.backtrack;
38: s.backtrack = s.backtrack \ {t.tid};
4.8 Detecting Bugs 39: s.sleep = {t ∈ s.enabled | t.tid ∈ s.done};
Inspect detects data races and deadlocks while updat- 40: s.done = s.done ∪ {t.tid};
ing the backtracking information. If two transitions on a 41: repeat
shared data object are enabled in the same state, inspect 42: S.push(s);
will report a data race. Deadlocks are detected by checking 43: T ′ .append(t);
whether there is a cycle in resource dependency. Inspect 44: s′ = next(s, t);
keeps a resource dependent graph among threads, checks 45: s′ .sleep = { t′ ∈ s.sleep | (t, t′ ) are indepen-
and updates the graph before every blocking transition. dent};
Besides races and deadlocks, inspect can report incorrect 46: s′ .enabled = s′ .enabled \ s′ .sleep;
usages of synchronization primitives. The incorrect usages 47: s = s′ ;
include: (1) using an uninitialized mutex/condition vari- 48: update backtrack info();
able; (2) not destroying mutex/condition variables after all 49: choose t ∈ s.enabled;
threads exit; (3) releasing a lock that is held by another 50: until (s.enabled = ∅)
thread; (4) waiting on the same condition variable with dif- 51: }
ferent mutexes; (5) missing a pthread-exit call at the end of 52: catch (FoundDeadlockException) { ... }
function main. 53: catch (AssertViolationException) { ... }
54: }
5. IMPLEMENTATION Figure 8: Runtime model checking with DPOR
Inspect is designed in a client/server style. The server
side is the scheduler which controls the program’s execu-
tion. The client side is linked with the program under test
to communicate with the scheduler. The client side includes a wrapper for the pthread library, and facilities for commu-
auto instrument(program P ) {
have an inter-procedural escape analysis on P to find Table 1: Checking indexer and fsbench
out all possible shared variables among threads;
threads runs transitions time(s) runs/sec
for each call of the thread library routines 1-11 1 6 272 0.01 -
replace the call with the call to the correspondent 13 64 6,033 1.44 44.44
wrapper function; indexer 14 512 42,635 12.58 40.69
for each access of a shared variable v { 15 4,096 351,520 108.74 37.68
if (read access) 16 32,768 2,925,657 988.49 33.15
insert a reading request for v before reading v; 1-13 1 6 209 0.01 -
else 16 8 1,242 0.14 -
insert a write request for v before updating v; fsbench 18 32 4,893 0.64 50
} 20 128 20,599 2.76 46.38
for each entry of threads 22 512 84,829 11.94 42.88
insert a thread start notification to the scheduler, 24 2,048 367,786 54.82 37.36
before the first statement in the thread; 26 8,192 1,579,803 261.40 31.33
for each exit point of threads
insert a thread end notification after the last state-
ment of the thread;
} -O2.
In Table 1, it shows that when the number of threads
Figure 9: Automated instrumentation increases, more conflicts among threads slow down the pro-
gram. However, inspect can still explore more than 30
different interleavings per second.
nication with the scheduler. We also tried inspect on several small applications that
We have the scheduler and the program under test com- use pthread on sourceforge.net and freshmeat.net. Ta-
municate using Unix domain sockets. Comparing with Inter- ble 2 shows the result. Application aget[21] is an ftp client in
net domain sockets, Unix domain sockets are more efficient which multiple threads are used to download different seg-
as they do not have the protocol processing overhead, such ments of a large file concurrently. Application pfscan[22] is
as the network headers to add or remove, the check sums a multithreaded file scanner that combines the functional-
to calculate, the acknowledgments to send, etc. Besides, ity of find, xargs, and fgrep. It uses multiple threads to
the Unix domain datagram service is reliable. Messages will search in parallel through directories. Application tplay[23]
neither be lost nor be delivered out of order. is a multimedia player that uses one thread to prefetch the
In the automated instrumentation part, we first use CIL [20] audio data, and the other thread to play the audio. Finally,
as a pre-processor to simplify the code. Then we use our own libcprops[24] is a C prototyping tools library which provides
program analysis and transformation framework based on thread-safe data structures such as linked list, AVL tree,
gcc’s C front end to do the instrumentation. We first have hash list, as well as a thread pool and thread management
an inter-procedural flow-sensitive alias analysis to compute framework.
the alias information. With the alias information, we use an In Table 2, the second column LOC (lines of code) for
inter-procedural escape analysis to discover the shared vari- each application is counted with wc. The right most col-
ables among threads. Finally we follow the algorithm in Fig- umn shows the number of errors we found. As inspect
ure 9 to do the source code transformation. Right now the may report the same error multiple times while backtrack-
automatic instrumentation can only work for C programs ing and re-executing the program repeatedly, we only count
because of the lack of a front end for C++. the unique number of errors.
For aget, we found one data race on writing the statistic
data bwritten to a file. This data race is also reported in
6. EXPERIMENTS AND EVALUATION [7]. When testing aget with inspect, we need to construct
We evaluate inspect on two sets of benchmarks. The a closed environment for it. As the network may introduce
first set includes two benchmarks in [1]. The second set con- non-determinism to the environment, we reduced the size of
tains several small applications that use pthread on source- the data package, which aget gets from the ftp server, to 512
forge.net and freshmeat.net[21, 22, 23, 24]. bytes.
The performance of inspect for the benchmarks in [1] is In pfscan, we found four errors. One error is that a condi-
shown in Table 1. The first program, indexer, captures the tion variable is used without initialization. This is a danger-
scenarios in which multiple threads insert messages into a ous behavior, and may completely mess up synchronization
hash table concurrently. The second benchmark, fsbench, is among threads and end up with incorrect results. In addi-
an abstraction of the synchronization idiom in Frangipani tion, two mutexes that are initialized at the beginning of the
file system. We re-wrote the code using C and the POSIX program never get released, which results in resource leak-
thread library. The source code is available at [25]. In the age. Also, we found that a pthread exit was missing at the
original indexer benchmark, a compare-and-swap is used. end of main. As a result, when the main thread exits, some
As C does not have such an atomic routine, we replaced it worker threads may be killed before they completely finish
with a function and used a mutex to guarantee the mutual their work. .
exclusion. The execution time was measured on a PC with As for libcprops, it is a thread-safe library and test drivers
two Intel Pentium CPUs of 3.0GHz, and 2GB of memory. are required for testing it. We adapted the test cases in
inspect was compiled with gcc-3.3.5 at optimization level libcprops release into multithreaded versions, and used them
Table 2: Checking real applications
benchmark LOC threads Errors
races deadlock other errors
aget-0.4 1,098 3 1 0 0
pfscan-1.0 1,073 4 1 0 4
tplay-0.6.1 3,074 2 0 0 0
avl 1,432 1-3 2 0 0
heap 716 1-3 0 0 0
libcprops-0.1.6 hashlist 1,953 1-3 1 0 1
linked list 1,476 1-3 1 0 0
splay tree 1,211 1-3 1 0 0

as test drivers. Inspect revealed several data races in the ConTest[28] debugs multithreaded programs by injecting
code. After manually examining the source code, we found context switching code to randomly choose the threads to
that most of the races are benign races. Besides, we also be executed. As randomness does not guarantee all inter-
found that in hashlist, a condition variable is destroyed with- leavings will be explored for a certain input, it is possible
out initialization. This may lead to undefined behaviors. that ConTest can miss bugs.
jCute[29] uses a combination of symbolic and concrete ex-
6.1 Discussion ecution to check a multithreaded Java program by feeding it
Our experiments show that inspect can be very help- with different inputs and replaying the program with differ-
ful in testing and debugging multithreaded C/C++ appli- ent schedules. jCute is more powerful in discovering inputs
cations. However, it also has limitations. First, inspect that can have the program execution take different paths.
needs a set of test cases incorporated in its test driver to get We think the difference between our work and jCute is in the
good coverage of the code being verified. Secondly, runtime implementation part. jCute uses the Java virtual machine
monitoring puts an overhead on the program, especially in to intercept visible operations of a multithreaded Java pro-
programs that have a lot of visible operations on shared gram. Here we use socket communication and an external
data objects. Also, the intrusive instrumentation limits in- scheduler for C/C++ programs.
spect from checking programs that have strict timing re- Helmstetter et al.[30] show how to generate scheduling
quirements. As inspect checks the program’s behavior by based on dynamic partial order reduction. We think that
monitoring the concrete executions of the program, is not the differences between our work and theirs lie in: (i) We
able to check system-level code like RacerX, LockSmith, etc., are focusing on application-level multithreaded C programs,
can do. while they focused on the schedulings of SystemC simula-
It is obvious that to check a program, we must be able tions; and (ii) Instead of generating the scheduling only, our
to concretely execute the program. When doing our exper- work reruns the program and tries to verify safety proper-
iments, however, we also tried running several other open- ties.
source applications. Unfortunately, some problems were en- CHESS[31] is the work which is probably most similar
countered: (i) some programs kept crashing because of other to ours. The difference between CHESS and our work lies
existing bugs; (ii) it is inconvenient to construct a closed in the instrumentation part and how to take control of the
world for server programs such as http servers. Other than scheduling away from the operation system. In CHESS, the
these limitations, we think inspect is a powerful assistant instrumentation allocates a semaphore for each thread that
tool in the process of unit testing and debugging for multi- is created. It also requires an invariant to be preserved:
threaded software. that at any time every thread but one is blocked on its
semaphore. In contrast, we do the instrumentation at the
source code level, and use blocking sockets to communicate
7. OTHER RELATED WORK between scheduler and the threads.
Lei et al.[26] designed RichTest, which used reachability
testing to detect data races in concurrent programs. Reach-
ability testing views an execution of a concurrent program 8. CONCLUSION
as a partially-ordered synchronization sequence. Instead, In this paper, we propose a new approach to model check
dynamic partial order reduction views it as an interleav- safety properties including deadlocks and stuttering invari-
ing of visible operations from multiple threads. Compared ants in multithreaded C/C++ programs. Our method works
with RichTest, inspect focuses on checking multithreaded by automatically enumerating all possible interleavings of
C/C++ programs, and it can detect not only data races, but the threads in a multithreaded program, and forcing these
also deadlocks and other errors. However, inspect cannot interleavings to execute one by one. We use dynamic partial-
yet handle send/receive events between multiple processes. order reduction to eliminate unnecessary explorations. Our
CMC[27] verifies C/C++ programs by using a user-model preliminary results show that this method is promising for
Linux as a virtual machine. CMC captures the virtual ma- revealing bugs in real multithreaded C programs. Finally,
chine’s state as the state of a program. Unfortunately, CMC inspect is available from [25].
is not fully-automated. As CMC takes the whole kernel plus In the future, inspect can be improved in many ways. We
the user space as the state, it is not convenient for CMC to can combine the static analysis techniques with the dynamic
adapt the dynamic partial order reduction method. partial order reduction to further reduce the number of in-
terleavings we need to explore to reveal errors. Inspect can [15] Patrice Godefroid. Model checking for programming
also adapt more efficient algorithms such as Goldilocks[32] languages using verisoft. In POPL, pages 174–186, 1997.
for computing happen-before relations to improve efficiency. [16] David R. Butenhof. Programming with POSIX Threads.
The automated instrumentation part can be improved by Addison-Wesley, 1998.
employing more efficient and precise pointer-alias analysis. [17] Edmund M. Clarke, Orna Grumberg, and Doron A. Peled.
Model Checking. MIT Press, 2000.
Acknowledgments: We thank Subdoh Sharma for help- [18] Patrice Godefroid. Partial-Order Methods for the
ing implement the escape analysis part, and Sarvani Vakkalanka Verification of Concurrent Systems: An Approach to the
State-Explosion Problem. Springer-Verlag New York, Inc.,
for comments. This work is funded by NSF CNS-0509379,
Secaucus, NJ, USA, 1996. Foreword By-Pierre Wolper.
SRC 2005-TJ-1318, and a grant from Microsoft. [19] Alexandru Salcianu and Martin Rinard. Pointer and escape
analysis for multithreaded programs. In PPoPP ’01:
9. REFERENCES Proceedings of the eighth ACM SIGPLAN symposium on
[1] Cormac Flanagan and Patrice Godefroid. Dynamic Principles and practices of parallel programming, pages
partial-order reduction for model checking software. In Jens 12–23, New York, NY, USA, 2001. ACM Press.
Palsberg and Martı́n Abadi, editors, POPL, pages 110–121. [20] http://manju.cs.berkeley.edu/cil/.
ACM, 2005. [21] http://freshmeat.net/projects/aget/.
[2] Stefan Savage, Michael Burrows, Greg Nelson, Patrick [22] http://freshmeat.net/projects/pfscan.
Sobalvarro, and Thomas Anderson. Eraser: a dynamic data [23] http://tplay.sourceforge.net/.
race detector for multithreaded programs. ACM Trans. [24] http://cprops.sourceforge.net/.
Comput. Syst., 15(4):391–411, 1997.
[25] http://www.cs.utah.edu/∼yuyang/inspect.
[3] Nicholas Nethercote and Julian Seward. Valgrind: A
program supervision framework. Electr. Notes Theor. [26] Yu Lei and Richard H. Carver. Reachability testing of
Comput. Sci., 89(2), 2003. concurrent programs. IEEE Trans. Software Eng.,
32(6):382–403, 2006.
[4] Jong-Deok Choi, Keunwoo Lee, Alexey Loginov, Robert
O’Callahan, Vivek Sarkar, and Manu Sridharan. Efficient [27] Madanlal Musuvathi, David Y. W. Park, Andy Chou,
and precise datarace detection for multithreaded Dawson R. Engler, and David L. Dill. Cmc: A pragmatic
object-oriented programs. In Proceedings of the ACM approach to model checking real code. In OSDI, 2002.
SIGPLAN Conference on Programming language design [28] Orit Edelstein, Eitan Farchi, Evgeny Goldin, Yarden Nir,
and implementation, pages 258–269, New York, NY, USA, Gil Ratsaby, and Shmuel Ur. Framework for testing
2002. ACM Press. multi-threaded java programs. Concurrency and
[5] Dawson Engler and Ken Ashcraft. Racerx: effective, static Computation: Practice and Experience, 15(3-5):485–499,
detection of race conditions and deadlocks. In SOSP ’03: 2003.
Proceedings of the nineteenth ACM symposium on [29] Koushik Sen and Gul Agha. Concolic testing of
Operating systems principles, pages 237–252, New York, multithreaded programs and its application to testing
NY, USA, 2003. ACM Press. security protocols. Technical Report
[6] Cormac Flanagan, K. Rustan M. Leino, Mark Lillibridge, UIUCDCS-R-2006-2676, University of Illinois at Urbana
Champaign, 2006.
Greg Nelson, James B. Saxe, and Raymie Stata. Extended
static checking for java. In Proceedings of the ACM [30] Claude Helmstetter, Florence Maraninchi, Laurent
SIGPLAN Conference on Programming language design Maillet-Contoz, and Matthieu Moy. Automatic generation
and implementation, pages 234–245, New York, NY, USA, of schedulings for improving the test coverage of
2002. ACM Press. systems-on-a-chip. fmcad, 0:171–178, 2006.
[7] Polyvios Pratikakis, Jeffrey S. Foster, and Michael Hicks. [31] http://research.microsoft.com/projects/CHESS/.
Locksmith: context-sensitive correlation analysis for race [32] Tayfun Elmas, Shaz Qadeer, and Serdar Tasiran.
detection. In Proceedings of the ACM SIGPLAN conference Goldilocks: Efficiently computing the happens-before
on Programming language design and implementation, relation using locksets. In Formal Approaches to Software
pages 320–331, New York, NY, USA, 2006. ACM Press. Testing and Runtime Verification, LNCS, pages 193–208,
[8] Thomas A. Henzinger, Ranjit Jhala, and Rupak Majumdar. Berlin, Germany, 2006. Springer.
Race checking by context inference. In PLDI ’04:
Proceedings of the ACM SIGPLAN 2004 conference on
Programming language design and implementation, pages
1–13, New York, NY, USA, 2004. ACM Press.
[9] Robby, Matthew B. Dwyer, and John Hatcliff. Bogor: an
extensible and highly-modular software model checking
framework. In ESEC / SIGSOFT FSE, pages 267–276,
2003.
[10] Sagar Chaki, Edmund M. Clarke, Alex Groce, Somesh Jha,
and Helmut Veith. Modular verification of software
components in c. In ICSE, pages 385–395. IEEE Computer
Society, 2003.
[11] Tony Andrews, Shaz Qadeer, Sriram K. Rajamani, Jakob
Rehof, and Yichen Xie. Zing: A model checker for
concurrent software. In Computer Aided Verification, 16th
International Conference, CAV 2004, Boston, MA, USA,
July 13-17, 2004, Proceedings, volume 3114 of Lecture
Notes in Computer Science, pages 484–487. Springer, 2004.
[12] Willem Visser, Klaus Havelund, Guillaume P. Brat, and
Seungjoon Park. Model checking programs. In ASE, pages
3–12, 2000.
[13] Gerard J. Holzmann. The Spin Model Checker: Primer and
Reference Manual. Addison-Wesley, 2004.
[14] http://nescc.sourceforge.net/.

Você também pode gostar