1. Technical Field
The present invention relates to program analysis and more particularly to system and methods for analyzing memory models.
2. Description of the Related Art
In a multi-threaded shared memory system, a memory consistency model or a memory model is a contract between a programmer and a programming environment. The memory model specifies the behavior of accesses to shared locations, and specifically, the values observed by each read access. There is a vast amount of work on models at a hardware interface, and more recently, at a programming language level. The most intuitive memory model is a sequential consistency model, which is similar to the behavior of memory in a sequential setting. This requires that all accesses appear to execute one at a time, respecting the program order of each thread. This simplicity comes at a price: sequential consistency disallows many compiler and hardware optimizations that reorder instructions, because it enforces a strict order among accesses. Many relaxed memory models have been proposed that ease these restrictions, striking a balance between ease-of-programming and allowing compiler and hardware optimizations. However, the added complexity makes relaxed memory models difficult to reason about.
A system and method for analyzing a test program with respect to a memory model includes preprocessing a test program into an intermediate form and translating the intermediate form of the test program into a relational logic representation. The relational logic representation is combined with a memory model to produce a legality formula. A set of bounds are computed on a space to be searched for the memory model or on a core of the legality formula. A relational satisfiability problem is solved, which is defined by the legality formula and the set of bounds to determine a legal trace of the test program or debug the memory model.
A system for analyzing a test program with respect to a memory model includes a processor configured to execute and analyze programs stored in memory. The processor receives as input a test program converted to an intermediate form and a memory model. A translation module is stored in memory and executed using the processor. The translation module is configured to translate the intermediate form of the test program into a relational logic representation of the test program using a translation function. A constraint assembler, stored in memory and executed using the processor, is configured to combine the relational logic representation with the memory model to produce a legality formula. A bound assembler, stored in memory and executed using the processor, is configured to compute a set of bounds on a space to be searched for the memory model or a core of the legality formula. A solver is configured to solve a relational satisfiability problem defined by the legality formula and the set of bounds to at least one of determine a legal trace of the test program and debug the memory model.
These and other features and advantages will become apparent from the following detailed description of illustrative embodiments thereof, which is to be read in connection with the accompanying drawings.
The disclosure will provide details in the following description of preferred embodiments with reference to the following figures wherein:
Memory models are difficult to reason about due to their complexity. Memory models need to strike a balance between ease-of-programming and allowing compiler and hardware optimizations. An automated tool in accordance with the present principles helps in debugging and reasoning about memory models. The tool takes as input a memory model described axiomatically by a set of constraints, as well as a multi-threaded test program containing assertions, and outputs a trace of the program for which the assertions are satisfied, if one can be found. The tool is fully automatic, requiring no guidance from the user and is based on a satisfiability (e.g., SAT) solver.
If the tool cannot find a trace, it outputs a minimal subset of the constraints that are unsatisfiable. This feature helps the user in debugging the memory model because it shows which constraints cause the test program to have no executions that satisfy all of its assertions.
The present principles provide an extensible framework for defining memory models in an axiomatic style, a tool that takes a test program with assertions and a memory model as input, and finds a trace satisfying the assertions, if one can be found. Otherwise, the tool outputs an unsatisfiable core, which shows which constraints prevent the assertions from being satisfied.
As will be appreciated by one skilled in the art, aspects of the present invention may be embodied as a system, method or computer program product. Accordingly, aspects of the present invention may take the form of an entirely hardware embodiment, an entirely software embodiment (including firmware, resident software, micro-code, etc.) or an embodiment combining software and hardware aspects that may all generally be referred to herein as a “circuit,” “module” or “system.” Furthermore, aspects of the present invention may take the form of a computer program product embodied in one or more computer readable medium(s) having computer readable program code embodied thereon.
Any combination of one or more computer readable medium(s) may be utilized. The computer readable medium may be a computer readable signal medium or a computer readable storage medium. A computer readable storage medium may be, for example, but not limited to, an electronic, magnetic, optical, electromagnetic, infrared, or semiconductor system, apparatus, or device, or any suitable combination of the foregoing. More specific examples (a non-exhaustive list) of the computer readable storage medium would include the following: an electrical connection having one or more wires, a portable computer diskette, a hard disk, a random access memory (RAM), a read-only memory (ROM), an erasable programmable read-only memory (EPROM or Flash memory), an optical fiber, a portable compact disc read-only memory (CD-ROM), an optical storage device, a magnetic storage device, or any suitable combination of the foregoing. In the context of this document, a computer readable storage medium may be any tangible medium that can contain, or store a program for use by or in connection with an instruction execution system, apparatus, or device.
A computer readable signal medium may include a propagated data signal with computer readable program code embodied therein, for example, in baseband or as part of a carrier wave. Such a propagated signal may take any of a variety of forms, including, but not limited to, electro-magnetic, optical, or any suitable combination thereof. A computer readable signal medium may be any computer readable medium that is not a computer readable storage medium and that can communicate, propagate, or transport a program for use by or in connection with an instruction execution system, apparatus, or device.
Program code embodied on a computer readable medium may be transmitted using any appropriate medium, including but not limited to wireless, wireline, optical fiber cable, RF, etc., or any suitable combination of the foregoing. Computer program code for carrying out operations for aspects of the present invention may be written in any combination of one or more programming languages, including an object oriented programming language such as Java, Smalltalk, C++ or the like and conventional procedural programming languages, such as the “C” programming language or similar programming languages. The program code may execute entirely on the user's computer, partly on the user's computer, as a stand-alone software package, partly on the user's computer and partly on a remote computer or entirely on the remote computer or server. In the latter scenario, the remote computer may be connected to the user's computer through any type of network, including a local area network (LAN) or a wide area network (WAN), or the connection may be made to an external computer (for example, through the Internet using an Internet Service Provider).
Aspects of the present invention are described below with reference to flowchart illustrations and/or block diagrams of methods, apparatus (systems) and computer program products according to embodiments of the invention. It will be understood that each block of the flowchart illustrations and/or block diagrams, and combinations of blocks in the flowchart illustrations and/or block diagrams, can be implemented by computer program instructions. These computer program instructions may be provided to a processor of a general purpose computer, special purpose computer, or other programmable data processing apparatus to produce a machine, such that the instructions, which execute via the processor of the computer or other programmable data processing apparatus, create means for implementing the functions/acts specified in the flowchart and/or block diagram block or blocks.
These computer program instructions may also be stored in a computer readable medium that can direct a computer, other programmable data processing apparatus, or other devices to function in a particular manner, such that the instructions stored in the computer readable medium produce an article of manufacture including instructions which implement the function/act specified in the flowchart and/or block diagram block or blocks. The computer program instructions may also be loaded onto a computer, other programmable data processing apparatus, or other devices to cause a series of operational steps to be performed on the computer, other programmable apparatus or other devices to produce a computer implemented process such that the instructions which execute on the computer or other programmable apparatus provide processes for implementing the functions/acts specified in the flowchart and/or block diagram block or blocks.
The flowchart and block diagrams in the Figures illustrate the architecture, functionality, and operation of possible implementations of systems, methods and computer program products according to various embodiments of the present invention. In this regard, each block in the flowchart or block diagrams may represent a module, segment, or portion of code, which comprises one or more executable instructions for implementing the specified logical function(s). It should also be noted that, in some alternative implementations, the functions noted in the block may occur out of the order noted in the figures. For example, two blocks shown in succession may, in fact, be executed substantially concurrently, or the blocks may sometimes be executed in the reverse order, depending upon the functionality involved. It will also be noted that each block of the block diagrams and/or flowchart illustration, and combinations of blocks in the block diagrams and/or flowchart illustration, can be implemented by special purpose hardware-based systems that perform the specified functions or acts, or combinations of special purpose hardware and computer instructions.
Referring now to the drawings in which like numerals represent the same or similar elements and initially to
If the test program contains no loops, the result of tool 10 is sound and complete, meaning that there are no spurious witnesses, and if a witness exists, it is found. If the tool 10 uses an under-approximation, it may miss witnesses, so it is sound but not complete. In practice, however, we have found that most tests for memory models 12 do not contain loops.
Our case studies indicate that the tool 10 can be used to quickly and easily run multi-threaded test cases against different memory models. Previous approaches to checking memory models include theorem proving, model checking, constraint solving, and logic programming. Unlike most techniques, the present tool 10 is not bound to a specific memory model. We have a well-defined interface for specifying a new model, and supporting any memory model that can be defined in terms of constraints over a few simple relations. The present approach is different from previous frameworks in that se support an axiomatic-style of specification, and the tool 10 can handle current model versions.
The present approach permits rapid prototyping of memory models: the user can quickly see the effect of changes on the tests, and the unsatisfiable core helps to identify what constraints, if any, need to be changed to obtain the desired behavior. The tool 10 is designed as an extensible framework for specifying, testing and debugging memory models 12.
The tool 10 takes as inputs the multi-threaded test program 16 with one or more assertions 18; a specification of a memory model 12 in relational logic; and a set of code finitization parameters, such as the number of times to unwind loops and the length of bitvectors used to represent integers. The test program 16 is then finitized (by unwinding loops, inlining method calls, and replacing integers with bitvectors) and translated to relational logic. The resulting constraints are combined with the memory model constraints and handed off to a SAT-based constraint solver 22. If the combined constraints are satisfiable, the program 16 is said to be legal with respect to the memory model 12, and the output of the tool 10 is a concrete witness of legality, expressed in terms of the relations defined by the memory model 12. Otherwise, the program 16 is said to be illegal, and the output is a proof of illegality, expressed as a minimal unsatisfiable core of the combined constraints.
Test program (16): A test program consists of an (implicit) initialization thread and two or more user threads. The initialization thread executes first, writing default values to all shared memory locations referenced in the program. The user threads execute after the initialization has completed, running either in parallel or in a partial order specified by the user. Programs are encoded in a subset of Java™ that includes control flow constructs, the synchronized construct (which generates lock and unlock instructions on a given monitor), method calls, field and array accesses, integer and boolean operations, and assertions.
Referring to
Memory model specification: When a test program is executed, it performs a finite set of memory-related operations or actions. An action belongs to one thread and has one of the following action kinds: thread start, thread end, volatile read, volatile write, normal read, normal write, lock, unlock, and special action. Read, write, lock and unlock actions are generated by executing read, write and synchronize instructions. Thread start and end actions mark the beginning and termination of a thread and do not correspond to any instructions. Special actions are generated by calls to methods that are designated as “special” in the definition of a given memory model. The Java Memory Model (JMM), for example, designates all I/O methods as special, and calls to these methods affect ordering of memory operations.
The kind of an action and the thread to which it belongs are static properties that the tool 10 infers from the program text. In our framework, these properties are given as relational constants; that is, constants whose meaning is a set of tuples. For example, the relative ordering of actions within a program's control flow graph is modeled by the binary relation co. The value of co for the program in
Runtime properties of a program are given as a set of relational variables which collectively define an execution of that program. An execution is a structure E=(|A, W, V, l, m, On, . . . , Of|) in which A denotes the subset of the program's actions that are executed; the write-seen relation W maps each executed read to the write whose value is seen by that read; the value-written relation V maps each write action to the value that is written; the location-accessed relation I maps each read and write to the memory location that it accesses; and the monitor-used relation m maps each lock and unlock to its associated monitor. The definition of an execution may also include any number of ordering relations Oi over A (e.g. happens-before relations) that are specific to a given memory model.
A memory model is specified as a set of constraints over the relational constants that describe a program and the relational variables that describe an execution. The constraints are given in relational logic, that is, first order logic with quantifiers, relations, and transitive closure. One feature of the logic is that it does not distinguish between scalars, sets and relations. In particular, sets are treated as unary relations and scalars as singleton unary relations.
Example: Sequential Consistency: Sequential consistency (SC) is an easily understood memory model that simply needs that all executed actions appear in a (weak) total order that is consistent with the program order.
We define sequential consistency in terms of the execution structure E=(|A, W, V, l, m, ord|) and program constants co, to, t, Read and Write. The variable ord models the ordering of the executed actions A; the constant t maps each action in a program to the thread that executes it; the constant “to” denotes the partial execution order among threads; and Read and Write model all actions in a program whose action kind is a read or a write, respectively. The first three formulas in
Example: Java Memory Model: The Java Memory Model (JMM) specifies what behavior is legal for a given program using a “committing semantics”. An execution is legal if it can be derived from a series of speculative executions of the program, constructed according to the following rules. The first execution in the series is “well-behaved”: its reads can only see writes that happen-before them. The happens-before ordering (hb) transitively relates reads and writes in an execution according to the program order (pa) and the synchronizes-with (sw) order implied by synchronization constructs. The remaining executions in the series are derived from the initial well-behaved execution by “committing” and executing data races. After each execution, one or more data races from that execution are chosen, and the reads and writes involved in those data races are remembered, or “committed”. The committed data races are then executed in the next execution: each read in the execution must either be committed and see a committed write through a race, or it must see a write through the happens-before relation. The committed writes are also executed, and they write the committed values. Any execution reachable through this process is legal under the JMM.
Proof of legality (witness): Given a test program P and a memory model M, the tool 10 generates a legality formula F(P, M) of the form F(P,E)̂Fa(P,E)̂i=1i<k F(P,Ei) ̂Mp(E, El, . . . , Ek), where k≧0; F(P,E) is true only if the execution E respects the intra-thread semantics of P; Fa(P, E) is true only if E satisfies all of the assertions in P; and Mp(E, El, . . . , Ek) is true only if the constraints that constitute Mare satisfied with respect to the constants that describe P and the variables that define E, El, . . . , Ek. For memory models that are not specified in terms of speculative executions, F(P, M) simplifies to F(P,E) ̂Fa(P,E) ̂Mp(E).
A model of the formula F(P, M) is an assignment of relational values (i.e. sets of tuples) to the variables in E, El, . . . , Ek which makes each constraint in the formula true. This assignment, if it exists, is a concrete witness that at least one execution of P is both legal with respect to M and satisfies all the assertions in P. The tuples that comprise a model for tool 10 are drawn from a finite set, or universe, of symbolic values computed by the tool based on the program text and the values of the finitization parameters. The universe for a program P consists of six kinds of symbolic values: 1) heap objects (Note that the heap for a finitized test program P is necessarily finite: a sound upper bound on its size can be computed simply by counting the object allocation (i.e. new) statements in P.) that may be allocated by P; 2) the locations (fields) referenced within P; 3) memory actions that may be performed by P; 4) the threads that comprise P; 5) the bit values used for representing integers; and 6) the boolean values true and false.
Referring to
Operationally, the execution E in
Proof of illegality (minimal core): A formula that has no models is said to be unsatisfiable. Unsatisfiability of a formula F(P, M) for tool 10 means that the (finitized) program P has no executions that are legal with respect to M and that also satisfy all the assertions in P. If the user of the tool 10 expects P to be legal with respect to M, a lack of witnesses indicates a bug either in the specification of M or in the encoding of P or in the setting of the finitization parameters. But even if P is expected to be illegal, the unsatisfiability of F(P, M) alone is not a sufficient indicator that M and P are free of bugs. The formula may be trivially unsatisfiable, for example, because M is overconstrained, admitting no executions of any P.
To aid in the understanding of causes of illegality, the tool 10 outputs a minimal unsatisfiable core for each unsatisfiable formula F(P, M). An unsatisfiable core is a subset of the formula's constraints that is itself unsatisfiable. Every such subset includes one or more critical constraints that cannot be removed without making the remainder of the core satisfiable. Non-critical constraints, if any, are irrelevant to unsatisfiability and generally decrease a core's diagnostic utility. Cores that include only critical constraints are said to be at minimal.
An example of a minimal core produced by the tool 10 in accordance with the present principles is illustratively shown in
We expect all sequentially consistent executions of the test program 16 in
Referring to
The translator 204, constraint assembler 206, and bounds assembler 208 are provided in accordance with the present principles. Preprocessing and solving may utilize known modules for program analysis libraries and constraint solvers.
The present approach uses of relational logic, which extends first-order logic with relational algebra and signed bitvector arithmetic. This logic is a relation: a set of tuples of equal length, drawn from a common universe of atoms. Atoms can denote integers or uninterpreted symbolic values. The arity of a relation, which can be any positive integer, determines the length of its tuples. We refer to unary relations (i.e. relations of arity 1) as “sets” and to singleton unary relations as “scalars.”
The kernel of the logic, illustratively shown in
Preprocessing (202): To translate a test program P to relational logic, tool 10 first, finitizes P's code by unwinding all loops and inlining all method calls. The finitized code is then transformed into an intermediate form that captures its data, control and synchronization dependencies. The intermediate form of P is the structure I(P)=(|efg, guard, pointsTo, maySee|), in which efg denotes the extended control flow graph of P; guard maps each instruction in efg to the control conditions that guard its execution; pointsTo maps each variable to the heap objects, if any, that it may point to at runtime; and maySee maps each read in efg to the set of writes that it may observe. All four components of I(P) are computed using standard analyses (using e.g., WALA tools).
An example of I(P) is shown in
Translation (204): The translation of a preprocessed program I(P) to its relational representation R(P) relies on a translation function T: JExper→Expr (
For expressions that are not defined by heap reads, the function T yields the same relational expressions as prior encodings for sequential programs.
The relational representation R(P) is a stricture (|I(P), L, V, G|), which captures the semantics of the program I(P) with the partial functions L, V and G (
Ifs is a read or a write of a static field f, L[[s]] yields the constant relation f whose value, {f}, consists of the atom that represents the field f. If s reads or writes an instance field f, L[[s]] yields f∪T└└vref┘┘, whose value is a set of two unary tuples, one of which represents the field f and the other the object referenced by vref. For monitor statements, L[[s]] produces an expression that evaluates to the object that is locked or unlocked by s. The function V maps writes and assertions to relational encodings of the values that they write or assert. The function G takes each statement s in its domain to a relational formula that represents the guard of s.
Constraint assembly (206): Tool 10 encodes the legality of a program R(P) with respect to a memory model MP(E, El, . . . , Ek) using the recursive constraint assembly procedure defined in
The procedure F takes as input a relational representation R(P) and a memory model specification MP(E, El, . . . , Ek) and produces the legality formula F(P, M). The base step, F(s, Ei), allocates a fresh unary relation asi for each statement s and execution Eiε{E, El, . . . , Ek} to represent the action that Ei performs if it executes s. The function σ(fe, Ei) replaces all placeholder relations pv in the formula or expression fe with Vi[Wi[F(def (v),Ei]], which is the value observed by the read that defines the variable v in the context of Ei. In other words, the application of σ supplants the placeholders generated in the translation stage with the values specified by the memory model.
The recursive step F(R(P),Ei) constrains the execution Ei to respect the semantics of R(P) by generating the following formulae: (1) a statement executed by Ei can perform at most one action; (2) a statement performs an action if and only if its guard is true in the context of Ei; (3) different statements, if executed, must perform different actions; (4) the value-written (Vi), location-accessed (li) and monitor-used (mi) relations of Ei are consistent with the corresponding values given by V and L; and (5) the set of all actions executed by Ei (denoted by Ai) is the union of the actions performed by the executed statements. The recursive step F(s, R(P), Ei) constrains the relations that define the execution of the statements to conform to the program semantics. The step Fa is applied only to the main execution E, constraining it to satisfy all assertions in P.
Bounds assembly and solving (208): The last phase of the analysis-finding a model or a core of the assembled legality formula-is delegated to a Kodkod constraint solver. Kodkod takes as input a relational satisfiability problem, which is solved by reduction to boolean satisfiability and application of a SAT solver to the resulting boolean constraints. A relational satisfiability problem consists of a formula in relational logic, a universe of atoms in which the formula is to be interpreted, and a lower and upper bound on the value of each relation in the formula. These bounds are given as sets of tuples drawn from the provided universe. The upper bound Bu(r) specifies the tuples that the relation r may contain in a model of the formula. The lower bound Bl(r)⊂Bu(r) designates the tuples that r must contain, if any. Relations with the same lower and upper bound, such as the relation co (described above), are said to be constant. Relations with different lower and upper bounds are called variables. The total number of variable tuples—i.e. Σr|Bu(r)\Bl(r)|—determines the exponent in the size of the search space explored by Kodkod. Minimizing Bu(r) and maximizing Bl(r) is therefore needed for performance. An algorithm for setting the bounds judiciously will be presented, so that the resulting search space is both compact and includes all potential witnesses.
In
The procedure COMPUTE-ACTS for computing the acts function is presented in
An example of the acts mapping and of the resulting bounds is shown in
1. each statement s is mapped to at least one atom;
2. if s and s′ may both be performed in some execution, the union of their acts sets contains at least two atoms;
3. if s and s′ are in different branches of a thread but may generate the same memory event, the intersection of their acts contains at least one atom; and
4. if the execution of s implies that of s′, the intersection of their acts sets is empty.
The first two properties (1. and 2.) ensure that witnesses are not missed because the search space excludes executions that perform certain statements or certain combinations of statements. For example, if acts(s) is empty for some s, then both the lower and the upper bound on the relation asi are also empty, which forces the solver to treat asi as the constant relation . As a result, the only way to satisfy the legality constraint σ(G[[s]], Ei)F(s, Ei)≠ is to have the guard of s evaluate to false. An empty acts set for s therefore rules out all witnesses that perform s. Similarly, if acts(s) ∪ acts(s′) contains just one atom aij, then Bu(asi)=Bu(ast)={aij}. In this case, the only way to satisfy the legality constraint F(s, Ei)∩ F(s′, Ei)= is to set either asi, or as′i, (or both) to the empty set, thus ruling out all witnesses that execute both s and s′.
The third property (3.) ensures that witnesses are not missed because the memory model equates actions performed by different statements in the context of different executions. For example, the program in
The fourth property (4.) ensures compactness of the search space. Namely, if the execution of s implies that of s′ (i.e. s postdominates s′ or s′ dominates s), it is not necessary for acts(s) and acts(s) to intersect. We can therefore leave acts(s) ∩ acts(s) empty to get a smaller search space without losing any witnesses. To see that no witnesses are lost, consider two assignments acts and actsø which are the same except that acts(s) ∩ acts(s′)≠ and actsø (s) ∩ actsø (s′)=. For every execution Ei allowed by acts, there is an equivalent Eiø allowed by actsø. First, suppose that Ei executes s. Because ss′, Ei must also execute s′. Hence, asi∩as′i=ø, which can be satisfied by bounds based on actsø. That is, Eiø=Ei. Now, suppose that Ei that executes only s′. Once again, asi∩as′i=ø and Eiø=Ei. This shows that actsø will never eliminate a witness that involves a single execution. A similar argument can be applied to witnesses involving multiple executions.
In accordance with the present principles, a fully automated tool (10) enables debugging and reasoning about axiomatic specifications of memory models. The tool 10 was used to check the JMM, a revised version of JMM, and several well-known hardware-level memory models. These experiments confirmed previously known discrepancies in the expected behavior of test programs, and uncovered new ones. The tool 10 is fully automated and can handle the current axiomatic specification of the JMM.
Having described preferred embodiments for a system and method for debugging memory consistency models (which are intended to be illustrative and not limiting), it is noted that modifications and variations can be made by persons skilled in the art in light of the above teachings. It is therefore to be understood that changes may be made in the particular embodiments disclosed which are within the scope of the invention as outlined by the appended claims. Flaying thus described aspects of the invention, with the details and particularity required by the patent laws, what is claimed and desired protected by Letters Patent is set forth in the appended claims.