| 1 | \part{Introduction}
|
|---|
| 2 | \label{part:intro}
|
|---|
| 3 |
|
|---|
| 4 | \chapter{Acknowledgement}
|
|---|
| 5 |
|
|---|
| 6 | The CIVL project is funded by the U.S.\ National Science Foundation
|
|---|
| 7 | under awards CCF-1346769 and CCF-1346756.
|
|---|
| 8 |
|
|---|
| 9 | \chapter{What is CIVL?}
|
|---|
| 10 |
|
|---|
| 11 | \textbf{CIVL} stands for \emph{Concurrency Intermediate Verification
|
|---|
| 12 | Language}. The \emph{CIVL platform} encompasses:
|
|---|
| 13 | \begin{enumerate}
|
|---|
| 14 | \item the programming language \textbf{CIVL-C}, a dialect of C with
|
|---|
| 15 | additional primitives supporting concurrency, specification, and modeling;
|
|---|
| 16 | \item verification and
|
|---|
| 17 | analysis tools, including a symbolic execution-based model checker for
|
|---|
| 18 | checking various properties of, or finding defects in, CIVL-C
|
|---|
| 19 | programs; and
|
|---|
| 20 | \item tools that translate from many commonly used
|
|---|
| 21 | languages/APIs to CIVL-C.
|
|---|
| 22 | \end{enumerate}
|
|---|
| 23 |
|
|---|
| 24 | The CIVL-C language is primarily intended to be an intermediate
|
|---|
| 25 | representation for verification. A C program using
|
|---|
| 26 | MPI~\cite{mpi-forum:2012:mpi30}, CUDA~\cite{cuda-programming-guide},
|
|---|
| 27 | OpenMP~\cite{openmp-standard}, OpenCL~\cite{opencl-standard}, or
|
|---|
| 28 | another API (or even some combination of APIs), will be automatically
|
|---|
| 29 | translated into CIVL-C and then verified. The advantages of such a
|
|---|
| 30 | framework are clear: the developer of a new verification technique
|
|---|
| 31 | could implement it for CIVL-C and then immediately see its impact
|
|---|
| 32 | across a broad range of concurrent programs. Likewise, when a new
|
|---|
| 33 | concurrency API is introduced, one only needs to implement a
|
|---|
| 34 | translator from it to CIVL-C in order to reap the benefits of all the
|
|---|
| 35 | verification tools in the platform. Programmers would have a valuable
|
|---|
| 36 | verification and debugging tool, while API designers could use CIVL as
|
|---|
| 37 | a ``sandbox'' to investigate possible API modifications, additions,
|
|---|
| 38 | and interactions.
|
|---|
| 39 |
|
|---|
| 40 | This manual covers all aspects of the CIVL framework, and is organized in parts
|
|---|
| 41 | as follows:
|
|---|
| 42 | \begin{enumerate}
|
|---|
| 43 | \item this introduction, including ``quick start'' instructions for
|
|---|
| 44 | downloading and installing CIVL and several examples;
|
|---|
| 45 | \item a complete description of the CIVL-C language;
|
|---|
| 46 | \item a formal semantics for the language; and
|
|---|
| 47 | \item a description of the tools in the framework.
|
|---|
| 48 | \end{enumerate}
|
|---|
| 49 |
|
|---|
| 50 | \chapter{Installation and Quick Start}
|
|---|
| 51 |
|
|---|
| 52 | This chapter gives instructions for downloading and installing CIVL,
|
|---|
| 53 | and running the verification tool on an example.
|
|---|
| 54 |
|
|---|
| 55 | \subsection*{Notes}
|
|---|
| 56 |
|
|---|
| 57 | \begin{itemize}
|
|---|
| 58 | \item The instructions say to install three theorem provers. In
|
|---|
| 59 | reality, each of these is optional. CIVL will still work without
|
|---|
| 60 | any theorem provers, but the results will not be very precise, i.e.,
|
|---|
| 61 | it will produce a lot of false warnings. The more provers you
|
|---|
| 62 | install, the more precise the analysis.
|
|---|
| 63 | \end{itemize}
|
|---|
| 64 |
|
|---|
| 65 | \subsection*{Instructions}
|
|---|
| 66 |
|
|---|
| 67 | \begin{enumerate}
|
|---|
| 68 | \item Install the automated theorem prover CVC3 (if you have not
|
|---|
| 69 | already). The easiest way to do this is to visit
|
|---|
| 70 | \url{http://www.cs.nyu.edu/acsys/cvc3/download.html} and download
|
|---|
| 71 | the latest, optimized build with static library and executable for
|
|---|
| 72 | your OS. Place the executable file \texttt{cvc3} somewhere in your
|
|---|
| 73 | \texttt{PATH}. You can discard everything else. Alternatively, on
|
|---|
| 74 | some linux systems, CVC3 can be installed using the package manager
|
|---|
| 75 | via ``\texttt{sudo apt-get install cvc3}''. This will place
|
|---|
| 76 | \texttt{cvc3} in \texttt{/usr/bin}.
|
|---|
| 77 |
|
|---|
| 78 | \item Install the automated theorem prover CVC4 (if you have not
|
|---|
| 79 | already). The easiest way to do this is to visit
|
|---|
| 80 | \url{http://cvc4.cs.nyu.edu/downloads/} and choose one of the
|
|---|
| 81 | installation approaches. You only need the binary (\texttt{cvc4}),
|
|---|
| 82 | and you must put it in your \texttt{PATH}. Alternatively, on OS X
|
|---|
| 83 | you may install using MacPorts by ``\texttt{sudo port install
|
|---|
| 84 | cvc4}''.
|
|---|
| 85 |
|
|---|
| 86 | \item Install the automated theorem prover Z3 (if you have not
|
|---|
| 87 | already). Follow instructions at
|
|---|
| 88 | \url{http://z3.codeplex.com/SourceControl/latest#README}. Make sure
|
|---|
| 89 | the executable \texttt{z3} is in your path.
|
|---|
| 90 |
|
|---|
| 91 | \item Install a Java 7 SDK if you have not already. Go to
|
|---|
| 92 | \url{http://www.oracle.com/technetwork/java/javase/downloads/} for
|
|---|
| 93 | the latest from Oracle. On linux, you can instead use the package
|
|---|
| 94 | manager: ``\texttt{sudo apt-get install openjdk-7-jdk}''.
|
|---|
| 95 |
|
|---|
| 96 | \item Download and unpack the latest stable release of CIVL from
|
|---|
| 97 | \url{http://vsl.cis.udel.edu/civl}.
|
|---|
| 98 |
|
|---|
| 99 | \item The resulting directory should be named
|
|---|
| 100 | \texttt{CIVL-\textit{tag}} for some string \textit{tag} which
|
|---|
| 101 | identifies the version of CIVL you downloaded. Move this directory
|
|---|
| 102 | wherever you like.
|
|---|
| 103 |
|
|---|
| 104 | \item The JAR file in the \texttt{lib} directory is all you need to
|
|---|
| 105 | run CIVL. You may move this JAR file wherever you want. You run
|
|---|
| 106 | CIVL by typing a command of the form ``\texttt{java -jar
|
|---|
| 107 | /path/to/civl-TAG.jar ...}''. For convenience, you may instead
|
|---|
| 108 | use the shell script \texttt{civl} included in the \texttt{bin}
|
|---|
| 109 | directory. This allows you to replace ``\texttt{java -jar
|
|---|
| 110 | /path/to/civl-TAG.jar}'' with just ``\texttt{civl}'' on the
|
|---|
| 111 | command line. Simply edit the \texttt{civl} script to reflect the
|
|---|
| 112 | path to the JAR file and place the script somewhere in your
|
|---|
| 113 | \texttt{PATH}. Alternatively, you can define an alias in your
|
|---|
| 114 | \texttt{.profile}, \verb!.bash_profile!, \texttt{.bashrc}, or
|
|---|
| 115 | equivalent, such as
|
|---|
| 116 | \begin{verbatim}
|
|---|
| 117 | alias civl='java -jar /path/to/civl-TAG.jar'
|
|---|
| 118 | \end{verbatim}
|
|---|
| 119 | In the following, we will assume that you have defined a command
|
|---|
| 120 | \texttt{civl} in one of these ways.
|
|---|
| 121 |
|
|---|
| 122 | \item From the command line, type ``\texttt{civl help}''. You should see
|
|---|
| 123 | a help message describing the command line syntax.
|
|---|
| 124 |
|
|---|
| 125 | \item From the command line, type ``\texttt{civl config}''. This should
|
|---|
| 126 | report that \texttt{cvc3}, \texttt{cvc4}, and \texttt{z3} were
|
|---|
| 127 | found, and it should create a file called \texttt{.sarl} in your
|
|---|
| 128 | home directory.
|
|---|
| 129 |
|
|---|
| 130 | \end{enumerate}
|
|---|
| 131 |
|
|---|
| 132 | To test your installation, copy the file
|
|---|
| 133 | \texttt{examples/concurrency/locksBad.cvl} to your working directory.
|
|---|
| 134 | Look at the program: it is a simple 2-process program with two shared
|
|---|
| 135 | variables used as locks. The two processes try to obtain the locks in
|
|---|
| 136 | opposite order, which can lead to a deadlock if both processes obtain
|
|---|
| 137 | their first lock before either obtains the second. Type
|
|---|
| 138 | ``\verb!civl verify locksBad.cvl!''. You should see some output
|
|---|
| 139 | culminating in a message
|
|---|
| 140 | \begin{verbatim}
|
|---|
| 141 | The program MAY NOT be correct. See CIVLREP/locksBad_log.txt
|
|---|
| 142 | \end{verbatim}
|
|---|
| 143 |
|
|---|
| 144 | Type ``\verb!civl replay locksBad.cvl!''. You should see a
|
|---|
| 145 | step-by-step account of how the program arrived at the deadlock.
|
|---|
| 146 |
|
|---|
| 147 |
|
|---|
| 148 | \chapter{Examples}
|
|---|
| 149 |
|
|---|
| 150 | In this section we show a few simple CIVL-C programs which illustrate
|
|---|
| 151 | some of the pertinent features of the language. We also show the results
|
|---|
| 152 | of running some of the tools on them.
|
|---|
| 153 |
|
|---|
| 154 | \section{Dining Philosophers}
|
|---|
| 155 |
|
|---|
| 156 | Dijkstra's well-known Dining Philosophers system can be encoded in
|
|---|
| 157 | CIVL-C as shown in Figure \ref{fig:dining}.
|
|---|
| 158 |
|
|---|
| 159 | \begin{figure}[t]
|
|---|
| 160 | \begin{small}
|
|---|
| 161 | \begin{verbatim}
|
|---|
| 162 | #include <civlc.cvh>
|
|---|
| 163 |
|
|---|
| 164 | $input int B = 4; // upper bound on number of philosophers
|
|---|
| 165 | $input int n; // number of philosophers
|
|---|
| 166 | $assume(2<=n && n<=B);
|
|---|
| 167 |
|
|---|
| 168 | _Bool forks[n]; // Each fork will be on the table ($true) or in a hand ($false).
|
|---|
| 169 |
|
|---|
| 170 | void dine(int id) {
|
|---|
| 171 | int left = id;
|
|---|
| 172 | int right = (id + 1) % n;
|
|---|
| 173 |
|
|---|
| 174 | while (1) {
|
|---|
| 175 | $when (forks[left]) forks[left] = $false;
|
|---|
| 176 | $when (forks[right]) forks[right] = $false;
|
|---|
| 177 | forks[right] = $true;
|
|---|
| 178 | forks[left] = $true;
|
|---|
| 179 | }
|
|---|
| 180 | }
|
|---|
| 181 |
|
|---|
| 182 | void main() {
|
|---|
| 183 | $for(int i: 0 .. n-1)
|
|---|
| 184 | forks[i] = $true;
|
|---|
| 185 | $parfor(int i: 0 .. n-1)
|
|---|
| 186 | dine(i);
|
|---|
| 187 | }
|
|---|
| 188 | \end{verbatim}
|
|---|
| 189 | \end{small}
|
|---|
| 190 | \caption{\texttt{diningBad.cvl}: CIVL-C encoding of Dijkstra's Dining Philosophers}
|
|---|
| 191 | \label{fig:dining}
|
|---|
| 192 | \end{figure}
|
|---|
| 193 |
|
|---|
| 194 | In this encoding, an upper bound \ct{B} is placed on the number of
|
|---|
| 195 | philosophers \ct{n}. When verifying this program, a concrete value
|
|---|
| 196 | will be specified for \ct{B}. Hence the result of verification will
|
|---|
| 197 | apply to all \ct{n} between $2$ and \ct{B}, inclusive.
|
|---|
| 198 |
|
|---|
| 199 | Both \ct{B} and \ct{n} are delcared as \emph{input} variables using
|
|---|
| 200 | the type qualifier \cinput. An input variable may be
|
|---|
| 201 | initialized with any valid value of its type. In contrast, non-input
|
|---|
| 202 | variables declared in file scope will be initialized with a
|
|---|
| 203 | special \emph{undefined} value; if such a variable is read before it
|
|---|
| 204 | is defined, an error will be reported. In addition, any input variable
|
|---|
| 205 | may have a concrete initial value specified on the command line. In
|
|---|
| 206 | this case, we will specify a concrete value for \ct{B} on the command
|
|---|
| 207 | line but leave \ct{n} unconstrained.
|
|---|
| 208 |
|
|---|
| 209 | An $\cassume$ statement restricts the set of executions of the program
|
|---|
| 210 | to include only those traces in which the assumptions hold. In
|
|---|
| 211 | contrast with an $\cassert$ statement, CIVL does not check that the
|
|---|
| 212 | assumed expression holds, and will not generate an error message if it
|
|---|
| 213 | fails to hold. Thus an $\cassume$ statement allows the programmer to
|
|---|
| 214 | say to CIVL ``assume that this is true,'' while an $\cassert$
|
|---|
| 215 | statement allows the programmer to say to CIVL ``check that this is
|
|---|
| 216 | true.''
|
|---|
| 217 |
|
|---|
| 218 | A $\cwhen$ statement encodes a \emph{guarded command}. The $\cwhen$
|
|---|
| 219 | statement includes a boolean expression called the \emph{guard} and a
|
|---|
| 220 | statement body. The $\cwhen$ statement is enabled if and only if the
|
|---|
| 221 | \emph{guard} evaluates to \emph{true}, in which case the body may be
|
|---|
| 222 | executed. The first atomic statement in the body executes atomically
|
|---|
| 223 | with the evaluation of the guard, so it is guaranteed that the guard
|
|---|
| 224 | will hold when this initial sub-statement executes. Since assignment
|
|---|
| 225 | statements are atomic in CIVL, in this example the bodiy of each
|
|---|
| 226 | $\cwhen$ statement executes atomically with the guard evaluation.
|
|---|
| 227 |
|
|---|
| 228 | The $\cfor$ statement is very similar to a for loop. The main
|
|---|
| 229 | difference is that it takes a domain and loops over it.
|
|---|
| 230 |
|
|---|
| 231 | The $\cparfor$ statement is a combination of $\cfor$ and $\cspawn$.
|
|---|
| 232 | The latter is very similar to a function call. The main
|
|---|
| 233 | difference is that the function called is invoked in a new process
|
|---|
| 234 | which runs concurrently with the existing processes.
|
|---|
| 235 |
|
|---|
| 236 | The program may be verified for an upper bound of $5$ by typing the
|
|---|
| 237 | following at the command line:
|
|---|
| 238 | \begin{verbatim}
|
|---|
| 239 | civl verify -inputB=5 diningBad.cvl
|
|---|
| 240 | \end{verbatim}
|
|---|
| 241 |
|
|---|
| 242 | The output indicates that a deadlock has been found and a
|
|---|
| 243 | counterexample has been produced and saved. We can examine the
|
|---|
| 244 | counterexample, but it is more helpful to work with a \emph{minimal}
|
|---|
| 245 | counterexample, i.e., a deadlocking trace of minimal length. To find a
|
|---|
| 246 | minimal counterexample, we issue the command
|
|---|
| 247 |
|
|---|
| 248 | \begin{verbatim}
|
|---|
| 249 | civl verify -inputB=5 -min diningBad.cvl
|
|---|
| 250 | \end{verbatim}
|
|---|
| 251 |
|
|---|
| 252 | \begin{figure}[t]
|
|---|
| 253 | \begin{small}
|
|---|
| 254 | \begin{verbatim}
|
|---|
| 255 | CIVL v1.5 of 2015-10-31 -- http://vsl.cis.udel.edu/civl
|
|---|
| 256 |
|
|---|
| 257 | Violation 0 encountered at depth 19:
|
|---|
| 258 | CIVL execution violation (kind: DEADLOCK, certainty: PROVEABLE)
|
|---|
| 259 | at diningBad.cvl:31.11-12 ";":
|
|---|
| 260 | A deadlock is possible:
|
|---|
| 261 | ...
|
|---|
| 262 |
|
|---|
| 263 | Logging new entry 0, writing trace to CIVLREP/diningBad_0.trace
|
|---|
| 264 | Restricting search depth to 18
|
|---|
| 265 |
|
|---|
| 266 | Violation 1 encountered at depth 14:
|
|---|
| 267 | CIVL execution violation (kind: DEADLOCK, certainty: PROVEABLE)
|
|---|
| 268 | at diningBad.cvl:31.11-12 ";":
|
|---|
| 269 | A deadlock is possible:
|
|---|
| 270 | Path condition: true
|
|---|
| 271 | Enabling predicate: false
|
|---|
| 272 | process p0 (id=0): false
|
|---|
| 273 | process p1 (id=1): false
|
|---|
| 274 | process p2 (id=2): false
|
|---|
| 275 |
|
|---|
| 276 | Context:
|
|---|
| 277 | true
|
|---|
| 278 |
|
|---|
| 279 | Call stacks:
|
|---|
| 280 | process p0 (id=0):
|
|---|
| 281 | _CIVL_system at diningBad.cvl:31.11-12 ";"
|
|---|
| 282 | process p1 (id=1):
|
|---|
| 283 | dine at diningBad.cvl:21.4-9 "$when"
|
|---|
| 284 | process p2 (id=2):
|
|---|
| 285 | dine at diningBad.cvl:21.4-9 "$when"
|
|---|
| 286 |
|
|---|
| 287 | ...
|
|---|
| 288 |
|
|---|
| 289 | === Stats ===
|
|---|
| 290 | time (s) : 0.95
|
|---|
| 291 | memory (bytes) : 128974848
|
|---|
| 292 | max process count : 5
|
|---|
| 293 | states : 81
|
|---|
| 294 | states saved : 53
|
|---|
| 295 | state matches : 2
|
|---|
| 296 | transitions : 77
|
|---|
| 297 | trace steps : 56
|
|---|
| 298 | valid calls : 510
|
|---|
| 299 | provers : cvc4, z3, cvc3
|
|---|
| 300 | prover calls : 6
|
|---|
| 301 |
|
|---|
| 302 | === Result ===
|
|---|
| 303 | The program MAY NOT be correct. See CIVLREP/diningBad_log.txt
|
|---|
| 304 | \end{verbatim}
|
|---|
| 305 | \end{small}
|
|---|
| 306 | \caption{Output from \texttt{civl verify -inputB=5 diningBad.cvl}}
|
|---|
| 307 | \label{fig:diningOut}
|
|---|
| 308 | \end{figure}
|
|---|
| 309 |
|
|---|
| 310 | The result of this command is shown in Figure \ref{fig:diningOut}. The
|
|---|
| 311 | output indicates that a minimal counterexample has length 14, i.e.,
|
|---|
| 312 | involves 15 states and 14 transitions (the depth of 19 is five more
|
|---|
| 313 | than 14). It was the 2nd and shortest trace found. It was deemed
|
|---|
| 314 | equivalent to the earlier traces and hence the earlier ones were
|
|---|
| 315 | discarded and only this one saved. We can replay the trace with the command
|
|---|
| 316 | \begin{verbatim}
|
|---|
| 317 | civl replay -showTransitions diningBad.cvl
|
|---|
| 318 | \end{verbatim}
|
|---|
| 319 |
|
|---|
| 320 | \begin{figure}
|
|---|
| 321 | \begin{small}
|
|---|
| 322 | \begin{verbatim}
|
|---|
| 323 | ...
|
|---|
| 324 | Step 0: State 0, p0:
|
|---|
| 325 | 0->1: B=5 at diningBad.cvl:9.0-16 "$input int B = 4"
|
|---|
| 326 | 1->2: n=InitialValue(n) [n:=X0] at diningBad.cvl:10.0-12 "$input int n"
|
|---|
| 327 | 2->3: $assume((2<=X0)&&(X0<=5)) at diningBad.cvl:11.0-21 "$assume(2<=n && n ... )"
|
|---|
| 328 | 3->4: forks=InitialValue(forks) [forks:=(lambda i : int . false)] at diningBad.cvl:13.0-14 "_Bool forks[n]"
|
|---|
| 329 | --> State 1
|
|---|
| 330 | ...
|
|---|
| 331 | Step 2: State 2, p0:
|
|---|
| 332 | 5->6: LOOP_BODY_ENTER(($domain(1)){(0..1#1)} has next for (NULL)) at diningBad.cvl:28.14-22 "0 .. n-1"
|
|---|
| 333 | 6->7: $for((NULL) has next in ($domain(1)){(0..1#1)} at diningBad.cvl:28.2-6 "$for"
|
|---|
| 334 | --> State 3
|
|---|
| 335 | ...
|
|---|
| 336 | Step 6: State 6, p0:
|
|---|
| 337 | ...
|
|---|
| 338 | 9->10: $parfor(i0: ($domain(1)){(0..1#1)}) $spawn dine(i0) at diningBad.cvl:30.2-9 "$parfor"
|
|---|
| 339 | --> State 7
|
|---|
| 340 |
|
|---|
| 341 | Step 7: State 7, p1:
|
|---|
| 342 | 12->13: left=0 at diningBad.cvl:16.2-15 "int left = id"
|
|---|
| 343 | 13->14: right=(0+1)%2 [right:=1] at diningBad.cvl:17.2-26 "int right = (id ... n"
|
|---|
| 344 | --> State 8
|
|---|
| 345 | ...
|
|---|
| 346 | Step 11: State 11, p1:
|
|---|
| 347 | 15->16: forks[0]=false at diningBad.cvl:20.24-44 "forks[left] = $false"
|
|---|
| 348 | --> State 12
|
|---|
| 349 |
|
|---|
| 350 | Step 12: State 12, p2:
|
|---|
| 351 | 15->16: forks[1]=false at diningBad.cvl:20.24-44 "forks[left] = $false"
|
|---|
| 352 | --> State 13
|
|---|
| 353 | ...
|
|---|
| 354 | Violation of Deadlock found in State 13:
|
|---|
| 355 | A deadlock is possible:
|
|---|
| 356 | Path condition: true
|
|---|
| 357 | Enabling predicate: false
|
|---|
| 358 | process p0 (id=0): false
|
|---|
| 359 | process p1 (id=1): false
|
|---|
| 360 | process p2 (id=2): false
|
|---|
| 361 |
|
|---|
| 362 | Trace ends after 13 trace steps.
|
|---|
| 363 | Violation(s) found.
|
|---|
| 364 | ...
|
|---|
| 365 | \end{verbatim}
|
|---|
| 366 | \end{small}
|
|---|
| 367 | \caption{Output from \texttt{civl replay -showTransitions diningBad.cvl}}
|
|---|
| 368 | \label{fig:diningReplay}
|
|---|
| 369 | \end{figure}
|
|---|
| 370 |
|
|---|
| 371 | The result of this command is shown in Figure \ref{fig:diningReplay}.
|
|---|
| 372 | The output indicates that a deadlock has been found involving 2
|
|---|
| 373 | philosophers. The trace has 15 transitions; after the initialization
|
|---|
| 374 | sequence, each philosopher picks up her left fork.
|
|---|
| 375 |
|
|---|
| 376 | \section{A Multithreaded MPI Example}
|
|---|
| 377 |
|
|---|
| 378 | \begin{figure}[t]
|
|---|
| 379 | \begin{small}
|
|---|
| 380 | \begin{verbatim}
|
|---|
| 381 | #include<civlc.h>
|
|---|
| 382 | #define TAG 0
|
|---|
| 383 | #define NPROCS 2
|
|---|
| 384 | #define NTHREADS 2
|
|---|
| 385 |
|
|---|
| 386 | $gcomm gcomm = $gcomm_create($here, NPROCS);
|
|---|
| 387 |
|
|---|
| 388 | void MPI_Process (int rank) {
|
|---|
| 389 | $comm comm = $comm_create($here, gcomm, rank);
|
|---|
| 390 | $proc threads[NTHREADS];
|
|---|
| 391 |
|
|---|
| 392 | void Thread(int tid) {
|
|---|
| 393 | int x = rank;
|
|---|
| 394 | $message in, out = $message_pack(rank, 1-rank, TAG, &x, sizeof(int));
|
|---|
| 395 |
|
|---|
| 396 | for (int j=0; j<2; j++) {
|
|---|
| 397 | if (rank == 1) {
|
|---|
| 398 | for (int i=0; i<2; i++) $comm_enqueue(comm, out);
|
|---|
| 399 | for (int i=0; i<2; i++) in = $comm_dequeue(comm, 1-rank, TAG);
|
|---|
| 400 | } else {
|
|---|
| 401 | for (int i=0; i<2; i++) in = $comm_dequeue(comm, 1-rank, TAG);
|
|---|
| 402 | for (int i=0; i<2; i++) $comm_enqueue(comm, out);
|
|---|
| 403 | }
|
|---|
| 404 | }
|
|---|
| 405 | }
|
|---|
| 406 |
|
|---|
| 407 | for (int i=0; i<NTHREADS; i++) threads[i] = $spawn Thread(i);
|
|---|
| 408 | for (int i=0; i<NTHREADS; i++) $wait(threads[i]);
|
|---|
| 409 | $comm_destroy(comm);
|
|---|
| 410 | }
|
|---|
| 411 |
|
|---|
| 412 | void main() {
|
|---|
| 413 | $proc procs[NPROCS];
|
|---|
| 414 |
|
|---|
| 415 | for (int i=0; i<NPROCS; i++) procs[i] = $spawn MPI_Process(i);
|
|---|
| 416 | for (int i=0; i<NPROCS; i++) $wait(procs[i]);
|
|---|
| 417 | $gcomm_destroy(gcomm);
|
|---|
| 418 | }
|
|---|
| 419 | \end{verbatim}
|
|---|
| 420 | \end{small}
|
|---|
| 421 | \caption{\texttt{mpi-pthreads.cvl}: CIVL-C model of a (defective)
|
|---|
| 422 | multithreaded MPI program.}
|
|---|
| 423 | \label{fig:mpithreads}
|
|---|
| 424 | \end{figure}
|
|---|
| 425 |
|
|---|
| 426 | Figure \ref{fig:mpithreads} is an example of a CIVL-C model of
|
|---|
| 427 | multithreaded MPI program. The program consists of two processes,
|
|---|
| 428 | each of which spawns two threads. All four threads issue
|
|---|
| 429 | message-passing operations.
|
|---|
| 430 |
|
|---|
| 431 | This example illustrates some of the message-passing primitives
|
|---|
| 432 | provided in CIVL-C. A \emph{global communicator} object is allocated
|
|---|
| 433 | in the root scope. The constant $\chere$ has type $\cscope$ and
|
|---|
| 434 | refers to the scope in which the expression occurs; in this case it is
|
|---|
| 435 | the root (i.e., file) scope. This global communicator is declared to
|
|---|
| 436 | have \texttt{NPROCS} \emph{places}; these are points from which
|
|---|
| 437 | messages can be sent or received. The function \verb!MPI_Process! is
|
|---|
| 438 | used to model an MPI process. Each instance will create its own
|
|---|
| 439 | \emph{local communicator} object which specifies the global
|
|---|
| 440 | communicator and a place; this is the object that will be used to send
|
|---|
| 441 | or receive messages at that place.
|
|---|
| 442 |
|
|---|
| 443 | Each process spawns two instances of function \texttt{Thread}. Each
|
|---|
| 444 | thread creates a message object from a buffer, specifying the source
|
|---|
| 445 | and destination places, tag, pointer to the beginning of the buffer,
|
|---|
| 446 | and the size of the buffer. The message is \emph{enqueued} into the
|
|---|
| 447 | communication universe using the local communicator. Similarly,
|
|---|
| 448 | messages are dequeued by specifying the local communicator, source
|
|---|
| 449 | place, and tag.
|
|---|
| 450 |
|
|---|
| 451 | The program has a subtle defect, which only manifests on very specific
|
|---|
| 452 | interleavings of the threads. This defect can be found using
|
|---|
| 453 | \texttt{civl verify}.
|
|---|
| 454 |
|
|---|
| 455 | \section{Verifying C programs}
|
|---|
| 456 | CIVL can be used to verify C programs, with a number of transformers. One can also insert macros to C programs to tune it for verification, which, most of time, involves defining input variables. This is usually accomplished by the default macro \texttt{\_CIVL}.
|
|---|
| 457 |
|
|---|
| 458 | For example, Figure~\ref{fig:CIVLmacro} is a simple program that computes the sum of a number of positive numbers. The program can be compiled by any C compiler, as long as no \texttt{\_CIVL} macro is defined in the command for compiling. When CIVL runs this program, it will automatically have \texttt{\_CIVL} defined and thus \texttt{N} becomes an input variable and \texttt{sum} becomes an output variable and there is an assertion to check the correctness the sum computed by the program.
|
|---|
| 459 |
|
|---|
| 460 | If one wants to CIVL to treat a program as it is originally, then the command line option \texttt{-\_CIVL} can be set to false to disable the \texttt{\_CIVL} macro.
|
|---|
| 461 |
|
|---|
| 462 | \begin{figure}[h]
|
|---|
| 463 | \begin{small}
|
|---|
| 464 | \begin{verbatim}
|
|---|
| 465 | #ifdef _CIVL
|
|---|
| 466 | #include<civlc.cvh>
|
|---|
| 467 | #endif
|
|---|
| 468 |
|
|---|
| 469 | #ifdef _CIVL
|
|---|
| 470 | $input int N;
|
|---|
| 471 | $output int sum;
|
|---|
| 472 | #else
|
|---|
| 473 | #define N 100
|
|---|
| 474 | int sum;
|
|---|
| 475 | #endif
|
|---|
| 476 |
|
|---|
| 477 | void main() {
|
|---|
| 478 | int localsum = 0;
|
|---|
| 479 | for (int i = 1; i <= N; i++) {
|
|---|
| 480 | localsum+=i;
|
|---|
| 481 | }
|
|---|
| 482 | sum = localsum;
|
|---|
| 483 | #ifdef _CIVL
|
|---|
| 484 | $assert(sum == (N+1)*N/2);
|
|---|
| 485 | #endif
|
|---|
| 486 | }
|
|---|
| 487 |
|
|---|
| 488 | \end{verbatim}
|
|---|
| 489 | \end{small}
|
|---|
| 490 | \caption{\texttt{\_CIVL}: the default macro}
|
|---|
| 491 | \label{fig:CIVLmacro}
|
|---|
| 492 | \end{figure}
|
|---|
| 493 |
|
|---|
| 494 |
|
|---|
| 495 | \subsection{Verifying MPI C programs}
|
|---|
| 496 | CIVL generates default input variables for verifying MPI programs:
|
|---|
| 497 | \begin{itemize}
|
|---|
| 498 | \item \texttt{\_mpi\_nprocs}: number of MPI processes to be created;
|
|---|
| 499 | \item \texttt{\_mpi\_nprocs\_lo}/\texttt{\_mpi\_nprocs\_hi}: lower/upper bound of the number of MPI processes to be created.
|
|---|
| 500 | \end{itemize}
|
|---|
| 501 |
|
|---|
| 502 | CIVL requires at least either \texttt{\_mpi\_nprocs} or \texttt{\_mpi\_nprocs\_hi} be specified in the command line in order to verify MPI programs (the default value of \texttt{\_mpi\_nprocs\_lo} is 1). For example, one can specify \texttt{civl verify -input\_mpi\_nprocs=5 ring.c}.
|
|---|
| 503 |
|
|---|
| 504 | \subsection{Verifying OpenMP C programs}
|
|---|
| 505 | CIVL introduces a default input variables \texttt{\_omp\_thread\_max} for OpenMP programs, and it needs to be specified in the command line. CIVL will create 1 to \texttt{\_omp\_thread\_max-1} threads for all OpenMP parallel region during the verification. If \texttt{\_omp\_thread\_max} is not specified, then somewhere in the OpenMP program must be specifying the number of threads explicitly. By default, CIVL applies simplification to OpenMP based on independent loop analysis, and optimally that might reduce the program to be purely sequential. The option \texttt{ompNoSimplify} can be set to false so as to skip such simplification. Another option, \texttt{ompLoopDecomp} can be used to specify the loop decomposition strategy, which can be \texttt{ALL}, \texttt{ROUND\_ROBIN} or \texttt{RANDOM}.
|
|---|
| 506 |
|
|---|
| 507 | \subsection{Verifying Pthreads C and CUDA C programs}
|
|---|
| 508 | There are no special option or default input variables for Pthreads or CUDA programs.
|
|---|
| 509 |
|
|---|
| 510 |
|
|---|
| 511 |
|
|---|