Towards Source-Level Timing Analysis of Embedded Software Using Functional Verification Methods
Total Page:16
File Type:pdf, Size:1020Kb
Fakultat¨ fur¨ Elektrotechnik und Informationstechnik Technische Universitat¨ Munchen¨ Towards Source-Level Timing Analysis of Embedded Software Using Functional Verification Methods Martin Becker, M.Sc. Vollst¨andigerAbdruck der von der Fakult¨at f¨urElektrotechnik und Informationstechnik der Technischen Universit¨atM¨unchenzur Erlangung des akademischen Grades eines Doktor-Ingenieurs (Dr.-Ing.) genehmigten Dissertation. Vorsitzender: Prof. Dr. sc. techn. Andreas Herkersdorf Pr¨ufendeder Dissertation: 1. Prof. Dr. sc. Samarjit Chakraborty 2. Prof. Dr. Marco Caccamo 3. Prof. Dr. Daniel M¨uller-Gritschneder Die Dissertation wurde am 13.06.2019 bei der Technischen Universit¨atM¨uncheneingereicht und durch die Fakult¨atf¨urElektrotechnik und Informationstechnik am 21.04.2020 angenommen. Abstract *** Formal functional verification of source code has become more prevalent in recent years, thanks to the increasing number of mature and efficient analysis tools becoming available. Developers regularly make use of them for bug-hunting, and produce software with fewer de- fects in less time. On the other hand, the temporal behavior of software is equally important, yet rarely analyzed formally, but typically determined through profiling and dynamic testing. Although methods for formal timing analysis exist, they are separated from functional veri- fication, and difficult to use. Since the timing of a program is a product of the source code and the hardware it is running on – e.g., influenced by processor speed, caches and branch predictors – established methods of timing analysis take place at instruction level, where enough details are available for the analysis. During this process, users often have to provide instruction-level hints about the program, which is a tedious and error-prone process, and perhaps the reason why timing analysis is not as widely performed as functional verification. This thesis investigates whether and how timing analysis can be performed at source code level, by leveraging mature analysis methods from general software engineering, and thus departs from the traditional instruction-level approach. Specifically, we focus on computing the worst-case execution time (WCET) of a program, which is required to prove and certify the timeliness of software in real-time systems, and used to prove its adherence to deadlines under all conditions. Not only would a source-level timing analysis be more accessible for users, but it promises other advantages, as well. As opposed to traditional instruction-level analysis, where semantic information is obfuscated or removed by the compiler, the source code carries all that information, making it easier to automatically track control and data flows. As a result, a source-level timing analysis should be able to infer more properties on the program with less user inputs, and to provide more precise timing estimates. The obvious challenge towards source-level WCET analysis is that the temporal behavior of a program is only defined by the machine code of the program and the microarchitecture of the target. The source code, however, is only specifying functional behavior, lacking all of this information. Many aspects, e.g., the specific instructions being used, are only decided during compilation from source to binary, and can have a dramatic impact on the timing. As a consequence, we have to annotate the source with a timing model computed from the binary and target properties, which requires establishing a mapping from instructions to source code. Another challenge is posed by performance-enhancing features in processors, such as instruction caches or branch predictors. Such features should be supported soundly and with reasonable precision; it is however not obvious whether and how they can be modeled in the source code, since its control flow structure can deviate significantly from the control flow of the machine instructions. Our approach to source-level WCET analysis is structured as follows. We start by evaluat- ing current methods and tools for formal functional verification of source code, to identify their shortcomings and select a viable analysis method. Based on the findings, we have chosen Bounded Model Checking as primary analysis method, which enables maximally precise timing estimates with a high degree of automatism, alleviating the need for user iii Abstract inputs. To address the back-annotation problem, we introduce an automated, compiler- and target-independent method for instruction-to-source mapping and back-annotation, which can tolerate compiler optimization. It is based on a careful combination of hierarchical flow partitioning to subdivide the mapping problem into smaller problems, and a dominator homomorphism and control dependency analysis to construct the mapping from incomplete debugging information. To address the known complexity issues of Model Checking, we propose source code transformations that are based on program slicing, loop acceleration and program abstraction, which keep the complexity of the models low. We further introduce a novel process of “timing debugging”, that is, a debugger-based reconstruction and replay of the WCET path, which sets critical variables identified during static analysis to steer into the execution into the worst-case path. Last but not least, we propose a precise source-level model for instruction caches. To guarantee its soundness, flow differences between binary and source are reconsolidated with an implicit path enumeration technique, exploiting the support for non-determinism in the analyzers. The increased program complexity caused by these microarchitectural models is controlled by computing microarchitectural invariants at either source or binary level, which effectively prevent a complexity explosion. Our source-level timing analysis has been verified against both cycle-accurate simulations and existing binary-level analyzers, for two different microarchitectures. In both cases our source-level analysis produced comparable and often better results than traditional binary- level analysis, especially under compiler optimization and in the presence of caches, and with less user input. For a simple microcontroller, we obtained WCET estimates which were 17% tighter than binary-level analysis, and on a processor with caches this number improved to even 56% tighter estimates. As a downside, the scalability of the proposed source-level approach is inferior to traditional approaches. Typical analysis times range between seconds and minutes in our experiments, lagging behind the faster traditional methods, which consistently terminate within seconds. However, we found several ways how this could be improved. Furthermore, we have only considered mono-processors with single-level instruction caches. Modeling more advanced processors is feasible and has been sketched, but is also left for future work. Overall, the results demonstrate that source-level timing analysis is a promising research direction with certain benefits, but also that traditional binary-level analysis cannot be entirely replaced. Instead, future research should focus on hybrid approaches, leveraging the best of both source-level and binary-level worlds. In summary, the main contributions of this thesis are as follows. (1) We introduce an overall workflow for WCET analysis at source code level using Model Checking, which addresses the known scalability issues with source code transformations. (2) We propose a generic, compiler- and target-independent method to establish a sound and precise mapping between machine instructions and source code, which is used to back-annotate the instruction timing to the source code, and serves as link between source-level analysis and microarchitectural analysis. (3) We propose a novel source-level model for set-associative instruction caches and address its complexity issues by computing invariants. We further sketch models for data caches, branch predictors and other performance-enhancing features, and discuss their impact on the analysis. (4) We present a novel process for “timing debugging” which allows the user to examine the program’s timing behavior in a debugger, while we automatically steer it into the WCET case. This method can be applied to counterexample reconstruction in general functional verification without any changes. (5) Lastly, and opposing a commonly held view, the experiments in this thesis demonstrate that Model Checking can indeed be applied for the WCET problem and scale well, albeit only when used intelligently. iv Zusammenfassung (German Abstract) *** Formale funktionale Verifikation von Software kommt zunehmend haufiger¨ zur Anwendung, dank der wachsenden Zahl von ausgereiften und effizienten Analysewerkzeugen. Entwickler nutzen diese regelmaßig¨ zur Fehlersuche, und produzieren robustere Software in kurzerer¨ Zeit. Andererseits ist das zeitliche Verhalten von Software ebenso wichtig, wird jedoch selten formal analysiert, sondern typischerweise durch Profiling und dynamische Tests bewertet. Obwohl Methoden zur formalen Zeitanalyse existieren, werden sie getrennt von funktionaler Verifikation angewandt, und sind schwierig zu bedienen. Da das Zeitverhalten eines Pro- gramms ein Resultat von Quelltext und der ausfuhrenden¨ Hardware ist – z.B. beeinflusst durch Prozessorgeschwindigkeit, Caches und Sprungvorhersagen – arbeiten etablierte Me- thoden der Zeitanalyse auf Instruktionsebene, wo genugend¨ Details fur¨ die Analyse bereit stehen. Wahrend¨ dieses Prozesses muss der Benutzer oftmals Hinweise auf Instruktionsebene