190 likes | 212 Views
Dynamic Program Analysis with Partial Execution and Summary. Thomas Huining Feng http://www.eecs.berkeley.edu/~tfeng/ CHESS, UC Berkeley May 8, 2007 CS 294-5 Class Project. Motivating Example 1. public class A { void f(boolean op1, boolean op2, boolean op3, boolean failure) {
E N D
Dynamic Program Analysis with Partial Execution and Summary Thomas Huining Feng http://www.eecs.berkeley.edu/~tfeng/ CHESS, UC Berkeley May 8, 2007 CS 294-5 Class Project
Motivating Example 1 public class A { void f(boolean op1, boolean op2, boolean op3, boolean failure) { if (op1) { System.out.println("Operation 1"); } if (op2) { System.out.println("Operation 2"); } if (op3) { System.out.println("Operation 3"); } @Failure("failure") int fail; } } • JCute finds 24=16 paths. • Overall failure constraint is [failure] (after simplification). • Can we reduce the number of paths? Parameters are generated by JCute. op1 op2 op3 if (failure) { System.out.println(“FAILURE”); return; } else { return; } fail
Motivating Example 2 public class B { void f(int i, int j) { for (int k = 0; k < i; k++) { if (i == j) { @Failure(“true”) int fail; } } } } • JCute never halts. • An unbounded number of paths exist, because i is arbitrary. • Can we handle this problem? Yes. • Can we handle this problem within 10 runs? Yes. Raise a failing signal when [i == j]. Equivalent to: @Failure(“i == j”) int fail;
Overview • Examples exhibit 2 problems: • Exponential paths to explore. • Unbounded paths to explore before a loop constraint is found. • Assumption: Free of side effect, deterministic Java programs. • To solve problem 1: • For a program with n = n1 + n2 + … + nk sequential tests • split the program into k trunks • reduce the complexity to 2n1 + 2n2 + … + 2nk (best case) • Let n1 = n2 = … = nk = 1 to achieve linear growth. • To solve problem 2: • Partially execute the loop. • Compute a fixpoint of constraints.
Solving Problem 1 • Examples exhibit 2 problems: • Exponential paths to explore. • Unbounded paths to explore before a loop constraint is found. • Assumption: Free of side effect, deterministic Java programs. • To solve problem 1: • For a program with n = n1 + n2 + … + nk sequential tests • split the program into k trunks • reduce the complexity to 2n1 + 2n2 + … + 2nk (best case) • Let n1 = n2 = … = nk = 1 to achieve linear growth. • To solve problem 2: • Partially execute the loop. • Compute a fixpoint of constraints.
Partition the Program public class A { void f(boolean op1, boolean op2, boolean op3, boolean failure) { if (op1) { System.out.println("Operation 1"); } if (op2) { System.out.println("Operation 2"); } if (op3) { System.out.println("Operation 3"); } @Failure("failure") int fail; } } • 1st execution: the last test of the program. 2 branches; 4 paths. • Constraint: [op3 && failure || !op3 && failure] ⇔[failure] public class A { void f(boolean op1, boolean op2, boolean op3, boolean failure) { if (op3) { System.out.println("Operation 3"); } @Failure("failure") int fail; } } Seg. 3 Seg. 1 Seg. 2 Seg. 1
Rewrite the Program public class A { void f(boolean op1, boolean op2, boolean op3, boolean failure) { if (op1) { System.out.println("Operation 1"); } if (op2) { System.out.println("Operation 2"); } if (op3) { System.out.println("Operation 3"); } @Failure("failure") int fail; } } public class A { void f(boolean op1, boolean op2, boolean op3, boolean failure) { if (op1) { System.out.println("Operation 1"); } if (op2) { System.out.println("Operation 2"); } @Failure("failure") int fail; } } Rewrite
Segment the Program Again public class A { void f(boolean op1, boolean op2, boolean op3, boolean failure) { if (op1) { System.out.println("Operation 1"); } if (op2) { System.out.println("Operation 2"); } @Failure("failure") int fail; } } • 2nd execution: the last test of the program. 2 branches; 4 paths. • Constraint: [op2 && failure || !op2 && failure] ⇔[failure] public class A { void f(boolean op1, boolean op2, boolean op3, boolean failure) { if (op2) { System.out.println("Operation 3"); } @Failure("failure") int fail; } } Seg. 3 Seg. 2 Seg. 2
Assessment • Repeat this process until head of the program is reached. • A big gain? op1 op2 op2 op3 op1 op3 fail fail fail fail Total # of paths: 24 = 16 Total # of paths: 4 + 4 + 4 = 12
Assessment Total # of paths: 8 * 8 * 2 = 128 Total # of paths: 8*2 + 8*2 = 32
Solving Problem 2 • Examples exhibit 2 problems: • Exponential paths to explore. • Unbounded paths to explore before a loop constraint is found. • Assumption: Free of side effect, deterministic Java programs. • To solve problem 1: • For a program with n = n1 + n2 + … + nk sequential tests • split the program into k trunks • reduce the complexity to 2n1 + 2n2 + … + 2nk (best case) • Let n1 = n2 = … = nk = 1 to achieve linear growth. • To solve problem 2: • Partially execute the loop. • Compute a fixpoint of constraints.
Bounded Loop Execution • First, identify the loop. public class B { void f(int i, int j) { int k; k = 0; l1: if (k < i) { if (i == j) { @Failure(“true”) int fail; } k++; goto l1; } } } public class B { void f(int i, int j) { for (int k = 0; k < i; k++) { if (i == j) { @Failure(“true”) int fail; } } } }
Bounded Loop Execution public class B { void f(int i, int j) { int k; k = 0; l1: if (k < i) { if (i == j) { @Failure(“true”) int fail; } k++; goto l1; } } } • Assume that the loop exits in n = 3 iterations. • Partition the program into 4 segments. Seg. 4 k<i k<i Seg. 3 N Y N Y i==j i==j N Y N Y k<i k<i Seg. 2 i==j i==j k<i k<i Seg. 1 i==j i==j k<i … …
Bounded Loop Execution • Run the tree from the tail. • Repeat computing the failure constraint. • Test for fixpoint. • Reduce the loop into a simple branch. k<i k<i k<i C3 N N N N Y Y Y Y i==j i==j i==j N N N Y Y Y Test 3: C3 C2 k<i k<i i==j i==j Test 2: C2 C1 k<i i==j Test 1: C1
Bounded Loop Execution • 1st execution: 3 paths. • Constraint: [k <= i-1 && i == j] public class B { void f(int i, int j) { int k; k = 0; l1: if (k < i) { if (i == j) { @Failure(“true”) int fail; } k++; goto l1; } } } public class B { void f(int i, int j) { int k = JCute.input.Integer(); if (k < i) { if (i == j) { @Failure(“true”) int fail; } k++; } } } Seg
Bounded Loop Execution • Analyze 1 more iteration. • Re-execute: 4 paths. • Constraint: [k <= i-1 && i == j] (fixpoint) public class B { void f(int i, int j) { int k = JCute.input.Integer(); if (k < i) { if (i == j) { @Failure(“true”) int fail; } k++; if (k < i) { if (i == j) { @Failure(“true”) int fail; } k++; } } } } public class B { void f(int i, int j) { int k = JCute.input.Integer(); if (k < i) { if (i == j) { @Failure(“true”) int fail; } k++; @Failure(“k <= i-1 && i == j”) int fail; } } } public class B { void f(int i, int j) { int k; k = 0; l1: if (k < i) { if (i == j) { @Failure(“true”) int fail; } k++; goto l1; } } } Seg
Bounded Loop Execution • Substitute the whole loop with the fixpoint constraint. • Execute the program: 3 paths. • Constraint: [i >= 1 && i == j] • Total # of paths: 3 + 4 + 3 = 10 public class B { void f(int i, int j) { int k; k = 0; l1: if (k < i) { if (i == j) { @Failure(“true”) int fail; } k++; goto l1; } } } public class B { void f(int i, int j) { int k; k = 0; @Failure(“k <= i-1 && i == j”) int fail; } } Seg
Conclusion: Advantage and Drawback • Advantages: • Greatly reduce the number of paths in some cases. • Derive constraints for unbounded loops. • Limitations • Need transformation and compilation every time you want to simplify the program with the newly derived constraints. • Advantage 1 is lost if constraints are not (cannot be) simplified. void f(boolean op1, boolean op2, boolean op3, boolean failure) { if (op1) { System.out.println("Operation 1"); } if (op2) { System.out.println("Operation 2"); } if (op3) { System.out.println("Operation 3"); } @Failure("failure“) int fail; } [op1 && op2 && op3 && failure || op1 && op2 && !op3 && failure || ...] (16 paths) [op2 && op3 && failure || op2 && !op3 && failure || !op2 && op3 && failure || !op2 && !op3 && failure] (8 paths) [op3 && failure || !op3 && failure] (4 paths)