Tuesday, October 5, 2010

Programming Abstractions with Debugging Support for Resource-Constrained Devices

Abstractions are crucial in order to manage complex systems. In pervasive computing, though, common programming abstractions tend to be too expensive for the employed resource-constrained devices. In recent years, the wireless sensor network community has proposed several solutions to this problem. However, little has been done to also support debugging on the level of the abstraction. Instead, a developer is forced to understand the lower-level details in order to find and correct defects. This clearly hampers the development of applications. We aim at advancing the state of the art in programming of resource-constrained devices by introducing debugging support for programming abstractions.

The unobtrusive integration of pervasive applications into the real world often requires tiny, battery-powered devices to cooperate in a distributed fashion over wireless links. Due to these requirements, pervasive applications tend to be complex and thus difficult to develop, deploy and maintain. In order to make the complexity manageable, suitable programming abstractions which help writing and maintaining code for the devices are required. Unfortunately, programming abstractions offered by high-level programming languages tend to require more CPU and memory resources than a pervasive computing device typically offers. As a consequence, pervasive applications are often implemented in the C programming language which offers only few and rather low-level programming abstractions. Thus, most pervasive applications are notoriously hard to write, deploy and maintain.

The need for programming abstractions for resource-constrained devices has since long been recognized by the research community, in particular in the field of wireless sensor network (WSN). WSNs are a pervasive computing technology which supports monitoring of physical phenomena over a potentially large spatial region. Because the peculiarities of pervasive computing which drive the demand for programming abstractions are especially predominant in WSNs, a number of solutions have been proposed in the past few years [Mottola].

Little has been done, though, to also support debugging on the level of the abstraction [Mottola]. As a consequence, software developers often have to understand lower-level details such as the syntax and semantics of generated code in order to trace program errors. As hiding lower-level details is one of the main goals of abstractions, we consider programming abstractions without debugging support as incomplete.

We thus want to investigate programming abstractions with debugging support for resource-constrained devices. In particular, we focus on WSN because many programming abstractions without debugging support already exist in this field. We furthermore expect our results to be also applicable to other fields of pervasive computing.

State of the Art

WSN programming abstractions can be classified by two major orthogonal dimensions: node-centric vs. distributed and imperative vs. declarative [Mottola]. Node-centric abstractions focus on programming single nodes which might or might not form a distributed system. In contrast, abstractions for distributed applications account for the distributed nature of the application and support the programming of groups of nodes such as spatial or logical neighborhoods.

Irrespective of the first dimension, with imperative abstractions the intended application processing is expressed through statements that explicitly indicate how to change the program state [Mottola]. In contrast, for declarative abstractions the application goal is described without specifying how it is accomplished [Mottola]. Out of the four possible classes of programming abstractions only three are relevant in practice and we discuss them in the following.

Protothreads Protothreads and TinyVT are node-centric, imperative programming abstractions. They offer compiler-based thread abstractions by taking a thread-based program as input and generating a semantically equivalent event-based program for the targeted event-based operating system. While this combines the comfort of thread-based programming with the efficiency of events, both of them have no debugging support. Thus, a developer is forced to step through the generated event-based code in order to identify defects in the thread-based program.

A second class of programming abstractions is imperative and distributed programming abstractions which are commonly known as macro-programming in the WSN community. While the existing abstractions like Kairos and Regiment are powerful, their practical adoption seems to suffer from the lack of debugging support. In this case, besides the knowledge of lower-level details, a software developer additionally needs special tools such as distributed debuggers (Clairvoyant) because a single macro-program controls the behavior of many distributed devices. Compared to node-centric abstractions, the lack of debugging support on the level of the abstraction is therefore even more severe. To the best of our knowledge, the only macro-programming language with debugging support is MacroLab which is a dialect of MATLAB and can be debugged with the Macrodebugger, a post-mortem debugger specifically crafted for MacroLab.

Two well-known declarative and distributed programming abstractions are Cougar Cougar and TinyDB. They model a WSN as a relational database and support SQL-like queries to retrieve information about the monitored physical phenomena. While this is what users presumably want, if an error occurs somewhere in the distributed system, the user has to resort to other means like passive inspection of the network traffic (SNIF) in order to find the cause of the problem.

Since WSN applications are rather diverse, suitable programming abstractions will have to be specifically crafted for a given application domain. Such programming abstractions are usually implemented by so-called domain-specific languages (DSL). In software engineering there is an ongoing trend towards DSLs in general and DSLs for building DSLs in particular (actifsource, MPS, Xtext, Spoofax, MetaEdit+). With the support of these so-called language workbenches it is rather easy to quickly introduce DSLs and to implement the corresponding compiler. Again, though, the issue of debugging on the level of the abstraction has not been solved yet.

Goals of my Thesis

The goal of my thesis is to introduce debugging support of programming abstractions for resource-constrained devices. To this end, we will focus our investigations on two major topics.

First, as a concrete example we want to introduce a debuggable, compiler-based thread abstraction for event-based operating systems. The expected outcome is that from a developer's perspective there is not much difference from using our compiler to using a thread library. But still, the achieved efficiency is as close as possible to the efficiency of hand-written event-based code. Furthermore, source-level debugging of the thread-based program is possible.

Compared to thread libraries there are theoretical limits of what features a compiler-based approach can offer. These limitations stem from the fact that a compiler can only process decidable problems. In order to stay within these theoretical bounds, we can neither commonly support calling blocking operations by function pointers nor calling blocking functions from recursive functions. Furthermore, dynamically starting new threads is not possible. However, as it is advisable to statically ensure that the scarce resources of a given device suffice the demands of a given program, such dynamic features are rarely used for embedded systems. Thus, we don't consider these limitations to be severe, especially considering that we expect to be able to get close to the efficiency of the event-based paradigm.

Additionally, if the compiler records which thread-based code was translated into which part of the event-based program, it becomes possible to perform source level debugging in the thread-based code. In principle, this technique is very similar to what C tool chains use in order to support source-level debugging of C programs. Thus, we expect to be able to reuse and build upon a large number of existing work such as the DWARF Debugging Standard.

For the second part of the thesis, we want to investigate in more general terms how programming abstractions can be designed and built in a way such that debugging support is included. Therefore we want to identify standard debugging techniques for the various types of programming abstractions encountered in the WSN field. By extending existing language workbenches we enable the design and implementation of various types of DSLs with the respective debugging support integrated. The expected outcome is that software developers will be able to easily create programming abstractions like TinyDB or Kairos with integrated debugging support.

For node-centric and imperative DLSs, simple debugging information emitted by the compiler is known to suffice. For distributed DLSs, though, existing debugging tools such as distributed debuggers (Clairvoyant) or passive distributed assertions (PDA) must be incorporated into the tool chain such that information about an error which is reported from the debugging tool points to the error's cause in the program. In the case of declarative programming abstractions, debugging is even harder because there is no one-to-one mapping between the program and the generated code. Thus, more detailed information is necessary in order to be able to map from the generated code back to the program.

Meta

This work has been partially supported by the National Competence Center in Research on Mobile Information and Communication Systems (NCCR-MICS), a center supported by the Swiss National Science Foundation under grant number 5005-67322, and by CONET, the Cooperating Objects Network of Excellence, funded by the European Commission under FP7 with contract number FP7-2007-2-224053.