Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 ECE 453 – CS 447 – SE 465 Software Testing & Quality Assurance Instructor Kostas Kontogiannis.

Similar presentations


Presentation on theme: "1 ECE 453 – CS 447 – SE 465 Software Testing & Quality Assurance Instructor Kostas Kontogiannis."— Presentation transcript:

1 1 ECE 453 – CS 447 – SE 465 Software Testing & Quality Assurance Instructor Kostas Kontogiannis

2 2 Overview  Structural Testing  Introduction – General Concepts  Flow Graph Testing  Data Flow Testing  Definitions  Some Basic Data Flow Analysis Algorithms  Define/use Testing  Slice Based Testing  Guidelines and Observations  Hybrid Methods  Retrospective on Structural Testing

3 3 Data Flow Testing Data flow testing refers to a category of structural testing techniques that focus on the points of the code variables obtain values (are defined) and the points of the program these variables are referenced (are used) We will focus on testing techniques that are centered –around faults that may occur when a variable is defined and referenced in not a proper way A variable is defined but never used A variable is used but never defined A variable that is defined twice (or more times) before it is used –Parts of a program that constitute a slice – a subset of program statements that comply with a specific slicing criterion (i.e. all program statements that are affected by variable x at point P).

4 4 Fundamental Principles of Data Flow Analysis Data flow analysis has been used extensively for compiler optimization algorithms Data flow analysis algorithms are based on the analysis of variable usage in the basic blocks of the program’s Control Flow Graph (CFG) We will focus in the next few slides on some basic data flow analysis terminology, and some basic data flow analysis algorithms For the data flow analysis algorithms presented below the source of information is the book “Compilers – Principles, Techniques and Tools” by Aho, Sethi, and Ulman [1]

5 5 gen, kill, in, out Sets Data flow analysis problems deal with the computation of specific properties of information related to definitions and uses of variables in basic blocks in the CFG. There are different data flow analysis problems, and each one requires different information to be considered. In general, we say that: –gen[B]: pertains to the information relevant to the problem and that is created in the block B –kill[B]: pertains to the information relevant to the problem and that is invalidated in the block B –in[B]: pertains to the information relevant to the problem and that holds upon entering the block B –out[B]: pertains to the information relevant to the problem and that holds upon exiting the block B

6 6 Reaching Definitions (1) A definition of a variable x is a statement that assigns, or may assign, a value to x. The most common form of a definition is an assignment statement, or a read statement. These statements certainly define a value for the variable x and we refer to them as unambiguous definitions of the variable x Unambiguous definitions have a strong impact on the problem of reaching definitions There are ambiguous definitions of a variable usually through: –Procedure call where x is passed by value is used as a global in the procedure –An assignment through a pointer (i.e. *p = y, if p points to x then we have an assignment to variable x) So, what is the problem of reaching definitions?

7 7 Reaching Definitions (2) We say that a definition d reaches a point p if there is a path following d to p, such that d is not killed, along that path Intuitively, if a definition d of some variable x, reaches point p, then d might be the place at which the value of x used at p might have been defined We kill a definition of a variable x if between two points along the path there is an unambiguous re-definition of x For these types of problems we take conservative or “safe” solutions (i.e. all edges of the graph can be traversed – while in practice only one path will be traversed for a given execution) In applications of reaching definitions it is normally conservative to assume that a definition can reach a point even if it might not. Thus, we allow paths that may never be traversed in any execution of a program, and we allow definitions to pass through ambiguous definitions of the same variable [1]

8 8 gen, kill, in, out for Reaching Definitions gen[S], where S is a basic block (node) in the program’s Control Flow Graph, is the set of definitions generated by S. That is a definition d (e.g. a=b+c) in S reaches the end of S via a path that does not go outside S. kill[S], is the set of definitions that never reach the end of S, event if they reach the beginning. In order for a definition d to be in the set kill[S], –every path from the beginning to end of S must have an unambiguous definition of the same variable defined by d (note d may be “input” to S by another basic block), and –if d appears in S (i.e. the definition is a statement in the basic block S), then following every occurrence of d along any path must be another definition in S of the same variable.

9 9 Example of Definitions d1: i = m – 1 d2: j = n d3: a = u1 d4: i = i + 1 d5: j = j - 1 d6: a = u2 d7: i = u3 B1 B2 B4 B3 Ref: “Compilers – Principles, Techniques and Tools” by Aho, Sethi, and Ulman

10 10 gen and kill Principles in Data Flow Analysis (1) s d 1 : a=b+c gen[S] = {d 1 } kill[S] = D a – {d} out[S] = gen[S] U (in[S] – kill[S]) s gen[S] = gen[S2] U (gen[S1] – kill[S2]) kill[S] = kill[S2] U (kill[S1] – gen[S2]) in[S1] = in[S] in[S2] = out[S1] out[S] = out[S2] s1 s2 =

11 11 Gen and Kill Principles in Data Flow Analysis (2) s gen[S] = gen[S1] U gen[S2] kill[S] = kill[S1] ∩ kill[S2] in[S1] = in[S2] = in[S] out[S] = out[S1] U out[S2] s gen[S] = gen[S1] kill[S] = kill[S1] in[S1] = in[S] U out[S1] out[S] = out[S1] where, out[S1] = gen[S1] U (in[S1] – kill[S1]) = s2 s1 =

12 12 Conservative Estimation of Data Flow Information In data flow analysis we assume that the statements are “un-interpreted” (i.e. both the then and the else parts of an if-statement are considered in the analysis while in practice during execution only one branch is taken) For each data flow analysis problem we must examine the effect of inaccurate estimates We generally accept discrepancies that are safe in the sense that they may not provide inaccurate information in the context of applying an optimization, devising a test case, or revealing dependencies between program statements In the previous example (reaching definitions) the “true” gen is a subset of the computed gen, and the “true” kill is a superset of the computed kill. Intuitively, increasing gen, adds to the set of definitions that can reach a point, and cannot prevent a definition from reaching a place if it can be truly reached. The same holds for decreasing the kill set.

13 13 Computation of in and out Sets There are data flow analysis problems that can be solved by just using directly gen, and kill sets However there are also many other algorithms that require the use of inherited information such as the in set which represents information (related to the specific data flow analysis we consider) that is flowing into a basic block Using gen, kill, and in sets we can compute synthesized information such as the out set. Note that the out set can be used as in set for the next basic blocks further down the control flow. So, in this case we can say that the out set is computed as a function of the gen, kill, and in sets (forward computation) There are also data flow analysis problems where the in set is computed as a function of the gen, kill, and out sets (backward computation)

14 14 Dealing with Loops s = s1 Let’s consider again the loop structure. The data flow equations (for reaching definitions) where: gen[S] = gen[S1] kill[S] = kill[S1] in[S1] = in[S] U out[S1] out[S] = out[S1] However, it seems that we cannot compute in[S1] just by using the equation: in[S1] = in[S] U out[S1] since we need to know out[S1] and we want to compute out[S1] in terms of gen, kill, and in. It turns out that we can express out[S1] by the equation: out[S1] = gen[S1] U (in[S1] – kill[S1])

15 15 Iterative Algorithm for Reaching Definitions (1) Input: A flow graph for which kill[B] and gen[B] have been computed for each basic block B of the graph Output: in[B] and out[B] sets for each basic block B Method: An iterative algorithm, starting with an “estimate” in[B] = for all B and converging to the desired values of in and out sets. A Boolean variable records whether we have achieved a “stable” state that is in consecutive iterations we have no changes in the in and out sets, that is we have achieved convergence. Ref: “Compilers – Principles, Techniques and Tools” by Aho, Sethi, and Ulman

16 16 Iterative Algorithm for Reaching Definitions (2) for each block B do in[B] = O for each block B do out[B] = gen[B] change = true; while change do begin change = false; for each block B do begin in[B] = U P (where P is a predecessor of B) out[P] oldout – out[B]; out[B] = gen[B] U (in[B] – kill[B]); if out[B]≠ oldout then change = true end Ref: “Compilers – Principles, Techniques and Tools” by Aho, Sethi, and Ulman

17 17 Example of Reaching Definitions (1) d1: i = m – 1 d2: j = n d3: a = u1 d4: i = i + 1 d5: j = j - 1 d6: a = u2 d7: i = u3 Ref: “Compilers – Principles, Techniques and Tools” by Aho, Sethi, and Ulman DATA FLOW gen, kill Sets gen[B1] = {d1, d2, d3} kill[B1] = {d4, d5, d6, d7} gen[B2] = {d4, d5} kill[B2] = {d1, d2, d7} gen[B3] = {d6} kill[B3] = {d3,} gen[B4] = {d7} kill[B4] = {d1, d4} B1 B2 B4 B3

18 18 Example of Reaching Definitions (2) Block Initial PassPass 1Pass 2 in[B]out[B]in[B]out[B]in[B]out[B] B1000 0000111 0000000 0000111 0000000 0000111 0000 B2000 0000000 1100111 0011001 1110111 1111001 1110 B3000 0000000 0010001 1110000 1110001 1110000 1110 B4000 0000000 0001001 1110001 0111001 1110001 0111

19 19 Live Variable Analysis In live variable analysis we are interested to know for variable x and point p, whether the value of x at p could be used along some path in the flow graph starting at p. If so, we say that x is live at p, otherwise x is dead at p. Data flow problem definition: –in[B]: the set of variables live upon entering the block –out[B]: the set of variables upon exiting the block –def[B]: the set of variables definitely assigned values in B –use[B]: the set of variables whose values may be used in B prior to any definition of the variable –Then: in[B] = use[B] U (out[B] – def[B]) out[B] = U s a successor block of B in[S]

20 20 Iterative Live Variable Analysis Algorithm Input: A flow graph with def and use sets computed for each basic block Output: out[B], the set of variables live on exit for each basic block B of the CFG for each block B do in[B] = O while changes to any of in’s occur do for each block B do begin out[B] = U s is a successor block of B in[S] in[B] = use[B] U (out{B] – def[B]) end

21 21 Definition–Use Chains The du-chaining problem is to compute for a point p the set of uses s of a variable x, such that there is a path from p to s that does not redefine x. Essentially we want for a given definition to compute all its uses. We say that a variable is used in a statement when its r-value is required. For example b, c are used in a=b+c and in a[b]=c, but not a. As with live variables analysis, if we can compute out[B], the set of uses reachable from the end of block B, then we can compute the definitions reached from any point p within the block by scanning the portion of the block B that follows p.

22 22 Def-use Chains Data flow problem definition: –in[B]: the set of definitions reached from the beginning of block B –out[B]: out[B], the set of uses reachable from the end of block B –def[B]: The set of pairs such that s is a statement which uses x, s is nit in B, and B has a definition of x. –upward_use[B]: the set of pairs such that s is a statement in B which uses variable x and such that no prior definition of x occurs in B. –Then: in[B] = upward_use[B] U (out[B] – def[B]) out[B] = U s a successor block of B in[S] The iterative algorithm is similar to the one used for live variables analysis

23 23 Definition–Use Chains Consider the following code segment: int x = 0; x = x + 10; //d1 // Block(1) x = 20; //d2 // Block(2) The use-define chain would indicate in Block(1) that X’s uses come from definition d1. In Block(2), it would be that the x’s uses may come from either definitions d1, or d2. We could then determine that since p2 is after p1, and there are no branches in Block(1) and Block(2), then the use of x in Block(2) should be coming from d2, and thus x == 20.


Download ppt "1 ECE 453 – CS 447 – SE 465 Software Testing & Quality Assurance Instructor Kostas Kontogiannis."

Similar presentations


Ads by Google