Two-Dimensional Pinpointing: Debugging with Formal Specifications

11
TWO-DIMENSIONAL PINPOINTING: DEBUGGING WITH FORMAL SPECIFICATIONS Unlike standard debuggers that rely on lowlevel inputs and checking, the Annalyzer helps you debug AnnarpecifiedAda from the top down using formal specifications. DAVID LUCKHAM SRIRAM SANKAR SHUZO TAKAHASHI Stanford University ebugging has always been a creative D endeavor. Using today’s crude tools to discover errors in programs requires a great deal of ingenuity and effort. By using formal specifications, s o h a r e engineers can develop much more powerful debug- ging techniques. Formal specification languages express information about a program that is not normally part of the program’s text. Spec- ifications are expressed in a machme-pro- cessible form. They can be parsed, checked for static semantic errors, and, in many cases, compiled into runtime tests. This article describes debugging tech- 74 07407459/91/0100/0074/$01 00 Q IEEE niques and tools that draw on both the high-level concepts (defined as functions) used in formal specifications and the ab- straction and information-hiding con- structs used in modern languages. The techmque we propose is based on two components: + A new specification language with support tools. You specify Ada programs with a language we created called Anna. You use our tool set, the Annalyzer,’ to check the Ada program’s runtime behav- ior for consistency with the Anna specifi- cations. 4 Methods for applying Anna and the JANUARY 1991

Transcript of Two-Dimensional Pinpointing: Debugging with Formal Specifications

TWO-DIMENSIONAL PINPOINTING: DEBUGGING WITH FORMAL SPECIFICATIONS

Unlike standard debuggers that rely on

lowlevel inputs and checking, the Annalyzer

helps you debug Annarpecified Ada from

the top down using formal specifications.

DAVID LUCKHAM SRIRAM SANKAR

SHUZO TAKAHASHI Stanford University

ebugging has always been a creative D endeavor. Using today’s crude tools to discover errors in programs requires a great deal of ingenuity and effort. By using formal specifications, soha re engineers can develop much more powerful debug- ging techniques.

Formal specification languages express information about a program that is not normally part of the program’s text. Spec- ifications are expressed in a machme-pro- cessible form. They can be parsed, checked for static semantic errors, and, in many cases, compiled into runtime tests.

This article describes debugging tech-

7 4 07407459/91/0100/0074/$01 00 Q IEEE

niques and tools that draw on both the high-level concepts (defined as functions) used in formal specifications and the ab- straction and information-hiding con- structs used in modern languages.

The techmque we propose is based on two components:

+ A new specification language with support tools. You specify Ada programs with a language we created called Anna. You use our tool set, the Annalyzer,’ to check the Ada program’s runtime behav- ior for consistency with the Anna specifi- cations.

4 Methods for applying Anna and the

J A N U A R Y 1 9 9 1

Annalyzer. You use the Annalyzer both to find missing specifications by comparing the specification with program prototypes and to test and debug Ada programs once you’ve developed an accepted specifica- tion.

Anna (annotated Ada) is an Ada exten- sion that includes facilities for formally specifylng the intended behavior of Ada programs. It augments Ada with precise machine-processible annotations, so well- established formal methods of specifica- tion and documentation can be applied to Ada programs. The box on p. 82 gives ex- amples of Anna annotations.

Our methods are intended to deal with complex Ada program units &e Ada pack- ages that have several structural levels and nested units that may share common data.

Each Ada program level has specifica- tions. Using hgh-level abstract concepts, the top-level specifications express what the program itself does. By referring to implementation details hdden at hgher levels, lower level specifications express what the nested units do and how they are related.

Our method, called two-dunensional pinpointing, locates inconsistencies in software that is structured in levels. It uses sequences of calls to program operations and levels of annotations. It uses a breadth- first search strategy, which tests annota- tions at only the highest level until no new results are provided by the tests there. It uses a two-dimensional search space to lo- cate errors: the program’s structural levels and the test-sequence length. Our method provides general, informal guidelines for adding new annotations to help with the breadth-first approach and other guide- lines to deal with special situations.

ADVANTAGES

Our method of debugging with speci- fications has several advantages over pres- ent debugging methods and tools:

+ It defines and structures the debug- ging problem precisely. The specifications to be tested at each structural level are also a formal definition of the behavior to be tested at that level. Thus, pinpointing can involve several people with understand-

ings of different levels and software com- ponents. For example, a systems designer who works with hgh-level specifications could give a precise problem to a component’s programmer: ‘‘Th~s test se- quence violates this specification of that component.”

+ It detects violations of specifications automatically. Current debuggers gather information from a program’s lowest im- plementation levels and its runtime envi- ronment, and you must deduce what is happening. More advanced debuggers can test Boolean assertions about program variables. But the Annalyzer can test very general constraints on packages, abstract types, data structures, and subprograms. It elimi- nates the need to search output traces for errors.

+ It lets you formulate and check verv comdex

goals, and the subgoals of its various parts. Also, you apply informal reasoning, whch all programmers use now and then, to for- mal specifications and their program con- texts.

However, unhke strict methods, re- laxed methods stop short of mathematical proofs to justify various steps. Our first goal is to encourage you to apply specifi- cations to debuggmg problems. If you get used to debuggingwith specifications, you may well use rigorous formal proofs in place of informal reasoning. After all, the

information a formal

tests automatically. For structure( example, you can formal- ize specifications against side effects on global data easily and test them automatically. Typical one-line ab- stract specifications could, with other cur- rent debuggers, require manual insertion of many breakpoints, print statements, or assertions.

+ You can use the Annalyzer with any Ada compiler and runtime environment.

+ You can apply it earlier in develop- ment to, for example, analyze simulations and prototypes to develop formal, stan- dard specifications for module interfaces.

T h s article does not address other test issues like selecting test data’ or generat- ing test sequences fiom specifications au- tomatically, but these research dn-ections might be amenable to guidehes hke our pinpointing guidehes.

Our method, called two-dimensional

pinpointing, locates inconsistencies in software that is

in levels.

FORMAL METHODS

Our hierarchal debugging technique is a relaxed formal method, as opposed to a strict formal method. With a relaxed method, you use a specification language to express the concepts on which a pro- gram is based, the program’s functional

proof requires is already explicit in the specifica- tions and annotations.

Using specifications to debug is an evolutionary step toward formalizing every development stage. Most formal methods focus on early develop- ment stages and propose a top-down process in which abstract specifica- tions are developed first,

then refined in detailed steps to produce executable ~of tware .~ )~ However, when applied, these methods are not strictly top-down: Activities sirmlar to debugging take place in the early stages.

ASSUMPTIONS

Our approach assumes that formal specifications defining the program’s de- sired behavior have already been written and placed at the program’s various struc- tural levels. We assume that these specifi- cations correctly express the program’s re- quirements and that the concepts (functions) used in the specifications have been correctly implemented. These as- sumptions simplify the debugging task: If the program violates a specification dur- ing debugging, you have only to analyze the program for errors - not the specifi- cation.

We based these assumptions on the idea that the specifications -and the con- cepts used in them - were developed at an earlier stage in the development pro- cess and have aleady been analyzed. In-

I E E E S O F T W A R E 7 s

deed, we hope that by the debugging stage, there is general agreement among project personnel that the specifications will not be changed. Also, you should im- plement libraries of specification concepts and rigorously test them before beginning any project that uses them.

But we do not assume that the specifi- cations are complete. Our debugging meth- odology allows for the possibility that some aspects of the program’s behavior have been overlooked or that specifica- tions of lower level details are missing.

These correctness assumptions let us simplify our debugging method. (If these assumptions tum out not to be true, you

FIGURE 1 EXAMPLE ADA PACKAGES STRUCTURAL

LEVELS

FIGURE 2 ANNALYZER MENUS FOR SUPPRESSING Ah- NOTATIONS

must use another, more complicated methodology.) This hgment illustrates our assumptions:

procedure INSERT @:ELEMENT; Qinout QUEUE);

-- I where

-- I -- I Out (LENGTH(Q) =

- - I LENGTH(in Q) + l),

In thls example, Length and Is-Member are concepts that have been used to specify Insert. During debugging, we assume that the specification of Insert is correct and that the concepts Length and Is-Member are correctly implemented. This example also shows our methodology’s support of incompleteness: The specifications do not specify that elements already in the queue remain there after an Insert operation.

Out (lS-MEMBER(E, Q));

ADA PACKAGES

Ada packages have several structural levels, an organization we use as one di- mension of our method. Structural levels in a simple Ada package are:

+ The vziible level, where you define the package’s interface to the rest of the program. The visible level typically con- sists of a private type and subprogram dec- larations.

+ The data level, which includes the data structures and the data-object defini- tions. The private part of the package also belongs to the data level.

+ The subprogram level, consisting of actual subprogram bodies inside the pack- age body.

+ The statement level, composed of the statement sequence in the subprogram body. This level can be split into more than one level. For example, a compound statement like a loop can be considered at a hgher structural level than the simple Statements in it.

Figure 1 shows the structural levels of an example Ada package, Queue-Man- ager, which we use to illustrate our method in a later section.

To debug a package, you first choose a test sequence - a sequence of operations in the package that you want to test. You then execute the sequence of package op- ?rations; if you detect an inconsistency, you begin to pinpoint the error.

ANNALYZER

The Annalyzer is a set of programs that convert Anna annotations into code that checks consistency with the specifications at runtime and insert this code into the underlying Ada program. When you exe- cute a transformed Anna program, a spe- cialized Anna debugger takes control and provides a top-level interface between you (the user) and the program being tested. You can transfer control to the underlying program, but it returns control to the de- bugger and raises the exception Anna- Error when the program violates an anno- tation.

A diagnostic message tells you which annotation was violated and the violation’s location. You can designate annotations to be suppressed or unsuppressed. A sup- pressed annotation is not checked.

You interact with the debugger through menus, choosing displayed op- tions with an input device like a mouse. Figure 2 shows how to suppress annota- tions. After you have selected Suppress from the Options menu, you select from another menu that displays the annota- tions you can suppress.

As Figure 3 shows, another window displays the program execution. When an annotation is violated, two more windows open: One shows the annotation violated; the other shows the program text around the statement where it was violated.

TWO-DIMENSIONAL PINPOINTING

To locate errors in an Ada program with two-dimensional pinpointing, you reduce the region of suspicion using the Annalyzer’s diagnostic messages and your understanding of the program. You exe- cute operation sequences and use the Annalyzer to compare their runtime be- havior with the specifications, checlung only the highest-level specifications, un- less a violation occurs.

Two-dimensional pinpointing starts when the Annalyzer h d s violations. A specification violation defines a wgion of mpicion. In a package, the region of suspi- cion may include not only the text (speci- fications and code) of the subunit whose

7 6 J A N U A R Y 1 9 9 1

execution caused the violation but also units at the same level that are related to it by specifications, and even units called earlier in the test sequence. The goal is to reduce the region showing inconsistent behavior to a subunit that vou feel can be

OK QSTORE (Q.IN-PTR) := 4 DRIVER>> INSERT (2,QO) ; QSIZE := QSIZE t 1;

ANNA-ERROR is detected. end INSERT; I

FIGURE 3 PROGRAM I/O WINDOW repaired or replaced. When to stop pin- pointing and start repairing is a subjective decision.

Pinpointing strategies use an Ada program’s hierarchical structure. When you find a region of suspicion, you aug- ment the existing specifications with new, more detailed specifications and annota- tions at the same program level. These new specifications should be related to the violated specification so they are likely to be violated by the same test. Then you repeat the test sequence. The new viola- tion may reduce the region of suspicion by providing more information or by occur- ring earlier in the test sequence. You check only a few hgh-level specifications during a test sequence.

C h e c h g one level at a time reduces the time required both to run tests and to pinpoint an error with a single test. If you checked all levels at once and found an error after a long test sequence, you would have run many more tests to 6nd the spec- ification that was violated. It’s also likely that a very low-level annotation would be violated first. Then you would have to test higher level specifications to understand the low-level violation and determine its relevance to the hgh-level functional pro- gram specification.

Our method’s breadth-first search strategy concentrates efforts at one level until n o h g more can be tested there. Only then do you debug at the next lower level. The Annalyzer supports this ap- proach by letting you suppress all lower level annotations. Suppressing annota- tions avoids irrelevant checks during con- sistency checking and lets you concentrate on the part of the program being de- bugged.

Four general rules follow &om our as- sumptions about specifications:

+ No initial specification may be changed. T h s follows 6-om the assump- tion that the specifications are correct.

+ New specification may be added, provided they are consistent with the old

ones. This follows from the assumption that the specifications, although correct, may still be incomplete.

+ Intuitive intentions that were not formally expressed as specifications may not be satisfied even though no specifica- tion is violated.

+ The operation in which an inconsis- tency is detected is not necessarily at fault. The region of suspicion must include op- erations earlier in the test sequence. This also follows from the assumption that specifications may be incomplete. For ex- ample, when the operation in which the inconsistency was detected began execut- ing, the global data it operates on may al- ready have been inconsistent because of a problem in an earlier operation. The ear- lier problem was not detected because of a missing specification.

To reduce the region of suspicion, you either shorten the test sequence leadmg to a violation (the first dimension) or find a violated lower level annotation (the sec- ond dimension). Our methods suggests three guidelines to do thls, in order of use:

1. Try to add annotations at the level being tested. Do thls whenever you rhmk you can determine annotations that should be added, especially when program behavior is inconsistent with an intuitive intention but no annotation was violated. This strategy usually reduces the region of suspicion by shortening the test sequence in which a violation occurs.

2 . When you cannot think of any missing specification, test the package at the next lower level. The region of suspi- cion will be reduced if a lower level anno- tation is violated. The breadth-first search strategy requires that you apply guidehe 2 only when you no longer can apply guidehe 1.

3 . To verify that you have pinpointed the subprogram w i h the test sequence whose call violated a specification, make sure the following are true:

+ You cannot think of more missing specifications at the data level. Because package operations interact only through the data level, a complete specification set at the visible and data levels means that every test operation is executed in a con- sistent package state. Thus, a violation can be due only to a problem within the violat- ing operation itself.

+ You are confident about all other operations in the test sequence except the violated operation (this can happen even at the visible level).

+ Subprogram annotations at the sub- program body level are violated before data-level or visible-level annotations.

+ The first subprogram in the test se- quence is violated.

DEBUGGING SESSION

To illustrate our method, th ls article shows our testing and debugging of the Queue-Manager package written in Add Anna. T h s example is simple, but it does illustrate two-dimensional pinpointing.

Queue-Mallclger. Figure 4 shows the specification for the Queue-Manager package. It provides an abstract queue type, functions, procedures, and excep- tions. The package declaration contains the abstract Anna specification, whch is visible to users. This AddAnna visible specification deiines the behavior of the package’s types and operations.

The Queue-Manager visible specifi- cation in Figure 4 declares:

+ Types and exceptions. The abstract

I E E E S O F T W A R E 7 7

generic

type ELEMENT is private; MAX : POSITIVE;

package QUEUI-MANAGER is

type QUEUE is private; EMFTY,FULL:exception;

Tbe&hing are cmeptr used in qeafiatims. h-Empty and h-Fu.U are &ned in terms of Length

- - : function IS-MEMBER(E:ELEMENT;Q:QUEUE) - _ . . re tumBOOLW,

function LENGTH(Q:QUEUE) return INTEGER; function TOP(QQUEUE) return ELEMENT; function IS_EMFTY(Q:QUEUE) return BOOLEAN;

- - I <<SPEC-IS-EMPTY>> where -- I return LENGTH(Q) = 0;

function IS-FULL(Q:QUEUE) return BOOLEAN; - - I <<SPEC-IS-FULL>> where - - I return LENGTH(Q) = MAX;

The fillowing are operativns qeczjied !y conctptr.

procedure INSERT(E:ELEMENT;Qin out QUEUE);

- - I <<SPEC-INSERT>> where - - I IS-FULL(Q) =>raise FULL,

- - I raiseFULL=>Q=inQ, -- I -- I out(IS-MEMBER(E,Q));

out(LENGTH(Q) = LENGTH(in Q) + I),

procedure REMOVE(E:out ELEMENT; Q:in out QUEUE);

- - I <<SPEC-REMOVE>> where - - I - - I raiseEMPTY=>Q=inQ,

IS-EMPTY(Q) => raise EMPTY,

- - I - - I

out(LENGTH(Q) = LENGTH(in Q) - l), out(E = TOP(in Q));

- - I axiom ...

This example omits axiomatic qectjkations

private type QUEUJ-ARRAY is array

type QUEUEis (INTEGER range <>) of ELEMENT;

record ST0RE:QUEUE-ARRAY( l..MAX); W-lTR,OUT-€'TR:INTEGERrangel..MAX:= 1; SIZE:INTEGER mnge 0 . M := 0;

end record; -- I < < Q U E U E - " T > > where

-- I (Q.IN-PTR-Q.OUT-PTR-Q.SIZE) -- I modMAX=O;

-- I in out Q:QUEUE =>

end QUEUE-MANAGER;

FIGURE 4. QUEUE-MANAGER SPECIFICATION BOLDFACE INDICATES KEYWORDS, rALlCS ARE NOTES TO THE READER

data type Queue is declared as an Ada pri- vate type.

+ The functions Is-Member, Length, Top, Is-Empty, and Is-Full. We call these functions basic concepts because you use them to specify all other Queue-Manager operations (although the basic concepts Is-Empty and Is-Full are themselves de- fined in terms of Length). We assume that these functions are correctly imple- mented.

+ The procedures Insert and Remove, which are specified in terms of the basic concepts.

+ The private part, which includes the abstract data type Queue represented as a record structure. T h s is the h t and most critical implementation decision because it constrains Queue's values and so ex- presses a major decision about its imple- mentation.

Figure 5 shows the implementation of Queue-Manager. The implementation of the package body, including the private part, hides details fkom users. The hidden part, which has three levels, also contains local annotations specifymg how the im-

plementation works. Because they refer to hdden implementation details, the hid- den annotations are implementation-de- pendent.

The Queue-Manager implementation in Figure 5 implements:

+ The bodies of the basic concepts. + The procedure bodies specified in

terms of the basic concepts. The session to debug ths package con-

sisted of five interactions where we tested, analyzed results, and took action.

Intemct#n 1: Intuitive intention violated. To test the package body's behavior for con- sistency with the visible specifications, we declared a queue variable, QO, and an ele- ment variable, EO. We suppressed all an- notations in the private part and body and executed a test sequence of three calls to package operations:

INSERT(1,QO); INSERT(2,QO); REMOVE@O,QO); This test sequence resulted in no viola-

tion at the visible level; Figure 6b shows the program UO. But the results contra- dicted our intuitive intentions: 2 was re-

moved from QO, whle the first element inserted was 1. The visible specification did not express all our intuitive require- ments for queues - clearly something had been forgotten.

We were dealing with the package's visible level. The region of suspicion, shown in Figure 6a, is made of the visible specifications of Insert and Remove, the data level, and the bodies of these opera- tions.

In applying the general guidelines here to pinpoint the region of suspicion, you would start with the last operation in the sequence and work back through the test sequence. First, you would supply a formal annotation for the intuitive requirement that was violated. Then you would con- sider how the previous test operations in- fluenced the violated requirement. If you could think of any specification missing from the previous operations related to the last operation, you would express it as a formal annotation. Missing specifica- tions at the debugging stage are often in- variants ofvisible operations.

In this example, we followed guidehe

7 8 J A N U A R Y 1 9 9 1

package body QUEUE-MXVAGER;

function IS-A@MBER(E:ELElMENT;Q:QUEUE) return BOOLJXbJ is

1 : N E G E R := Q.OUT-PTR; bee

ifIS-EMpTY(Q) then

end $ return FALSE;

ifQ.STORE(I) = E then return TRLTT;

end if, I : = I m o d , W + 1; if1 = Q.IN-€TR then

return FALSE; end $

end loop; end IS-MEMBER,

function LENGTH(Q:QUEUE) return INTEGER is - - I <<BODY-LENGTH>> where -- I returnQ.SIZE;

return Q.SIZE; begin

end LENGTH;

function TOP(Q:QUELTE) return ELEMENT is I <<BODY-TOP>> where

-- I return Q.STORE(Q.OLT-PTR); begin

if IS-EMmr(Q) then

else

end iE end TOP;

raise EAIIPTY;

return( Q . STORE(Q .OuT_PTR));

function IS-FMI?TY(Q:QUEUE) retum BOOLEAN is - - I <<BODY-IS-EMPTY>> where -- I returnQ.SIZE=0;

begin return QSIZE = 0;

end I S - E L W ;

function IS-FULL(Q:QUEUE) return BOOLEAN is - - I <<BODY-IS-FULL>> where -- I E~~~~Q.SIZE=IZ/IAX;

return Q.SIZE =.MAX; begin

end IS-FULL;

procedure I N S E K ~ ( E : E ~ I i Q i n out QUEUE) is bee

ifIS-FLJLL(Q) then

end if, raise FULL;

Q.STORE(Q.N-PTR) := E; Q.SIZE := Q.SIZE + 1;

end INSERT;

procedure RJ!.\lOVE(E:out ELEMENT;

begin Q:in out QLTUE) is

ifIS-LMPTY(Q) then

end $

Q.OUT_PTR:= Q.OUT-PTRmodMAX + 1;

raise L W ;

E := Q.STORE(Q.IF-PTR);

().SIZE := Q.SIZE I ; end REMOVE;

FIGURE 5 QUEUE-MANAGER IMPLEMENTATION BOLDFACE INOICATES KEYWOROS, ITALICS ARE NOTES TO THE READER

DRIVER>> INSERT (2 ,QO),

DRIVER>> REMOVE (EO, QO) ,

FIGURE 6 [AI REGION OF SUSPICION FOR INTERACTION 1 [E) INTUrWE INTENTION VIOLATED

FIGURE 7. [A) REGION OF SUSPICION FOR INTERACTION 2, [B) INCONSISTENCY WKH THE M M D SPECIFICATION I 1 and looked for missing visible specifica- tions. First, we guessed that Remove didn’t return the value Top(@). However, we saw that the visible specification of Re- move contains

- - I Out (E = TOP@ Q)); and that it wasn’t violated. Thus, our

first guess had been wrong, because we assumed that Top is correct.

Next, we guessed that the value Top(Q 0) was changed when Insert executed. So we considered how we expected Insert to behave with respect to the concept Top. We redzed that Top(Q) for a nonempty Q must remain invariant under an Insert op- eration and that this is missing in the visi- ble specification of Insert. The missing specification is formally expressed as

procedure INSEFT@ : ELEMENT; Q : m outQUELJE>

-- I <<SPEC-INSEKT>> - - I where - - I ouOOP(Q) =if IS-FMFTY(in Q) then -- I E else TOP@ Q) end ia

We added this to the other Insert annota- tions and ran the same test sequence to see if our second guess was correct.

When you apply two-dimensional pin- pointingat the visible level, you should not reason about an inconsistency‘s cause on the basis of a particular implementation detail (for example, elements being placed in some positional order in a common data structure like an array or a M e d list). At the visible level, you should consider only the information contained in the visible

E O

subprogram declarations and their anno- tations. For example, you should disre- gard, as we did, the fact that the value of Top(Q0) is the first element in an array.

In th is interaction, you could prove formally that the new annotation of Insert in the visible package specification must be violated by the test sequence. Also, invari- ants hke the one expressed by the new an- notation are often overlooked and there- fore are not included initially as part of the specification.

To check whether the above guesses were correct using a standard debugger, you would have to print out the values of low-level variables in the package body and reconsmct the values of the visible- level concepts, either in your mind or with penal and paper. The Annalyzer, on the other hand, automatically checks whether the guesses are correct, once you express the guesses as visible formal annotations.

lntemction 2 Inconsistency wih new spedfi- cation. We ran the same test sequence of three operations again; this time we got only as far as the second operation, In- sert(2,Q 0). The Annalyzer detected avio- lation of our new specification and gave the messages shown in Figure 6b.

As we guessed, Insert had an effect be- yond what was specified. The window dis- playing a violated annotation shows that the value of Top is not invariant when in- serting into nonempty queues. The sec-

ond Insert operation violates the specifica- tion, as the program I/O window shows in Figure 7b.

So far, we had decreased the length of the sequence where a violation OCCUTS,

thus reducing the region of suspicion to the visible specification of Insert, the data level, and the body of Insert. T h s reduc- tion, shown in Figure 7a, does not mean that Remove is correct; it only means that there is an inconsistency in the reduced region of suspicion.

In applying the general guidelines here to reduce the region of suspicion to the data or body level, you would test the In- sert subprogram against lower level anno- tations (such as data invariants in the pri- vate part or body) and subprogram body annotations.

With a standard debugger, you interact with the program at the same level as the low-level information shown in the win- dow displaying the violation’s location. With our method, you interact with the program at the same level as the high-level specification shown in the window dis- playing a violated annotation. At thls stage in two-dimensional pinpointing, you don’t understand how the low-level infor- mation relates to the high-level specifica- tion.

In this example, we followed guideline 2 to further reduce the region of suspicion, interacting at the data level, the next lower level in the package structure. We unsup-

J A N U A R Y 1 9 9 1

pressed Queue-Invariant in the private part and ran the same test sequence. For a package operation to terminate normally, Queue-Invariant must be satisfied by all variables (and parameters) of a Queue re- cord. At the (lower) data level, queue structure and components are visible.

Interaction 3 Inconsistency with Queue-In- variant. We ran the test sequence again, and the Annalyzer detected a violation at the first call of Insert. Figure 8b shows the displayed messages.

After an element was inserted, the in- variant condition among Q.In-Pn; Q.Out-Ptr, and QSize was violated. The violated test-sequence length was further reduced, and the violated annotation level was lower. Figure 8a shows the region of suspicion reduced to the data level and the body of Insert.

Again, with a standard debugger, you have to print out the values of QO.In-Ptr, QO.Out-Pn; and QO.Size - before and after each execution of the operation In- sert - to see whether Queue-Invariant is satisfied. The Annalyzer, on the other hand, checks h s invariant automatically.

hTow we had to either reduce the re-

@on of suspicion wittun the single subpro- 1 informally concluded that ifwe added the g a m bodyor repair the bod). 6 follo&g guideline 3, we could have added to the subprogram body an annotation that ex- presses a relationship between the hidden variables and is equivalent to the visible specification that had been violated. We could have used such an annotation for further pinpointing or repair. We could have continued h s process and added more detailed subprogram annotations or added statement annotations like loop in- variants or assertions w i h the subpro- gram body code.

We decided instead to repair the body of Insert by using goal-oriented program- ming using assertions. We considered that Insert must acheve four goals:

1. out(Q.STORE(in Q.IN-PTR) = E) (insert E in Store).

2. out(Q.SIZE = in Q.SIZE + 1) (increment Length).

3. out(Q.IN_PTR = in Q."_F'TR mod MAX + 1) (increment In-Ptr).

4. <<QUEUE-IXV"T>>. Because the fourth goal was violated,

the body did not achieve it. Lookingat the body, we realized that it achieves the first and second goals but not the h r d . UE

h r d goal to the body as an assignment statement, the fourth goal would also be met. So, as a repair, we added the follow- ing:

Q.IN-PTK := Q.IF_PTR mod MAX + 1;

Interacfh 4: Another inconsistency. We ran Insert(l,QO);Insert(2,QO) against the an- notations at all levels to 6nd out if our repair was correct for t h ~ s test, and we ran Insert(l,QO);hert(2,QO);Remove(EO,QO) against lisible specifications to continue testing the visible level.

The program VO window in Figure 9b shows that Insert, as repaired, was con- sistent with all existing specifications for t h l s test sequence. However, the test had now uncovered another inconsistency in a visible-level specification. Figure 9a shows that the region of suspicion was now the visible specifications of Insert and Re- move, the data level, and the body ofthese procedures.

Both the test sequence and the level of the violated annotation were the same as in the first test (Figure 6a). However, we now know that - for &IS test - Insert is consistent with all existing annotations at

FIGURE 8 [AI REGION OF SUSPICION FOR INTERACTION 3 [El INMNSISTENCY WmH QUEUE_~NVARIANT

- .~ ~____ _--2

I E E E S O F T W A R E 8 1

AWWA AND THE CHECKING METHODOLOGY Anna is based onjn-t-order log& and itssyntax is a &aightjiur-

ward extm'on ofAda yntax. Anna connrzlcts appear asjkmd com- ments within the Ada source text. Here, we outline a fa Anna annota- tim; we provide a cwnpkte d&ition elsewhere.'

Anna defines two k i d offormal mments intmduced ly special cmranent indicam to d i a i n p h themjimn injhnulmmentr. Thesefbrmal commentsaw virtual Ada text, each line ofwhich be- @TIS with the indicator - - :, and annotations, each line ofwblch be- @TIS with the indicator - - I.

Virtual text. Vhuul Ada text appears " 1 cormrtents but other- wise obeys all Ada ruks. Vhuul text may refer to actual text, but it b not allowed to @ect actual computation. A& text cannot refer to vir- tzlal text. V z l Ada text defmes the cmepts used in annotations. ofen, aprogram?fiormalspeczjikations willrefwto cmepts thepro- gram does not implement eqlicidy. You @e these cmeptsas vi& Ada text aklaratiom. Vi& Ada text can ah0 compute values the pro- gram does not cwnpute, but that are us& m defining its behavwl:

An exampk o f v i d Ada text is - - : package QUEUE-MANAGER is

type QUEUE is private; ...

... -_ . . function IS-MEMBER(E:ELEMENT;Q:QUEUE) return BOOLEAN;

... end QUEUE-MANAGER;

where the Is-Member@~ution is not a Queut-Mmger operation; it b a virtzralfinction used in actuul whprogram annotations.

/h".Annotdionsspec*thatapply ovwprensempes andconmain the &lyingAdapgrm They are Boolean-valuedex- p r ~ w h a s e l o c d i o n a n d ~ i n d i u n e t h e m n s t r a i n t t h e y ~ ~ ,

particular Ada c m . Anna expressions extend (are supersets 08 Ada exprem'ons. Here, we h d e the k i d ofannotatim used in the example Ada program in Figure 5 in the main text andsketch how the Annalyzer checksfir cadencies between the program and the annota- tim. Details about the checking method are availubk ehewhere.2

+ Type ur subtype annotations apply a mnrtraint throughout an Ada we @nition ?scope. They c m immediately .$er the we @i- tion and are bound to it by the ktywmd Where:

Anna providesdflerent kindr of annotationr, each associded with a

- - I typeQUEUEis record

ST0RE:QUEUE-ARRAY( I..MAX); INJ"IR,OUT_PTR:INTEGERrange 1 . M := 1; SIZEINTEGER range 0 . M := 0;

end record;

(Q.IN-PTR - Q.OUT-ETR - Q.SIZE) mod MAX = 0; This we annotation conrhains all @ace values so their romprents In-% Out-Ptq and Size satufj the equution in the annotation.

To check a subwe annotaticon the Annalyzer tran.$m it into a singk checkingfinction. At allplaces where sllbtype variables change value - fur example, at assignment statements and sclbpvgram caUs - the Anna&zer caLk thb@nction.

In the exampk s h m in the main text? Figure F, the type annota- tion occurs in a mcd$ed f m . Mod$ed type annotations can be used only in packages. A$er the keyord Where, the wmh In Ozct indicate that type values are cowtrained only at the beginning and end of each package operation.

To check d f i d subtype annotations, the Anmlyzer treats them as input and output canstrainis on the package's v d l e qeratim. The con-

- - I where QQUEUE r>

- - I

straints are then handled as subprogram annotations. + Sulpogram annotations are lirtr ofannotations that descnze sub-

program behavior and are bound to an Ada subprgram speajiiation by the ktyword where. In general, each subprogram annotation specijies an input condition, an output condition, man exception condition. Ah, subpvgram annotations of@&ans may spe& the redting value re- turned by a$".

This result annotation spenfier that the vahe returned by the@nc- tion Is-Full is the Bookan value Lengh(@ = Max:

- - I function IS-FULL(Q:QUEUE) return BOOLEAN;

-- I where - - I These out annotations on the procedure Insert must be satujied

whenever a call to Insert terminates normally - when it doem kpropa- gate an exception. whenever a call to Insert terminates " a l l y , the kngth ofthe r d t i n g Q must be one more than the kngtth ofthe vahe o f Q on enny to Insert. Ao, on termination, E must be a member of Q:

- - I <<SPEC-IS-FULL>>

return LENGTH(Q) =MAX;

-- I procedure INSEm(E:ELEMW;Q:in out QUEUE);

-- I where -- I <<SPEC-INSERT>>

- - I -- I OU~(IS-MEMBER(JZ,Q))

out(LENGTH(Q) = LENGTH(i Q) + l),

Statement annotations letyou q e n f j , more &tail at the statement h e 1 m the subpvgrm ? body.

To check subprogram annotations, the Amalper i n r m explicit check fur result annotations immediately befre wery Return statement in itsscope. Itchecksin andoutannotatMnrwitb expllicitchecksatsub- program beginning and at allpossibk exit points. The optional annota- tion names - Spec-Lr-Fd and Spec-Inrert in these examph - he& the user of the interactive &bugger (an Annalyzer cmponent) r.fw to annotations.

+ Exception, or propagation, annotationr spenjj exceptions in sub- program behaviol: There are two k i d : strong and weak.

A strong propagation annotation spec@ conditions - at the initid state ofthe subprogram - undw which exceptionsshould beppa- gated. I f the conditim are s&ed the subprogram must terminate by propagating the specifid exception.

Fur exmpk, thb annom'on p+s that fh-Fd(@ is me on entry tDInrwtthen Inrertmwtterminate Lypvpagatingtheac@m F d .

- - I procedure INSERT@ELE"T;Q: in out QUEUE) -- I where - - I A weak propagatim annotation specifies cunditions that the +o-

T a m must satt& ifa call terminates by propagating a spe@ed excep- non. Thb annotation spe@ies that f theptxedm Insert terminates by propagating the exception FuU then Insert does mt change QS vdw:

-- I procedure INSERT(E:ELEMENT;Q:in out QUEUE) -- I where - - I raiseFULL=>Q=inQ The A d y w checks exception annotations by inserting an Ada

block statement in the sutprogvam. It then ins& exception ban& and out annotations into thb block to ensure that all nmmalmdabnm- mal terminations@ the sulprogram s a e the cditim imposed by

1s-FULL(Q) => raise FULL;

thmmutions. (OUtannotRthdo notspecfjJabnomtoltdtim)

REFERENCES 1. D.C. Luckham et al., Anna: A Lmpgefi Amorming Ada Pmgrm, Lecture

2. S . Sankar, D.S. Rosenblum, and R.B. Neff, "An Implementation ofAnna,"Ada Notes in Computer Science No. 260, Springer-Verlag, New York, 1987.

in LJre: Pmc. Ada Int'l Con$, Cambridge Univ. Press, Cambridge, England, 1985, pp. 285-296.

8 2 J A N U A R Y 1 9 9 1

potkage QUEUE-MANAGER is type QUEUE is private;

visible annotation is

~ ~ I out@ = TOP(in Q)); which tellsusexactlywhatthevalueofEin

procedure INSERT (. . . ; YE[. . . I ;

private

procedure RE:AIO\'E(E:out ELEAlEhT; 1 1 ~~ 1 <<RODY_-KE,LlO\'E>> ~~ I where

Q i n out QUEUE)

~- I =

i ... end QUEUE-MANAGER;

package body QUEUE-MANAGER is

the body of Remove. ~

Now, our aim was to reduce the region

I Data structures and

. . .

Q.OUT-PTR := Q.OUT-PTR mod MAX + I : QSIZE := QSIZE - 1;

OK DRIVER>> INSERT (2 ,QO) ;

OK DRIVER>> REMOVE (E0,QO) ;

ANNA-ERROR is detected.

[Bl I

~ ~ ~~ - ~ ~~~ ~~ ~~ ~~ ~~ ~~ ~~

FIGURE 9 [A] REGION OF SUSPICION FOR INTERACTION 4 [E] ANOTHER INCONSISTENCY

1 ~

pratedure INSERT ( 1 , procedure REMOVE [ 1 ,

private

package body QUEUE-MANAGER is I

Data structures and ~ variables

Q.OUT-PTR : = Q.OUT-PTR mod MAX t 1 QSIZE := QSIZE - 1;

OK DRIVER>> INSERT (2 ,QO) ;

OK DRIVER>> REMOVE (EO, (10) ;

ANNA-ERROR is detected.

~~~ ~~~ ~- FIGURE I O [A] REGION OF SUSPICION FOR INTERACTION 5 [El INCONSISTENCY WITH THE ADDED SPECIFICATION

I E E E S O F T W A R E 8 3

This transformed annotation told us ex- actly how E should be updated in the Re- move body. We added this annotation to the body and ran the same test.

hkmdbnZEArepci.TheAmalymdetected a violation of the nay body annotation of Re- move. Figure lob shows the mesMges & played; Figure 1Da shows the ~ g i o n ofsuspi- cionnxludmd-~ebodyofRemove.

The annotation Body-Remove told us the exact component in Q.Stox to be re- turned as the value of E. We read the body of Remove and realzed that we had to change In-h to out-Ptr in

E := Q.STORE(Q.IN-FTR); After h g the bug, we ran the same

test sequence. The Annalyzer detected no violation.

0-dmensional pinpointing is inde-

environment. We have developed support tools that transform Anna annotations into runtime checks for Ada programs, and they can be developed for s i d a r modem programming and specification languages. We are developing similar pin- pointing methods for concurrent soft-

Using abstract specifications in testing and debugging will become commonplace as programming and specification languages become more powerful, and analysis tools like the Andyzer become readdy available. The approach is particularly appropriate in software reuse, where, for example, you might implement a standard i n t d c e spec- ification many times to fit differing environ- ments or m e it by adding requirements.

We are applymg pinpointing to packages more complex than the simple example shown here. T h ~ s involves a theory for building correct specifications, as well as a waytocheckalgebraicspecifications. Anob- jective is to develop deded guidelines for many situations, a step toward de-based automated debugging aids.

Also, we are considering formal proof in debugging and repair. The approach we presented here uses intuitive judgments and guesswork, but f o n d prooii could j+ steps in two-dimensional pinpointing. +

E pendent of any compiler or runtime

ware.

ACKNOWLEDGMENTS This researchwas supported by the US Defense Dept.’s Advanced Research Projects Agency under con-

tractN00039-84-C-0211. GeoffMendal implemented much ofthe Anna tool suite. David Rosenblum, Chuan- Chieh KO, and other members of the program analysis and verification group implemented other basic Anna tools. We thank the anonymous referees for their helpful comments on the original manuscript. This article has been substantially revised by the magazine editorial s&.

REFERENCES 1. S. Sankar, D.S. Rosenblwn, and R.B. Neff, “An Implementation ofAnna,”Ada m me: R-K. Ada InrY Cif:,

2. R A DeMillo et al., ~ u r e Zdngund Evalwtim, BenjamidCummings, Menlo Park, Calif., 1987. 3. D. Bjomer and C. Jones, F m i Spenfiwtion and Soffure Deve[opment, Prentice-Hall, Englewood Cl i i ,

4. F.L. Bauer et al., “Formal Program Conshuction by Transformations: Computer-Aided, Intuition-Guided

Cambridge Univ. Press, Oxford, England, 1985, pp. 285-296.

NJ., 1982.

Progra”ing,”IEEE Tram. SOfrwaareEng., Feb. 1989, pp. 165-180.

David C. Luckham is a professorofelectricalengi- neering in the Computer SystemsLaboratoryatStan- fordUnivenity.Hisre- searchinterestsincludespec- ificationandprototyping languages,programmingen- vironmentsmultiproces- ing,hardware-designlan- pages, and formalmethods

ofsystem development.

Massachusetts Institute ofT&ology. He is a mem- ber of ACM and IEEE.

Luckham received a PhD in mathematics from

Sriram Sankar is a re- search associate in the Computer Systems Labora- tory at Stanford University. His research interests in- clude progTamming meth- ods, programming and specification languages, and the application of formal reasoning in software devel-

opment and validation. Sankar received a B Tech in computer science

from the Indian Institute of Technology in Kanpur and an MS and PhD in computer science €rom Stanford University. He is a member of ACM and IEEE.

,Shuz.cn Takabashi IS a research assmate at the Insnmte for hhthemahcd Studes in the So- ad Saences at Stanford Unrversity. HIS research mterests include mathemaacd leammg theory and the apphcaaon of formal methods to software development

PhD m philosophy from Stanford University. He IS a member of the Amencan Mathemah- cal Soaety.

Takahash~ received a BS in mathematics from the Tokyo hShtute ofTechnology and a

Address queshons about &IS amcle to Sankar at Computer Systems Laboratory, Stan- ford Umversity, Stanford, CA94305; Internet [email protected].

J A N U A R Y 1 9 9 1 84