About

All articles, tagged with “stop”

m0cxx0r And Return Types

The core of m0cxx0r is the creation of an object that records method calls and compares them to expectations. This is done by using C++ placement new to create a VTableDonor object in allocated memory the same size as the object being mocked and then returning the memory as a m0cxx0r::Mock class which inherits from T, the class of the object being mocked.

When methods are called on the mock object instead of invoking the methods in T, the virtual methods in VTableDonor are called instead and are able to record the calls made and compare them to expectations. The problem is that the signature of the original method and the VTableDonor method may not match.

In order to be able to find and compare parameters the VTableDonor methods take a single parameter which they can use as a fix point to find other parameters that may be passed to the call via pointer arithmetic. Luckily the rules for parameter layout are fairly simple, so if you know the address of the first parameter, it’s easy to find the others.

Unfortunately the same isn’t true for return values. Depending on the return type, space for the return value might be pushed on to the stack as a hidden parameter, a pointer to a heap location might be pushed or the caller may expect the return to be saved in a register. The rules for which mechanism is used depends on some combination of the compiler, platform and sometimes which C++ features the return type uses. To make matters worse the this pointer is also pushed as a hidden parameter which can become corrupted when there is a return type mismatch. All of this makes it very difficult to call a VTableDonor virtual method with a void return type in place of a virtual method on T with a non-void return type and have everything work correctly. You can see why people generally use the much simpler C ABI to nail binaries together.

After a lot of research and some trial and error I’ve managed to get m0cxx0r working with virtual methods returning primitive types and non-POD types by value in Visual Studio 2005 on Windows and using g++ 3.3 on Linux. The new code can be found here. I’m still having trouble getting it working on g++ 4.0.1 on Darwin where dyld seems to be noticing my monkeying around, causing the process to exit with a _dyld_misaligned_stack_error — hopefully it will be possible to work around.

A potentially better solution is used by mockitopp, a brand new dynamic mock framework for C++ that I found on my travels around the internet today. Where m0cxx0r uses a compiler generated VTableDonor class and then attempts to work around the signature mismatch problems, mockitopp builds the mock vtable at run time which has the advantage that the entries can be made to match the signatures in the class being mocked. It looks to be a promising approach and I’m looking forward to investigating mockitopp further.

m0cxx0r on Windows

In order for m0cxx0r to be useful for writing tests at Linden Lab, it needs to work on all of the platforms that we target with C++ applications, so today I tried building and running m0cxx0r on Windows.

Initially it looked good: m0cxx0r built in the default Visual Studio Debug configuration, but then crashed on construction of Mock objects due to accessing unitialised memory. This was relatively easy to fix, just requiring a call to memset to zero out the memory that would become the m0cxx0r::Mock object.

The next problem was harder to fix. One of the hacks at the core of m0cxx0r is pointing the mock object’s vptr at a donor vtable populated with methods that record calls to the methods. The problem is that the signatures of the original and replacement methods may not match, so multiple parameters may be passed to a method expecting a single parameter. This shouldn’t be a problem as long as the caller manages the stack unwinding: the caller just pushes parameters on to the stack which are ignored and then popped the back off again.

Although m0cxx0r just worked when compiled with GCC on darwin, the run time checks performed in Debug by Visual Studio caught the stack pointer mismatch and stopped execution. In Release the situation was even worse: the tests just crashed out without error. Luckily after some poking around I was able to turn of the stack pointer run time check in Debug and after some trial and error I found that disabling optimisations in the Release configuration with the default __cdecl calling convention allowed the tests to run without error in Release.

With these property changes made, m0cxx0r built and ran it’s tests fine in Visual Studio 2005 on Windows. Get the Visual Studio 2005 project and solution files along with the m0cxx0r code from Google Code.