blob: afe3a534965162edff34a5c38261b991d73979c0 [file] [log] [blame]
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-C++-*-
*****************************************************************************
*
* File: UdrFFDC.cpp
* Description: MXUDR FFDC
*
* Created: 5/4/02
* Language: C++
*
*
*
*
*****************************************************************************
*/
#include "Platform.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <signal.h>
#include "vproc.h"
#include "udrglobals.h"
#include "udrutil.h"
#include "UdrDebug.h"
#include "UdrFFDC.h"
#include "logmxevent.h"
#include "copyright.h"
extern void my_mpi_fclose();
#define TEXT_SIZE 1024
extern UdrGlobals *UDR_GLOBALS; // UDR globals area
struct SignalAttr {
public:
Int32 sigType;
const char * sigName;
struct sigaction udrSigAction;
struct sigaction javaSigAction;
};
// We install UDR signal handler for all the signals that can cause the program to
// terminate by default. The signals that are discarded by default (SIGIGN) or
// the signals that can not be caught do not have entries in the following table.
static SignalAttr signalAttrTable [] =
{
// Avoid the signals used by Foundation
// Signal Name Udr Sigaction Java Sigaction
{ SIGHUP, "SIGHUP", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGINT, "SIGINT", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGQUIT, "SIGQUIT", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGILL, "SIGILL", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
/* SIGURG -- discarded by default */
/* SIGTRAP -- used by SQF */
{ SIGABRT, "SIGABRT", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGBUS, "SIGBUS", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGFPE, "SIGFPE", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
/* SIGKILL -- can not be caught */
/* SIGUNCP -- discarded by default */
{ SIGSEGV, "SIGSEGV", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
/* SIGWINCH -- discarded by default */
/* SIGPIPE -- used by SQF */
/* SIGALRM -- used by SQF */
{ SIGTERM, "SIGTERM", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
/* SIGUSR1 -- used by SQF */
/* SIGUSR2 -- used by SQF */
/* SIGCHLD -- discarded by default */
/* SIGSTOP -- can not be caught */
/* SIGTSTP -- does not result in abend */
/* SIGCONT -- execution continues */
/* SIGTTIN -- does not result in abend */
/* SIGTTOU -- does not result in abend */
/* SIGPROF -- used by SQF */
{ SIGVTALRM, "SIGVTALRM", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGXCPU, "SIGXCPU", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGXFSZ, "SIGXFSZ", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGSTKFLT, "SIGSTKFLT", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGIO, "SIGIO", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGPWR, "SIGPWR", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
{ SIGUNUSED, "SIGUNUSED", {SIG_DFL,0, 0}, { SIG_DFL, 0, 0} },
};
static void getActiveRoutineInfoMsg(char *msg, NABoolean &isRoutineActive)
{
char routineName[ComMAX_ANSI_NAME_EXTERNAL_LEN + 1];
char routineLanguage[10];
char routineType[20];
routineName[0] = '\0';
routineType[0] = '\0';
routineLanguage[0] = '\0';
// Note: accessing the global UdrGlobals instance directly
if (UDR_GLOBALS)
{
getActiveRoutineInfo(UDR_GLOBALS, routineName, routineType,
routineLanguage, isRoutineActive);
}
if (routineName[0] == '\0')
sprintf(msg, "A user routine is not currently being processed");
else {
sprintf(msg,
"User routine being processed : %s, Routine Type : %s, Language Type : %s",
routineName, routineType, routineLanguage);
if (isRoutineActive == TRUE)
strcat(msg, ", Error occurred inside the user routine code");
else
strcat(msg, ", Error occurred outside the user routine code");
}
}
void logEMS(const char *msg) {
SQLMXLoggingArea::logSQLMXAbortEvent("",
0,
msg);
}
// This method writes to stderr which gets redirected to monitor log file
inline void logStdErr(const char *msg) {
write (2, msg, strlen(msg));
}
void comTFDS(const char *msg1, const char *msg2, const char *msg3, const char *msg4, const char *msg5, NABoolean dialOut
, NABoolean writeToSeaLog
)
{
setSignalHandlersToDefault();
if(!msg1)
msg1 = "";
if(!msg2)
msg2 = "";
if(!msg3)
msg3 = "";
if(!msg4)
msg4 = "";
if(!msg5)
msg5 = "";
UDR_DEBUG1("[SIGNAL] %s", msg1);
UDR_DEBUG1("[SIGNAL] %s", msg2);
UDR_DEBUG1("[SIGNAL] %s", msg3);
UDR_DEBUG1("[SIGNAL] %s", msg4);
UDR_DEBUG1("[SIGNAL] %s", msg5);
UDR_DEBUG0("[SIGNAL] Logging an EMS message");
char msg[TEXT_SIZE];
strncpy(msg, msg1, sizeof(msg));
strncat(msg, ", ", sizeof(msg)-strlen(msg));
strncat(msg, msg2, sizeof(msg)-strlen(msg));
strncat(msg, ", ", sizeof(msg)-strlen(msg));
strncat(msg, msg3, sizeof(msg)-strlen(msg));
strncat(msg, ", ", sizeof(msg)-strlen(msg));
strncat(msg, msg4, sizeof(msg)-strlen(msg));
strncat(msg, ", ", sizeof(msg)-strlen(msg));
strncat(msg, msg5, sizeof(msg)-strlen(msg));
if (writeToSeaLog)
logEMS(msg);
else
logStdErr(msg);
UDR_DEBUG0("[SIGNAL] Aborting...");
abort();
}
void makeTFDSCall(const char *msg, const char *file, UInt32 line, NABoolean dialOut
, NABoolean writeToSeaLog
)
{
const char *msg1 = COPYRIGHT_UDRSERV_PRODNAME_H " Internal Error";
const char *msg1a = COPYRIGHT_UDRSERV_PRODNAME_H " Abort";
const char *msg2 = msg;
char msg3[TEXT_SIZE];
char msg4[2 * TEXT_SIZE]; // to accommodate 3-part ANSI name for routine
// TEXT_SIZE is big enough
if (line > 0)
snprintf(msg3, TEXT_SIZE, "Error occurred in source file %s on line %d", file, line);
else
strcpy(msg3, "Source file information unavailable");
NABoolean isRoutineActive;
getActiveRoutineInfoMsg(msg4, isRoutineActive);
comTFDS(dialOut ? msg1 : msg1a,
msg2,
msg3,
msg4,
NULL,
dialOut
, writeToSeaLog
);
}
// Udr Trap(NSK)/signal(Linux) handler routine
// Main purpose of this handler routine on NSK is to call TFDS when the trap occurs and
// log an EMS event message showing signal name and any active user routine name at that time.
// On Linux, we do not call Sealog as it can cause a hang while sending message.
// Instead we write the message to STDERR which gets redirected to monitor log.
// Also the handler uses some library functions such as sprintf which are not known to be
// "async signal safe", so if there are any issues found, it may need changes.
void UdrSignalHandler( Int32 signo )
{
// Block any more signals inside signal handler
sigset_t sigMask;
sigfillset (&sigMask);
sigprocmask(SIG_BLOCK, &sigMask, NULL);
UDR_DEBUG1("[SIGNAL] Caught signal %d", signo);
const char *msg1 = COPYRIGHT_UDRSERV_PRODNAME_H " Signal Handler";
char msg2[TEXT_SIZE];
char msg3[2 * TEXT_SIZE]; // to accommodate 3-part ANSI name for routine
sprintf(msg2, "SIGNAL %d: ", signo); // TEXT_SIZE is big enough.
const char *sigText;
switch( signo )
{
case SIGHUP:
sigText = "Hangup.";
break;
case SIGINT:
sigText = "Interrupt.";
break;
case SIGQUIT:
sigText = "Quit.";
break;
case SIGILL:
sigText = "Instruction failure.";
break;
case SIGABRT:
sigText = "Abnormal termination.";
break;
case SIGBUS:
sigText = "Bus error.";
break;
case SIGFPE:
sigText = "Arithmetic overflow.";
break;
case SIGSEGV:
sigText = "Illegal address reference.";
break;
case SIGPIPE:
sigText = "Pipe error.";
break;
case SIGALRM:
sigText = "Alarm.";
break;
case SIGTERM:
sigText = "Software Terminate.";
break;
case SIGUSR1:
sigText = "User defined 1.";
break;
case SIGUSR2:
sigText = "User defined 2.";
break;
case SIGTSTP:
sigText = "Interactive Stop.";
break;
case SIGVTALRM:
sigText = "Virtual alarm clock.";
break;
case SIGXCPU:
sigText = "CPU time limit exceeded.";
break;
case SIGXFSZ:
sigText = "File size limit exceeded.";
break;
case SIGSTKFLT:
sigText = "Stack fault.";
break;
case SIGIO:
sigText = "I/O now possible.";
break;
case SIGPWR:
sigText = "Power failure.";
break;
case SIGUNUSED:
sigText = "Unused signal.";
break;
case SIGTTIN:
sigText = "Background Read error.";
break;
case SIGTTOU:
sigText = "Background Write error.";
break;
default:
sigText = "Unknown error.";
}
strncat(msg2, sigText, sizeof(msg2) - strlen(msg2));
NABoolean isRoutineActive;
getActiveRoutineInfoMsg(msg3, isRoutineActive);
comTFDS(msg1, msg2, msg3, NULL, NULL, TRUE, FALSE);
} // UdrTrapHandler()
void setUdrSignalHandlers()
{
struct sigaction oldAction, sigAct;
sigemptyset(&(sigAct.sa_mask));
sigAct.sa_flags = 0;
sigAct.sa_handler = UdrSignalHandler;
for(Int32 i=0; i<sizeof(signalAttrTable)/sizeof(SignalAttr); i++){
if (sigaction (signalAttrTable[i].sigType, &sigAct, &oldAction)) {
cerr << "[MXUDR DEBUG SIGNAL HANDLER] "
<< "sigaction() failed, cannot set signal handler for "
<< signalAttrTable[i].sigName << endl;
}
}
}
// LCOV_EXCL_START
void printSignalHandlers()
{
struct sigaction oldAction;
void (*oldHdlr)(Int32);
for(Int32 i=0; i<sizeof(signalAttrTable)/sizeof(SignalAttr); i++){
if (sigaction (signalAttrTable[i].sigType, (struct sigaction*)NULL, &oldAction)) {
cerr << "[MXUDR DEBUG SIGNAL HANDLER] "
<< "sigaction() failed, cannot obtain old signal handler for "
<< signalAttrTable[i].sigName << endl;
}
else {
oldHdlr = oldAction.sa_handler;
if(oldHdlr == SIG_DFL)
printf("[MXUDR DEBUG SIGNAL HANDLER] %s SIG_DFL\n", signalAttrTable[i].sigName);
else if(oldHdlr == SIG_IGN)
printf("[MXUDR DEBUG SIGNAL HANDLER] %s SIG_IGN\n", signalAttrTable[i].sigName);
else if(oldHdlr != SIG_ERR)
printf("[MXUDR DEBUG SIGNAL HANDLER] %s %p\n", signalAttrTable[i].sigName, oldHdlr);
}
}
}
// LCOV_EXCL_STOP
// Saves the UDR trap signal handlers
NABoolean saveUdrTrapSignalHandlers()
{
UDR_DEBUG0("[SIGNAL] Save UDR Trap signal handlers");
struct sigaction oldAction;
for(Int32 i=0; i<sizeof(signalAttrTable)/sizeof(SignalAttr); i++){
if (sigaction (signalAttrTable[i].sigType, (struct sigaction*)NULL, &oldAction)) {
cerr << "[MXUDR DEBUG SIGNAL HANDLER] "
<< "sigaction() failed, cannot obtain old signal handler for "
<< signalAttrTable[i].sigName << endl;
return FALSE;
}
signalAttrTable[i].udrSigAction.sa_handler = oldAction.sa_handler;
signalAttrTable[i].udrSigAction.sa_mask = oldAction.sa_mask;
signalAttrTable[i].udrSigAction.sa_flags = oldAction.sa_flags;
}
return TRUE;
}
// Sets the UDR server signal handlers to default so that the JVM won't complain.
NABoolean setSignalHandlersToDefault()
{
struct sigaction oldAction, sigAct;
sigemptyset(&(sigAct.sa_mask));
sigAct.sa_flags = 0;
sigAct.sa_handler = SIG_DFL;
UDR_DEBUG0("[SIGNAL] Set signal handlers to default before Java LM initialization");
for(Int32 i=0; i<sizeof(signalAttrTable)/sizeof(SignalAttr); i++){
if (sigaction(signalAttrTable[i].sigType, &sigAct, &oldAction)) {
char msg[256];
sprintf(msg,
"[UDR WARNING] sigaction() failed, cannot change %s to the default handler\n",
signalAttrTable[i].sigName);
logStdErr(msg);
return FALSE;
}
}
return TRUE;
}
// Restores the UDR server signal handlers to JVM signal handlers before re-entering
// any Java code.
NABoolean restoreJavaSignalHandlers()
{
UDR_DEBUG0("[SIGNAL] Restore Java signal handlers before entering Java LM");
struct sigaction oldAction, sigAct;
sigemptyset(&(sigAct.sa_mask));
sigAct.sa_flags = 0;
for(Int32 i=0; i<sizeof(signalAttrTable)/sizeof(SignalAttr); i++){
if (sigaction (signalAttrTable[i].sigType, (struct sigaction*)NULL, &oldAction)) {
cerr << "[MXUDR DEBUG SIGNAL HANDLER] "
<< "sigaction() failed, cannot obtain old signal handler for "
<< signalAttrTable[i].sigName << endl;
return FALSE;
}
if ( oldAction.sa_handler == signalAttrTable[i].javaSigAction.sa_handler ) {
UDR_DEBUG1("[SIGNAL] Java signal handler for %s already active in restoreJavaSignalHandlers",
signalAttrTable[i].sigName);
continue;
}
sigAct.sa_handler = signalAttrTable[i].javaSigAction.sa_handler;
sigAct.sa_mask = signalAttrTable[i].javaSigAction.sa_mask;
sigAct.sa_flags = signalAttrTable[i].javaSigAction.sa_flags;
if (sigaction (signalAttrTable[i].sigType, &sigAct, (struct sigaction*)NULL)) {
cerr << "[MXUDR DEBUG SIGNAL HANDLER] "
<< "sigaction() failed, cannot restore Java signal handler for "
<< signalAttrTable[i].sigName << endl;
return FALSE;
}
}
return TRUE;
}
// Restores the UDR server signal handlers to original signal handlers after returning
// from Java code. Also saves the current java signal handlers optionally.
NABoolean restoreUdrTrapSignalHandlers(NABoolean saveJavaSignalHandlers)
{
UDR_DEBUG0("[SIGNAL] Restore UDR Trap signal handlers after returning from Java LM");
struct sigaction oldAction, sigAct;
sigemptyset(&(sigAct.sa_mask));
sigAct.sa_flags = 0;
for(Int32 i=0; i<sizeof(signalAttrTable)/sizeof(SignalAttr); i++){
if (sigaction (signalAttrTable[i].sigType, (struct sigaction*)NULL, &oldAction)) {
cerr << "[MXUDR DEBUG SIGNAL HANDLER] "
<< "sigaction() failed, cannot obtain old signal handler for "
<< signalAttrTable[i].sigName << endl;
return FALSE;
}
if ( oldAction.sa_handler == signalAttrTable[i].udrSigAction.sa_handler ) {
UDR_DEBUG1("[SIGNAL] UDR trap signal handler for %s already active in restoreUdrTrapSignalHandlers",
signalAttrTable[i].sigName);
continue;
}
sigAct.sa_handler = signalAttrTable[i].udrSigAction.sa_handler;
sigAct.sa_mask = signalAttrTable[i].udrSigAction.sa_mask;
sigAct.sa_flags = signalAttrTable[i].udrSigAction.sa_flags;
if (sigaction (signalAttrTable[i].sigType, &sigAct, (struct sigaction*)NULL)) {
cerr << "[MXUDR DEBUG SIGNAL HANDLER] "
<< "sigaction() failed, cannot restore UDR trap signal handler for "
<< signalAttrTable[i].sigName << endl;
return FALSE;
}
else {
if (saveJavaSignalHandlers == TRUE) {
signalAttrTable[i].javaSigAction.sa_handler = oldAction.sa_handler;
signalAttrTable[i].javaSigAction.sa_mask = oldAction.sa_mask;
signalAttrTable[i].javaSigAction.sa_flags = oldAction.sa_flags;
}
}
}
return TRUE;
}
// Udr exit handler routine
// Main purpose of this handler routine is to check if the UDR called exit
// which it is not supposed to do. In case UDR called exit, we log an
// event message and abort.
void UdrExitHandler()
{
const char *msg1 = COPYRIGHT_UDRSERV_PRODNAME_H " Exit Handler";
char msg2[TEXT_SIZE];
char msg3[2 * TEXT_SIZE]; // to accommodate 3-part ANSI name for routine
UDR_DEBUG0("[EXIT HANDLER] Entered MXUDR Exit handler");
NABoolean isRoutineActive;
getActiveRoutineInfoMsg(msg3, isRoutineActive);
// If the UDR was active and it called exit intentionally,
// log a message and capture TFDS state.
if (isRoutineActive == TRUE) {
UDR_DEBUG0("[EXIT HANDLER] UDR invoked exit()");
strcpy(msg2, "UDR invoked exit() ");
comTFDS(msg1, msg2, msg3, NULL, NULL, FALSE, FALSE);
}
// Call MPI exit handler routine from this exit handler
my_mpi_fclose();
}
void setExitHandler()
{
if (getenv("SQLMX_UDR_NO_EXIT_HANDLER"))
{
return;
}
UDR_DEBUG0("[EXIT HANDLER] Setting up the MXUDR Exit handler");
Int32 rc = atexit(UdrExitHandler);
UDR_DEBUG0("[EXIT HANDLER] Completed setting up the MXUDR Exit handler");
}