#include "firebird.h" //#include "fb_exception.h" #include #include #include #include #include "gen/iberror.h" #include "../common/classes/alloc.h" namespace { // This logic taken from JRD is bad, bad, bad! // Replace it with attachment-level buffers whenever possible const size_t ENGINE_FAILURE_SPACE = 4096; typedef Firebird::CircularStringsBuffer CircularBuffer; class InterlockedStringsBuffer : public CircularBuffer { public: virtual char* alloc(const char* string, size_t length) { buffer_lock.enter(); char* new_string = CircularBuffer::alloc(string, length); buffer_lock.leave(); return new_string; } private: Firebird::Mutex buffer_lock; }; ISC_STATUS dupStringTemp(const char* s) { const size_t len = strlen(s); char *string = FB_NEW(*getDefaultMemoryPool()) char[len + 1]; memcpy(string, s, len + 1); return (ISC_STATUS)(IPTR)(string); } void fill_status(ISC_STATUS *ptr, ISC_STATUS status, va_list status_args) { // Move in status and clone transient strings *ptr++ = isc_arg_gds; *ptr++ = status; while (true) { const ISC_STATUS type = *ptr++ = va_arg(status_args, ISC_STATUS); if (type == isc_arg_end) break; switch (type) { case isc_arg_cstring: { const size_t len = *ptr++ = va_arg(status_args, ISC_STATUS); char *string = FB_NEW(*getDefaultMemoryPool()) char[len]; const char *temp = reinterpret_cast(va_arg(status_args, ISC_STATUS)); memcpy(string, temp, len); *ptr++ = (ISC_STATUS)(IPTR)(string); break; } case isc_arg_string: case isc_arg_interpreted: { *ptr++ = dupStringTemp(reinterpret_cast(va_arg(status_args, ISC_STATUS))); break; } default: *ptr++ = va_arg(status_args, ISC_STATUS); break; } } } } // namespace namespace Firebird { /********************************* status_exception *******************************/ status_exception::status_exception() throw() : m_strings_permanent(true), m_status_known(false) { memset(m_status_vector, 0, sizeof(m_status_vector)); } status_exception::status_exception(const ISC_STATUS *status_vector, bool permanent) throw() : m_strings_permanent(permanent), m_status_known(status_vector != NULL) { if (m_status_known) { ISC_STATUS *ptr = m_status_vector; while (true) { const ISC_STATUS type = *ptr++ = *status_vector++; if (type == isc_arg_end) break; if (type == isc_arg_cstring) *ptr++ = *status_vector++; *ptr++ = *status_vector++; } } } void status_exception::set_status(const ISC_STATUS *new_vector, bool permanent) throw() { release_vector(); m_status_known = (new_vector != NULL); m_strings_permanent = permanent; if (m_status_known) memcpy(m_status_vector, new_vector, sizeof(m_status_vector)); } void status_exception::release_vector() throw() { if (m_strings_permanent || (!m_status_known)) return; // Free owned strings ISC_STATUS *ptr = m_status_vector; while (true) { const ISC_STATUS type = *ptr++; if (type == isc_arg_end) break; switch (type) { case isc_arg_cstring: ptr++; delete[] reinterpret_cast(*ptr++); break; case isc_arg_string: case isc_arg_interpreted: delete[] reinterpret_cast(*ptr++); break; default: ptr++; break; } } } status_exception::~status_exception() throw() { release_vector(); } void fatal_exception::raiseFmt(const char* format, ...) { va_list args; va_start(args, format); char buffer[1024]; VSNPRINTF(buffer, sizeof(buffer), format, args); buffer[sizeof(buffer) - 1] = 0; va_end(args); throw fatal_exception(buffer); } void status_exception::raise() { throw status_exception(); } void status_exception::raise(const ISC_STATUS *status_vector) { throw status_exception(status_vector, true); } void status_exception::raise(ISC_STATUS status, ...) { va_list args; va_start(args, status); ISC_STATUS_ARRAY temp; fill_status(temp, status, args); va_end(args); throw status_exception(temp, false); } /********************************* system_call_failed ****************************/ system_call_failed::system_call_failed(const char* v_syscall, int v_error_code) : status_exception(0, false), errorCode(v_error_code) { ISC_STATUS temp[] = {isc_arg_gds, isc_sys_request, isc_arg_string, dupStringTemp(v_syscall), SYS_ARG, errorCode, isc_arg_end}; set_status(temp, false); } void system_call_failed::raise(const char* syscall, int error_code) { throw system_call_failed(syscall, error_code); } void system_call_failed::raise(const char* syscall) { #ifdef WIN_NT int error_code = GetLastError(); #else int error_code = errno; #endif throw system_call_failed(syscall, error_code); } /********************************* fatal_exception ********************************/ fatal_exception::fatal_exception(const char* message) : status_exception(0, false) { ISC_STATUS temp[] = {isc_arg_gds, isc_random, isc_arg_string, dupStringTemp(message), isc_arg_end}; set_status(temp, false); } // Moved to the header due to gpre. Gpre non-static can't receive it, but we // want to avoid problems with picky compilers that can't find what(). // We can't link this file into normal gpre. // Keep in sync with the constructor above, please; "message" becomes 4th element // after initialization of status vector. //const char* fatal_exception::what() const throw() //{ // return reinterpret_cast(value()[3]); //} void fatal_exception::raise(const char* message) { throw fatal_exception(message); } /************************** exception handling routines ***************************/ static InterlockedStringsBuffer engine_failures; ISC_STATUS stuff_exception(ISC_STATUS *status_vector, const std::exception& ex, StringsBuffer *sb) throw() { // Note that this function will call unexpected() that will terminate process // if exception appears during status vector serialization if (!sb) sb = &engine_failures; const std::type_info& ex_type = typeid(ex); if (ex_type == typeid(std::bad_alloc)) { *status_vector++ = isc_arg_gds; *status_vector++ = isc_virmemexh; *status_vector++ = isc_arg_end; return isc_virmemexh; } try { const status_exception& c_ex = dynamic_cast(ex); if (c_ex.status_known()) { ISC_STATUS *sv = status_vector; const ISC_STATUS *ptr = c_ex.value(); if (c_ex.strings_permanent()) { // Copy status vector while (true) { const ISC_STATUS type = *sv++ = *ptr++; if (type == isc_arg_end) break; if (type == isc_arg_cstring) *sv++ = *ptr++; *sv++ = *ptr++; } } else { // Move in status and clone transient strings while (true) { const ISC_STATUS type = *sv++ = *ptr++; if (type == isc_arg_end) break; switch (type) { case isc_arg_cstring: { const UCHAR len = *sv++ = *ptr++; char *temp = reinterpret_cast(*ptr++); *sv++ = (ISC_STATUS)(IPTR) (sb->alloc(temp, len)); break; } case isc_arg_string: case isc_arg_interpreted: { char *temp = reinterpret_cast(*ptr++); *sv++ = (ISC_STATUS)(IPTR) (sb->alloc(temp, strlen(temp))); break; } default: *sv++ = *ptr++; break; } } } } return status_vector[1]; } catch (const std::bad_cast&) { } // Other random C++ exceptions char temp[256]; SNPRINTF(temp, sizeof(temp) - 1, "Unexpected C++ exception (class=\"%s\", what()=\"%s\")", ex_type.name(), ex.what()); temp[sizeof(temp) - 1] = 0; *status_vector++ = isc_arg_gds; *status_vector++ = isc_random; *status_vector++ = isc_arg_string; *status_vector++ = (ISC_STATUS)(IPTR) (sb->alloc(temp, strlen(temp))); *status_vector++ = isc_arg_end; return isc_random; } const char* status_string(const char* string) { return status_nstring(string, strlen(string)); } const char* status_nstring(const char* string, size_t length) { return engine_failures.alloc(string, length); } } // namespace Firebird