Re: Unit testing kexts
Re: Unit testing kexts
- Subject: Re: Unit testing kexts
- From: Alastair Houghton <email@hidden>
- Date: Thu, 25 Aug 2016 17:35:37 +0100
On 25 Aug 2016, at 15:29, Andrew Keller <email@hidden> wrote:
>
> For a while now, we’ve been unit testing individual pieces of our kernel extension by cross-compiling them as a standard user-space application and performing our tests there. Over time, as we have increased our code coverage of the unit tests, we’re noticing a pretty strong upward trend of the number of compiler conditionals required to support cross-compiling between a kext and a user-space app. For example:
>
> #ifdef UNIT_TEST
> #include <stdio.h> // for snprintf
> #else
> #include <libkern/libkern.h> // for snprintf
> #endif
>
> and
>
> #ifdef UNIT_TEST
> str = (char*) malloc(sz);
> #else
> str = (char*) OSMalloc(sz, gOSMallocTag);
> #endif
>
> (There are some more ugly examples, but I’m betting you can imagine how bad it can get at times)
The latter one would be MUCH better written as e.g.
#ifdef UNIT_TEST
#define OSMalloc(sz,t) malloc(sz)
#endif
in a header somewhere.
> Suffice it to say, we’re wondering if there’s a better way. Ideally, we would like to unit test the code that’s actually running in production. These compiler-level conditionals force us to essentially maintain two versions of our code. Worse than that, the unit tested version is not the production version.
It depends what your KEXT does, but I think unit testing the code *outside* kernel mode is a good idea. Unit testing *in* kernel mode sounds like a bad plan - what if your unit test crashes? Do you really want the machine to panic?!
What I’d try to do is to isolate all of the kernel or userland specific bits of code somehow, such that your code doesn’t actually need lots of #ifdef UNIT_TEST blocks everywhere. I think a good way to go about this would be to put the code you think you want to test into a static library, with separate targets for your unit tests and the KEXT itself, both of which link to the static library. Then put any userland/kernel-specific stuff in source files specific to those other targets.
If you do things this way, then for example you might end up with a source file in your userland code that does this:
#include <stdlib.h>
void *our_malloc(size_t sz) {
return malloc(sz);
}
while in your kernel code you’d have another one like this:
#include <libkern/libkern.h>
void *our_malloc(size_t sz) {
return OSMalloc(sz, gOSMallocTag);
}
then from the rest of the program, don’t call malloc() or OSMalloc(), but instead call our_malloc(). (Equally, you could do something similar with #defines in a header file, but you get the idea.)
Kind regards,
Alastair.
--
http://alastairs-place.net
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden