Hierarchical Tracing of Concurrent Programs Wei Jen Yeh Michal Young Department of Computer Sciences Purdue University West Lafayette, IN 47907
fyeh,
[email protected] Abstract
Practical analysis of large systems must proceed piecemeal, and preferably in a hierarchical manner with suppression of details at each level. Reports of possible errors, on the other hand, may be helpful only if they describe in detail how the error can occur. A common error reporting technique in analysis of concurrent systems is to present an example trace (sequence of actions) that exhibits an undesired property (e.g., a deadlock or critical race). Since suppression of detail may make such a trace less useful, we need to recover a detailed trace despite having suppressed details during the analysis. We describe an approach to recovering detailed traces of possible task interactions from a hierarchical state-space analysis, and report performance of the method in a prototype state-space analysis tool for Ada systems.
1 Introduction In [YY91] we described a hierarchical, incremental approach to analysis of Ada programs using process algebra. Algebraic operations of restriction, abstraction, and reduction modulo congruence of terms in the algebraic system were applied to control the combinatorial explosion of possible evolutions of systems with many tasks. Suppression of detail in this manner is a vital tool in ghting the \state space explosion" problem, but it may also make diagnostic reports less useful. In this paper we describe how fully detailed traces may be recovered from such an analysis when it uncovers errors such as potential deadlock. Performance of an analysis tool using this approach is reported and compared to the alternative of retaining full detail in the analysis. The paper is structured as follows. Section 2 rst reviews the process algebra-based compositional approach for reachability analysis and illustrates the need for a mechanism to recover details suppressed during the analysis. Section 3 introduces a hierarchical tracing technique and describes how a detailed sequence of actions is reconstructed during the trace. In Section 4 the advantage of using hierarchical tracing to reconstruct detailed traces in the compositional approach is demonstrated using the dining philosophers problem and a remote temperature sensor system as examples. Section 5 discusses related state-space analysis approaches, and Section 6 concludes.
2 Background Reachability analysis is a family of techniques for analyzing the synchronization structure of concurrent systems. It is attractive because it is simple and relatively straightforward to This research was supported in part by the National Science Foundation under grant #CCR-9157629, with additional support from Defense Advanced Research Projects Agency under Grant Number MDA972-91-J-1010 and from the Software Engineering Research Center, an NSF Industry/University Cooperative Research Center. The content of the information does not necessarily re ect the position or the policy of the Government and no ocial endorsement should be inferred.
automate, and can be used in conjunction with model-checking procedures to verify properties like freedom from deadlocks and race conditions. Reachability analysis involves enumeration of reachable states from individual nite state representations of processes (e.g., Ada tasks) in a concurrent system. The number of reachable states grows in the worst case as the product of the numbers of states of all the processes. This \state explosion" problem is a major obstacle to practical application of reachability analysis to real systems. In [YY91] we presented a compositional analysis approach based on process algebra. Ada tasks are translated to terms in an algebra, with corresponding state graph representations. The associative and commutative properties of the composition operator allow one to decompose a system and choose the most convenient order to compose tasks. The restriction operation and the abstraction operation can be used to suppress local actions according to scope rules (e.g., based on a source Ada program or design under analysis) and transform local communications into internal actions. A congruence relation, such as observation equivalence [Mil89], can be used to simplify the graph models at each stage in the hierarchical analysis. We have built a prototype analysis tool, Pal, to evaluate this algebraic approach to Ada system designs expressed in an Ada-based program design language. We have successfully analyzed larger systems than a global analysis can handle, including the automated gas station example [HL85] with 3 customers and 2 pumps, 100 dining philosophers, a model of a set of elevators, and a redesigned version of the remote temperature sensor system described by Sanden [San89]. While compositional analysis cannot guarantee acceptable performance1 , experience thus far makes us optimistic that with suitably modular design one can eectively control state explosion and scale analysis to larger systems. The strength of the process algebra-based approach is its ability to restrict and abstract internal details. However, abstraction also causes problems when diagnosing system behaviors. If a desired property is not veri ed, or an indication of possible anomalous behaviors is uncovered, an analysis tool should provide useful diagnostic information to the user, e.g., an example trace (sequence of actions) that violates the desired property. However, using a process algebra-based analysis, some details in the trace may be lost due to the simpli cations that occur during the analysis. We illustrate with a small excerpt from the remote temperature sensor system. The PAL description in Figure 1 is a fragment of the re-designed remote temperature sensor system [San89]. Rendezvous between task CP and task CP Input are abstracted during the analysis in the simpli ed representation of (CP k CP Input)nfCP Input.Receiveg in Figure 2. One would prefer that these lost rendezvous be present in the diagnostic trace sequences provided to users, as in Table 1.
3 Reconstructing traces in hierarchical analysis When analysis uncovers potential errors or anomalies in the behavior of a system, one kind of useful diagnostic information is an example sequence of actions (a trace) exhibiting the anomalous behavior. The trace can be obtained, for example, by a simple search of the state graph of the system for a path that leads to the anomalous state, or by a monitoring agent that observes the (sub-)system and detects any behavior that violates some speci ed conditions. However, for approaches that involve intermediate reductions of the graph model, the trace 1 Basic complexity results [Lad79, Tay83, Smo84] imply that no analysis technique, enumerative or otherwise, can guarantee good performance in all cases.
task CP is new XP one arg(cp packets t, cp input); generic task XP one arg type xp packets t is (); task xp input is entry receive(xp: xp packets t); end xp input; is entry send renames xp output.send; ?? ... details omitted task xp output is ?? ... entry send(xp: xp packets t); entry acknak(an: acknak t; b: bit); begin curr bit := 0; loop accept send(xp); loop x output.xp(xp, curr bit); select accept acknak(an, b); if an = ACK and b = curr bit then curr bit := FlipBit(curr bit); exit; end if; or delay 10; end select; end loop; end loop; end xp output; ?? Additional tasks x output, x input, acknack output, ?? and acknack input, which are internal to XP one arg, ?? are omitted here to save space. end XP one arg; task cp input is ?? ... entry receive (cp: cp packets t); begin loop accept receive(cp) do FPack.new int(cp); end receive; end loop; end cp input; Figure 1: Excerpt of remote temperature sensor example. A diagnostic trace generated from this example is shown in Table 1.
(CP || CP_Input) \ { CP_Input.Receive}:
−CP.Send(0)
FPack.New_Int(0)
−CP.Send(1)
FPack.New_Int(1)
FPack.New_Int_end
Figure 2: State graph for the sub-system (CP k CP Input)nfCP Input.Receiveg. Rendezvous between CP and CP Input are abstracted during the analysis. Steps cp cp input 1 -send(0): -> 2 +receive(0): -> -receive(0): -> 3 +new int(0): -> 4 5 +new int end: -> 6 +receive end: -> -receive end: -> next steps of cp at state : -cp.send(1) -> -cp.send(0) -> next steps of cp input at state : -cp input.receive(0) -> -cp input.receive(1) -> next steps of fpack at state : -fpack.new int(1) -> -fpack.new int(0) -> next steps of ui at state : dp input.result(1, 1) -> dp input.result(1, 0) -> dp input.result(0, 1) -> dp input.result(0, 0) -> states traversed: 21
fpack
ui +send(0): ->
-new int(0): -> -> -new int end: ->
Table 1: An example trace from the remote temperature sensor system. Each task (only four in this case) is displayed in one column. Entry calls are indicated with +, and accepts with ?; a call and corresponding accept in one row indicates a rendezvous, also called a joined action. The \next steps" information describes a potential deadlock state. may not contain enough details to be helpful to users in diagnosing the cause of the problem. We describe in this section a hierarchical tracing technique that reconstructs a fully detailed trace, given a high level trace sequence with some details suppressed. Global tracing operates on an unstructured collection of processes (e.g., Ada tasks). In contrast, hierarchical tracing operates in a recursive manner on a set of processes organized as a tree. The tree re ects the visibility structure of the source program by default, but additional guidance can be provided to direct the hierarchical analysis. The state graph of a node in the
hierarchy represents the combined behavior of the sub-system that the node represents. Hierarchical tracing works in two passes. In the rst pass the state graph of the whole (sub-)system is composed in a bottom-up fashion, with simpli cation at each internal node in the tree to suppress internal details of sub-systems. In the second pass traces observed at high levels are decomposed in a top-down manner, lling in details as it recurses downward through the hierarchy of processes. Assume that a global trace routine gtrace is available, which given a list of processes and a sequence of observed actions2 as input, will return a detailed sequence of joined or unjoined moves. A move is a (sources,actions) pair containing the action(s) taken in the move and the originating process(es) of the action(s). A joined move represents a rendezvous (communication) between two processes and an unjoined move represents either an internal action or an external action from a single process. The main algorithm htrace is described below. A node representing a sub-system in the hierarchy of processes and a sequence of actions observed from outside of that sub-system are given as input to the routine htrace. Each node in the hierarchy of processes has its state graph pre-computed and simpli ed.
procedure htrace (node, actions) return moves is if expansion?limit(node) then return actions?to?moves(actions, node); end if; nodes := children (node); moves := gtrace (nodes, actions); for child in nodes loop actions := compact (project (moves, child)); ner?moves[child] := htrace(child, actions); end loop; return reconstruct (moves, ner?moves);
The array finer-moves is used to record traces obtained from each child of the node. The routine expansion-limit determines whether traces should be expanded to unveil internal communication actions of a sub-system; our current implementation stops when it encounters a scope wall. actions-to-moves converts a list of actions into a list of moves by adding the source information, project extracts actions of a particular process, and compact removes invisible internal actions. The routine reconstruct aggregates individual traces at the lower level (finer-moves) into a single list of moves to be returned. The described procedure does not require back-tracking across levels, as long as the simpli cation taking place at each node has the property that the language of a simpli ed graph is a subset of the language of the same graph before simpli cation. Figure 3 illustrates the traces obtained from the lowerlevel tasks CP and CP Input of the remote temperature sensor example, using -CP.Send(0) FPack.New Int(0) FPack.New Int end as the high-level trace. The new sequence of moves is constructed by rst extracting all internal (joined or unjoined) moves from each component. Then moves from the components are extracted using the original moves as a guide. Each time internal moves following the extracted move(s) are also extracted. Figure 4 illustrates. We have implemented a general tracing facility in the Pal analysis tool based on this hierarchical tracing algorithm. With dierent additional terminating conditions, it can be 2 An observed (or observable) action is a non-communication (i.e., un-joined) action acting as an interface to the environment. An internal action can be a communication event (a joined action involving two parties, as an Ada rendezvous) or an un-joined action representing a non-deterministic choice.
(CP || CP_Input) \ { CP_Input.Receive}:
−CP.Send(0)
−CP.Send(1)
FPack.New_Int(0)
FPack.New_Int(1)
FPack.New_Int_end
htrace((CP, CP_Input), (−CP.Send(0), FPack.New_Int(0), FPack.New_Int_end))
CP_Input:
CP:
−CP.Send(0)
−CP.Send(1)
CP_Input.Recv(0)
−CP_Input.Recv(0)
FPack.New_Int(0)
CP_Input.Recv(1)
−CP_Input.Recv(1)
FPack.New_Int(1)
FPack.New_Int_end CP_Input.Recv_end
−CP_Input.Recv_end
Figure 3: An example of a decomposition step in the hierarchical tracing. Separate lower-level traces are derived from a high-level trace.
a
high level sequence η traces at lower level
a
c η
η
b
η
η
c
η
η
−b
merge reconstructed trace
η
η
a
η
[b]
η
η
η
c
η
Figure 4: Reconstructing a single detailed trace from two separate traces using a c as the high level trace. The action [b] represents a communication (or a rendezvous) involving the entry b, and is an invisible internal move. (Actions in a trace are separated by dotted lines to illustrate their nal positions in the single reconstructued trace; these dotted lines are not part of the actual data structures.)
furnaces
2 3 global hierarchical global hierarchical states visited 1566 70 7854 145 analysis time 41 17 + 3 292 265 + 4
Table 2: Deadlock detection using hierarchical tracing. Figures were obtained on a SUN Sparc 2 with 48MB of memory. Analysis times for hierarchical tracing are presented as time to construct and simplify graph models + time to search the graphs. used to reconstruct a detailed trace sequence given a list of observed actions, or to construct a detailed trace sequence that leads to a state with a given property, e.g., a deadlock state or a state whose next actions contain a speci ed action. Table 1 gives the output produced by Pal illustrating a sequence of detailed actions leading to a deadlock in the remote temperature sensor system.
4 Performance of hierarchical tracing
We have tested the hierarchical tracing facility of Pal on several example programs. Our experience with hierarchical tracing makes us optimistic that it scales to larger systems, albeit only if those systems exhibit suciently modular design. We illustrate the performance of hierarchical tracing using two examples, dining philosophers and a modi ed version of the remote temperature sensor system [San89]. The former is chosen because it is a regularly structured system that can be scaled to a large number of tasks. The latter is more representative of practical Ada systems, and was chosen partly to examine the impact of dierent design strategies on the eectiveness of hierarchical state-space analysis. The design presented by Sanden has been extensively reworked to enhance modularity with respect to service abstraction. Figure 5 describes the structure of the new system. A PAL description of the system and more detailed discussions of the improved design will be given in [Yeh93]. For performance comparisons we directed Pal to apply both global tracing and hierarchical tracing on versions of the furnace system with a potential deadlock. Analysis time includes composing and reducing graph models at each level of a hierarchy of modules, as well as extracting traces that lead to deadlock. The number of states visited during the analysis is also reported. Table 2 reports performance for versions of the furnace system with two and three furnaces. Note the drastic decreases in the number of visited states and tracing time. In the system with three furnaces, the hierarchical trace visits far fewer states (an important consideration, since storage tends to be the limiting factor in state-space analysis), but the total analysis time is not greatly improved. It is clear from the table that reconstructing the traces is a minor part of the total cost of analysis, which is dominated by construction and simpli cation of the graph models. Hierarchical tracing of larger numbers of tasks is illustrated by the dining philosophers problem. In this version of dining philosophers, each philosopher picks up its left fork rst; a deadlock can occur if all philosophers pick up their left forks before any philosopher picks up its right fork. The communication topology is a ring, with no intrinsic hierarchy, but for
CLIENT
REMOTE FURNACE SYSTEM FPACK
ACK IN
CP INPUT
Xp_ack
Alert
AckNak
UI
XI
NewInt
XO
Recv
in_chan
Xp
Send
XP OUT
AckNak
ReadTemp
CP
INTR
FURNACES
ACK OUT
ReadTemp
THERMOMETER
AckNak AckNak
ACK OUT
XP OUT
Send
Xp_ack
XO
DT_Intr Xp
XI
In_chan
DP INPUT
Recv
Rslt
DP
Req
ACK IN
DEVICES
Figure 5: System structure of the modi ed remote temperature sensor system. CP and DP represent the reliable communication modules for transmitting control packets and data packets. The diagram describes both the client part and the system part. hierarchical tracing we instruct Pal to impose a tree structure as follows: ( : : : ( (...((phil1 fork2 phil2) (fork3 phil3)) ...) (fork phil )) ...). Analysis times and space requirements for tracing a system of 3 philosophers up to a system of 100 philosophers are given in Table 3 and depicted in Figures 6 and 7. Since Pal is designed for hierarchical analysis, and moreover is implemented in Lisp, we also compare with Cats [YTFB89] which is implemented in Ada and is not (yet) biased toward hierarchical analysis. With hierarchical tracing, we were able to analyze a system of 100 philosophers in about 21 minutes, and the growth of the analysis time and space are nearly linear. With global tracing, the combinatorial explosion of possible behaviors limited Pal to 10 philosophers in 2 hours and 36 minutes on a 48MB SUN Sparc 2, and 10 philosophers in 2 hours and 15 minutes for Cats on a 64MB SUN 4/670. Avrunin et al. [ABC+ 91] have also analyzed the dining philosopher examples using their improved constrained expression tool set [ABC+ 91]. The constrained expression approach does not enumerate possible system states, instead using a set of inequations to represent necessary conditions for undesirable behaviors. More detailed discussions of the constrained expression approach can be found in [ABC+ 91]. Performance gures published in [ABC+ 91] are reproduced in Table 3 and in Figure 6. Although no more than order-of-magnitude comparisons are justi ed given dierent hardware and software platforms, these gures indicate that a compositional enumerative approach to state-space analysis is at least competitive with the non-enumerative constrained expression approach on this problem. i
i
seconds 10000
global tracing (Pal) global tracing (CATS) hierarchical tracing (Pal) constrained expression
3000
1000
300
100
30
10
3
5
10
20
50
100
philosophers
Figure 6: Timing comparison of hierarchical tracing, global tracing and the constrained expression approach, on a log-log scale. Figures for Pal were obtained on a 48MB SUN Sparc 2. Figures for Cats were obtained on a 64 MB Sun 4/670. Figures for the constrained expression tool set were obtained on a 24MB DEC 3100. Total analysis time, including parsing of sources through display of traces, is given. (In future Cats wil be retro tted to support hierarchical tracing of Ada programs.) global tracing (Pal)
states 30000
global tracing (CATS) hierarchical tracing (Pal)
10000
3000
1000
300
100
30
10 5
10
20
50
100
philosophers
Figure 7: Space comparison of hierarchical tracing and global tracing on a log-log scale for the dining philosopher example. Number of visited states in global tracing and hierarchical tracing are given for Pal. Figures for Cats represent the sizes of the TICG graph.
Pal
philosophers 3 4 5 6 7 8 9 10 20 40 60 80 100
Cats
Global Tracing Hierarchical Tracing time visitedtime visited- totalpreproc. tracing states preproc. tracing states time 5 0 12 12 1 24 1.6 5 2 33 21 2 38 2.1 7 7 98 31 2 52 2.2 7 18 302 40 4 66 2.9 8 76 941 54 3 80 5.2 9 335 2945 60 3 94 26 10 1704 9237 76 5 108 336.8 16 9359 29010 86 6 122 8090.1 174 16 262 406 35 542 590 68 822 845 150 1102 1100 185 1382
TICG states 26 80 242 728 2186 6560 19682 59048
constr. expr.
totaltime
167 375 629 926 1250
Table 3: Performance comparison of Pal (compositional approach), Cats, and the constrained expression tool set (non-enumerative approach) on the dining philosopher example containing deadlocks. Both global tracing and hierarchical tracing were used for Pal. Pal was running on a 48MB SUN Sparc 2, Cats on a 64 MB Sun 4/670, and the constrained expression tool set on a 24MB DEC 3100.
5 Related Work Reachability analysis techniques of various kinds have been around at least since the 1960's, and attempts to control state explosion are nearly as old. The non-enumerative constrained expression approach has already been discussed in Section 4. We will now look at three other state-space analysis approaches | partial order model, state-space caching, and symbolic model checking.
Partial order model. The interleaving model of concurrency plays a signi cant part in
causing state-space explosion. All possible interleavings of a sequence of events must be explored. In [God90, GW91] a partial order model based on Mazurkiewicz's trace theory [Maz86] is used. In that theory a trace is an equivalence class containing linear orderings of a set of partial ordered events, and two linear orderings of events are equivalent if one can be transformed into the other by exchanging independent events. To avoid ring transitions that will lead to traversing equivalent traces of dierent interleavings, sets of enabled transitions which need not be red (sleep sets ) are identi ed. A similar partial order approach might be used in hierarchical tracing. The hierarchical tracing facility of Pal reconstructs a linear detailed trace (cf. Figure 4). Instead of extracting the internal moves from each component being traced and interleaving them into a single linear trace sequence, an alternative would be to arrange the internal moves from dierent components in a fork-join style based on a partial order relation.
State-space caching. In [Hol87] Holzmann takes a dierent approach by recording only
part of the visited states in a cache of xed size. As pointed out in [Hol87], the success of this approach depends on the caching scheme and the structure of the system being analyzed, and, as shown in [GHP92], the limited cache space results in each state being visited multiple
times in the analysis, thus much time is spent on traversing redundant paths. In [GHP92] sleep sets are incorporated into the state-space caching technique. When sleep sets are used, the frequency of a same state being explored is lowered, thus improving performance of the state-space caching since the rate of cache misses is now lower. Applying the hierarchical tracing technique to state-space caching is dicult, if not impossible, since the approach is global in nature and does not construct the intermediate graph representation at all in the tracing.
Symbolic model checking. Burch et al. [BCM+90] have used Binary Decision Diagrams to combine symbolic representations of nite-state processes with formulas in the -calculus, which in turn can represent formulas of propositional temporal logic. In many cases a symbolic representation of a transition relation is far more compact than an explicit enumeration of nodes and edges in a process graph [EFT91]. The symbolic form of model checking is in principle subject to the same complexity bounds as conventional reachability analysis, and the practical complexity is very sensitive to the choice of BDD encoding and the structure of the concurrent system [EFT91, McM92, Bry92], but symbolic models of digital hardware systems with over 1020 states have been constructed and checked. The symbolic approach retards state explosion but does not entirely avoid it; beyond some limit even very clever symbolic encodings will require a divide and conquer approach.
6 Summary and future work Analysis techniques will scale up to large systems only to the extent that they can be applied in a modular, incremental fashion. Usually one prefers a hierarchical approach, in which some details of module behavior are suppressed at each level of analysis. However, when analysis uncovers an error, the developer may require more detail to understand and correct the problem. Hierarchical tracing is a method for reconstructing example program behaviors (sequences of task interactions) from a hierarchical state-space analysis. Pal extracts nite-state models of tasks in an Ada-based program design language and performs state-space analysis using process algebra. It controls the combinatorial explosion of possible task interleavings by performing reductions (suppressing internal details) at each level in a hierarchical analysis. When an undesirable sequence (e.g., potential deadlock) is detected, Pal reconstructs fully detailed sequences of task interactions from a tree of partial traces. Our experience so far indicates that reconstruction of detailed traces from a hierarchical analysis is much less expensive in space and time than direct exploration of the fully detailed state-space, and that it does not compromise the scalability of compositional reachability analysis. Pal performs hierarchical tracing in a basic manner; several improvements are possible. For one, it is desirable (and should be straightforward) to selectively expand traces to view the interactions of any desired subset of tasks. Hierarchical tracing techniques similar to that employed in Pal should be applicable to other modular analysis techniques, and not only those based on process algebra, provided an anomaly detected at a one level is guaranteed to have at least one representative at more detailed levels.
References
[ABC+ 91] George S. Avrunin, Ugo A. Buy, James C. Corbett, Laura K. Dillon, and Jack C. Wileden. Experiments with an improved constrained expression toolset. In Proceedings of the Symposium on Software Testing, Analysis, and Veri cation (TAV4), pages 178{187, Victoria, British Columbia, October 1991. ACM SIGSOFT, ACM Press. [BCM+ 90] J.R. Burch, E.M. Clarke, K.L. McMillan, D.L. Dill, and L.J. Hwang. Symbolic model checking: 1020 states and beyond. In Proceedings of the Fifth Annual Symposium on Logic in Computer Science, June 1990. [Bry92] Randal E. Bryant. Symbolic boolean manipulation with ordered binary decision diagrams. Computing Surveys, 24(3):293{318, September 1992. [EFT91] Reinhard Enders, Thomas Filkorn, and Dirk Taubner. Generating BDDs for Smbolic Model Checking in CCS. In Proceedings of the Third Workshop on Computer Aided Veri cation, pages 263{278, 1991. [GHP92] Patrice Godefroid, Gerard J. Holzmann, and Didier Pirottin. State space caching revisited. In Proceedings of the Fourth Workshop on Computer-Aided Veri cation, pages 175{186, Montreal, Quebec, Canada, July 1992. [God90] Patrice Godefroid. Using Partial Orders to Improve Automatic Veri cation Methods. In Proceedings of the second international conference, CAV '90, pages 176{185, 1990. LNCS 531 . [GW91] Patrice Godefroid and Pierre Wolper. Using Partial Orders for the Ecient Veri cation of Deadlock Freedom and Safety Properties. In Proceedings of the Third Workshop on Computer Aided Veri cation, CAV '91, pages 417{428, 1991. [HL85] David Helmbold and David Luckham. Debugging Ada tasking programs. IEEE Software, 2(2):47{ 57, March 1985. [Hol87] Gerard J. Holzmann. Automated protocol validation in argos : Assertion proving and scatter searching. IEEE Transactions on Software Engineering, SE-13(6):683{696, June 1987. [Lad79] Richard E. Ladner. The complexity of problems in systems of communicating sequential processes. In Proceedings of the Eleventh Annual ACM Symposium on Theory of Computing, pages 214{223, Atlanta, GA, April 1979. [Maz86] A. Mazurkiewicz. Trace Theory. In In Petri Nets: Applications and Relationships to Other Models of Concurrency, Advances in Petri Nets, Part II; Proceedings of an Advanced Course., pages 279{324, 1986. LNCS 255 . [McM92] K.L. McMillan. Symbolic model checking: An approach to the state explosion problem. PhD thesis, School of Computer Science, Carnegie-Mellen University, 1992. [Mil89] Robin Milner. Communication and Concurrency. Prentice Hall, London, 1989. [San89] Bo Sanden. Entity-life modeling and structured analysis in real-time software design{a comparison. Communications of the ACM, 32(12):1458{1466, December 1989. [Smo84] Scott A. Smolka. Analysis of Communicating Finite State Processes. PhD thesis, Department of Computer Science, Brown University, 1984. Department of Computer Science Technical Report No. CS-84-05. [Tay83] Richard N. Taylor. Complexity of analyzing the synchronization structure of concurrent programs. Acta Informatica, 19:57{84, 1983. [Yeh93] Wei Jen Yeh. Controlling State Explosion in Reachability Analysis. PhD thesis, Purdue University, Department of Computer Sciences, West Lafayette, IN 47907-1398, 1993. In preparation. [YTFB89] Michal Young, Richard N. Taylor, Kari Forester, and Debra Brodbeck. Integrated concurrency analysis in a software development environment. In Proceedings of the ACM SIGSOFT '89 Third Symposium on Software Testing, Analysis, and Veri cation (TAV3), pages 200{209, Key West, Florida, December 1989. Published as ACM SIGSOFT Software Engineering Notes 14 (8). [YY91] Wei Jen Yeh and Michal Young. Compositional reachability analysis using process algebra. In Proceedings of the Symposium on Software Testing, Analysis, and Veri cation (TAV4), pages 49{ 59, Victoria, British Columbia, October 1991. ACM SIGSOFT, ACM Press.