Welcome to Scribd, the world's digital library. Read, publish, and share books and documents. See more
Download
Standard view
Full view
of .
Look up keyword
Like this
2Activity
0 of .
Results for:
No results containing your search query
P. 1
Genetic Programming

Genetic Programming

Ratings: (0)|Views: 68 |Likes:
Published by vthung

More info:

Published by: vthung on May 07, 2009
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

06/02/2010

pdf

text

original

 
Automatically Finding Patches Using Genetic Programming
Westley Weimer
University of Virginia
weimer@virginia.edu
ThanhVu Nguyen
University of New Mexico
tnguyen@cs.unm.edu
Claire Le Goues
University of Virginia
legoues@virginia.edu
Stephanie Forrest
University of New Mexico
forrest@cs.unm.edu
Abstract
 Automatic repair of programs has been a longstand-ing goal in software engineering, yet debugging remainsa largely manual process. We introduce a fully automated method for locating and repairing bugs in software. The ap- proach works on off-the-shelf legacy applications and doesnot require formal specifications, program annotations or special coding practices. Once a program fault is discov-ered, an extended form of genetic programming is used toevolve program variants until one is found that both retainsrequired functionality and also avoids the defect in ques-tion. Standard test cases are used to exercise the fault and to encode program requirements. After a successful repair has been discovered, it is minimized using using structuraldifferencing algorithms and delta debugging. We describethe proposed method and report results from an initial set of experiments demonstrating that it can successfully repair ten different C programs totaling 63,000 lines in under 200seconds, on average.
1 Introduction
Fixing bugs is a difficult, time-consuming, and manualprocess. Some reports place software maintenance, tradi-tionally defined as any modification made on a system afterits delivery, at 90% of the total cost of a typical softwareproject [27, 30]. Modifying existing code, repairing defects,and otherwise evolving software are major parts of thosecosts [28]. The number of outstanding software defects typ-ically exceeds the resources available to address them [4].Maturesoftwareprojectsareforcedtoshipwithbothknownand unknown bugs [23] because they lack the developmentresources to deal with every defect. For example, in 2005,one Mozilla developer claimed that, “everyday, almost 300bugs appear [...] far too much for only the Mozilla pro-grammers to handle” [5, p. 363].To alleviate this burden, we propose an automatic tech-nique for repairing program defects. Our approach doesnot require difficult formal specifications, program anno-tations or special coding practices. Instead, it works onoff-the-shelf legacy applications and readily-available test-cases. We use genetic programming to evolve program vari-ants until one is found that both retains required function-ality and also avoids the defect in question. Our techniquetakes as input a program, a set of successful positive test-cases that encode required program behavior, and a failingnegative testcase that demonstrates a defect.
Genetic programming
(GP) is a computational methodinspired by biological evolution, which discovers computerprograms tailored to a particular task [22]. GP maintains apopulation of individual programs. Computational analogsof biological mutation and crossover produce program vari-ants. Each variant’s suitability is evaluated using a user-defined fitness function, and successful variants are selectedfor continued evolution. GP has solved an impressive rangeof problems (e.g., see [1]), but to our knowledge it has notbeen used to evolve off-the-shelf legacy software.A significant impediment for an evolutionary algorithmlike GP is the potentially infinite-size search space it mustsample to find a correct program. To address this problem,we introduce two key innovations. First, we restrict the al-gorithm to only produce changes that are based on struc-tures in other parts of the program. In essence, we hypoth-esize that a program that is missing important functionality(e.g., a null check) will be able to copy and adapt it fromanother location in the program. Second, we constrain thegeneticoperationsofmutationandcrossovertooperateonlyon theregion oftheprogramthatisrelevantto theerror(thatis, the portions of the program that were on the executionpath that produced the error). Combining these insights, wedemonstrate automatically generated repairs for ten C pro-grams totaling 63,000 lines of code.We use GP to maintain a population of variants of thatprogram. Each variant is represented as an abstract syn-tax tree (AST) paired with a weighted program path. Wemodify variants using two genetic algorithm operations,crossover and mutation, specifically targeted to this repre-sentation; each modification produces a new abstract syntaxtree and weighted program path. The fitness of each variantis evaluated by compiling the abstract syntax tree and run-ning it on the testcases. Its final fitness is a weighted sum of 
 
the positive and negative testcases it passes. We stop whenwe have evolved a program variant that passes all of thetestcases. Because GP often introduces irrelevant changesor dead code, we use tree-structured difference algorithmsand delta debugging techniques in a post-processing step togenerate a 1-minimal patch that, when applied to the origi-nal program, causes it to pass all of the testcases.The main contributions of this paper are:
Algorithms to find and minimize program repairsbased on testcases that describe desired functionality.
A novel and efficient representation and set of opera-tions for scaling GP in this domain. To the best of ourknowledge, this is the first use of GP to scale to andrepair real unannotated programs.
Experimental results showing that the approach cangenerate repairs for different kinds of defects in tenprograms from multiple domains.The structure of the paper is as follows. In Section 2 wegive an example of a simple program and how it might berepaired. Section 3 describes the technique in detail, includ-ing our program representation (Section 3.2), genetic op-erators (Section 3.3), fitness function (Section 3.4) and ap-proach to repair minimization (Section 3.5). We empiricallyevaluate our approach in Section 4, including discussions of convergence (Section 4.4) and repair quality (Section 4.3).We discuss related work in Section 5 and conclude.
2 Motivating Example
This section uses a small example program to illustratethe important design decisions in our approach. Considerthe C program below, which implements Euclid’s greatestcommon divisor algorithm:
1
/*requires: a >= 0, b >= 0*/
2
voidgcd(inta,intb) {
3
if(a == 0) {
4
printf("%d", b);
5
}
6
while(b != 0) {
7
if(a > b) {
8
a = a - b;
9
}else{
10
b = b - a;
11
}
12
}
13
printf("%d", a);
14
exit(0);
15
}
The program has a bug; when
a
is zero and
b
is positive, theprogram prints out the correct answer but then loops for-ever on lines 6–9–10. The code could have other bugs: itdoes not handle negative inputs gracefully. In order to re-pair the program we must understand what it is supposedto be doing; we use testcases to codify these requirements.For example, we might use the testcase
gcd(0,55)
with de-sired terminating output
55
. The program above fails thistestcase, which helps us identify the defect to be repaired.Our algorithm attempts to automatically repair the de-fect by searching for valid variants of the original program.Searching randomly through possible program modifica-tions for a repair may not yield a desirable result. Considerthe following program variant:
1
voidgcd_2(inta,intb) {
2
printf("%d", b);
3
exit(0);
4
}
This
gcd_2
variant passes the
gcd(0,55)
testcase, but failsto implement other important functionality. For example,
gcd_2(1071,1029)
produces
1029
instead of 
21
. Thus,the variants must pass the negative testcase while retain-ing other core functionality. This is enforced through pos-itive testcases, such as terminating with output
21
on input
gcd(1071,1029)
. In general, several positive testcases willbe necessary to encode the requirements, although in thissimple example a single positive testcase suffices.For large programs, we would like to bias the modifi-cations towards the regions of code that are most likely tochange behavior on the negative testcase without damagingperformance on the positive testcases. We thus instrumentthe program to record all of the lines visited when process-ingthetestcases. Thepositivetestcase
gcd(1071,1029)
vis-its lines 2–3 and 6–15. The negative testcase
gcd(0,55)
visits lines 2–5, 6–7, and 9–12. When selecting portions of the program to modify, we favor locations that were visitedduring the negative testcase and were not also visited dur-ing the positive one. In this example, repairs are focused onlines 4–5.Even if we know where to change the program, the num-ber of possible changes is still huge, and this has been a sig-nificant impediment for GP in the past. We could add arbi-trarycode, deleteexistingcode, orchangeexistingcodeintonew arbitrary code. We make the assumption that most de-fectscanberepairedbyadoptingexistingcodefromanotherlocation in the program. In practice, a program that makes amistake in one location often handles the situation correctlyin another [14]. As a simple example, a program missinga null check or an array bounds check is likely to have asimilar working check somewhere else that can be used as atemplate. When mutating a program we may insert, deleteor modify statements, but we insert only code that is sim-ilar in structure to existing code. Thus, we will not insertan arbitrary
if
conditional, but we might insert
if(a==0)
or
if(a>b)
because they already appear in the program.Similarly, we might insert
printf("%d",a)
,
a=a-b
,
b=b-a
,
 printf("%d",b)
, or
exit(0)
, but not arbitrary statements.
 
Given the bias towards modifying lines 4–5 and our pref-erence for insertions similar to existing code, it is reason-able to consider inserting
exit(0)
and
a=a-b
between lines4 and 5, which yields: variant:
1
voidgcd_3(inta,intb) {
2
if(a == 0) {
3
printf("%d", b);
4
exit(0);// inserted
5
a = a - b;// inserted
6
}
7
while(b != 0) {
8
if(a > b) {
9
a = a - b;
10
}else{
11
b = b - a;
12
}
13
}
14
printf("%d", a);
15
exit(0);
16
}
This
gcd_3
variant passes all of the positive testcases andalso passes the negative testcase; we call it the primary re-pair. We could return it as the final repair. However, the
a = a - b
inserted on line 5 is extraneous. The GP methodoften produces such spurious changes, which we minimizein a final step. We consider all of the changes between theoriginal
gcd
and the primary repaired variant
gcd_3
, retainthe minimal subset of changes that, when applied to
gcd
, al-low it to pass all positive and negative testcases. This mini-mal patch is the final repair:
3
if(a == 0) {
4
printf("%d", b);
5
+ exit(0);
6
}
7
while(b != 0) {
In the next section, we describe a generalized form of this procedure.
3 Genetic Programming for Software Repair
The core of our method is a GP that repairs programs byselectively searching through the space of nearby programvariants until it discovers one that avoids known defects andretains key functionality. We use a novel GP representa-tion and make assumptions about the probable nature andlocation of the necessary repair to make the search more ef-ficient. Given a defective program, we must address fivequestions:1.
What is it doing wrong?
We take as input a set of 
negative testcases
that characterizes a fault. The inputprogram fails all negative testcases.2.
What is it supposed to do?
We take as input a setof 
positive testcases
that encode functionality require-ments. The input program passes all positive testcases.
Input:
Program
to be repaired.
Input:
Set of positive testcases
PosT 
.
Input:
Set of negative testcases
NegT 
.
Output:
Repaired program variant.
1:
Path 
PosT 
 p
PosT 
statements visited by
(
 p
)
2:
Path 
NegT 
n
NegT 
statements visited by
(
n
)
3:
Path 
update weights
(
Path 
NegT 
,
Path 
PosT 
)
4:
Popul 
initial population
(
P,
pop size
)
5:
repeat
6:
Viable
← {
P,
Path 
,
 ∈
Popul 
|
f >
0
}
7:
Popul 
← ∅
8:
NewPop
← ∅
9:
for all
 p
1
,p
2
 ∈
sample
(
Viable
,
pop size
/
2)
do
10:
c
1
,c
2
 ←
crossover
(
 p
1
,p
2
)
11:
NewPop
NewPop
∪ {
 p
1
,p
2
,c
1
,c
2
}
12:
end for
13:
for all
V,
Path 
,
 ∈
NewPop
do
14:
Popul 
Popul 
∪ {
mutate
(
V,
Path 
)
}
15:
end for
16:
until
∃
V,
Path 
,
 ∈
Popul 
. f 
=
max fitness
17:
return
minimize
(
V,P,
PosT 
,
NegT 
)
Figure 1.
High-level pseudocode for our technique.Lines 5–16 describe the GP search for a feasible vari-ant. Subroutines such as
mutate
(
V,
Path 
)
are de-scribed subsequently.3.
Where should we change it?
We favor changingprogram locations visited when executing the negativetestcases and avoid changing program locations visitedwhen executing the positive testcases.4.
How should we change it?
We insert, delete, andswap program statements and control flow. We favorinsertions based on the existing program structure.5.
When are we finished?
The primary repair is the firstvariant that passes all positive and negative testcases.We minimize the differences between it and the origi-nal input program to produce the final repair.Pseudocode for the algorithm is given in Figure 1. lines1–2 determine the paths visited by the program on the inputtestcases. Line 3 combines the weights from those paths(see Section 3.2). With these preprocessing steps complete,Line 4 constructs an initial GP population based on the in-put program. Lines 5–16 encode the main GP loop (seeSection 3.1), which searches for a feasible variant. On eachiteration, we remove all variants that fail every testcase (line6). We then take a weighted random sample of the remain-ing variants (line 9), favoring those variants that pass moreof the testcases (see Section 3.4). We apply the crossoveroperator (line 10; see Section 3.3) to the selected variants;each pair of parent variants produces two child variants. We

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->