/*##############################################################################
Copyright (C) 2011 HPCC Systems.
All rights reserved. This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
############################################################################## */
#include "platform.h"
#include
#include "jexcept.hpp"
#include
#include
#include
#include
#include "jptree.hpp"
#ifdef _WIN32
#include "psapi.h"
#include
#elif defined (__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
#include
#include
#include
#include
#ifdef __linux__
#include // comment out if not present
#endif
#ifdef __APPLE__
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE
#endif
#include
#endif
#endif
//#define NOSEH
#define NO_LINUX_SEH
#define EXTENDED_EXCEPTION_TRACE
#ifdef EXTENDED_EXCEPTION_TRACE
#include "jmisc.hpp"
#define LINUX_SIGNAL_EXCEPTION
#endif
class jlib_thrown_decl StringException: public CInterface, public IException
{
public:
IMPLEMENT_IINTERFACE;
StringException(int code, const char *str, MessageAudience aud = MSGAUD_user) : errcode(code), msg(str), audience(aud) {};
int errorCode() const { return errcode; }
StringBuffer & errorMessage(StringBuffer &str) const { str.append(msg); return str;}
MessageAudience errorAudience() const { return audience; }
protected:
int errcode;
StringAttr msg;
MessageAudience audience;
};
IException *MakeStringExceptionVA(int code, const char *format, va_list args)
{
StringBuffer eStr;
eStr.limited_valist_appendf(1024, format, args);
return new StringException(code, eStr.str());
}
IException *MakeStringException(int code,const char *format, ...)
{
va_list args;
va_start(args, format);
IException *ret = MakeStringExceptionVA(code, format, args);
va_end(args);
return ret;
}
IException jlib_decl *MakeStringExceptionDirect(int code,const char *why)
{
return new StringException(code,why);
}
IException *MakeStringExceptionVA(MessageAudience aud, int code, const char *format, va_list args)
{
StringBuffer eStr;
eStr.limited_valist_appendf(1024, format, args);
return new StringException(code, eStr.str(), aud);
}
IException *MakeStringException(MessageAudience aud, int code, const char *format, ...)
{
va_list args;
va_start(args, format);
IException *ret = MakeStringExceptionVA(aud, code, format, args);
va_end(args);
return ret;
}
IException jlib_decl *MakeStringExceptionDirect(MessageAudience aud,int code,const char *why)
{
return new StringException(code,why,aud);
}
void jlib_decl ThrowStringException(int code,const char *format, ...)
{
va_list args;
va_start(args, format);
IException *ret = MakeStringExceptionVA(code, format, args);
va_end(args);
throw ret;
}
class jlib_thrown_decl OsException: public CInterface, public IOSException
{
public:
IMPLEMENT_IINTERFACE;
OsException(int code) : errcode(code) {};
OsException(int code, const char *_msg) : msg(_msg), errcode(code) {};
~OsException() {}
int errorCode() const { return errcode; }
StringBuffer & errorMessage(StringBuffer &str) const
{
if (msg)
str.append(msg).append(", ");
formatSystemError(str, errcode);
return str;
}
MessageAudience errorAudience() const { return MSGAUD_user; }
protected:
StringAttr msg;
int errcode;
};
IOSException *MakeOsException(int code)
{
return new OsException(code);
}
IOSException *MakeOsException(int code, const char *msg, ...)
{
StringBuffer eStr;
va_list args;
va_start(args, msg);
eStr.limited_valist_appendf(1024, msg, args);
va_end(args);
return new OsException(code, eStr.str());
}
class jlib_thrown_decl ErrnoException: public CInterface, public IErrnoException
{
public:
IMPLEMENT_IINTERFACE;
ErrnoException(int errn) : audience(MSGAUD_user) { errcode = errn==-1?errno:errn; }
ErrnoException(int errn, const char *_msg, MessageAudience aud = MSGAUD_user) : msg(_msg), audience(aud) { errcode = errn==-1?errno:errn; }
~ErrnoException() { }
int errorCode() const { return errcode; }
StringBuffer & errorMessage(StringBuffer &str) const
{
if (msg)
str.append(msg).append(", ");
if (errcode==DISK_FULL_EXCEPTION_CODE)
str.append("Disk full");
else
str.append(strerror(errcode));
return str;
}
MessageAudience errorAudience() const { return audience; }
protected:
StringAttr msg;
int errcode;
MessageAudience audience;
};
IErrnoException *MakeErrnoException(int errn)
{
return new ErrnoException(errn);
}
IErrnoException *MakeErrnoException(int errn, const char *msg, ...)
{
StringBuffer eStr;
va_list args;
va_start(args, msg);
eStr.limited_valist_appendf(1024, msg, args);
va_end(args);
return new ErrnoException(errn, eStr.str());
}
IErrnoException *MakeErrnoException(const char *msg, ...)
{
StringBuffer eStr;
va_list args;
va_start(args, msg);
eStr.limited_valist_appendf(1024, msg, args);
va_end(args);
return new ErrnoException(-1, eStr.str());
}
IErrnoException *MakeErrnoException(MessageAudience aud, int errn)
{
return new ErrnoException(errn, "", aud);
}
IErrnoException *MakeErrnoException(MessageAudience aud, int errn, const char *msg, ...)
{
StringBuffer eStr;
va_list args;
va_start(args, msg);
eStr.limited_valist_appendf(1024, msg, args);
va_end(args);
return new ErrnoException(errn, eStr.str(), aud);
}
IErrnoException *MakeErrnoException(MessageAudience aud, const char *msg, ...)
{
StringBuffer eStr;
va_list args;
va_start(args, msg);
eStr.limited_valist_appendf(1024, msg, args);
va_end(args);
return new ErrnoException(-1, eStr.str(), aud);
}
const char* SerializeMessageAudience(MessageAudience ma)
{
const char* ret;
switch(ma)
{
case MSGAUD_unknown: ret = "unknown"; break;
case MSGAUD_operator: ret = "operator"; break;
case MSGAUD_user: ret = "user"; break;
case MSGAUD_monitor: ret = "monitor"; break;
case MSGAUD_performance: ret = "performance"; break;
case MSGAUD_internal: ret = "internal"; break;
case MSGAUD_programmer: ret = "programmer"; break;
case MSGAUD_legacy: ret = "legacy"; break;
case MSGAUD_all: ret = "all"; break;
default: ret = "unknown"; break;
}
return ret;
}
MessageAudience DeserializeMessageAudience(const char* text)
{
MessageAudience ma = MSGAUD_unknown;
if (text && *text)
{
if (!strcmp(text, "operator"))
ma = MSGAUD_operator;
else if (!strcmp(text, "user"))
ma = MSGAUD_user;
else if (!strcmp(text, "monitor"))
ma = MSGAUD_monitor;
else if (!strcmp(text, "performance"))
ma = MSGAUD_performance;
else if (!strcmp(text, "internal"))
ma = MSGAUD_internal;
else if (!strcmp(text, "programmer"))
ma = MSGAUD_programmer;
else if (!strcmp(text, "legacy"))
ma = MSGAUD_legacy;
else if (!strcmp(text, "all"))
ma = MSGAUD_all;
}
return ma;
}
class jlib_thrown_decl CMultiException : public CInterface,
implements IMultiException
{
public:
IMPLEMENT_IINTERFACE
CMultiException(const char* source=NULL)
{
if (source)
source_.append(source);
}
//convenience methods for handling this as an array
virtual aindex_t ordinality() const
{
synchronized block(m_mutex);
return array_.ordinality();
}
virtual IException& item(aindex_t pos) const
{
synchronized block(m_mutex);
return array_.item(pos);
}
virtual const char* source() const
{
synchronized block(m_mutex);
return source_.str();
}
//for complete control...caller is responsible for thread safety!
virtual IArrayOf& getArray() { return array_; }
//add another exception
virtual void append(IException& e)
{
synchronized block(m_mutex);
array_.append(e);
}
virtual void append(IMultiException& me)
{
synchronized block(m_mutex);
IArrayOf& exceptions = me.getArray();
const char* source = me.source();
ForEachItemIn(i, exceptions)
{
IException& e = exceptions.item(i);
if (source && *source)
{
StringBuffer msg;
msg.appendf("[%s] ",source);
e.errorMessage(msg);
array_.append(*MakeStringException(e.errorAudience(), e.errorCode(), "%s", msg.str()));
}
else
array_.append(*LINK(&e));
}
}
StringBuffer& serialize(StringBuffer& buffer, unsigned indent = 0, bool simplified=false, bool root=true) const
{
synchronized block(m_mutex);
if (root)
buffer.append("");
if (!simplified)
{
if (indent) buffer.append("\n\t");
buffer.appendf("%s", source_.str());
}
ForEachItemIn(i, array_)
{
IException& exception = array_.item(i);
if (indent) buffer.append("\n\t");
buffer.append("");
//tag order is important for some soap clients (i.e. Java Axis)
if (indent) buffer.append("\n\t\t");
buffer.appendf("%d
", exception.errorCode());
if (indent) buffer.append("\n\t\t");
buffer.appendf("%s", SerializeMessageAudience( exception.errorAudience() ));
if (simplified)
{
if (indent) buffer.append("\n\t\t");
StringBuffer msg;
buffer.appendf("%s", source_.str());
}
if (indent) buffer.append("\n\t\t");
StringBuffer msg;
StringBuffer encoded;
encodeXML(exception.errorMessage(msg).str(), encoded);
buffer.appendf("%s", encoded.str());
if (indent) buffer.append("\n\t");
buffer.append("");
}
if (root)
buffer.append("");
return buffer;
}
virtual void deserialize(const char* xml)
{
synchronized block(m_mutex);
StringBuffer wrapper;
if (strncmp(xml, "", 12))
xml = wrapper.appendf("%s", xml).str();
Owned pTree = createPTreeFromXMLString(xml);
if (!pTree)
throw MakeStringException(-1, "Failed to deserialize IMultiException!");
Owned i = pTree->getElements("Exception");
if (pTree->hasProp("Source"))
source_.clear().append( pTree->queryProp("Source"));
else
{
if (i->first())
{
IPropertyTree* pNode = &i->query();
source_.clear().append( pNode->queryProp("Source"));
}
}
array_.kill();
ForEach(*i)
{
IPropertyTree* pNode = &i->query();
IException* pException =
MakeStringExceptionDirect(
DeserializeMessageAudience(pNode->queryProp("Audience")),
pNode->getPropInt("Code", -1),
pNode->queryProp("Message"));
array_.append(*pException);
}
}
//the following methods override those in IIException
//
virtual int errorCode() const
{
synchronized block(m_mutex);
return ordinality() == 1 ? item(0).errorCode() : -1;
}
virtual StringBuffer& errorMessage(StringBuffer &msg) const
{
synchronized block(m_mutex);
ForEachItemIn(i, array_)
{
IException& e = item(i);
StringBuffer buf;
msg.appendf("[%3d: %s] ", e.errorCode(), e.errorMessage(buf).str());
}
return msg;
}
virtual MessageAudience errorAudience() const
{
synchronized block(m_mutex);
return ordinality() == 1 ? item(0).errorAudience() : MSGAUD_unknown;
}
private:
CMultiException( const CMultiException& );
IArrayOf array_;
StringBuffer source_;
mutable Mutex m_mutex;
};
IMultiException *MakeMultiException(const char* source/*=NULL*/)
{
return new CMultiException(source);
}
void pexception(const char *msg,IException *e)
{ // like perror except for exceptions
StringBuffer s;
fprintf(stderr,"%s : %s\n",msg,e?e->errorMessage(s).toCharArray():"NULL Exception!");
}
void userBreakpoint()
{
#ifdef _WIN32
try
{
DebugBreak();
}
catch (...)
{
//if not debugging don't give an unhandled exception.
}
#endif
}
void RaiseAssertException(const char *assertion, const char *file, unsigned line)
{
PrintStackReport();
StringBuffer s;
s.append("assert(");
s.append(assertion);
s.append(") failed - file: ");
s.append(file);
s.append(", line ");
s.append(line);
ERRLOG("%s",s.str()); // make sure doesn't get lost!
queryLogMsgManager()->flushQueue(10*1000);
#ifdef _DEBUG
// cause a breakpoint in the debugger if we are debugging.
//userBreakpoint();
#endif
#if 0
#ifndef USING_MPATROL
#ifdef _WIN32
// disable memory leak dump since it is meaningless in this case
int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
tmpFlag &= ~_CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag( tmpFlag );
#endif
#endif
#endif
throw MakeStringException(3000, "%s", s.toCharArray()); // 3000: internal error
}
void RaiseAssertCore(const char *assertion, const char *file, unsigned line)
{
PrintStackReport();
StringBuffer s;
s.append("assert(");
s.append(assertion);
s.append(") failed - file: ");
s.append(file);
s.append(", line ");
s.append(line);
ERRLOG("%s",s.str()); // make sure doesn't get lost!
queryLogMsgManager()->flushQueue(10*1000);
#ifdef _WIN32
userBreakpoint();
ExitProcess(255);
#else
raise(SIGABRT);
_exit(255);
#endif
}
static int SEHnested = 0;
static IExceptionHandler *SEHHandler = NULL;
static bool SEHtermOnSystemDLLs = false;
static bool SEHtermAlways = false;
#ifdef _WIN32
static void *SEHrestore;
#ifdef EXTENDED_EXCEPTION_TRACE
static LPTSTR GetExceptionString( DWORD dwCode )
{
#define EXCEPTION( x ) case EXCEPTION_##x: return #x;
switch ( dwCode )
{
EXCEPTION( ACCESS_VIOLATION )
EXCEPTION( DATATYPE_MISALIGNMENT )
EXCEPTION( BREAKPOINT )
EXCEPTION( SINGLE_STEP )
EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
EXCEPTION( FLT_DENORMAL_OPERAND )
EXCEPTION( FLT_DIVIDE_BY_ZERO )
EXCEPTION( FLT_INEXACT_RESULT )
EXCEPTION( FLT_INVALID_OPERATION )
EXCEPTION( FLT_OVERFLOW )
EXCEPTION( FLT_STACK_CHECK )
EXCEPTION( FLT_UNDERFLOW )
EXCEPTION( INT_DIVIDE_BY_ZERO )
EXCEPTION( INT_OVERFLOW )
EXCEPTION( PRIV_INSTRUCTION )
EXCEPTION( IN_PAGE_ERROR )
EXCEPTION( ILLEGAL_INSTRUCTION )
EXCEPTION( NONCONTINUABLE_EXCEPTION )
EXCEPTION( STACK_OVERFLOW )
EXCEPTION( INVALID_DISPOSITION )
EXCEPTION( GUARD_PAGE )
EXCEPTION( INVALID_HANDLE )
}
static CHAR szBuffer[512] = { 0 };
FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
GetModuleHandle( "NTDLL.DLL" ),
dwCode, 0, szBuffer, sizeof( szBuffer ), 0 );
return szBuffer;
}
static BOOL GetLogicalAddress( PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD& offset )
{
szModule[0] = 0;
section = 0;
offset = 0;
if ((unsigned)addr<0x10000)
return FALSE;
MEMORY_BASIC_INFORMATION mbi;
if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) )
return FALSE;
DWORD hMod = (DWORD)mbi.AllocationBase;
if ( !GetModuleFileName( (HMODULE)hMod, szModule, len ) )
return FALSE;
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
DWORD rva = (DWORD)addr - hMod;
for (unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++ )
{
DWORD sectionStart = pSection->VirtualAddress;
DWORD sectionEnd = sectionStart
+ std::max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
if ( (rva >= sectionStart) && (rva <= sectionEnd) )
{
section = i+1;
offset = rva - sectionStart;
return TRUE;
}
}
return FALSE;
}
#if defined(_WIN32)
#ifdef __cplusplus
extern "C" {
typedef BOOL (CALLBACK* LPENUMPROCESSMODULES)(HANDLE,HMODULE *,DWORD,LPDWORD);
typedef BOOL (CALLBACK* LPGETMODULEINFORMATION)(HANDLE,HMODULE,LPMODULEINFO,DWORD);
}
#endif
#endif
static void ModuleWalk()
{
HMODULE hmSystem = LoadLibrary("psapi.dll");
if (!hmSystem)
return;
LPENUMPROCESSMODULES enumProcessModules = (LPENUMPROCESSMODULES)GetProcAddress(hmSystem,"EnumProcessModules");
if (!enumProcessModules)
return;
LPGETMODULEINFORMATION getModuleInformation = (LPGETMODULEINFORMATION)GetProcAddress(hmSystem,"GetModuleInformation");
if (!getModuleInformation)
return;
DWORD processID = GetCurrentProcessId();
PrintLog( "Process ID: %u", processID );
// Get a list of all the modules in this process.
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, processID );
if (NULL == hProcess)
return;
HMODULE hMods[1024];
DWORD cbNeeded;
if (enumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
for (unsigned i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ ) {
char szModName[MAX_PATH];
szModName[0] = 0;
// Get the full path to the module's file.
MODULEINFO modinfo;
memset(&modinfo,0,sizeof(modinfo));
getModuleInformation(hProcess, hMods[i], &modinfo, sizeof(modinfo));
GetModuleFileName( hMods[i], szModName, sizeof(szModName));
PrintLog("%8X %8X %8X %s",(unsigned)modinfo.lpBaseOfDll,(unsigned)modinfo.SizeOfImage,(unsigned)modinfo.EntryPoint,szModName);
}
}
CloseHandle( hProcess );
FreeLibrary(hmSystem);
}
static void StackWalk( size_t pc, size_t bp )
{
PrintLog( "Call stack:" );
PrintLog( "Address Frame Logical addr Module" );
size_t * pFrame;
size_t * pPrevFrame=NULL;
pFrame = (size_t*)bp;
do
{
TCHAR szModule[MAX_PATH] = "";
DWORD section = 0, offset = 0;
if (pc>0x10000)
GetLogicalAddress((PVOID)pc, szModule,sizeof(szModule),section,offset );
else
strcpy(szModule,"NULL");
PrintLog( "%08X %08X %04X:%08X %s",
pc, pFrame, section, offset, szModule );
if ( (size_t)pFrame & 0x80000003 )
break;
if ( (size_t)pFrame <= (size_t)pPrevFrame )
break;
if ( IsBadWritePtr(pFrame, sizeof(PVOID)*2) )
break;
pc = pFrame[1];
if ( IsBadCodePtr((FARPROC) pc) )
break;
pPrevFrame = pFrame;
pFrame = (size_t *)pFrame[0];
} while ( 1 );
}
static void doPrintStackReport( size_t ip, size_t _bp, size_t sp )
{
if (_bp==0) {
#ifdef _AMD64_
PrintLog("inline assembler is not supported in 64bit AMD compiler; StackReport incomplete bp tend to not be used");
#else
__asm {
mov eax,ebp
mov _bp,eax
}
#endif
}
for (unsigned i=0;i<8;i++) {
StringBuffer s;
#ifdef _AMD64_
s.appendf("Stack[%016X]:",sp);
#else
s.appendf("Stack[%08X]:",sp);
#endif
for (unsigned j=0;j<8;j++) {
if ( IsBadReadPtr((const void *)sp, sizeof(unsigned)) )
break;
size_t v = *(size_t *)sp;
sp += sizeof(unsigned);
#ifdef _AMD64_
s.appendf(" %016X",v);
#else
s.appendf(" %08X",v);
#endif
}
PrintLog( "%s",s.str());
}
StackWalk( ip , _bp);
ModuleWalk();
StringBuffer threadlist;
PrintLog( "ThreadList:\n%s",getThreadList(threadlist).str());
}
static void PrintExceptionReport( PEXCEPTION_POINTERS pExceptionInfo)
{
PrintLog("=====================================================");
PrintMemoryStatusLog();
PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
PrintLog( "Exception code: %08X %s",
pExceptionRecord->ExceptionCode,
GetExceptionString(pExceptionRecord->ExceptionCode) );
PrintLog( "Fault address: %08X", pExceptionRecord->ExceptionAddress);
TCHAR szFaultingModule[MAX_PATH];
DWORD section, offset;
GetLogicalAddress( pExceptionRecord->ExceptionAddress,
szFaultingModule,
sizeof( szFaultingModule ),
section, offset );
PrintLog("Fault module: %02X:%08X %s", section, offset, szFaultingModule);
PCONTEXT pCtx = pExceptionInfo->ContextRecord;
PrintLog( "\nRegisters:" );
#ifdef _AMD64_
PrintLog("RAX:%016" I64F "X RBX:%016" I64F "X RCX:%016" I64F "X RDX:%016" I64F "X RSI:%016" I64F "X RDI:%016" I64F "X",
pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx, pCtx->Rsi, pCtx->Rdi );
PrintLog( "CS:RIP:%04X:%016" I64F "X", pCtx->SegCs, pCtx->Rip );
PrintLog( "SS:PSP:%04X:%016" I64F "X PBP:%016" I64F "X",
pCtx->SegSs, pCtx->Rsp, pCtx->Rbp );
#else
PrintLog("EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X",
pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi );
PrintLog( "CS:EIP:%04X:%08X", pCtx->SegCs, pCtx->Eip );
PrintLog( "SS:ESP:%04X:%08X EBP:%08X",
pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
#endif
PrintLog( "DS:%04X ES:%04X FS:%04X GS:%04X",
pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
PrintLog( "Flags:%08X", pCtx->EFlags );
#ifdef _AMD64_
doPrintStackReport(pCtx->Rip, pCtx->Rbp,pCtx->Rsp);
#else
doPrintStackReport(pCtx->Eip, pCtx->Ebp,pCtx->Esp);
#endif
if (SEHtermOnSystemDLLs || SEHtermAlways) {
char *s = szFaultingModule;
while (*s) {
char *sep = strchr(s,'\\');
if (!sep) {
sep = strchr(s,'.');
if (sep)
*sep = 0;
break;
}
s = sep+1;
}
if (SEHtermAlways || (stricmp(s,"ntdll")==0)||(stricmp(s,"kernel32")==0)||(stricmp(s,"msvcrt")==0)||(stricmp(s,"msvcrtd")==0)) {
TerminateProcess(GetCurrentProcess(), 1);
}
}
}
class jlib_thrown_decl CSEHException: public CInterface, public ISEH_Exception
{
public:
IMPLEMENT_IINTERFACE;
CSEHException(unsigned int u, _EXCEPTION_POINTERS* pExp) : errcode((int)u)
{
#ifdef EXTENDED_EXCEPTION_TRACE
PrintExceptionReport(pExp);
#endif
#ifdef ALLREGS
char s[256]; // not too good on stack faults!
sprintf(s,"SEH Exception(%x)\n"
"EAX=%08X EBX=%08X ECX=%08X EDX=%08X ESI=%08X\n"
"EDI=%08X EBP=%08X ESP=%08X EIP=%08X FLG=%08X\n"
"CS=%04X DS=%04X SS=%04X ES=%04X FS=%04X GS=%04X",
u,
pExp->ContextRecord->Eax, pExp->ContextRecord->Ebx,
pExp->ContextRecord->Ecx, pExp->ContextRecord->Edx,
pExp->ContextRecord->Esi, pExp->ContextRecord->Edi,
pExp->ContextRecord->Ebp, pExp->ContextRecord->Esp,
pExp->ContextRecord->Eip, pExp->ContextRecord->EFlags,
pExp->ContextRecord->SegCs, pExp->ContextRecord->SegDs,
pExp->ContextRecord->SegSs, pExp->ContextRecord->SegEs,
pExp->ContextRecord->SegFs, pExp->ContextRecord->SegGs);
#else
char s[80];
#ifdef _AMD64_
sprintf(s,"SEH Exception(%08X) at %04X:%016" I64F "X\n",u,pExp->ContextRecord->SegCs,pExp->ContextRecord->Rip);
#else
sprintf(s,"SEH Exception(%08X) at %04X:%08X\n",u,pExp->ContextRecord->SegCs,pExp->ContextRecord->Eip);
#endif
#endif
msg.set(s);
};
int errorCode() const { return errcode; }
StringBuffer & errorMessage(StringBuffer &str) const { str.append(msg); return str;}
MessageAudience errorAudience() const { return MSGAUD_user; }
static void Translate(unsigned int u, _EXCEPTION_POINTERS* pExp)
{
#ifdef _DEBUG
if (u == 0x80000003) return; // int 3 breakpoints
static CriticalSection crit;
{
CriticalBlock b(crit);
PrintExceptionReport(pExp);
}
#endif
ISEH_Exception *e = new CSEHException(u,pExp);
if (SEHHandler && SEHHandler->fireException(e)) return;
throw(e);
}
protected:
int errcode;
StringAttr msg;
};
#endif
#else
#ifdef LINUX_SIGNAL_EXCEPTION
static IException *sigsegv_exc;
static int excsignal;
class jlib_thrown_decl CSEHException: public CInterface, public ISEH_Exception
{
public:
IMPLEMENT_IINTERFACE;
CSEHException(int signum, const char *s)
{
errcode = signum;
msg.set(s);
};
int errorCode() const { return errcode; }
StringBuffer & errorMessage(StringBuffer &str) const { str.append(msg); return str;}
MessageAudience errorAudience() const { return MSGAUD_user; }
protected:
int errcode;
StringAttr msg;
};
static void throwSigSegV()
{
int childpid = fork();
if (childpid <= 0) { // the child
// generate a coredump on different process
signal(excsignal, SIG_DFL);
raise(excsignal);
return;
}
PROGLOG("Dumping core using child process %d",childpid);
waitpid(childpid, NULL, 0);
if (SEHHandler && SEHHandler->fireException(sigsegv_exc))
return;
throw sigsegv_exc;
}
void excsighandler(int signum, siginfo_t *info, void *extra)
{
static byte nested=0;
if (nested++)
return;
#ifdef NO_LINUX_SEH
signal(SIGSEGV, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGILL, SIG_DFL);
signal(SIGFPE, SIG_DFL);
#endif
StringBuffer s;
#if __WORDSIZE == 64
#define I64X "%016" I64F "X"
ucontext_t *uc = (ucontext_t *)extra;
#ifdef __APPLE__
__int64 ip = uc->uc_mcontext->__ss.__rip;
__int64 sp = uc->uc_mcontext->__ss.__rsp;
#else
__int64 ip = uc->uc_mcontext.gregs[REG_RIP];
__int64 sp = uc->uc_mcontext.gregs[REG_RSP];
#endif
excsignal = signum;
s.appendf("SIG: %s(%d), accessing "I64X", IP="I64X, strsignal(signum),signum, (__int64)info->si_addr, ip);
PROGLOG("================================================");
PROGLOG("Signal: %d %s",signum,strsignal(signum));
PROGLOG("Fault IP: "I64X"", ip);
PROGLOG("Accessing: "I64X"", (unsigned __int64) info->si_addr);
PROGLOG("Registers:" );
PROGLOG("EAX:"I64X" EBX:"I64X" ECX:"I64X" EDX:"I64X" ESI:"I64X" EDI:"I64X"",
#ifdef __APPLE__
(unsigned __int64) uc->uc_mcontext->__ss.__rax, (unsigned __int64)uc->uc_mcontext->__ss.__rbx,
(unsigned __int64) uc->uc_mcontext->__ss.__rcx, (unsigned __int64)uc->uc_mcontext->__ss.__rdx,
(unsigned __int64) uc->uc_mcontext->__ss.__rsi, (unsigned __int64)uc->uc_mcontext->__ss.__rdi);
PROGLOG( "CS:EIP:%04X:"I64X"", ((unsigned) uc->uc_mcontext->__ss.__cs)&0xffff, ip );
PROGLOG( " ESP:"I64X" EBP:"I64X"", sp, (unsigned __int64) uc->uc_mcontext->__ss.__rbp );
#else
(unsigned __int64) uc->uc_mcontext.gregs[REG_RAX], (unsigned __int64)uc->uc_mcontext.gregs[REG_RBX],
(unsigned __int64) uc->uc_mcontext.gregs[REG_RCX], (unsigned __int64) uc->uc_mcontext.gregs[REG_RDX],
(unsigned __int64) uc->uc_mcontext.gregs[REG_RSI], (unsigned __int64) uc->uc_mcontext.gregs[REG_RDI] );
PROGLOG( "CS:EIP:%04X:"I64X"", ((unsigned) uc->uc_mcontext.gregs[REG_CSGSFS])&0xffff, ip );
PROGLOG( " ESP:"I64X" EBP:"I64X"", sp, (unsigned __int64) uc->uc_mcontext.gregs[REG_RBP] );
#endif
for (unsigned i=0;i<8;i++) {
StringBuffer s;
s.appendf("Stack["I64X"]:",sp);
for (unsigned j=0;j<8;j++) {
__int64 v = *(size_t *)sp;
sp += sizeof(unsigned);
s.appendf(" "I64X"",v);
}
PROGLOG( "%s",s.str());
}
#elif defined (__linux__)
ucontext_t *uc = (ucontext_t *)extra;
unsigned ip = uc->uc_mcontext.gregs[REG_EIP];
unsigned sp = uc->uc_mcontext.gregs[REG_ESP];
excsignal = signum;
s.appendf("SIG: %s(%d), accessing %p, IP=%x", strsignal(signum),signum, info->si_addr, ip);
PROGLOG("================================================");
PROGLOG("Signal: %d %s",signum,strsignal(signum));
PROGLOG("Fault IP: %08X", ip);
PROGLOG("Accessing: %08X", info->si_addr);
PROGLOG("Registers:" );
PROGLOG("EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X",
uc->uc_mcontext.gregs[REG_EAX], uc->uc_mcontext.gregs[REG_EBX],
uc->uc_mcontext.gregs[REG_ECX], uc->uc_mcontext.gregs[REG_EDX],
uc->uc_mcontext.gregs[REG_ESI], uc->uc_mcontext.gregs[REG_EDI] );
PROGLOG( "CS:EIP:%04X:%08X", uc->uc_mcontext.gregs[REG_CS], ip );
PROGLOG( "SS:ESP:%04X:%08X EBP:%08X",
uc->uc_mcontext.gregs[REG_SS], sp, uc->uc_mcontext.gregs[REG_EBP] );
for (unsigned i=0;i<8;i++) {
StringBuffer s;
s.appendf("Stack[%08X]:",sp);
for (unsigned j=0;j<8;j++) {
size_t v = *(size_t *)sp;
sp += sizeof(unsigned);
s.appendf(" %08X",v);
}
PROGLOG( "%s",s.str());
}
PROGLOG("Frame:");
unsigned* bp = (unsigned*) (uc->uc_mcontext.gregs[REG_EBP]);
for (unsigned n=0; n<64; n++) {
unsigned * nextbp = (unsigned *) *bp++;
unsigned fip = *bp;
if ((fip < 0x08000000) || (fip > 0x7fffffff) || (nextbp < bp))
break;
PROGLOG("%2d %08X %08X",n+1,fip,(unsigned) bp);
bp = nextbp;
}
#endif
#ifdef _EXECINFO_H
PrintStackReport();
#endif
StringBuffer threadlist;
PROGLOG( "ThreadList:\n%s",getThreadList(threadlist).str());
queryLogMsgManager()->flushQueue(10*1000);
#ifndef NO_LINUX_SEH
void (* _P)() = throwSigSegV;
uc->uc_mcontext.gregs[REG_ESP]-=4;
uc->uc_mcontext.gregs[REG_EIP] = (unsigned)_P;
unsigned *spp = (unsigned *)sp;
*spp = ip;
sigsegv_exc = new CSEHException(signum,s.str());
#else
if (SEHHandler && SEHHandler->fireException(new CSEHException(signum,s.str())))
return;
#endif
nested--;
}
#endif
#endif
void jlib_decl setTerminateOnSEHInSystemDLLs(bool set)
{
SEHtermOnSystemDLLs = set;
}
void jlib_decl setTerminateOnSEH(bool set)
{
SEHtermAlways = set;
}
void *EnableSEHtranslation()
{
#ifdef NOSEH
return NULL;
#else
#ifdef _WIN32
return _set_se_translator( CSEHException::Translate );
#else
UNIMPLEMENTED;
#endif
#endif
}
void jlib_decl *setSEHtoExceptionHandler(IExceptionHandler *handler)
{
#ifdef NOSEH
return NULL;
#endif
void *ret = SEHHandler;
SEHHandler = handler;
return ret;
}
void jlib_decl EnableSEHtoExceptionMapping()
{
#ifdef NOSEH
return;
#endif
if (SEHnested++)
return; // already done
#ifdef _WIN32
enableThreadSEH();
SEHrestore = EnableSEHtranslation();
#else
struct sigaction act;
sigset_t blockset;
sigemptyset(&blockset);
act.sa_mask = blockset;
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = &excsighandler;
sigaction(SIGSEGV, &act, NULL);
sigaction(SIGILL, &act, NULL);
sigaction(SIGBUS, &act, NULL);
sigaction(SIGFPE, &act, NULL);
#endif
}
void jlib_decl DisableSEHtoExceptionMapping()
{
#ifdef NOSEH
return;
#endif
if (--SEHnested)
return;
#ifdef _WIN32
if (SEHrestore) {
void *restore = SEHrestore;
SEHrestore = NULL;
_set_se_translator( (_se_translator_function)restore );
}
#else
signal(SIGSEGV, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGILL, SIG_DFL);
signal(SIGFPE, SIG_DFL);
#endif
}
StringBuffer & formatSystemError(StringBuffer & out, unsigned errcode)
{
#ifdef _WIN32
const char * lpMessageBuffer=NULL;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errcode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default
(LPTSTR) &lpMessageBuffer,
0,
NULL );
if (lpMessageBuffer) {
out.append(lpMessageBuffer);
LocalFree( (void *)lpMessageBuffer );
}
else {
out.append(errcode);
}
#else
int saverr = errno;
errno = 0;
const char *errstr = strerror(errcode);
if (errno==0) {
out.append(errstr);
}
else {
out.append(errcode);
}
errno = saverr;
#endif
return out;
}
IException * deserializeException(MemoryBuffer & in)
{
byte nulle;
in.read(nulle);
if (nulle) return NULL;
int code;
StringAttr text;
in.read(code);
in.read(text);
return MakeStringException(code, "%s", text.get());
}
void jlib_decl serializeException(IException * e, MemoryBuffer & out)
{
if (!e)
out.append((byte)1);
else
{
out.append((byte)0);
StringBuffer text;
out.append(e->errorCode());
out.append(e->errorMessage(text).str());
}
}
void PrintStackReport()
{
#ifdef _WIN32
unsigned onstack=1234;
doPrintStackReport(0, 0,(unsigned)&onstack);
#elif defined(__linux__)
DBGLOG("Backtrace:");
void *btarray[100];
unsigned btn = backtrace (btarray, 100);
char **strings = backtrace_symbols (btarray, btn);
for (unsigned i=0; iflushQueue(10*1000);
}
#ifdef SIGNAL_TO_EXCEPTION
/*static*/jmp_buf SignalToException::s_jmpbuf;
/*static*/bool SignalToException::s_bUnixTrapHandlerSet = false;
SignalToException::SignalToException()
{
if (!s_bUnixTrapHandlerSet)
setUnixTrapHandler();
memcpy(&m_old_jmpbuf,&s_jmpbuf,sizeof(jmp_buf));
}
SignalToException::~SignalToException()
{
memcpy(&s_jmpbuf,&m_old_jmpbuf,sizeof(jmp_buf));
}
/*static*/
void SignalToException::UnixTrapHandler(int sig)
{
longjmp(SignalToException::s_jmpbuf, sig);
}
/*static*/
void SignalToException::setUnixTrapHandler()
{
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = UnixTrapHandler;
int signals[] = {SIGSEGV, SIGILL, SIGFPE, SIGPIPE, SIGSYS};
for (int i = 0; i < sizeof(signals)/sizeof(signals[0]); i++)
if ( sigaction(signals[i], &action, NULL) == -1)
perror("sigaction failed while setting UnixTrapHandler");
s_bUnixTrapHandlerSet = true;
}
/*static*/
void SignalToException::processSetJmpResult(int res)
{
if (res != 0)
{
StringBuffer buf("throwing SIG");
switch (res)
{
case SIGSEGV: buf.append("SEGV");
break;
case SIGILL : buf.append("ILL" );
break;
case SIGFPE : buf.append("FPE" );
break;
case SIGPIPE: buf.append("PIPE");
break;
case SIGSYS : buf.append("SYS" );
break;
default: buf.append("NAL ").append(res);
break;
}
buf.append(" as exception");
throw MakeStringException(res, "%s", buf.toCharArray());
}
}
#ifdef TEST_SIGNAL_TO_EXCEPTION
int main(int argc, char**argv)
{
TRY
{
TRY
{
//generate SIGSEGV
int* p=0;
*p = 0;
cout << "Next stmt in inner block!" << endl;
}
CATCH(...)
{
cout << "inner catch (...)" << endl;
}
ENDCATCH;
//generate SIGFPE
int p=0;
int q=2/p;
cout << "Next stmt in outer block!" << endl;
}
CATCH (char*)
{
}
AND_CATCH(...)
{
cout << "outer catch (...)" << endl;
}
END_CATCH
return 0;
}
#endif //TEST_SIGNAL_TO_EXCEPTION
#endif //SIGNAL_TO_EXCEPTION
#ifdef _TEST
void raise1()
{
throw MakeStringException(-1,"test 1");
}
void raise2()
{
throw MakeOsException(3);
}
class DefaultExceptionHandler
{
unexpected_handler old;
public:
static void handler()
{
try {
throw;
}
catch (IException *e) {
StringBuffer s;
e->errorMessage(s);
printf("Unhandled Exception (%d): %s\n",e->errorCode(),(const char *)s.toCharArray());
e->Release();
}
}
DefaultExceptionHandler() { old = set_terminate(handler); }
~DefaultExceptionHandler() { set_terminate(old); }
} _DefaultExceptionHandler;
int main()
{
try {
raise1();
}
catch (IException *e) {
StringBuffer s;
e->errorMessage(s);
printf("exception %d '%s'\n",e->errorCode(),(const char *)s.toCharArray());
e->Release();
}
try {
raise2();
}
catch (IException *e) {
StringBuffer s;
e->errorMessage(s);
printf("exception %d '%s'\n",e->errorCode(),(const char *)s.toCharArray());
e->Release();
}
raise1();
return 0;
}
#endif