setjmp/longjmp violating RAII

126 views
Skip to first unread message

Knut Aksel Røysland

unread,
Apr 15, 2012, 7:16:20 AM4/15/12
to cpputest
Hi.

I have been using CppUTest only for a couple of months now, but I have
been testing C/C++ (both new and legacy) code for a few years using a
test framework developed in-house at my work. I deeply admire the
effort and aspiration behind CppUTest as well as the book "Test Driven
Development for Embedded C". I am delighted to see efforts like these
help drive a new attitude into the software industry: That using C/C++
or programming for embedded systems is no excuse for not using TDD
techniques.

The last couple of days I have struggled with an issue caused by
CppUTest's use for setjmp/longjmp when a test fails. The test
framework I used to work with, uses a C++ exception in this situation.
The unfortunate thing about setjmp/longjmp is that it breaks the C++
RAII guarantee. Any automatic object defined in the block of the test
case, will not have its destructor called when the block exits due to
a test failure.

In my particular case, I had just implemented a SyslogMock class that,
when instantiated, would team up with a link-time-injected test-double
of the standard library syslog() function. This SyslogMock would then
record the contents of all calls to syslog() from the code under test,
so that the particular test-case that had created the SyslogMock
instance, could investigate the contents of the fake syslog and verify
that the code under test was doing the right thing. The test-cases
that did not care about this, would never create a SyslogMock
instance, leading to the syslog() test-double function just silently
discarding everything it received during running of those tests.

Unfortunately, due to the violation of RAII caused by setjmp/longjmp,
when a test-case using SyslogMock failed, the SyslogMock would never
get properly destroyed and thereby never disassociate itself from the
syslog() test-double function.

When it was time to run a subsequent test-case, some new application
code using syslog() would get run, but now the syslog() test-double
function would be invoking a zombie SyslogMock instance still residing
on the stack which now was being used for something else. Once I
realized this, the segmentation fault I kept getting in a subsequent
test-case, was no longer a mystery.

A workaround in my particular case, is to move the creation and
destruction of the SyslogMock from the test-case block to the base
class or to the test group's setup() and teardown(). This is a trivial
workaround, once you know the reason you have to do it, but I find
that having to be aware of such a precaution, that the C++ code
written inside a test case block, is not fully functional C++, since
the RAII guarantee only holds as long as the test case does not fail,
is too much of a drawback to live with in the long run.

So to ensure CppUTest's continued gain of popularity, I believe this
is a kind of wart that needs to be ironed out. How would it work out
to convert the longjmp/setjmp stuff into something throwing and
catching a C++ exception?

By the way, I am using a Linux platform with GCC.

--
Thanks,
Knut Aksel Røysland

Bas Vodde

unread,
Apr 15, 2012, 7:52:46 AM4/15/12
to cppu...@googlegroups.com

Hi Knut,

Yah, we are aware of this. It has been a tough decision as CppUTest is designed to not depend on exception handling.
However, in the latest version, this should have been fixed already. Which version were you using?

In the latest version, what we did is that whenever you compile CppUTest with StdC++ library, then it will ASSUME exceptions are supported (as C++ library must use exceptions). However, when you decide to compile CppUTest without the StdC++ library THEN it will assume no exceptions are supported and will use setjmp/longjmp instead.

So, could you check whether this is still relevant with the latest version?

Thanks!

Bas

Knut Aksel Røysland

unread,
Apr 16, 2012, 6:49:52 AM4/16/12
to cppu...@googlegroups.com
Hi Bas,

Thanks a lot for your response. It's great to hear there is (or will
be) a way to avoid setjmp/longjmp. I am running Subversion trunk HEAD
(r714) (https://cpputest.svn.sourceforge.net/svnroot/cpputest/trunk)
and my failing CHECK() keeps ending up in
src/Platforms/Gcc/UtestPlatform.cpp line 116, inside
executePlatformSpecificExitCurrentTest(), which contains a longjmp().

So it would seem that the changes you are referring to, have not yet
found their way into Subversion trunk. However, I am quite new to
CppUTest, so if I am missing something essential here, I hope you can
point me in the right direction.

Thanks again.

--
Knut Aksel

2012/4/15 Bas Vodde <ba...@odd-e.com>:

Bas Vodde

unread,
Apr 16, 2012, 7:33:11 AM4/16/12
to cppu...@googlegroups.com

Bwwhahaha :)

You are right, the functionality I was talking about doesn't seem to be implemented. I must have either dreamt it or reverted it, but I was very sure it was there :)

That is very amusing.

Anyways, the catching is already there. So, if you change the longjmp to "throw CppUTestFailedException();" then it actually ought to work.

I'll need to spend some work to actually implement that proper. I think I probably skipped it due to the PlatformSpecific code.

I'll try to do that later this week and will let you know when its done. It should be there as I do agree with your argument :P

Bas

Knut Aksel Røysland

unread,
Apr 16, 2012, 10:59:25 AM4/16/12
to cpputest
Hi again,

Your suggested throw() works like a charm in my case, so I warmly
recommend a proper implementation along that line. It's great that you
intend to get it in so soon. Thanks a lot, and please let me know if
there is anything I might be able to do to assist.

--
Knut Aksel

On Apr 16, 1:33 pm, Bas Vodde <b...@odd-e.com> wrote:
> Bwwhahaha :)
>
> You are right, the functionality I was talking about doesn't seem to be implemented. I must have either dreamt it or reverted it, but I was very sure it was there :)
>
> That is very amusing.
>
> Anyways, the catching is already there. So, if you change the longjmp to "throw CppUTestFailedException();" then it actually ought to work.
>
> I'll need to spend some work to actually implement that proper. I think I probably skipped it due to the PlatformSpecific code.
>
> I'll try to do that later this week and will let you know when its done. It should be there as I do agree with your argument :P
>
> Bas
>
> On 16-Apr-2012, at 6:49 PM, Knut Aksel Røysland wrote:
>
>
>
>
>
>
>
> > Hi Bas,
>
> > Thanks a lot for your response. It's great to hear there is (or will
> > be) a way to avoid setjmp/longjmp. I am running Subversion trunk HEAD
> > (r714) (https://cpputest.svn.sourceforge.net/svnroot/cpputest/trunk)
> > and my failing CHECK() keeps ending up in
> > src/Platforms/Gcc/UtestPlatform.cpp line 116, inside
> > executePlatformSpecificExitCurrentTest(), which contains a longjmp().
>
> > So it would seem that the changes you are referring to, have not yet
> > found their way into Subversion trunk. However, I am quite new to
> > CppUTest, so if I am missing something essential here, I hope you can
> > point me in the right direction.
>
> > Thanks again.
>
> > --
> > Knut Aksel
>
> > 2012/4/15 Bas Vodde <b...@odd-e.com>:

Bas Vodde

unread,
Apr 27, 2012, 5:10:41 AM4/27/12
to cppu...@googlegroups.com

Hi Knut,

I've checked in the code that makes exception handling default, as long as you have a Standard C++ library :P

It took a bit more effort than expected, especially as there seems to be a bug in exception handling in gcc 4.6.2 (at least for cygwin and minGW) which drove me nuts.

Anyways, if you update to trunk you ought to have it. I'll probably do a zip release somewhere next week for those who don't like grabbing trunks...

Bas

Knut Aksel Røysland

unread,
Apr 30, 2012, 4:39:28 AM4/30/12
to cpputest
Hi Bas,

This looks great. Thanks a lot. :-)

--
Knut Aksel
Reply all
Reply to author
Forward
0 new messages