• No results found

4.9 Extensions

4.10.2 Related Work

A traditional approach to specifying language semantics in an operational way is called structural operational semantics [151], or SOS for short. Applying the

4.10 Conclusion

SOS approach to specifying the semantics of programming languages require a solid understanding of advanced logics and rewrite systems. We believe that the graph transformation approach fits better to the expertise and experience of the average software engineer, which would enable him or her to also be involved in higher level activities than actual code writing.

Although other work has been presented that used graphs and graph trans- formation rules for (parts) of language definitions (e.g., [39, 78]), none of these reach the same level of completeness. In [39], Corradini et al. use graph trans- formations to formalize the semantics of a realistic programming language: they address a fairly large fragment of Java. Technically, the difference is that they interpret method invocation unfolding, meaning that the program graph changes dynamically. This obviates the need for the frame graph, at the price of hav- ing program-dependent rules (namely, one per method implementation). That is, their approach specifies what is often called big-step semantics, whereas in our approach we specify the semantics of the language in the smallest possible steps, namely per language construct. Program-dependent rules specifying big- step semantics are very likely to yield more efficient simulations than program- independent rules specifying small-step semantics, since the former provide far less points of interaction if multiple threads are operating in parallel. On the other hand, subtle errors often occur due to incorrect interleavings of atomic execution steps in the original program. One could think of two threads ex- ecuting in parallel, both executing the same or different methods that reads and writes a global variable. If both method calls can only be performed as atomic steps, as suggested by the Corradini-approach, such errors will remain unrevealed. Our approach might require more resources (e.g., time and mem- ory) to simulate the program, at least it will examine all possible interleavings of the most atomic steps. Another difference is that Corradini et al. do not provide any tool support, and in that sense theirs is a more theoretic exercise. In contrast, our results have successfully been applied by Engels et al. [79] for proving that their implementation of a model transformation from UML Activ- ities to corresponding Taal programs is behaviour-preserving, as discussed in Section 4.8.

Another, less directly, related source of research is on defining dynamic se- mantics of (UML-type) design models, where also the idea of using graph trans- formations has been proposed (see, e.g., [78, 126, 127, 191]). Furthermore, in [78], Engels et al. present ideas on how to use collaboration diagrams, inter- preted as graph transformations rules, for defining SL semantics.

Chapter 4. Semantics Through Graph Transformations

4.10.3

Discussion

Parallelism. One of the advantages of applying our approach for specifying op- erational semantics on a self-defined language is that one can start with basic language constructs and investigate the effect of extending the language. Orig- inally, Taal has been specified as programming language without parallelism, i.e. without the language construct ForkStat. Introducing new language fea- tures involves syntactic and semantic efforts. In this work we have focussed on the semantic efforts. That is, in what sense do the graph production systems have to be modified in order to support the new language features. For paral- lelism, we only needed to specify two additional rules for generating control flow information and seven additional rules for simulating the semantics (of which four involve the parameter passing process in a similar way as for usual method calls). The other rules did not require any modification. Though, it would be naive to conclude from this one extension that our approach to specifying oper- ational semantics allows language extension in a modular fashion. Introducing a exception handling mechanism will very likely be much more involved.

Abstract Interpretation of Primitive Data Types. In the previous chapter we have shown that our approach provides easy ways of performing attributed graph transformations based on abstract algebras. We have also mentioned the spe- cial case in which the actual algebra is the final algebra of the specific signa- ture(s). A final algebra, intuitively, has a singleton carrier set for every sort in the signature. Any operation opo for some operation symbol o: s1· · ·sn→ s in

the signature will then return that single value from the carrier set As. When

applying such abstractions on the simulation of Taal programs, this yields in- teresting results, especially in cases where the continuation of the simulation depends on the evaluation of some expression, e.g. the simulation of a While- Stat or ConditionalStat. The condition will then always evaluate to this single abstract value. As a result, all the rules that take care of the different outcomes of the evaluation will then be applicable, since those concrete values will also be mapped to this one abstract value. Suppose we simulate the semantics of a WhileStat. The rules for both possible evaluations, being true and false, which would usually be applicable in a mutual exclusive way, are then both applica- ble. This means that the simulation continues along a separate branch in the state space for every possible concrete outcome of the evaluation. As mentioned before, the results of such abstract simulations can be useful, for example, for

4.10 Conclusion

Nested Quantification in Graph Transformations. Currently, some aspects of the se- mantics that involve multiple elements to be equally treated are specified for a single element at a time. For example, the process of parameter passing re- quired four separate rules distinguishing between the cases of no parameters, the first, every next, and last parameter to be passed (see Fig. 4.29). In the case of garbage collecting local variables of methods, a rule (Fig. 4.30(a) and Fig. 4.30(c)) has been specified that removes a single local variable every time it matches. For every local variable, the simulation contains a separate execution step for every local variable to be garbage collected. The work done on nested

quantification in graph transformations (see e.g. [160, 124]) enables to specify

such processes for all those elements in a single rule. In the case of the local variables, garbage collecting them can easily be specified by universally quan- tifying the locals-edge and referenced VarSlot and even be included in the rules shown in Fig. 4.30(b) and Fig. 4.30(d). In the case of parameter passing this is a bit more complicated. The way the abstract syntax of the language is currently defined does not provide sufficient means for specifying the process of param- eter passing by a universal quantified rule. This can only be achieved when the expressions representing the actual parameters are explicitly referencing the VarDecls of the formal parameters. If such an abstract syntax is available, the rule shown in Fig. 4.40 could specify the entire process of parameter passing. This rule includes a single level of universal quantification. The special node labelled ∀ indicates which elements should be matched universally.

Figure 4.40: Nested quantified rule specifying garbage collecting local variables.

Correctness. Using our approach to specifying static and dynamic (e.g., control flow) semantics of (programming) languages, questions arise concerning the way

Chapter 4. Semantics Through Graph Transformations

correctness can (formally) be proven. For Taal, for example, one could ques- tion whether the rules in taal-sem generate graphs that are indeed Execution Graphs. Likewise, for the cfsl, it would be interesting to investigate how one could prove that the rules in cfsl-sem generate graph production systems that indeed generate correct flow graphs.

One of the basic issues is that Groove performs graph transformations in an untyped setting. That is, Groove does not require an explicit specification of a so-called type graph [44] to which all graphs and transformation rules have a proper typing morphism. If such a type graph, say TG, could be constructed for a given graph transformation system, TG could (automatically) be compared with the type graph (or meta model) that defines the (abstract) syntax of the language. However, this would only partially solve the problem. Another part of the problem is due to the fact that termination of graph transformation systems is, in general, undecidable. There are some results by Ehrig et al. on identifying termination criteria for layered graph transformation systems [63]. Those termination criteria require that the transformation rules are assigned to different layers based on their effect on shared graph elements. For the graph transformation systems we are dealing with, such a layering does (often) not exists.

Nevertheless, in case of the cfsl, the correctness problem has partially been tackled. As usual with UML-like meta models, the meta-model from Fig. 4.37 is accompanied by a number of constraints on the combination of different el- ements. Some of the constraints have been specified as graph transformation rules. If one such a constraint is violated, the corresponding rule is applicable which then immediately terminates that branch of the transformation process. For an overview on which constraints have been formalized as graph transfor- mation rules, the interested reader is referred to [174].

5

Model Checking Graph Production Systems

5.1

Introduction

In the previous chapter we have shown that the graph transformation frame- work provides formal and intuitive means of specifying the semantics of object- oriented languages. When modelling the behaviour of software systems in gen- eral, and concurrent systems (in which multiple processes operate in parallel) more specifically, the graph transformation framework has been proven very powerful (see, e.g., [70]), and new application domains such as, e.g., various types of wireless sensor networks [166], are currently under investigation.

In this chapter we will focus on verifying state spaces generated from ar- bitrary graph production systems using the verification technique called model

checking. Since model checking has been introduced in the 1980s [74, 32, 155,

33], numerous model checking tools have been developed, among others, Spin [104], JPF [194], Slam [11], Blast [19], Magic [30], and SMV [137]. As mentioned in Section 1.2, model checking techniques are applied on a model of the system. Such models can be specified in dedicated languages such as, e.g., Promela for Spin [104], or in more general-purpose languages such as, e.g., Java source or byte-code for Java PathFinder [194]. Model checkers can furthermore be characterized in terms of (1) the techniques used to explore or traverse the state space and (2) the way individual (or sets of) states are stored. With respect to the two just mentioned characteristics, there is a distinction be- tween explicit-state model checkers and symbolic model checkers. The difference is that the former category of model checkers store states individually (often

Chapter 5. Model Checking Graph Production Systems

as so-called bit vectors), whereas the latter uses specific models to store sets of states (using so-called Binary Decision Diagrams [26]). Explicit-state model checkers are often used in practice since they provide useful diagnostics about why systems are not correct; symbolic model checkers have been shown to be successful for the verification of system in which state spaces are finite but may be huge (see, e.g., [28]).

When representing system states as graphs and system behaviour as graph productions, the next step is to generate the system’s state space, on which we can apply standard model checking techniques to verify whether the system satisfies specific properties. In this work we distinguish three different basic approaches to model checking, namely sequential, on-the-fly, and bounded model checking. In [113] we have proposed a fairly straightforward approach to verify graph production systems. There, we focussed on graph productions that give rise to finite state spaces and applied the sequential model checking approach. This means that we first generate the full state space and perform the verification process subsequently. In this approach, properties are specified in the branching temporal logic CTL [32] and the model checking procedure was based on the standard backward-state traversal algorithm [37, 17]. This approach provided a proof-of-concept and gave some insight in the advantages and disadvantages of the basic idea of model checking graph production systems.

In general, however, termination of graph production systems is undecid- able (cf. Section 4.10), and thus the state spaces they generate may be infinite. Therefore, the approach from [113] cannot be applied to arbitrary graph produc- tion systems. An obvious alternative approach could be to verify such systems using the on-the-fly model checking approach, in which the state space is verified

while it is generated. This would, however, only partially solve the problem since

such algorithms might dive into correct, though infinite, parts of the state space while other (possibly finite) parts of the system contain small counter-examples. To solve this, we propose an algorithm that combines a known algorithm for on-the-fly model checking with basic ideas from bounded model checking [21, 20]. That is to say, the algorithm iteratively generates ever-larger parts of the system’s state space and performs on-the-fly verification on those parts. Which parts to generate will be specified through boundary conditions that are updated in an iterative fashion as long as no system-executions violating the system requirements have been identified.

In this work, properties are specified as formulae in the linear time temporal logic LTL (for Linear Temporal Logic) [133]. We have chosen to formalize prop- erties as LTL formulae for the availability of a large body of research results in terms of model checking approaches and corresponding algorithms, especially

5.1 Introduction

in the sub-field of on-the-fly model checking. However, choosing LTL puts a strong restriction on the properties we can specify. More precisely, LTL pro- vides means to reason about properties holding in specific states along system executions but does not provide means to reason about how certain states could be reached in terms of actions performed by the system. Although this kind of information is provided by graph transition systems, it is lost in the translation from graph transition systems to Kripke structures. The same remark holds for the approach proposed in [113], where properties are specified as formulae in the branching temporal logic CTL.

One major advantage of the framework we propose is that transitions are computed only once and those results are reused whenever explored states are revisited. This is advantageous since computing transitions is one of the main bottlenecks in the performance of the graph transformation framework. This is due to the computational complexity of computing rule applications and state isomorphism checking. The main disadvantage of our algorithm is that it can- not guarantee completeness. That is, we cannot guarantee that the algorithm always finds a counter-example if there exists one. This is due to the fact that not all paths (and thus also not all counter-examples) have a finite rep- resentation. Whenever there exists a finitely representable counter-example, a so-called reachable accepting cycle, our algorithm is guaranteed to find it with a finite amount of resources.

Overview of the Chapter

This chapter is structured as follows. In Section 5.2 we briefly recall the basic idea, strengths, and weaknesses of the model checking technique and discuss our categorization of the basic approaches to model checking. Section 5.3 introduces the required concepts for LTL model checking and discusses the relation with the graph transformation framework. Next, in Section 5.4 we recall the automata theoretic approach to LTL model checking.

Section 5.5 then continues with introducing the ingredients used in the new on-the-fly bounded model checking algorithm, which is discussed in Section 5.6. This section also includes some analysis on the time-complexity of the algorithm based on different growth factors of systems on which the algorithm can be applied. In Section 5.7 we discuss some implementation issues and report on experimental results. This chapter finishes with some concluding remarks.

Chapter 5. Model Checking Graph Production Systems