Friday, March 1, 2013

Apport-Valgrind 2.9 supports unpackaged executables

Apport-valgrind 2.9 picks up support for unpackaged [1] executables (thanks Martin Pitt for working with me to land my changes in trunk).

Here's what this means.
  • Previously if you ran apport-valgrind EXE, where EXE was not installed by a debian package, the process quit with a warning because it could only obtain debug symbol packages through debian dependencies
  • As of release 2.9, execution succeeds, and it obtains debug symbols packages based on  the shared object (.so) files that the EXE is linked to

Digging deeper into how it works

As explained here, apport-valgrind is a wrapper for valgrind, the venerable memory leak finder (among other things). 

Apport-valgrind first creates a sandbox directory that contains debug symbol files related to the executable you are checking for memory leaks. It then launches valgrind and points it also at the new sandbox directory. Valgrind creates memory leak stack traces, looking in the standard system directories for debug symbol files, but also in the generated sandbox directory. It uses the debug symbol files to create the most useful stack traces it can: stack traces that display source file names (instead of installed library file names) and function names (instead of "???", which is an unresolved symbol). All this is done without installing debug symbol packages onto your system: they are unpacked into the temporary sandbox directory, which is automatically deleted after use (unless an optional persistent sandbox is used). 

Release 2.9 uses a new technique for populating the sandbox directory that extends support for executables that are not installed by a package. (More on why that may be useful below.)

Flow for packaged EXE

Previously, the sandbox was populated only using debian dependencies of the debian package that installed the executable, like so:
  • Find the package that installed the executable
  • Find that package's full set of dependencies (all the way down, that is, recursively)
  • For each, get the best debug symbol package available (this depends on your system's apt configuration, for example whether you have added "ddebs.ubuntu.com", as described here)
  • Unpack the debug symbol packages into the sandbox
This code path still exists and is used for packaged executables.

Flow for unpackaged EXE

With 2.9, the sandbox is populated with the debug packages related to the executable's linked .so files, as reported by ldd, like so:
  • Use ldd to determine the executable's linked shared libraries (.so files)
  • Find the package that installed each linked shared library
  • For each, get the best debug symbol package available
  • Unpack the debug symbol packages into the sandbox

Use case

One use case I see is that one can now write a C program specifically to memory check a library with apport-valgrind simply by including and using the library. You do not need to rely only on existing debian packages to find executables that use the libraries in order to memory check them. And, you can target your C program to precisely target what you want to memory check, and no more.

For example: libappindicator.

As an exercise, I wanted to find memory leaks in application indicators (the icons on the top right like power, network, etc.), which means I needed to launch an app indicator at the command line with apport-valgrind (apport-valgrind EXE). So I tried to kill those processes, and they relaunched automatically. 

So I thought: write my own app indicator -- which turned out to be unnecessary: I found some sample C code that demonstrates how to write a simple application indicator here on developer.ubuntu.com. (Scroll down to "Typical usage (C version)".)

So I grabbed it. It includes a libappindicator header file:

#include <libappindicator/app-indicator.h>

I compiled and linked this code, like so:

$ gcc myappindicator.c $(pkg-config --cflags --libs appindicator3-0.1) -o myappindicator.o 

That produces the unpackaged executable myappindicator.o

Running this at the command line (./myappindicator.o) creates a new icon in the appindicator area that has a menu and pops up a dialog with a text editing area. The indicator icon's menu has a Quit item, which works as expected, quitting the application and removing its indicator, so all is well.

Then, I ran it under apport-valgrind [2], like so:
$ apport-valgrind ./myappindicator.o 

And voila, the valgrind log is generated: ./valgrind.log

This sample C code could be simplified to more precisely target (memory check) libappindicator functions.

Quality of the unpackaged stack traces?

OK, so it works. But does it work well? That is, can one expect the same number of unresolved symbols for an unpackaged executable as for its identical packaged version, even though the code paths to create the sandbox are quite different?

Easy to find out by running apport-valgrind twice, once on a normal packaged executable found on the current PATH (for example apport-valgrind notify-send), and then again on an instance of notify-send simply copied to the current directory (which makes it unpackaged, since dpkg cannot find any package that owns it in this location, and which invokes the new ldd-based code path instead of the dependency based code path). Then counting the number of unresolved symbols with grep, like so:

Packaged:
$ apport-valgrind notify-send 
..
$ grep -c \?\?\? valgrind.log
89

Unpackaged:
$ cp $(which notify-send) .
$ apport-valgrind ./notify-send 
..
$ grep -c \?\?\? valgrind.log
89

Success! The same number of unresolved symbols in both cases means that the unpackaged stack traces are (here, at least) just as good as the packaged stack traces.

Footnotes

[1] You can tell if an executable is packaged with dpkg -S $(which EXE). For example, to find the package that installed ls:

$ dpkg -S $(which ls)
coreutils: /bin/ls


The package is coreutils.

[2] Always upgrade your system (apt-get update and apt-get dist-upgrade) before running apport-valgrind in order to increase the quality of the stack traces by ensuring the installed libraries are the most recent and therefore will match with the most recent debug symbol packages.

No comments:

Post a Comment