Keywords: VDM, interpreter, deterministic, scheduler, real time, multiple processors, .... tactic definitions must be transformed into their semantic equivalent ...
A Deterministic Interpreter Simulating a Distributed Real Time System using VDM K.G. Lausdahl1 , P.G. Larsen1 and N. Battle2 1
Aarhus School of Engineering, Dalgas Avenue 2, DK-8000 Aarhus C, Denmark 2 Fujitsu Services, Lovelace Road, Bracknell, Berkshire. RG12 8SN,UK
Abstract. The real time dialect of VDM, called VDM-RT, contains constructs for describing concurrent threads, synchronisation of such threads and the distribution of object instances and their threads over multiple CPUs with busses connecting them. Tools that simulate an executable subset of VDM-RT models benefit from being deterministic so that problems are reproducible and can be more easily investigated. We describe the deterministic scheduling features of our VDM-RT interpreter, and show how multi-threaded models can also be debugged deterministically.
Keywords: VDM, interpreter, deterministic, scheduler, real time, multiple processors, semantics
1
Introduction
The power of formal methods traditionally lies in being able to write a specification, and to analyse and refine that specification formally to produce a target implementation that is verified. However formal models can also be used for direct simulation of a system under construction. Benefit can be gained from exploring design options through simulation, even before any formal analysis of the model has been carried out [34]. One way for efficiently finding problems with a formal model is to evaluate expressions making use of the definitions from the model. In the event that such expressions do not yield the expected values, it is essential to be able to deterministically reproduce the problem, for example by debugging the model using a deterministic interpreter. In VDM-RT a model represents a potentially infinite set of semantic models due to the looseness [36] present in the language. This allows the modeller to gain abstraction and to avoid implementation bias. To perform a model simulation, tool support must provide an interpreter for the model language, and a debugging environment that allows the designer to investigate problems. Given a specification with looseness, the tool support must also provide a deterministic interpreter and debugger, otherwise problems with the model would not be reproducible and so could not be investigated easily. Programming language interpreters and compilers are typically not deterministic when the language includes concurrency, and this is even less likely for debuggers, where the interference of the user can easily change the behaviour of the program being debugged. Existing work has examined the problems of the deterministic execution of programming languages with threads [3]. Others have added assertions to check
the determinism of multi-threaded applications at the programming language level [7]. In this paper we demonstrate how it is possible to interpret and debug formal models written in VDM-RT in a deterministic manner. We hope that others can benefit from our experience and produce similar tools for executable subsets of other formal languages. The paper starts off with an overview of the VDM technology in Section 2. Afterwards, Section 3 briefly explains how the sequential part of VDM can be interpreted. Section 4 continues with an explanation about how the concurrent aspects of the VDM interpreter are made deterministic. This is followed by Section 5 which explains how the interaction with a debugger interface can be made deterministic. Section 6 provides related work and finally Section 7 provides concluding remarks.
2
The VDM Technology
The Vienna Development Method (VDM) [4,19,11] was originally developed at the IBM laboratories in Vienna in the 1970’s and as such it is one of the longest established formal methods. The VDM Specification Language is a language with a formally defined syntax, static and dynamic semantics [31,26]. Models in VDM are based on data type definitions built from simple abstract types such as bool, nat and char and type constructors that allow user-defined product and union types and collection types such as (finite) sets, sequences and mappings. Type membership may be restricted by predicate invariants which means that run-time type checking is also required from an interpreter perspective. Persistent state is defined by means of typed variables, again restricted by invariants. Operations that may modify the state can be defined implicitly, using standard pre- and post-condition predicates, or explicitly, using imperative statements. Such operations denote relations between inputs and pre-states and outputs and post-states, allowing for nondeterminism. Functions are defined in a similar way to operations, but may not refer to state variables. Recursive functions can have a measure defined for them to ensure termination [33]. Arguments passed to functions and operations are always passed by value, apart from object references. Three different dialects exists for VDM: The ISO standard VDM Specification Language (VDM-SL) [12], the object oriented extension VDM++ [13] and a further extension of that called VDM Real Time (VDM-RT) [40,17]. All three dialects are supported by the open source tool called Overture [23] as well as by VDMTools [14]. These tools, among other features, include standard parsers and type checkers that produce Abstract Syntax Trees (ASTs). Such ASTs form the basic input of the interpreter presented in this article. None of these dialects are generally executable since the languages permits the modeller to use type bindings with infinite domains, or implicitly defined functions and operations, but the dialects all have subsets that can be interpreted [24]. In addition some commonly used implicit definitions can be executed in principle [16]. A full description of the executable subset of the language can be found in [25]. In this paper we focus on the ability to execute a simulation when looseness is present in the specification in such a way that the results are deterministic and reproducible. This means that our interpreter’s result will correspond to a valid value from one model of the specification, and it will always produce that value. All valid mod-
els can be collected [22], however our industrial experience indicates that the ability to investigate multiple models is mainly of academic value. Loose specifications arise because specifications generally need to be stated at a higher level of abstraction than that of the final implementation. Looseness enables the modeller to express that it does not matter which particular value a certain expression yields, as long as it fulfils certain requirements [22]. VDM++ and VDM-RT allow concurrent threads to be defined. Such threads are synchronised using permission predicates that are associated with any operations that cannot allow concurrent execution. Where pre-conditions for an operation describe the condition the caller must ensure before calling it, the permission predicate describes the condition that must be satisfied before the operation can be activated, and until that condition is satisfied the operation call is blocked. The permission predicates can refer to instance variables as well as history counters which indicate the number of times an operation has been requested, activated or completed for the current object. In VDMRT, the concurrency modelling can be enhanced by deploying objects on different CPUs with busses connecting them. Operations called between CPUs can be asynchronous, so that the caller does not wait for the call to complete. In addition, threads can be declared as periodic, so that they run autonomously at regular intervals. For periodic threads it is also possible to express jitter, start time offset as well as the minimum arrival time between occurences of the operation used in a periodic thread3 . VDM-RT has a special system class where the modeller can specify the hardware architecture, including the CPUs and their bus communication topology; the dialect provides two predefined classes for the purpose, CPU and BUS. CPUs are instantiated with a clock speed (Hz) and a scheduling policy, either First-come, first-served (FCFS) or Fixed priority (FP). The initial objects defined in the model can then be deployed to the declared CPUs using the CPU’s deploy and setPriority operations. Buses are defined with a transmission speed (bytes/s) and a set of CPUs which they connect. Object instances that are not deployed to a specific CPU (and not created by an object that is deployed), are automatically deployed onto a virtual CPU. The virtual CPU is connected to all real CPUs through a virtual bus. Virtual components are used to simulate the external environment for the model of the system being developed. The semantics of VDM-RT has been extended with the concept of discrete time, such that all computations a thread performs take time, including the transmission of messages over a bus. Time delays can be explicitly specified by special duration and cycles statements, allowing the modeller to explicitly state that a statement or block consumes a known amount of time. This can be specified as a number of nanoseconds or a number of CPU cycles of the CPU on which the statement is evaluated. All virtual resources are infinitely fast: calculation can be performed instantaneously consuming no time, though if an explicit duration statement is evaluated on a virtual CPU, the system time will be incremented by the duration. The formal semantics of the kernel of VDM-RT is provided in [39] in an operational semantics style. This uses an interleaving semantics without restricting nondeterministic choices; in particular there is no requirement for specific scheduling poli3
In the current version of the VDMJ interpreter the allowable jitter is simply monitored but it is expected that the user in the future will be able to specify a desired jitter distribution.
cies. Thus, the semantics enables multiple different interleavings and as such the deterministic execution provided by the VDMJ [2] interpreter described here can be seen as one possible scheduling of a model containing non-determinism. Effectively, all the other models are ignored from the interpreter’s perspective. The VDM interpreter in Overture Tool is a Java implementation called VDMJ. It implements the scheduling and debug principles presented in this paper. It supports all VDM dialects, allowing deterministic interpretation of sequential, multi-threaded and distributed VDM models.
3
Interpreting Sequential VDM Models
In order to simulate the evaluation of functions or operations from a VDM model an interpreter first needs to be initialised with the definitions declared in the model. This is achieved with a tree traversal of the AST produced by the parser. Essentially, the syntactic definitions must be transformed into their semantic equivalent representations. However, since VDM is not designed to be interpreted, this transformation can be quite complicated, because of the potential dependencies between different definitions. Note however that the interpreter presented here operate with specific values and not symbolic values [20]. The initialization of a specification amounts to the evaluation of the state of the system, either in VDM-SL state definitions or VDM++ and VDM-RT static class definitions. This involves the evaluation of initialization clauses for the various state definitions, guided by their definition dependency ordering. VDM does not define a syntactic order for the evaluation of definitions, but rather the order is inferred from the definitions’ dependencies on each other: definitions that do not depend on others are initialized first, in any order; then definitions that depend on those are initialized, and so on4 . Every time a specification is re-initialized, it returns to the same initial state. When the initialisation is complete, the interpreter is ready to start the evaluation of a test expression, making use of the definitions and state from the VDM model. In order to perform such a test evaluation, the interpreter creates a runtime context that initially contains the values defined in the state (if any). The evaluation then proceeds by evaluating any arguments, by direct recursive traversal evaluation of the argument expressions in the AST, and then executing the function or operation body in a new stack frame that is linked to the state context. The evaluation evolves in a natural recursive fashion, reflecting the function or operation call structure of the specification on one particular thread. The interpreter is also able to check all pre- and post-conditions, type and state invariants, recursive measures and performs general runtime type checking. The additional checks can be switched on or off whenever the user requires additional checking. Extra checking naturally has an impact on the performance of the interpreter but this may be a faster way to discover problems in VDM models. Semantically, bottom values (denoting undefined) will result from different kinds of run-time errors from the interpreter depending upon whether such checks are performed or not, but the behaviour 4
There are some dependency orders which are perfectly legal VDM, but which cannot be calculated by the interpreter.
will always be deterministic. The special check for termination of recursive functions may be worth a special mention, since this is (as far as we know) not performed by any other interpreters. In VDM it is possible to define so-called measure functions that must be monotonically decreasing for recursive calls. This can be checked at run-time by the interpreter such that infinite recursion will always be detected. These additional checks, based on predicates, correspond to the kind of checking carried out in JML [6]. This can be illustrated using the conventional Factorial function (in the post-condition here a basic library function from the MATH library is used). The ordering of the additional checking can be seen in Figure 1. public Factorial: nat -> nat1 Factorial(n) == if n = 0 then 1 else n * Factorial(n - 1) pre n >= 0 post RESULT = MATH‘fac(n) measure Id; Id : nat -> nat Id(n) == n;
Fig. 1: Additional predicate checking in function evaluation.
Some VDM expressions contain looseness, for example a choice construct called a let-be expression looks like: let a in set {1,2} in a. This expression denotes
either 1 or 2 but it is deliberately left as an implementation choice for the implementer from a refinement perspective. In order to be able to reproduce executions, the interpreter must thus choose one of the possible semantic models in order to produce a deterministic interpretation. In the same way iterations over a set of elements must be performed in the same order every time to ensure a deterministic result. As a result, the evaluation of any sequential VDM model by the interpreter will always produce the same result value, even if looseness means that the result cannot be predicted (easily) ahead of time. With respect to the interpretation of logical expressions it is also worth mentioning that the standard left to right evaluation used in most programming languages are used. This means that from a semantic perspective it is equivalent to McCarthy logic [28] instead of the standard Logic of Partial Functions (LPF) handling of undefinedness in VDM [18,10]. Alternatively one could start parallel threads interpreting each subexpression in a logical expression and then only yield a run-time error (denoting undefined) in the event that none of them yield a result that is sufficient to determine the truth value of the logical operator according to the traditional LPF rules. However, it has been decided that the extra complexity of this would not be worthwhile.
4
Interpreting Concurrent Real-Time models
All VDM-SL specifications and non-threaded VDM++ specifications result in a simple single threaded evaluation, as described above. Their execution will always produce the same result because VDMJ treats all loose operations as under-determined rather than non-deterministic [26]. VDM-RT simulates distributed systems and thus the initialisation process explained for sequential VDM models above also needs to deal with the deployment of object instances to CPUs, for example. The user indicates the intended deployment in the special system class and so the interpreter needs to extract the necessary information from the AST of that class. In addition, the interpreter needs to make use of the deployment information to determine whether interprocess communication over a BUS is necessary. It is also worthwhile noting that if an object instance creates new object instances (using the new constructor) during the interpretation, those new instances must be deployed on the same CPU by the interpreter. Note that the interpreter here abstracts away from the challenges of being able to determine the global state in a distributed system [1]. Since the interpreter always will have consistent information about all the CPU’s at any point of time the traditional issues with unsynchronised clocks and dependability in distributed systems [21]. VDM++ and VDM-RT specifications can have multiple threads of execution, and their evaluation can easily become non-deterministic since the thread scheduling policy is deliberately left undefined in the VDM semantics. In order to eliminate this looseness, VDMJ uses a scheduler which coordinates the activity of all threads in the system and allows them to proceed, according to a policy, in a deterministic order. This guarantees repeatable evaluations even for highly threaded VDM specifications. VDMJ implements VDM threads using Java threads as opposed to a stack machine. Previous experience [14] with an implementation of a single threaded stack machine
turned out to be slower because of e.g. swap overhead. The Java language does not define the operation of the underlying JVM thread scheduler. Instead, Java places various constraints on the ordering of events that must occur between threads, in the light of synchronization primitives that control access to shared resources. This means that although a given Java program will have a well defined partial ordering of some of its operations, the JVM thread scheduler has a great deal of freedom regarding how to execute threads that are not using synchronized access to variables or methods. In particular, there is no way to control which thread gets control of which (real) CPU in the system. In order to ensure that the VDMJ interpreter is fully independent of the JVM scheduler we need to use Java synchronization primitives to enforce the semantics of the various VDM language features to control concurrency (permission predicates and mutexes), and to control the scheduling order and duration of timeslices allocated to different threads. VDMJ scheduling is controlled on the basis of multiple Resources by a Resource Scheduler. A Resource is a separate limited resource in the system, such as a CPU or a bus (see Figure 2). These are separate in the sense that multiple CPUs or busses may exist, and limited in the sense that one CPU can only run one thread at a time, and one bus can only be transmitting one message at a time. Therefore there is a queue of activity that should be scheduled for each Resource – threads to run on a CPU, or messages to be sent via a bus. The Resource Scheduler is responsible for scheduling execution on the Resources in the system.
Fig. 2: Overview of the VDM-RT resource scheduler. An interpreter (of any VDM dialect) has a single Resource Scheduler. A VDM-SL or VDM++ simulation will have only one CPU Resource (called a virtual CPU) and no bus Resources; a VDM-RT system will have as many CPUs and busses as are defined in the system class.
Every Resource has a scheduling policy5 potentially different for each instance of the Resource. A policy implements methods to identify the thread that is best to run next and for how long it is to be allowed to run (its timeslice). With VDM-RT, in the event that the active thread is trying to move system time, the Resource will identify this fact. The Resource Scheduler is responsible for waiting until all Resources are in this state, and then finding the minimum time step that would satisfy at least one of the waiting threads. System time is moved forward at this point, and those threads that have their time step satisfied are permitted to continue, while those that need to wait longer remain suspended. This reflects the semantics of VDMRT as defined in [39,17]. For example, if there are two threads running (on two CPUs), the Resource Scheduler will offer each of them timeslices in turn. When one thread wishes to move system time by (say) 10 units, its CPU Resource will indicate this to the Resource Scheduler, which will stop including it in the scheduling process, allowing the other CPU’s thread to run. Eventually, the second thread will also want to move system time (typically at the end of a statement), say by 15 units, and its CPU Resource will also indicate this to the Resource Scheduler. At this point, all active threads want to move time, one by 10 and the other by 15. The minimum time step that will satisfy at least one thread is a step of 10 units. So the Resource Scheduler moves system time by 10, which releases the first thread; the second thread remains trying to move time, but for the remaining 5 units that it needs. By default, all statements take a duration of 2 cycles, calculated with reference to the speed of the CPU on which the statement is executed. Statements (or blocks of statements) can have their default times overridden by duration and cycles statements. The core of the scheduler is illustrated by: progressing := false; for all resource in set resources do -- record if at least one resource is able to progress progressing := CanProgress(resource) or progressing; let timesteps = {resource.getTimestep() | resource in set resources}\{nil} in -- nobody can progress and nobody is waiting for time if not progressing and timesteps = {} then error -- deadlock is detected -- nobody can progress and somebody is waiting for time elseif not progressing and timesteps {} then let mintime = Min(timesteps) in (SystemClock.advance(mintime); for all resource in set resources do AdvanceTime(resource,mintime)) else -- continue scheduling
5
As described in Section 2, this can currently be either a “First Come First Served” or a “Fixed Priority” scheduling policy, but more could be added in the future and parameterisation of these can be imagined.
The initial loop establishes whether any resources can progress. The CanProgress operation does a compute step for the Resource, if possible. The progressing flag will be true if any Resource was able to progress. The getTimestep operation either returns the timestep requested by the Resource, or nil, indicating that it is not currently waiting for time to advance. If no Resource can progress and no Resource is waiting for a timestep, the system is deadlocked. Otherwise, if no Resource can progress and at least one is waiting for a timestep, then system time can advance by the smallest requested amount. In this event, every Resource is adjusted by the minimum step, which will result in at least one Resource being able to progress. This scheduling process continues until the original expression supplied by the user completes its evaluation. The critical point, for deterministic behaviour, is that only one Java thread is ever running at a given point of time: either a thread associated with a CPU or a bus Resource, or the Resource Scheduler itself (which runs in the main thread). Furthermore, threads run in a deterministic order and for deterministic timeslices, since the scheduling policies are deterministic. Timeslices are deterministic because they are implemented as the execution of a specific number of VDM statements or expressions, rather than a period of time. In VDM++ and VDM-RT threads are synchronised using permission predicates and this adds to the complexity of the VDMJ interpreter. As explained in Section 2 permission predicates can depend on the value of instance variables and history counters. An object’s history counters contain information about the history of requests, invocations and completions of all operations for the object. Here it is important to ensure that threads that have been blocked are awoken in a deterministic fashion. Given that the threads are otherwise running in a deterministic fashion, this is easily arranged since all the threads waiting for a history counter change (or a state variable value change) already have a fixed position in their CPU Resource’s scheduling list. When the waiting threads are signalled, all applicable threads are signalled at the same time, and no new threads can be scheduled until this is complete. So the way that the scheduling evolves after a history event (or state change) is deterministic, since the threads waiting at any point is also deterministic and the scheduling is deterministic. Finally, threads that are defined as periodic also need to be taken into account in the scheduling. A simple periodic thread will calculate that it needs to wait until system time has moved (say) 100 time units before it should execute its body again. The thread does this calculation when it is scheduled to run, which is deterministic since the scheduler is deterministic; the thread is re-scheduled when the required time is reached, which again is deterministic because the movement of time only occurs when there is no scheduling activity. Periodic threads can also have more complex repeat semantics, limiting the inter-run period for example, or adding random jitter to the period. However these calculations are still deterministic since a seedable pseudo-random number generator is used for the jitter. The generator is re-seeded whenever the specification is re-initialized. This guarantees the overall execution will be the same, given the same sequence of evaluations after an initialization.
5
Creating a Deterministic Debugger
In order to get a deterministic debugger for an interpreted language the interpreter naturally needs to be deterministic in itself as explained above. However, when a language includes concurrent threads, debuggers which have a deterministic interpreter are not necessarily deterministic since the debugging may effect the execution. Conventional debuggers for multi-threaded applications are usually non-deterministic because threads suspended for debug do not share their actual thread state and are just excluded from the scheduling, letting other threads carry on as normal. An example of such a debugger is the Java debugger in Eclipse and the C# debugger in Visual Studio. In the Java Virtual Machine (VM) a thread can be suspended through the Java Debug Wire Protocol (JDWP) [29] for inspection. However, the state of the suspended thread is not changed while the thread is stopped, making any other thread unable to detect that the thread was suspended. This causes non-deterministic scheduling since the suspended thread can no longer be scheduled, thus decreasing the number of threads in the scheduling algorithm by one. Even though the VM supports a total suspend of the entire VM, the problem still applies, since it makes use of the same internal mechanism, suspending one thread at a time, risking non-deterministic scheduling during the suspend process. 5.1
Deterministic Debugging
In VDMJ, deterministic scheduling is guaranteed during debugging. This ensures that threads will always be scheduled in the same order, independent of any debug actions made by the user. In relation to conventional debuggers, this is like a suspend of the VM causing the scheduling order to be preserved during debug. The main difference is that no single thread can be suspended, while others continue. If a thread hits a breakpoint which forces it into debug mode, its execution will be suspended and a signal will be sent simultaneously to all other threads to do the same. Similarly, no thread can continue before all threads have returned from debug mode. The debugger allows all threads to be inspected while in debug mode, because in effect, they all stopped at the same breakpoint. In order for interpretation to continue, all threads must be signalled and receive the same continue command from the user interface. The order in which the commands are sent to the threads is irrelevant because interpretation can only be continued after all threads have received the signal. Great care is taken not to lose signals during this process. The thread which initiated the debug session is signalled last during a resume, so that the other threads are in the state that they were before we hit the breakpoint. The thread order of this signal communication cannot affect the scheduling order, since it is strictly controlled and independent of the debugging session. 5.2
Debugging User Interface
A debugging interface must be both compelling and simple. However it must provide enough information and control of the model being debugged to determine if the inspected behaviour is correct. In Overture, the debug interface is based on the Debug feature of the Eclipse platform, allowing a simple, standardized interaction during debugging. A protocol is required to connect the debug interface with the interpreter
(VDMJ). The chosen protocol in Overture is Xdebug DBGP6 [32]. This protocol supports the debugging of multi-threaded models and provides commands for both control and retrieval of state information from the executing model. The protocol is designed to keep the debugger as simple as possible, using simple text commands for control with more complex XML responses to the user interface with information about the model or the general state of the interpreter. To integrate the DBGP protocol with the Eclipse Debug feature, a Debug Target is required which defines the connection between the debug interface included in Eclipse and the protocol. The debug target is responsible for sending commands to the interpreter and decoding responses either as state change of the interpreter, or as information representing threads, stack frames, variables and values.
(a) Normal flow of the debug protocol.
(b) Break in the debug protocol.
Fig. 3: Sequence diagrams of interaction with the debugger.
In Figure 3a the normal flow of debugging is shown for a single thread. When the thread connects to the debug target it sends an init command with a session id. This causes the debug target to initialize a debugging session and subsequently start the configuration of the debugger, redirecting output and setting debugger features and breakpoints followed by a run command starting the executing. This will allow the debugger to start the interpretation of the model, then the interpreter either hits a breakpoint shown in Figure 3b or runs to completion sending a stopped command to the debug target signalling the end of the debugging session. If a multi-threaded model is executed only the main thread will send the init command while other threads created later just connect with the session id which allows the debug target to group them into the same debug session. The server will always configure all threads after a connection has been established. 6
A common debugger protocol for languages and debugger UI communication.
Figure 3b shows the case where a breakpoint is hit in a thread. This causes the thread to send a break message notifying that a breakpoint was hit. When this happens the debugger will start populating the user interface with call stack through stackGet and variables which are in scope per selected stack frame by the getContext command. Threads with a short lifetime and with no breakpoints only report their existence if they exist when a breakpoint is hit, this avoids overloading of the debugging protocol in case many async operations and periodic threads are part of the model. This may seem trivial but in order to achieve a resonable performance of the interpretation of a VDM-RT model in the debugger this is a paramount design decision. 5.3
Debugging Multi-threaded applications
Concurrent VDM models makes use of permission predicates to control concurrent behaviour, by allowing synchronization of operations to be explicitly specified. However if thread execution is restricted by predicates, which includes history counters, then it is generally difficult to determine why a certain thread is blocked. To improve the debugging of multi-threaded models, VDMJ keeps track of all threads and all associated history counters and provides this information through the debug interface. This allows a user to inspect threads blocked by permission predicates as shown in Figure 4. All history counters associated with a thread will be available with the current value and operation name e.g. #fin(ClientListen).
Fig. 4: Debug UI Showing history counter values.
The permission predicate shown in Figure 4 from the POP3 client/ server application [13], guards the operation ClientSend to ensure a balance between send and received messages. The permission predicate includes several history counters, thus the variables view provides information about these. In Figure 4 it is easy to deduce the reason why a permission predicate may be blocked. From a usability perspective this is a very important feature making it much easier for users to deduce the reasons why a concurrent model is not behaving as imagined.
6
Related Work
The development that is closely related to the sequential part of this work is clearly the interpreter from VDMTools [24]. That interpreter has internally defined its own virtual machine and ensures its deterministic execution by simply having one master scheduler that controls all threads. However from a performance point of view the VDMJ interpreter is usually significantly faster than the interpreter from VDMTools, the exceptions being test cases that use higher order functions. The B-Method is also a model-oriented formal method and here the ProB tool [27] is related to the work presented here. Among other features ProB contains an animator feature. Since B focus on implicit definitions the executable subset is smaller than what we are able to cope with in this paper, and concurrency and distributed systems cannot be incorporated since it is not included in the B notation. The ProB interpreter is also deterministic but focus is more put on enabling the user to apply operations that have acceptable pre-conditions interactively rather than on traditional testing which is enabled with the VDMJ interpreter presented here. The ProB tool has also been used to animate Z specifications [30]. For Z many others have also produced interpreters/translators to programming languages for subsets of the notation [9,38,5,35]. Common to all these papers is that they do not include concurrency in the Z notation and thus do not have the challenges created for keeping determinism as presented in this paper. The POOSL approach has significant similarities with VDM-RT in its ability to describe a distributed system in a model-oriented fashion [37,15]. Here they have decided to resolve non-determinism by always selecting the first option when a finite collection of possibilities exists, though it is stated that it is possible that this way of interpreting the non-determism can be changed in the future. However, they do not have the interactive debugging functionality described in this paper, but are limited to logging primitives. A significantly different approach for interpreting formal models can be found in Maude [8]. However this technology is based on algebraic approaches using term rewriting and thus it is significantly different from the approach presented in this paper.
7
Concluding Remarks
Deterministic interpreters for distributed concurrent models are essential for repeatability of test executions. In this paper we have presented one approach for implementing an interpreter in Java for an executable subset of VDM, and a scheduling algorithm which takes distribution over resources and scheduling of threads within resources into account to provide deterministic execution. We have also presented how VDMJ is different to conventional debuggers by enabling debug without affecting the scheduling of threads. We believe that this property is essential in order for users to be able to discover why a VDM model with concurrency behaves in a specific way. In addition we have explained some of design decisions taken both to increase performance as well as for increasing the usability. We believe that the same principles can be adapted to interpreters for other formal modelling languages such as Circus [41].
Ongoing work on the semantics of a core kernel of VDM is currently being carried out. It is expected that the results of this effort will provide a more rigorous presentation of the material presented here. This will include mappings from the different VDM dialects to VDM Core as well as considerations for the models that are being ignored by the interpreter as well. Regarding the GUI debugger, we expect to improve the functionality by creating better ways of providing overviews of the status of all threads at all CPUs simulated in a VDM-RT model. Such improvements will make it easier for the user to quickly deduce the underlying reasons behind potential issues.
Acknowledgements The work reported in this paper have partly been carried out in the DESTECS project which have partially been funded by the European Commission. In addition we would like to thank Marcel Verhoef, Sune Wolff and the anonymous reviewers for valuable input on the work presented here.
References ¨ Marzullo, K.: Consistent Global States of Distributed Systems: Fundamental 1. Babaoglu, O., Concepts and Mechanisms. Tech. Rep. UBLCS-93-1, University of Bologna, Piazza di Porta S. Donato, 5, 40127 Bologna (Italy) (January 1993) 2. Battle, N.: VDMJ User Guide. Tech. rep., Fujitsu Services Ltd., UK (2009) 3. Bergan, T., Anderson, O., Devietti, J., Ceze, L., Grossman, D.: CoreDet: A Compiler and Runtime System for Deterministic Multithreaded Execution. In: ASPLOS’10. ACM (March 2010) 4. Bjørner, D., Jones, C. (eds.): The Vienna Development Method: The Meta-Language, Lecture Notes in Computer Science, vol. 61. Springer-Verlag (1978) 5. Breuer, P., Bowen, J.: Towards correct executable semantics for z. In: Bowen, J., Hall, J. (eds.) Z User Workshop. pp. 185–209. Springer-Verlag (1994), cambridge 6. Burdy, L., Cheon, Y., Cok, D., Ernst, M.D., Kiniry, J.R., Leavens, G.T., Leino, K.R.M., Poll, E.: An overview of JML Tools and Applications. Intl. Journal of Software Tools for Technology Transfer 7, 212–232 (2005) 7. Burnin, J., Sen, K.: Asserting and checking determinism for multithreaded programs. In: 17th ACM SIGSOFT Symposium on the Foundations of Software Engineering (FSE). ACM (2009) 8. Clavel, M., Dur´an, F., Eker, S., Lincoln, P., Mart´ı-Oliet, N., Meseguer, J., Talcott, C.L. (eds.): All About Maude - A High-Performance Logical Framework, How to Specify, Program and Verify Systems in Rewriting Logic, Lecture Notes of Computer Science, vol. 4350. SpringerVerlag (2007) 9. Dick, A., Krause, P., Cozens, J.: Computer aided transformation of Z into Prolog. In: Nicholls, J. (ed.) Z User Workshop, Oxford 1989. pp. 71–85. Workshops in Computing, Springer-Verlag (1990) 10. Fitzgerald, J.S.: The Typed Logic of Partial Functions and the Vienna Development Method. In: Bjørner, D., Henson, M.C. (eds.) Logics of Specification Languages, pp. 427–461. EATCS Monographs in Theoretical Computer Science, Springer (2007)
11. Fitzgerald, J.S., Larsen, P.G., Verhoef, M.: Vienna Development Method. Wiley Encyclopedia of Computer Science and Engineering (2008), edited by Benjamin Wah, John Wiley & Sons, Inc 12. Fitzgerald, J., Larsen, P.G.: Modelling Systems – Practical Tools and Techniques in Software Development. Cambridge University Press, The Edinburgh Building, Cambridge CB2 2RU, UK, Second edn. (2009), ISBN 0-521-62348-0 13. Fitzgerald, J., Larsen, P.G., Mukherjee, P., Plat, N., Verhoef, M.: Validated Designs for Object–oriented Systems. Springer, New York (2005), http://www.vdmbook.com 14. Fitzgerald, J., Larsen, P.G., Sahara, S.: VDMTools: Advances in Support for Formal Modeling in VDM. ACM Sigplan Notices 43(2), 3–11 (February 2008) 15. Florescu, O., Voeten, J., Verhoef, M., Corporaal, H.: Reusing Real-Time Systems Design Experience Through Modelling Patterns. In: Forum on specification and Description Languages (FDL). ECSI (2006), received the best paper award at FDL 2006. This paper is available online at http://www.es.ele.tue.nl/premadona/publications/FVVC06.pdf. 16. Fr¨ohlich, B.: Towards Executability of Implicit Definitions. Ph.D. thesis, TU Graz, Institute of Software Technology (September 1998) 17. Hooman, J., Verhoef, M.: Formal semantics of a VDM extension for distributed embedded systems. In: Dams, D., Hannemann, U., Steffen, M. (eds.) Concurrency, Compositionality, and Correctness, Essays in Honor of Willem-Paul de Roever. Lecture notes in Computer Science, vol. 5930, pp. 142–161. Springer-Verlag (2010) 18. Jones, C.B.: Program Specification and Verification in VDM. Logic of Programming and Calculi of Discrete Design F36, 149–184 (1987) 19. Jones, C.B.: Systematic Software Development Using VDM. Prentice-Hall International, Englewood Cliffs, New Jersey, second edn. (1990), iSBN 0-13-880733-7 20. Kneuper, R.: Symbolic Execution as a Tool for Validation of Specifications. Ph.D. thesis, Department of Computer Science, Univeristy of Manchester (March 1989), technical Report Series UMCS-89-7-1 21. Lamport, L.: Time, Clocks, and the Ordering of Events in a Distributed System. Communications of the ACM 21(7), 558–565 (July 1978) 22. Larsen, P.G.: Evaluation of underdetermined explicit expressions. In: M. Naftalin, T. Denvir, M.B. (ed.) FME’94: Industrial Benefit of Formal Methods. pp. 233–250. Springer-Verlag (October 1994) 23. Larsen, P.G., Battle, N., Ferreira, M., Fitzgerald, J., Lausdahl, K., Verhoef, M.: The Overture Initiative – Integrating Tools for VDM. ACM Software Engineering Notes 35(1) (January 2010) 24. Larsen, P.G., Lassen, P.B.: An Executable Subset of Meta-IV with Loose Specification. In: VDM ’91: Formal Software Development Methods. VDM Europe, Springer-Verlag (March 1991) 25. Larsen, P.G., Lausdahl, K., Battle, N.: The VDM-10 Language Manual. Tech. Rep. TR-201006, The Overture Open Source Initiative (April 2010) 26. Larsen, P.G., Pawłowski, W.: The Formal Semantics of ISO VDM-SL. “Computer Standards and Interfaces” 17(5–6), 585–602 (September 1995) 27. Leuschel, M., Butler, M.J.: ProB: an automated analysis toolset for the B method. STTT 10(2), 185–203 (2008) 28. McCarthy, J.: A Basis for a Mathematical Theory of Computation. In: Western Joint Computer Conference (1961), then published in: Computer Programming and Formal Systems (P.Braffort, D.Hirstberg eds.) North Holland 1967, 33–70 29. Microsystems, S.: Java Debug Wire Protocol. Sun Microsystems, Inc, 1.5.0 edn. (2004), http://download.oracle.com/javase/1.5.0/docs/guide/jpda/ jdwp-spec.html
30. Plagge, D., Leuschel, M.: Validating Z Specifications using the ProB Animator and Model Checker. In: Integrated Formal Methods (IFM 2007), LNCS 4591. pp. 480–500. SpringerVerlag (2007) 31. Plat, N., Larsen, P.G.: An Overview of the ISO/VDM-SL Standard. Sigplan Notices 27(8), 76–82 (August 1992) 32. Rethans, S.C.A.D.: A Common Debugger Protocol for Languages and Debugger UI Communication. XDEBUG, 2 edn. (- 2011), http://www.xdebug.org/docs-dbgp.php 33. Ribeiro, A., Larsen, P.G.: Proof obligation generation and discharging for recursive definitions in VDM. In: Song, J., Huibiao (eds.) The 12th International Conference on Formal Engineering Methods (ICFEM 2010). Springer-Verlag (November 2010) 34. Rushby, J.: Formal Methods: Instruments of Justification or Tools for Discovery? In: Nordic Seminar on Dependable Computing System 1994. The Technical University of Denmark, Department of Computer Science (August 1994) 35. Sherrell, L.B., Carver, D.L.: Experiences in Translating Z Designs to Haskell Implementations. Software Practice and Experience 24(12), 1159–1178 (December 1994) 36. Søndergaard, H., Sestoft, P.: Non-determinism in Functional Languages. The Computer Journal 35(5), 514–523 (October 1992) 37. Theelen, B., Florescu, O., Geilen, M., Huang, J., van der Putten, P., Voeten, J.: Software/hardware engineering with the parallel object-oriented specification language. In: Proceedings of the ACM-IEEE International Conference on Formal Methods and Models for Codeesign (MEMOCODE). pp. 139–148. IEEE Computer Society, Los Alamitos (USA) (2007), ISBN 1-4244-1050-9 38. Valentine, S.: Z−− , an executable subset of Z. In: Nicholls, J. (ed.) Z User Workshop, York 1991. pp. 157–187. Workshops in Computing, Springer-Verlag (1992) 39. Verhoef, M.: Modeling and Validating Distributed Embedded Real-Time Control Systems. Ph.D. thesis, Radboud University Nijmegen (2008), ISBN 978-90-9023705-3 40. Verhoef, M., Larsen, P.G., Hooman, J.: Modeling and Validating Distributed Embedded RealTime Systems with VDM++. In: Misra, J., Nipkow, T., Sekerinski, E. (eds.) FM 2006: Formal Methods. pp. 147–162. Lecture Notes in Computer Science 4085 (2006) 41. Woodcock, J.C.P., Cavalcanti, A.L.C.: A concurrent language for refinement. In: Butterfield, A., Pahl, C. (eds.) IWFM’01: 5th Irish Workshop in Formal Methods. BCS Electronic Workshops in Computing, Dublin, Ireland (July 2001)