summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlbert Cheng <acheng@hdfgroup.org>2010-04-28 17:11:11 (GMT)
committerAlbert Cheng <acheng@hdfgroup.org>2010-04-28 17:11:11 (GMT)
commit1c2f19b16e8cf20651b8893cfff884c9635586bd (patch)
tree5d7aef0e6d5aa341b5918082379d1a1b132f5e44 /src
parent5a26307f8666e50b37327e042f3005463636f73c (diff)
downloadhdf5-1c2f19b16e8cf20651b8893cfff884c9635586bd.zip
hdf5-1c2f19b16e8cf20651b8893cfff884c9635586bd.tar.gz
hdf5-1c2f19b16e8cf20651b8893cfff884c9635586bd.tar.bz2
[svn-r18658] Bug: 1764
Description: longjmp do not necessary restore signal that is blocked during the signal handling. This caused the Alignment test to fail quietly, resulting in wrong alignment information which will cause failures later. Solution: One can use sigsetjmp/siglongjmp to restore signal handling but not all systems such as Cray XT or VMS supports sigsetjmp. Backup solution is to use sigprocmask to reset the signal. Again, some systems may not support it either. Added code to try the first and then the second solution. Also added tests to verify if the signal_handler routines are working properly. Finally, added code to print results of the verification (in form of comments) to H5Tinit.c for inspection in case of failure. (Note that many platforms do not have alignment limits at all and ALIGNMNET code never raise the SIGBUS or SIGSEGV errors. In those cases, it does not matter whether the signal handlers work or not. Again, this can be deduced from the results comments near the end of the H5Tinit.c. If the sum of signal handlers called equals the total of verify, it means ALIGNMENT does not raise any signals.) Tested: htcommittested (linew is not accessible), jam(serial), Redstorm.
Diffstat (limited to 'src')
-rw-r--r--src/H5config.h.in9
-rw-r--r--src/H5detect.c259
2 files changed, 203 insertions, 65 deletions
diff --git a/src/H5config.h.in b/src/H5config.h.in
index ef072de..8210f38 100644
--- a/src/H5config.h.in
+++ b/src/H5config.h.in
@@ -253,6 +253,9 @@
/* Define to 1 if you have the `rand_r' function. */
#undef HAVE_RAND_R
+/* Define to 1 if you have the `setjmp' function. */
+#undef HAVE_SETJMP
+
/* Define to 1 if you have the <setjmp.h> header file. */
#undef HAVE_SETJMP_H
@@ -265,6 +268,12 @@
/* Define to 1 if you have the `signal' function. */
#undef HAVE_SIGNAL
+/* Define to 1 if you have the `sigprocmask' function. */
+#undef HAVE_SIGPROCMASK
+
+/* Define to 1 if you have the `sigsetjmp' function. */
+#undef HAVE_SIGSETJMP
+
/* Define to 1 if you have the `snprintf' function. */
#undef HAVE_SNPRINTF
diff --git a/src/H5detect.c b/src/H5detect.c
index ac1185f..1cfce96 100644
--- a/src/H5detect.c
+++ b/src/H5detect.c
@@ -59,6 +59,32 @@ static const char *FileHeader = "\n\
#include "H5Rpublic.h"
#define MAXDETECT 64
+
+/* The ALIGNMENT test code may generate the SIGBUS or SIGSEGV signals. We use
+ * setjmp/longjmp in the signal handlers for recovery. But setjmp/longjmp do
+ * not necessary restore the signal blocking status while sigsetjmp/siglongjmp
+ * do. If sigsetjmp/siglongjmp are not supported, need to use sigprocmask to
+ * unblock the signal before doing longjmp.
+ */
+/* Define H5SETJMP/H5LONGJMP depending on if sigsetjmp/siglongjmp are */
+/* supported. */
+#if defined(H5_HAVE_SIGSETJMP) && defined(H5_HAVE_SIGLONGJMP)
+/* Always save blocked signals to be restore by siglongjmp. */
+#define H5JMP_BUF sigjmp_buf
+#define H5SETJMP(buf) sigsetjmp(buf, 1)
+#define H5LONGJMP(buf, val) siglongjmp(buf, val)
+#define H5HAVE_SIGJMP # sigsetjmp/siglongjmp are supported.
+#elif defined(H5_HAVE_LONGJMP)
+#define H5JMP_BUF jmp_buf
+#define H5SETJMP(buf) setjmp(buf)
+#define H5LONGJMP(buf, val) longjmp(buf, val)
+#endif
+
+/* ALIGNMENT and signal-handling status codes */
+#define STA_NoALIGNMENT 0x0001 /* No ALIGNMENT Test */
+#define STA_NoHandlerVerify 0x0002 /* No signal handler Tests */
+
+
/*
* This structure holds information about a type that
* was detected.
@@ -109,7 +135,16 @@ static void detect_C99_integers32(void);
static void detect_C99_integers64(void);
static void detect_alignments(void);
static size_t align_g[] = {1, 2, 4, 8, 16};
-static jmp_buf jbuf_g;
+static int align_status_g = 0; /* ALIGNMENT Signal Status */
+static int sigbus_handler_called_g = 0; /* how many times called */
+static int sigsegv_handler_called_g = 0; /* how many times called */
+static int signal_handler_tested_g = 0; /* how many times tested */
+#if defined(H5SETJMP) && defined(H5_HAVE_SIGNAL)
+static int verify_signal_handlers(int signum, void (*handler)(int));
+#endif
+#ifdef H5JMP_BUF
+static H5JMP_BUF jbuf_g;
+#endif
/*-------------------------------------------------------------------------
@@ -350,20 +385,20 @@ precision (detected_t *d)
COMP_ALIGN = (size_t)((char*)(&(s.x)) - (char*)(&s)); \
}
-#if defined(H5_HAVE_LONGJMP) && defined(H5_HAVE_SIGNAL)
+#if defined(H5SETJMP) && defined(H5_HAVE_SIGNAL)
#define ALIGNMENT(TYPE,INFO) { \
char *volatile _buf = NULL; \
volatile TYPE _val = 1; \
volatile TYPE _val2; \
volatile size_t _ano = 0; \
- void (*_handler)(int) = signal(SIGBUS, sigbus_handler); \
- void (*_handler2)(int) = signal(SIGSEGV, sigsegv_handler); \
+ void (*_handler)(int) = HDsignal(SIGBUS, sigbus_handler); \
+ void (*_handler2)(int) = HDsignal(SIGSEGV, sigsegv_handler);\
\
_buf = (char*)malloc(sizeof(TYPE) + align_g[NELMTS(align_g) - 1]); \
- if(setjmp(jbuf_g)) _ano++; \
+ if(H5SETJMP(jbuf_g)) _ano++; \
if(_ano < NELMTS(align_g)) { \
- *((TYPE*)(_buf+align_g[_ano])) = _val; /*possible SIGBUS or SEGSEGV*/ \
- _val2 = *((TYPE*)(_buf+align_g[_ano])); /*possible SIGBUS or SEGSEGV*/ \
+ *((TYPE*)(_buf+align_g[_ano])) = _val; /*possible SIGBUS or SEGSEGV*/ \
+ _val2 = *((TYPE*)(_buf+align_g[_ano])); /*possible SIGBUS or SEGSEGV*/\
/* Cray Check: This section helps detect alignment on Cray's */ \
/* vector machines (like the SV1) which mask off */ \
/* pointer values when pointing to non-word aligned */ \
@@ -377,7 +412,7 @@ precision (detected_t *d)
memcpy(_buf+align_g[_ano]+(INFO.offset/8),((char *)&_val)+(INFO.offset/8),(size_t)(INFO.precision/8)); \
_val2 = *((TYPE*)(_buf+align_g[_ano])); \
if(_val!=_val2) \
- longjmp(jbuf_g, 1); \
+ H5LONGJMP(jbuf_g, 1); \
/* End Cray Check */ \
(INFO.align)=align_g[_ano]; \
} else { \
@@ -385,61 +420,18 @@ precision (detected_t *d)
fprintf(stderr, "unable to calculate alignment for %s\n", #TYPE); \
} \
free(_buf); \
- signal(SIGBUS, _handler); /*restore original handler*/ \
- signal(SIGSEGV, _handler2); /*restore original handler*/ \
+ HDsignal(SIGBUS, _handler); /*restore original handler*/ \
+ HDsignal(SIGSEGV, _handler2); /*restore original handler*/ \
}
#else
-#define ALIGNMENT(TYPE,INFO) (INFO.align)=0
-#endif
-
-#if 0
-#if defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID)
#define ALIGNMENT(TYPE,INFO) { \
- char *_buf; \
- TYPE _val=0; \
- size_t _ano; \
- pid_t _child; \
- int _status; \
- \
- srand((unsigned int)_val); /*suppress "set but unused" warning*/ \
- for (_ano=0; _ano<NELMTS(align_g); _ano++) { \
- fflush(stdout); \
- fflush(stderr); \
- if (0==(_child=fork())) { \
- _buf = malloc(sizeof(TYPE)+align_g[NELMTS(align_g)-1]); \
- *((TYPE*)(_buf+align_g[_ano])) = _val; \
- _val = *((TYPE*)(_buf+align_g[_ano])); \
- free(_buf); \
- exit(0); \
- } else if (_child<0) { \
- perror("fork"); \
- exit(1); \
- } \
- if (waitpid(_child, &_status, 0)<0) { \
- perror("waitpid"); \
- exit(1); \
- } \
- if (WIFEXITED(_status) && 0==WEXITSTATUS(_status)) { \
- INFO.align=align_g[_ano]; \
- break; \
- } \
- if (WIFSIGNALED(_status) && SIGBUS==WTERMSIG(_status)) { \
- continue; \
- } \
- _ano=NELMTS(align_g); \
- break; \
- } \
- if (_ano>=NELMTS(align_g)) { \
- INFO.align=0; \
- fprintf(stderr, "unable to calculate alignment for %s\n", #TYPE); \
- } \
+ align_status_g |= STA_NoALIGNMENT; \
+ (INFO.align)=0; \
}
-#else
-#define ALIGNMENT(TYPE,INFO) (INFO.align)=0
-#endif
#endif
+#if defined(H5LONGJMP) && defined(H5_HAVE_SIGNAL)
/*-------------------------------------------------------------------------
* Function: sigsegv_handler
*
@@ -448,7 +440,7 @@ precision (detected_t *d)
* it's not nearly as nice to work with, it does the job for
* this simple stuff.
*
- * Return: Returns via longjmp to jbuf_g.
+ * Return: Returns via H5LONGJMP to jbuf_g.
*
* Programmer: Robb Matzke
* Thursday, March 18, 1999
@@ -460,11 +452,24 @@ precision (detected_t *d)
static void
sigsegv_handler(int UNUSED signo)
{
- signal(SIGSEGV, sigsegv_handler);
- longjmp(jbuf_g, 1);
+#if !defined(H5HAVE_SIGJMP) && defined(H5_HAVE_SIGPROCMASK)
+ /* Use sigprocmask to unblock the signal if sigsetjmp/siglongjmp are not */
+ /* supported. */
+ sigset_t set;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGSEGV);
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+#endif
+
+ sigsegv_handler_called_g++;
+ HDsignal(SIGSEGV, sigsegv_handler);
+ H5LONGJMP(jbuf_g, SIGSEGV);
}
+#endif
+#if defined(H5LONGJMP) && defined(H5_HAVE_SIGNAL)
/*-------------------------------------------------------------------------
* Function: sigbus_handler
*
@@ -473,7 +478,7 @@ sigsegv_handler(int UNUSED signo)
* it's not nearly as nice to work with, it does the job for
* this simple stuff.
*
- * Return: Returns via longjmp to jbuf_g.
+ * Return: Returns via H5LONGJMP to jbuf_g.
*
* Programmer: Robb Matzke
* Thursday, March 18, 1999
@@ -485,12 +490,21 @@ sigsegv_handler(int UNUSED signo)
static void
sigbus_handler(int UNUSED signo)
{
- signal(SIGBUS, sigbus_handler);
- longjmp(jbuf_g, 1);
-#ifdef H5_HAVE_SIGLONGJMP
- siglongjmp(jbuf_g, 1);
-#endif /* H5_HAVE_SIGLONGJMP */
+#if !defined(H5HAVE_SIGJMP) && defined(H5_HAVE_SIGPROCMASK)
+ /* Use sigprocmask to unblock the signal if sigsetjmp/siglongjmp are not */
+ /* supported. */
+ sigset_t set;
+
+ HDsigemptyset(&set);
+ HDsigaddset(&set, SIGBUS);
+ HDsigprocmask(SIG_UNBLOCK, &set, NULL);
+#endif
+
+ sigbus_handler_called_g++;
+ HDsignal(SIGBUS, sigbus_handler);
+ H5LONGJMP(jbuf_g, SIGBUS);
}
+#endif
/*-------------------------------------------------------------------------
@@ -574,6 +588,7 @@ print_results(int nd, detected_t *d, int na, malign_t *misc_align)
/*******************/\n\
\n");
+
/* The interface initialization function */
printf("\n\
\n\
@@ -721,6 +736,57 @@ done:\n\
} /* end if */\n\
\n\
FUNC_LEAVE_NOAPI(ret_value);\n} /* end H5TN_init_interface() */\n");
+
+ /* Print the ALIGNMENT and signal-handling status as comments */
+ printf("\n"
+ "/****************************************/\n"
+ "/* ALIGNMENT and signal-handling status */\n"
+ "/****************************************/\n");
+ if (align_status_g & STA_NoALIGNMENT)
+ printf("/* ALIGNAMENT test is not available */\n");
+ if (align_status_g & STA_NoHandlerVerify)
+ printf("/* Signal handlers verify test is not available */\n");
+ /* The following is available in H5pubconf.h. Printing them here for */
+ /* convenience. */
+#ifdef H5_HAVE_SIGNAL
+ printf("/* Signal() support: yes */\n");
+#else
+ printf("/* Signal() support: no */\n");
+#endif
+#ifdef H5_HAVE_SETJMP
+ printf("/* setjmp() support: yes */\n");
+#else
+ printf("/* setjmp() support: no */\n");
+#endif
+#ifdef H5_HAVE_LONGJMP
+ printf("/* longjmp() support: yes */\n");
+#else
+ printf("/* longjmp() support: no */\n");
+#endif
+#ifdef H5_HAVE_SIGSETJMP
+ printf("/* sigsetjmp() support: yes */\n");
+#else
+ printf("/* sigsetjmp() support: no */\n");
+#endif
+#ifdef H5_HAVE_SIGLONGJMP
+ printf("/* siglongjmp() support: yes */\n");
+#else
+ printf("/* siglongjmp() support: no */\n");
+#endif
+#ifdef H5_HAVE_SIGPROCMASK
+ printf("/* sigprocmask() support: yes */\n");
+#else
+ printf("/* sigprocmask() support: no */\n");
+#endif
+
+ /* Print the statics of signal handlers called for debugging */
+ printf("\n"
+ "/******************************/\n"
+ "/* signal handlers statistics */\n"
+ "/******************************/\n");
+ printf("/* signal_handlers tested: %d times */\n", signal_handler_tested_g);
+ printf("/* sigbus_handler called: %d times */\n", sigbus_handler_called_g);
+ printf("/* sigsegv_handler called: %d times */\n", sigsegv_handler_called_g);
} /* end print_results() */
@@ -1485,6 +1551,55 @@ detect_alignments(void)
}
+#if defined(H5SETJMP) && defined(H5_HAVE_SIGNAL)
+/* Verify the signal handler for signal signum works correctly multiple times.
+ * One possible cause of failure is that the signal handling is blocked or
+ * changed to SIG_DFL after H5LONGJMP.
+ * Return 0 for success, -1 for failure.
+ */
+static int verify_signal_handlers(int signum, void (*handler)(int))
+{
+ void (*save_handler)(int) = HDsignal(signum, handler);
+ int i, val;
+ int ntries=5;
+ volatile int nfailures=0;
+ volatile int nsuccesses=0;
+
+ for (i=0;i<ntries; i++){
+ val=H5SETJMP(jbuf_g);
+ if (val==0)
+ {
+ /* send self the signal to trigger the handler */
+ signal_handler_tested_g++;
+ HDraise(signum);
+ /* Should not reach here. Record error. */
+ nfailures++;
+ }else{
+ if (val==signum){
+ /* return from signum handler. Record a sucess. */
+ nsuccesses++;
+ }else{
+ fprintf(stderr, "Unknown return value (%d) from H5SETJMP",
+ val);
+ nfailures++;
+ }
+ }
+ }
+ /* restore save handler, check results and report failures */
+ HDsignal(signum, save_handler);
+ if (nfailures>0 || nsuccesses != ntries){
+ fprintf(stderr, "verify_signal_handlers for signal %d did %d tries. "
+ "Found %d failures and %d successes\n",
+ signum, ntries, nfailures, nsuccesses);
+ return(-1);
+ }else{
+ /* all succeeded */
+ return(0);
+ }
+}
+#endif
+
+
/*-------------------------------------------------------------------------
* Function: main
*
@@ -1527,6 +1642,20 @@ main(void)
#endif
#endif
+#if defined(H5SETJMP) && defined(H5_HAVE_SIGNAL)
+ /* verify the SIGBUS and SIGSEGV handlers work properly */
+ if (verify_signal_handlers (SIGBUS, sigbus_handler) != 0){
+ fprintf(stderr, "Signal handler %s for signal %d failed\n",
+ "sigbus_handler", SIGBUS);
+ }
+ if (verify_signal_handlers (SIGSEGV, sigsegv_handler) != 0){
+ fprintf(stderr, "Signal handler %s for signal %d failed\n",
+ "sigsegv_handler", SIGSEGV);
+ }
+#else
+ align_status_g |= STA_NoHandlerVerify;
+#endif
+
print_header();
/* C89 integer types */