190 likes | 214 Views
Explore dynamic program analysis techniques using partial execution, failure constraints, and loop handling to reduce paths and complexity in Java programs. Solving exponential and unbounded path problems in deterministic programs.
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)