Contents

Humbug talk transcription

I have a pretty big thing for improving code coverage of tests. In my
work, it's absolutely crucial that error cases are checked and handled
appropriately. What I want is a way of modifying runtime behaviour
without modifying code. It was a comment of Rusty's that this
technique is apparently used in the netfilter test suite.

The idea is simple enough: provide a wrapper around a function that
you wish to tickle the error case for. Fork the process. In the
parent, call the real underlying function, in the child return a
failure condition.

LD_PRELOAD stuff is great, you can have all this hacky stuff off to
the side without polluting your actual application source.

ld_sym is a solaris API that most unixes have adopted.

The interpose library wraps resource allocation routines, at this
point in time only memory allocations but there's nothing to stop it
doing file, network resource allocators in the future. The beauty of
interposing on these resource allocation functions is that eventually
all programs rely on them, so just by injecting failures at these
points you can eventually reach 100% code coverage of your tests.

The implementation though gets ugly pretty quickly. Unfortunately the
glibc implementation of dl_sym calls malloc, which requires a call to
dl_sym, which calls malloc which causes..well it's turtles all the way
down. There are other problems with system calls like strdup calling
malloc, if you don't check for these a lot of unnecessary forking
takes place.

One gcc specific part of the code is the calling address code, it uses
a couple of gcc builtins to get a void* pointing to the calling
function, which addr2line can then turn into a source code address. It
would be pretty simple to pass along FILE and LINE to malloc
in my code, since they're already macro wrappers that use our error
handling code, but that might not be the case for other folks.

The ugliest bit so far is gcov handling. gcov is a gcc mode that
instruments programs to record what basic blocks are run, and a post
processing tool that converts that into source coverage
results. Unfortunately the gcov hooks call malloc, and it doesn't
handle failures well. Fortunately gcov is very persistent and won't
take NULL for an answer. I have tried using some at_exit() calls to
turn off interposing, but I could not get that work at the time.

The mtrace API is a glibc specific api to ask it to trace memory
allocation rcalls, and a post-run script can determine from this trace
what memory was leaked. Libinterpose does a very similar thing, except
that the traces it outputs include parent and process ids so that the
post-run interpose.py script can determine any leaks per process.

In order to make libinterpose work with my current testsuite, I'm
going to have to check the failure of the child in the parent, and
make sure it has failed for the right reason. Unfortunately this would
introduce our work specific error api into libinterpose, so there's
going to have to be a hook introduced.

Another tool that I regularly use is electric fence, and I'd
definitely like to use electric fence with libinterpose

  • make it work with gcov
  • make it work automake's check target
  • catch failures, aborts in parents
  • make sure programs fail for the right reason
  • make sure it works with electric fence

Labels parameters
Labels

Labels
  • None