Abstract : The world is day by day more computerized. There is more and more software running everywhere, from personal computers to data servers, and inside most of the new popularized inventions such as connected watches or intelligent washing machines. All of those technologies use software applications to perform the services they are designed for. Unfortunately, the number of software errors grows with the number of software applications.
In isolation, software errors are often annoyances, perhaps costing one person a few hours of work when their accounting application crashes. Multiply this loss across millions of people and consider that even scientific progress is delayed or derailed by software error: in aggregate, these errors are now costly to society as a whole.
There exists two techniques to deal with those errors. Bug fixing consists in repairing errors. Resilience consists in giving an application the capability to remain functional despite the errors.
This thesis focuses on bug fixing and resilience in the context of exceptions. Exceptions are programming language constructs for handling errors. They are implemented in most mainstream programming languages and widely used in practice.
We specifically target two problems:
Problem #1: There is a lack of debug information for the bugs related to exceptions. This hinders the bug fixing process. To make bug fixing of exceptions easier, we will propose techniques to enrich the debug information. Those techniques are fully automated and provide information about the cause and the handling possibilities of exceptions.
Problem #2: There are unexpected exceptions at runtime for which there is no error-handling code. In other words, the resilience mechanisms against exceptions in the currently existing (and running) applications is insufficient. We propose resilience capabilities which correctly handle exceptions that were never foreseen at specification time neither encountered during development or testing.
In this thesis, we aim at finding solutions to those problems. We present four contributions to address the two presented problems.
In Contribution #1, we lies the foundation to address both problems. To improve the available information about exceptions, we present a characterization of the exceptions (expected or not, anticipated or not), and of their corresponding resilience mechanisms. We provide definitions about what is a bug when facing exceptions and what are the already-in-place corresponding resilience mechanisms. We formalize two formal resilience properties: source-independence and pure-resilience as well as an algorithm to verify them. We also present a code transformation that uses this knowledge to enhance the resilience of the application.
Contribution #2 aims at addressing the limitations of Contribution #1. The limitations is that there are undecidable cases, for which we lack information to characterize them in the conceptual framework of Contribution #1. We focus on the approaches that use the test suite as their main source of information as in the case of Contribution #1. In this contribution, we propose a technique to split test cases into small fragments in order to increase the efficiency of dynamic program analysis. Applied to Contribution #1, this solution improves the knowledge acquired by providing more information on more cases. Applied to other dynamic analysis techniques which also use test suites, we show that it improve the quality of the results. For example, one case study presented is the use of this technique on Nopol, an automatic repair tool.
Contribution #1 and #2 are generic, they target any kind of exceptions. In order to further contribute to bug fixing and resilience, we need to focus on specific types of exceptions. This focus enables us to exploit the knowledge we have about them and further improve bug fixing and resilience. Hence, in the rest of this thesis, we focus on a more specific kind of exception: the null pointer dereference exceptions (NullPointerException in Java).
Contribution #3 focuses on Problem #1, it presents an approach to make bug fixing easier by providing information about the origin of null pointer dereferences. We present an approach to automatically provide information about the origin of the null pointer dereferences which happen in production mode (i.e. those for which no efficient resilience mechanisms already exists). The information provided by our approach is evaluated w.r.t. its ability to help the bug fixing process. This contribution is evaluated other 14 real-world null dereference bugs from large-scale open-source projects.
Contribution #4 addresses Problem #2, we present a way to tolerate the same kind of errors as Contribution #3: null pointer dereference. We first use dynamic analysis to detect harmful null dereferences, skipping the non-problematic ones. Then we propose a set of strategies able to tolerate this error. We define code transformations to 1) detect harmful null dereferences at runtime; 2) allow a runtime ehavior modification to execute strategies; 3) assess the correspondance between the modified behavior and the specifications. This contribution is evaluated other 11 real-world null dereference bugs from large-scale open-source projects.
To sum up, this thesis studies the exceptions, their behaviors, the information one can gathered from them, the problems they may cause and the applicable solutions to those problems.