Dear Marc,

On OpenBSD-current (and I suppose any other OBSD version) there's a PITA bug in latest Gambit when compiled with --enable-cplusplus. I tried it with G++ 4.7.2 and 4.6.3. Possibly this bug exists also with any Gambit version.

The bug is extremely easily reproducible:

$ gsc
Gambit v4.6.6

> ,q
terminate called after throwing an instance of '___throw_struct'
terminate called recursively
Abort trap (core dumped)
$

I've been hunting this bug down quite extensively now and it appears to me that Gambit's normal way of terminating (at least on OBSD and Debian) is by throwing an uncaught exception, containing a ___throw_struct whose content is a __SCMOBJ containing a fixnum by the number one (1)???

But if so, why is not an uncaught exception error printed at gsc termination on every platform (which is g++'s default behavior)???


Would greatly appreciate a hand in resolving this bug, it takes time generating the core file, clutters the terminal by requiring you to run reset after every gsc session, and primarily, it gives a terrible 'out of control' sense to the console interaction!..


Find my notes in hunting this one down below. If I can check for or try anything particular as to resolve the originating cause, please just tell me what.

Thanks!
Mikael

 -- 


I've tried to track this down while comparing execution flows between OpenBSD and Debian and I'm a but puzzled:

Fundamentally the error is caused by the ___THROW (err); inside ___propagate_error defined in lib/setup.c, row ~1895 , see GDB session below for this.

The invocation to ___propagate_error is made from one place in all Gambit namely |##exit-with-err-code-no-cleanup| in lib/_kernel.scm:~4356.

On both OBSD and Debian, ___USE_SETJMP is *not defined* so it's the *second* set of exception handling macros including ___THROW in include/gambit.h that are used i.e.:

#define ___BEGIN_TRY { ___err = ___FIX(___NO_ERR); try {
#define ___END_TRY } catch (___throw_struct ts) {  ___err = ts.err; } }
#define ___THROW(e)do { ___throw_struct ts; ts.err = e; throw (ts); } while (0)
#define ___ON_THROW(stat1,stat2) \
___ON_THROW_AUX(,stat1,stat2)

So this is the throw() that causes the uncaught exception, that produces this entire error.

On both OBSD and Debian, on normal Gambit termination, |##exit-with-err-code-no-cleanup| is invoked with the fixnum 1 as argument, leading to a ___THROW with 4 as argument, 4 is Gambit's internal object representation for the fixnum 1.

I tried to trace the source of the invocation to |##exit-with-err-code-no-cleanup| but didn't quite succeed.


I tried in three ways of catching this thrown exception, and they failed (listing of methods and code used below).

Below are the outputs from a related GDB session and failed attempts to hunt down 

The console output doesn't show anything, and except for a section highlighted below, configure doesn't seem to show anything.








GDB output: Tracking down the location of the throw() for the uncaught exception
# egdb gsc
GNU gdb (GDB) 7.5
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-openbsd5.2".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/bin/gsc...done.
(gdb) run
Starting program: /usr/bin/gsc
Gambit v4.6.6

>
Program received signal SIGTSTP, Stopped (user).
0x000016f1c39a951a in select () at <stdin>:2
2       <stdin>: No such file or directory.
(gdb) catch catch
Catchpoint 1 (catch)
(gdb) catch throw
Catchpoint 2 (throw)
(gdb) catch exception
Unable to insert catchpoint.  Is this an Ada main program?
(gdb) catch unknown exception
Undefined catch command: "unknown exception".  Try "help catch".
(gdb) catch exception unhandled
Unable to insert catchpoint.  Is this an Ada main program?
(gdb) cont
Continuing.
,q
Catchpoint 2 (exception thrown), __cxxabiv1::__cxa_throw (obj=0x16f1c758c480, tinfo=0x16efbce1e490 <typeinfo for ___throw_struct>, dest=0x0)
    at /usr/obj/gcc-4.7.2/gcc-4.7.2/libstdc++-v3/libsupc++/eh_throw.cc:70
70      /usr/obj/gcc-4.7.2/gcc-4.7.2/libstdc++-v3/libsupc++/eh_throw.cc: No such file or directory.
(gdb) bt
#0  __cxxabiv1::__cxa_throw (obj=0x16f1c758c480, tinfo=0x16efbce1e490 <typeinfo for ___throw_struct>, dest=0x0)
    at /usr/obj/gcc-4.7.2/gcc-4.7.2/libstdc++-v3/libsupc++/eh_throw.cc:70
#1  0x000016efbc830f78 in ___propagate_error(long) ()
#2  0x000016efbc855a7b in ___H__20___kernel(___processor_state_struct*) ()
#3  0x000016efbc831026 in ___call(int, long, long) ()
#4  0x000016efbc832cee in ___setup(___setup_params_struct*) ()
#5  0x000016efbc847a0e in ___main(___mod_or_lnk_union* (*)(___global_state_struct*)) ()
#6  0x000016efbc83c3b3 in ___main_char(int, char**, ___mod_or_lnk_union* (*)(___global_state_struct*), char*) ()
#7  0x000016efbc830216 in main (argc=1, argv=0x7f7ffffbdfa0) at _gsc_.c:13627
(gdb)



Failed attempts to catch the exception
(A) Hooking the uncaught exception handler via std::set_terminate(); , see example code for this below. Funny enough this did not work, it gave "no active exception".

(B) Tried running a REPL inside a try { .. } catch statement and do ,q from it. Did not work.

(C) Tried to put debug output include/gambit.h and lib/os_base.c around 1) Gambit's main() procedure and 2) around the try { .. } catch-code that actually is there. I would guess the code modified in 2) not runs though as it only runs when longjumps are not on. Printed "Gambit Started.\Past ___setup_base_module()\nInto ___main.\n". and that's all, all the way until C++' uncaught exception handler prints its error message. So, did not work.

 * (A) code
http://efesx.com/2010/03/21/catching-uncaught-exceptions-within-terminate/

(c-declare #<<c-declare

#include <iostream>
#include <stdexcept>
#include <exception>

void our_terminate (void) { 
     printf("Terminating..\n");

    static bool tried_throw = false;
 
    try {
        if (!tried_throw++) throw;
        std::cout << "no active exception" << std::endl;
    }
    catch (___SCMOBJ err) {
        printf("Got SCMOBJ %lld.\n",err);
    }
    catch (const std::runtime_error &err) {
        std::cout << "caught a runtime_error. what(): " <<
            err.what() << std::endl;
    }
    catch (...) {
        std::cout << "unknown exception occurred." <<
            std::endl;
    }

}

c-declare
           )

(c-initialize #<<c-initialize

std::set_terminate(our_terminate);

c-initialize
              )

 * (B) code
(c-define (runscm thunk) (scheme-object) void "runscm" ""
          (print "(runscm): Running thunk.\n")
          (thunk)
          (print "(runscm): Back from thunk.\n"))

(define (c++-try-catch thunk)
  ((c-lambda (scheme-object) void #<<c-lambda-end

___SCMOBJ thunk = ___arg1;
try {
     runscm(thunk);
} catch (___throw_struct e) {
     fprintf(stderr,"Caught a ___throw_struct!\n");
} catch (...) {
     fprintf(stderr,"Caught some other excepion.\n");
}

c-lambda-end
             ) thunk))

(c++-try-catch ##repl)

 * (C) code

include/gambit.h

#ifdef ___USE_SETJMP
...
#define ___BEGIN_TRY { ___err = ___FIX(___NO_ERR); try {
#define ___END_TRY } catch (___throw_struct ts) { printf("___END_TRY caught a ___throw_struct.\n"); ___err = ts.err; } }
#define ___THROW(e)do { ___throw_struct ts; ts.err = e; printf(stderr,"___THROW throws a ___throw_struct.\n"); throw (ts); } while (0)
#define ___ON_THROW(stat1,stat2) \
___ON_THROW_AUX(,stat1,stat2)

#endif

#define ___ON_THROW_AUX(decl,stat1,stat2) \
do { \
fprintf(stderr,"___ON_THROW_AUX\n"); \
     ___SCMOBJ ___err; decl \
     ___BEGIN_TRY stat1; ___END_TRY \
     if (___err != ___FIX(___NO_ERR)) stat2; \
   } while (0)



lib/os_base.c:
___EXP_FUNC(int,___main_char)
  ___P((int argc,
        char *argv[],
        ___mod_or_lnk (*linker)(___global_state_struct*),
        char *script_line),
       (argc,
        argv,
        linker,
        script_line)
int argc;
char *argv[];
___mod_or_lnk (*linker)();
char *script_line;)
{
  int result;
fprintf(stderr,"Gambit started.\n");

try {

  if (___setup_base_module () != ___FIX(___NO_ERR))
    result = ___EXIT_CODE_OSERR;
  else
    {
fprintf(stderr,"Past ___setup_base_module()\n");
      if (___NONNULLCHARSTRINGLIST_to_NONNULLUCS_2STRINGLIST
            (argv,
             &___program_startup_info.argv)
          != ___FIX(___NO_ERR))
        result = ___EXIT_CODE_SOFTWARE;
      else
        {
          if (___CHARSTRING_to_UCS_2STRING
                (script_line,
                 &___program_startup_info.script_line)
              != ___FIX(___NO_ERR))
            result = ___EXIT_CODE_SOFTWARE;
          else
            {
             fprintf(stderr,"Into ___main\n");
              result = ___main (linker);
                  fprintf(stderr,"Out of ___main\n");

              ___free_UCS_2STRING (___program_startup_info.script_line);
            }

          ___free_NONNULLUCS_2STRINGLIST (___program_startup_info.argv);
        }

      fprintf(stderr,"Into ___cleanup_base_module\n");
      ___cleanup_base_module ();
      fprintf(stderr,"Out of ___cleanup_base_module.\n");
    }
} catch (___throw_struct e) {
     fprintf(stderr,"Caught a ___throw_struct!\n");
} catch (...) {
     fprintf(stderr,"Caught some other excepion.\n");
}

fprintf(stderr,"Gambit ended.\n");


  return result;
}


./configure log
$ CXFLAGS="-g -ggdb -gdwarf-3 -gstrict-dwarf -O0" ./configure --enable-cplusplus --enable-single-host               (note misspelling on env var, anyhow)
checking build system type... amd64-unknown-openbsd5.2
checking host system type... amd64-unknown-openbsd5.2
checking target system type... amd64-unknown-openbsd5.2
checking for gcc... gcc
checking for C compiler default output file name... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables...
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking how to run the C preprocessor... gcc -E
checking for g++... g++
checking whether we are using the GNU C++ compiler... yes
checking whether g++ accepts -g... yes
checking how to run the C++ preprocessor... g++ -E
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for void*... yes
checking size of void*... 8
checking for long... yes
checking size of long... 8
checking for bool... yes
checking whether g++ defines __GNUC__... yes
checking whether g++ defines __clang__...
checking whether g++ defines __llvm__...
checking whether g++ defines _MSC_VER...
checking errno.h usability... yes
checking errno.h presence... yes
checking for errno.h... yes
checking float.h usability... yes
checking float.h presence... yes
checking for float.h... yes
checking signal.h usability... yes
checking signal.h presence... yes
checking for signal.h... yes
checking stdio.h usability... yes
checking stdio.h presence... yes
checking for stdio.h... yes
checking for stdlib.h... (cached) yes
checking for string.h... (cached) yes
checking time.h usability... yes
checking time.h presence... yes
checking for time.h... yes
checking for unistd.h... (cached) yes
checking pwd.h usability... yes
checking pwd.h presence... yes
checking for fcntl.h... yes
checking sys/ioctl.h usability... yes
checking sys/ioctl.h presence... yes
checking for sys/ioctl.h... yes
checking sys/socket.h usability... yes
checking sys/socket.h presence... yes
checking for sys/socket.h... yes
checking for strings.h... (cached) yes
checking for memory.h... (cached) yes
checking sys/sysctl.h usability... no
checking sys/sysctl.h presence... yes
configure: WARNING: sys/sysctl.h: present but cannot be compiled           - This is documented at http://fxr.watson.org/fxr/source/sys/sysctl.h?v=OPENBSD , doesn't look like anything that would crash an ordinary Gambit session??
configure: WARNING: sys/sysctl.h:     check for missing prerequisite headers?
configure: WARNING: sys/sysctl.h: see the Autoconf documentation
configure: WARNING: sys/sysctl.h:     section "Present But Cannot Be Compiled"
configure: WARNING: sys/sysctl.h: proceeding with the preprocessor's result
configure: WARNING: sys/sysctl.h: in the future, the compiler will take precedence
configure: WARNING:     ## -------------------------------------- ##
configure: WARNING:     ## Report this to gambit@iro.umontreal.ca ##
configure: WARNING:     ## -------------------------------------- ##
checking for sys/sysctl.h... yes
checking crt_externs.h usability... no
checking crt_externs.h presence... no
checking for crt_externs.h... no
checking ws2tcpip.h usability... no
checking ws2tcpip.h presence... no
checking for ws2tcpip.h... no
checking TargetConditionals.h usability... no
checking TargetConditionals.h presence... no
checking for TargetConditionals.h... no
checking for socklen_t... yes
checking for library containing asin... none required
checking for library containing dlopen... none required
checking for library containing shl_load... no
checking for library containing socket... none required
checking for library containing gethostbyname... none required
checking for library containing hstrerror... none required
checking for library containing openpty... -lutil
checking for library containing WSAStartup... no
checking for pipe... yes
checking for socketpair... yes
checking for chdir... yes
checking for execvp... yes
checking for getgrnam... yes
checking for getpid... yes
checking for getppid... yes
checking for getpwnam... yes
checking for ioctl... yes
checking for link... yes
checking for mkdir... yes
checking for mkfifo... yes
checking for opendir... yes
checking for rename... yes
checking for rmdir... yes
checking for socket... yes
checking for stat... yes
checking for stat64... no
checking for strerror... yes
checking for symlink... yes
checking for sysconf... yes
checking for sysctl... yes
checking for unlink... yes
checking for waitpid... yes
checking for mmap... yes
checking for tcgetattr... yes
checking for sigaction... yes
checking for sigemptyset/sigaddset... yes
checking for sigprocmask... yes
checking for signal... yes
checking for environ... yes
checking for _NSGetEnviron... no
checking for clock_gettime... yes
checking for getclock... no
checking for gettimeofday... yes
checking for ftime... no
checking for time... yes
checking for nanosleep... yes
checking for sleep... yes
checking for getrusage... yes
checking for times... yes
checking for clock... yes
checking for DosQuerySysInfo... no
checking for setitimer... yes
checking for dos_setvect... no
checking for DosStartTimer... no
checking for VInstall... no
checking for shl_load... no
checking for DosLoadModule... no
checking for dxe_load... no
checking for GetDiskFragment... no
checking for dlopen... yes
checking for NSLinkModule... no
checking for gethostname... yes
checking for inet_pton... yes
checking for getaddrinfo... yes
checking for gethostbyname... yes
checking for gethostbyaddr... yes
checking for getservbyname... yes
checking for getservbyport... yes
checking for getprotobyname... yes
checking for getprotobynumber... yes
checking for getnetbyname... yes
checking for select... yes
checking for openpty... yes
checking for getpt... no
checking for ptsname... no
checking for ctermid... yes
checking for isastream... no
checking for hstrerror... yes
checking for get_fpc_csr... no
checking for struct stat64... no
checking how g++'s preprocessor evaluates (__GNUC__*1000+__GNUC_MINOR__)>=4002&&(__GNUC__*1000+__GNUC_MINOR__)<=4003...
checking how g++'s preprocessor evaluates __llvm__ && !__clang__ && (__GNUC__*1000+__GNUC_MINOR__)<4005...
checking whether g++ accepts -no-cpp-precomp...
checking whether g++ accepts -fschedule-insns2...  -fschedule-insns2
checking whether g++ accepts -fno-trapping-math...  -fno-trapping-math
checking whether g++ accepts -fno-move-loop-invariants...  -fno-move-loop-invariants
checking whether g++ accepts -fno-keep-inline-dllexport...
checking whether g++ accepts -mieee...
checking whether g++ accepts -mieee-with-inexact...
checking whether g++ accepts -mieee-fp...  -mieee-fp
checking whether g++ accepts -Wall...  -Wall
checking whether g++ accepts -W...  -W
checking whether g++ accepts -Wno-unused...  -Wno-unused
checking whether g++ accepts -Wno-write-strings...  -Wno-write-strings
checking whether g++ accepts -ftest-coverage...  -ftest-coverage
checking whether g++ accepts -fprofile-arcs...  -fprofile-arcs
checking whether g++ accepts -fbranch-probabilities...  -fbranch-probabilities
checking whether g++ accepts -O1...  -O1
checking whether g++ accepts -O2...  -O2
checking whether g++ accepts -fno-math-errno...  -fno-math-errno
checking whether g++ accepts -fno-strict-aliasing...  -fno-strict-aliasing
checking whether g++ accepts -fwrapv...  -fwrapv
checking whether g++ accepts -fomit-frame-pointer...  -fomit-frame-pointer
checking whether g++ accepts -fPIC...  -fPIC
checking whether g++ accepts -fpic...  -fpic
checking whether g++ accepts -fno-common...  -fno-common
checking whether g++ accepts -rdynamic...
checking whether g++ accepts -shared...  -shared
checking for X... libraries /usr/X11R6/lib, headers /usr/X11R6/include
checking whether -R must be followed by a space... no
checking for gethostbyname... (cached) yes
checking for connect... yes
checking for remove... yes
checking for shmat... yes
checking for IceConnectionNumber in -lICE... yes
checking whether ln -s works... yes
checking for ranlib... ranlib
checking whether make sets $(MAKE)... yes
configure: creating ./config.status
config.status: creating makefile
config.status: creating include/makefile
config.status: creating include/gambit.h
config.status: creating lib/makefile
config.status: creating lib/guide/guidepro
config.status: creating lib/guide/makefile
config.status: creating lib/guide/images/makefile
config.status: creating gsi/makefile
config.status: creating gsc/makefile
config.status: creating bin/makefile
config.status: creating bin/gambc-cc.unix
config.status: creating bin/gambc-cc.bat.windows
config.status: creating bin/gambc-doc.unix
config.status: creating bin/gambc-doc.bat.windows
config.status: creating misc/makefile
config.status: creating doc/makefile
config.status: creating tests/makefile
config.status: creating examples/makefile
config.status: creating examples/distr-comp/makefile
config.status: creating examples/pi/makefile
config.status: creating examples/ring/makefile
config.status: creating examples/web-repl/makefile
config.status: creating examples/web-server/makefile
config.status: creating examples/tcltk/makefile
config.status: creating examples/Xlib-simple/makefile
config.status: creating examples/pthread/makefile
config.status: creating examples/misc/makefile
config.status: creating contrib/makefile
config.status: creating contrib/GambitREPL/makefile
config.status: creating prebuilt/makefile
config.status: creating prebuilt/macosx/makefile
config.status: creating prebuilt/macosx/build-phase2
config.status: creating prebuilt/windows/makefile
config.status: creating prebuilt/windows/build-phase2
config.status: creating include/config.h