Here is some guide to profile the GNU Mach kernel uses for a userland process.

First, it needs to be enabled: since this will leak kernel addresses to userland, it is not enabled by default. Pass --enable-kernelsample to the ./configure invocation, and rebuild the kernel.

Then you need to get the start and end of the text portion of the kernel:

nm gnumach | grep " _start$"
nm gnumach | grep " etext$"

And you need to patch glibc to use these start and end instead of the userland starts and ends: in glibc/csu/gmon-start.c, in the __monstartup call, replace TEXT_START and &etext with the start and end addresses obtained above. Rebuild libc (make lib is enough). Install csu/gcrt[01].o in /usr/lib/i386-gnu/.

Now, you can rebuild your application with -pg, run it (for translators, use settrans -a, work with it, and use settrans -fga to terminate it nicely), you will get a gmon.out file. Normally you would just run gprof application to get the profiling, but here we need to get symbols from the kernel:

nm -n gnumach > /tmp/list

and then we can run gprof -S /tmp/list application, and the kernel profiling will show up.

You will probably notice that spl0 will show up a lot, more precisely the sti instruction. This is because all the kernel code running with interrupts disabled can't be profiled, and will be accounted for on that instruction (which triggers the profiling interrupts which should have happened while the interrupts were disabled).