Ethereal-dev: [Ethereal-dev] Exception bugs
Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.
From: Richard van der Hoff <richardv@xxxxxxxxxxxxx>
Date: Thu, 28 Jul 2005 16:22:45 +0100
Hi, I've been trying to make ethereal's exception handling work, and, in particular, make use of a FINALLY block. As a Java programmer, I expected a FINALLY block would /always/ be executed. This doesn't seem to be the case - indeed, there doesn't seem to be any difference between a FINALLY block and the end of a TRY block in the current implementation. I then discovered that there aren't any dissectors using FINALLY at the moment. Perhaps this is because it doesn't work :). Going on to think about how this could be fixed, I also noticed a few other things which jar (if you'll excuse the pun) with my Java-bred expectations. In summary: - FINALLY blocks not executed on exceptions (caught, uncaught, or (re)thrown from a CATCH block) - Exceptions not caught by a CATCH block aren't propagated to the parent TRY, CATCH - instead they are just dropped. Hence unexpected exceptions are sometimes ignored. - throwing a new exception from a CATCH or FINALLY block re-enters the CATCH block; this could produce an infinite loop. I've attached a patch, exntest.patch, which adds a small stand-alone test program which aims to test some of the exception functionality, and exposes some of these problems. The uselessness of FINALLY, in particular, seemed worth fixing, and I think that dropping unexpected exceptions is also quite dangerous. I've therefore created a second patch, exceptions.patch, which reworks the macros in exceptions.h to address the above problems. I'm sorry it's a bit of a rewrite - however, fixing the existing code looked pretty tough. The only problem with this is that it exposes a bug in packet-frame.c ... so the third and final patch here, frame.patch, fixes that. Comments are welcome on the patches - or even my interpretation of how exception-handling ought to work. I'm away from my desk for the next few days, however, so apologies if I don't reply immediately. Thanks, Richard -- Richard van der Hoff <richardv@xxxxxxxxxxxxx> Systems Analyst Tel: +44 (0) 845 666 7778 http://www.mxtelecom.com
Index: epan/exceptions.h
===================================================================
RCS file: /cvs/ethereal/epan/exceptions.h,v
retrieving revision 1.1.1.2
retrieving revision 1.5
diff -u -r1.1.1.2 -r1.5
--- epan/exceptions.h 4 Jul 2005 18:17:28 -0000 1.1.1.2
+++ epan/exceptions.h 28 Jul 2005 14:49:23 -0000 1.5
@@ -66,23 +66,31 @@
* This is really something like:
*
* {
- * x = setjmp()
+ * caught = FALSE:
+ * x = setjmp();
* if (x == 0) {
* <TRY code>
* }
- * else if (x == 1) {
+ * if (!caught && x == 1) {
+ * caught = TRUE;
* <CATCH(1) code>
* }
- * else if (x == 2) {
+ * if (!caught && x == 2) {
+ * caught = TRUE;
* <CATCH(2) code>
* }
- * else if (x == 3 || x == 4) {
+ * if (!caught && (x == 3 || x == 4)) {
+ * caught = TRUE;
* <CATCH2(3,4) code>
* }
- * else {
- * <CATCH_ALL code> {
+ * if (!caught && x != 0) {
+ * caught = TRUE;
+ * <CATCH_ALL code>
* }
* <FINALLY code>
+ * if(!caught) {
+ * RETHROW(x)
+ * }
* }<ENDTRY tag>
*
* All CATCH's must precede a CATCH_ALL.
@@ -110,40 +118,65 @@
* CLEANUP_CB_CALL_AND_POP
*/
+/* we do up to three passes through the bit of code after except_try_push(),
+ * and except_state is used to keep track of where we are.
+ */
+#define EXCEPT_CAUGHT 1 /* exception has been caught, no need to rethrow at
+ * END_TRY */
+#define EXCEPT_RETHROWN 2 /* the exception was rethrown from a CATCH
+ * block. Don't reenter the CATCH blocks, but do
+ * execute FINALLY and rethrow at END_TRY */
+
+#define EXCEPT_FINALLY 4 /* we've entered the FINALLY block - don't allow
+ * RETHROW, and don't reenter FINALLY if a
+ * different exception is thrown */
#define TRY \
{\
except_t *exc; \
+ volatile int except_state = 0; \
static const except_id_t catch_spec[] = { \
{ XCEPT_GROUP_ETHEREAL, XCEPT_CODE_ANY } }; \
except_try_push(catch_spec, 1, &exc); \
- if (exc == 0) { \
+ \
+ if(except_state & EXCEPT_CAUGHT) \
+ except_state |= EXCEPT_RETHROWN; \
+ except_state &= ~EXCEPT_CAUGHT; \
+ \
+ if (except_state == 0 && exc == 0) \
/* user's code goes here */
#define ENDTRY \
- } \
+ /* rethrow the exception if necessary */ \
+ if(!(except_state&EXCEPT_CAUGHT) && exc != 0) \
+ except_rethrow(exc); \
except_try_pop();\
}
+/* the (except_state |= EXCEPT_CAUGHT) in the below is a way of setting
+ * except_state before the user's code, without disrupting the user's code if
+ * it's a one-liner.
+ */
#define CATCH(x) \
- } \
- else if (exc->except_id.except_code == (x)) { \
+ if (except_state == 0 && exc != 0 && exc->except_id.except_code == (x) && \
+ (except_state |= EXCEPT_CAUGHT)) \
/* user's code goes here */
#define CATCH2(x,y) \
- } \
- else if (exc->except_id.except_code == (x) || exc->except_id.except_code == (y)) { \
+ if (except_state == 0 && exc != 0 && \
+ (exc->except_id.except_code == (x) || exc->except_id.except_code == (y)) && \
+ (except_state|=EXCEPT_CAUGHT)) \
/* user's code goes here */
#define CATCH_ALL \
- } \
- else { \
+ if (except_state == 0 && exc != 0 && \
+ (except_state|=EXCEPT_CAUGHT)) \
/* user's code goes here */
+
#define FINALLY \
- } \
- { \
+ if( !(except_state & EXCEPT_FINALLY) && (except_state|=EXCEPT_FINALLY)) \
/* user's code goes here */
#define THROW(x) \
@@ -154,7 +187,24 @@
#define GET_MESSAGE except_message(exc)
-#define RETHROW except_rethrow(exc)
+#define RETHROW \
+ { \
+ /* check we're in a catch block */ \
+ g_assert(except_state == EXCEPT_CAUGHT); \
+ /* we can't use except_rethrow here, as that pops a catch block \
+ * off the stack, and we don't want to do that, because we want to \
+ * excecute the FINALLY {} block first. \
+ * except_throw doesn't provide an interface to rethrow an existing \
+ * exception; however, longjmping back to except_try_push() has the \
+ * desired effect. \
+ * \
+ * Note also that THROW and RETHROW should provide much the same \
+ * functionality in terms of which blocks to enter, so any messing \
+ * about with except_state in here would indicate that THROW is \
+ * doing the wrong thing. \
+ */ \
+ longjmp(except_ch.except_jmp,1); \
+ }
#define EXCEPT_CODE except_code(exc)
Index: epan/Makefile.am
===================================================================
RCS file: /cvs/ethereal/epan/Makefile.am,v
retrieving revision 1.1.1.7
retrieving revision 1.8
diff -u -r1.1.1.7 -r1.8
--- epan/Makefile.am 4 Jul 2005 18:17:28 -0000 1.1.1.7
+++ epan/Makefile.am 27 Jul 2005 22:42:28 -0000 1.8
@@ -54,6 +54,7 @@
Makefile.common \
Makefile.nmake \
tvbtest.c \
+ exntest.c \
doxygen.cfg.in
CLEANFILES = \
@@ -68,7 +69,12 @@
libethereal_la_DEPENDENCIES = @G_ASCII_STRTOULL_LO@ @INET_ATON_LO@ @INET_PTON_LO@ @INET_NTOP_LO@ dfilter/libdfilter.la ftypes/libftypes.la dissectors/libdissectors.la
tvbtest: tvbtest.o tvbuff.o except.o strutil.o
- $(LINK) -o tvbtest tvbtest.o tvbuff.o except.o strutil.o `glib-config --libs`
+ $(LINK) $^ $(GLIB_LIBS) -lz
+
+exntest: exntest.o except.o
+ $(LINK) $^ $(GLIB_LIBS)
+
+tvbtest.o exntest.o: exceptions.h
if HAVE_PLUGINS
Index: epan/exntest.c
===================================================================
RCS file: epan/exntest.c
diff -N epan/exntest.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ epan/exntest.c 28 Jul 2005 14:37:14 -0000
@@ -0,0 +1,202 @@
+/* Standalone program to test functionality of exceptions.
+ *
+ * $Id: exntest.c,v 1.1 2005/07/27 22:42:28 richardv Exp $
+ *
+ * Copyright (c) 2004 MX Telecom Ltd. <richardv@xxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <glib.h>
+#include "exceptions.h"
+
+gboolean failed = FALSE;
+
+void
+run_tests(void)
+{
+ volatile unsigned int ex_thrown, finally_called;
+
+ /* check that the right catch, and the finally, are called, on exception */
+ ex_thrown = finally_called = 0;
+ TRY {
+ THROW(BoundsError);
+ }
+ CATCH(BoundsError) {
+ ex_thrown++;
+ }
+ CATCH(ReportedBoundsError) {
+ printf("01: Caught wrong exception: ReportedBoundsError\n");
+ failed = TRUE;
+ }
+ CATCH_ALL {
+ printf("01: Caught wrong exception: %lu\n", exc->except_id.except_code);
+ failed = TRUE;
+ }
+ FINALLY {
+ finally_called ++;
+ }
+ ENDTRY;
+
+ if (ex_thrown != 1) {
+ printf("01: %u BoundsErrors (not 1) on caught exception\n", ex_thrown);
+ failed = TRUE;
+ }
+
+ if (finally_called != 1) {
+ printf("01: FINALLY called %u times (not 1) on caught exception\n", finally_called);
+ failed = TRUE;
+ }
+
+
+ /* check that no catch at all is called when there is no exn */
+ ex_thrown = finally_called = 0;
+ TRY {
+ }
+ CATCH(BoundsError) {
+ printf("02: Caught wrong exception: BoundsError\n");
+ failed = TRUE;
+ }
+ CATCH(ReportedBoundsError) {
+ printf("02: Caught wrong exception: ReportedBoundsError\n");
+ failed = TRUE;
+ }
+ CATCH_ALL {
+ printf("02: Caught wrong exception: %lu\n", exc->except_id.except_code);
+ failed = TRUE;
+ }
+ FINALLY {
+ finally_called ++;
+ }
+ ENDTRY;
+
+ if (finally_called != 1) {
+ printf("02: FINALLY called %u times (not 1) on no exception\n", finally_called);
+ failed = TRUE;
+ }
+
+
+ /* check that finally is called on an uncaught exception */
+ ex_thrown = finally_called = 0;
+ TRY {
+ TRY {
+ THROW(BoundsError);
+ }
+ FINALLY {
+ finally_called ++;
+ }
+ ENDTRY;
+ }
+ CATCH(BoundsError) {
+ ex_thrown++;
+ }
+ ENDTRY;
+
+ if (finally_called != 1) {
+ printf("03: FINALLY called %u times (not 1) on uncaught exception\n", finally_called);
+ failed = TRUE;
+ }
+
+ if (ex_thrown != 1) {
+ printf("03: %u BoundsErrors (not 1) on uncaught exception\n", ex_thrown);
+ failed = TRUE;
+ }
+
+
+ /* check that finally is called on an rethrown exception */
+ ex_thrown = finally_called = 0;
+ TRY {
+ TRY {
+ THROW(BoundsError);
+ }
+ CATCH_ALL {
+ ex_thrown += 10;
+ RETHROW;
+ }
+ FINALLY {
+ finally_called += 10;
+ }
+ ENDTRY;
+ }
+ CATCH(BoundsError) {
+ ex_thrown ++;
+ }
+ FINALLY {
+ finally_called ++;
+ }
+ ENDTRY;
+
+ if (finally_called != 11) {
+ printf("04: finally_called = %u (not 11) on rethrown exception\n", finally_called);
+ failed = TRUE;
+ }
+
+ if (ex_thrown != 11) {
+ printf("04: %u BoundsErrors (not 11) on rethrown exception\n", ex_thrown);
+ failed = TRUE;
+ }
+
+
+ /* check that finally is called on an exception thrown from a CATCH block */
+ ex_thrown = finally_called = 0;
+ TRY {
+ TRY {
+ THROW(BoundsError);
+ }
+ CATCH_ALL {
+ if(ex_thrown > 0) {
+ printf("05: Looping exception\n");
+ failed = TRUE;
+ } else {
+ ex_thrown += 10;
+ THROW(BoundsError);
+ }
+ }
+ FINALLY {
+ finally_called += 10;
+ }
+ ENDTRY;
+ }
+ CATCH(BoundsError) {
+ ex_thrown ++;
+ }
+ FINALLY {
+ finally_called ++;
+ }
+ ENDTRY;
+
+ if (finally_called != 11) {
+ printf("05: finally_called = %u (not 11) on exception thrown from CATCH\n", finally_called);
+ failed = TRUE;
+ }
+
+ if (ex_thrown != 11) {
+ printf("05: %u BoundsErrors (not 11) on exception thrown from CATCH\n", ex_thrown);
+ failed = TRUE;
+ }
+
+ if(failed == FALSE )
+ printf("success\n");
+}
+
+int main(void)
+{
+ except_init();
+ run_tests();
+ except_deinit();
+ exit(failed?1:0);
+}
Index: epan/dissectors/packet-frame.c
===================================================================
RCS file: /cvs/ethereal/epan/dissectors/packet-frame.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -u -r1.1.1.2 -r1.2
--- epan/dissectors/packet-frame.c 4 Jul 2005 18:17:31 -0000 1.1.1.2
+++ epan/dissectors/packet-frame.c 27 Jul 2005 22:30:16 -0000 1.2
@@ -198,6 +198,15 @@
0, 0, cap_len, "Capture Length: %d byte%s", cap_len,
plurality(cap_len, "", "s"));
+ /* we are going to be using proto_item_append_string() on
+ * hf_frame_protocols, and we must therefore disable the
+ * TRY_TO_FAKE_THIS_ITEM() optimisation for the tree by
+ * setting it as visible.
+ *
+ * See proto.h for details.
+ */
+ PTREE_DATA(fh_tree)->visible=1;
+
ti = proto_tree_add_string(fh_tree, hf_frame_protocols, tvb,
0, 0, "");
PROTO_ITEM_SET_GENERATED(ti);
- Prev by Date: Re: [Ethereal-dev] Re: [Ethereal-cvs] rev 15092: /trunk/: Makefile.nmake randpkt.c
- Next by Date: [Ethereal-dev] iax2 dissector patch
- Previous by thread: Re: [Ethereal-dev] ememification of tvb_get_tring() and friends
- Next by thread: [Ethereal-dev] iax2 dissector patch
- Index(es):





