Files
oceanbase/src/pl/ob_pl_exception_handling.cpp

624 lines
20 KiB
C++

/**
* Copyright (c) 2021 OceanBase
* OceanBase CE is licensed under Mulan PubL v2.
* You can use this software according to the terms and conditions of the Mulan PubL v2.
* You may obtain a copy of Mulan PubL v2 at:
* http://license.coscl.org.cn/MulanPubL-2.0
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PubL v2 for more details.
*/
#define USING_LOG_PREFIX PL
#include "ob_pl_exception_handling.h"
#ifdef OB_BUILD_ORACLE_PL
#include "pl/ob_pl_call_stack_trace.h"
#endif
namespace oceanbase
{
using namespace common;
using namespace jit;
namespace pl
{
_RLOCAL(_Unwind_Exception*, tl_eptr);
ObPLException pre_reserved_e(OB_ALLOCATE_MEMORY_FAILED); //预留的exception空间,防止出现没内存的时候抛不出来exception
void ObPLEH::eh_debug_int64(const char *name_ptr, int64_t name_len, int64_t object)
{
LOG_DEBUG(">>>>>>>>>>0", K(ObString(name_len, name_ptr)), K(object));
}
void ObPLEH::eh_debug_int64ptr(const char *name_ptr, int64_t name_len, const int64_t *object)
{
LOG_DEBUG(">>>>>>>>>>0", K(ObString(name_len, name_ptr)), K(*object));
}
void ObPLEH::eh_debug_int32(const char *name_ptr, int64_t name_len, int32_t object)
{
LOG_DEBUG(">>>>>>>>>>0", K(ObString(name_len, name_ptr)), K(object));
}
void ObPLEH::eh_debug_int32ptr(const char *name_ptr, int64_t name_len, const int32_t *object)
{
LOG_DEBUG(">>>>>>>>>>0", K(ObString(name_len, name_ptr)), K(*object));
}
void ObPLEH::eh_debug_int8(const char *name_ptr, int64_t name_len, const int8_t object)
{
LOG_DEBUG(">>>>>>>>>>0", K(ObString(name_len, name_ptr)), K(object));
}
void ObPLEH::eh_debug_int8ptr(const char *name_ptr, int64_t name_len, const int8_t *object)
{
LOG_DEBUG(">>>>>>>>>>0", K(ObString(name_len, name_ptr)), K(*object));
}
void ObPLEH::eh_debug_obj(const char *name_ptr, int64_t name_len, const ObObj *object)
{
LOG_DEBUG(">>>>>>>>>>0", K(ObString(name_len, name_ptr)), K(*object));
}
void ObPLEH::eh_debug_objparam(const char *name_ptr, int64_t name_len, const ObObjParam *object)
{
LOG_DEBUG(">>>>>>>>>>0", K(ObString(name_len, name_ptr)), K(*object));
}
int ObPLEH::eh_convert_exception(bool oracle_mode, int oberr, ObPLConditionType *type, int64_t *error_code, const char **sql_state, int64_t *str_len)
{
UNUSED(oracle_mode);
int ret = OB_SUCCESS;
if (OB_ISNULL(type) || OB_ISNULL(error_code) || OB_ISNULL(sql_state)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Invalid Argument", K(oberr), K(type), K(error_code), K(sql_state), K(ret));
} else {
*sql_state = ob_sqlstate(oberr);
*str_len = STRLEN(*sql_state);
if (oracle_mode) {
*error_code = oberr;
*type = ERROR_CODE;
} else {
if (OB_SP_RAISE_APPLICATION_ERROR == oberr) {
ObWarningBuffer *wb = NULL;
CK (OB_NOT_NULL(wb = common::ob_get_tsi_warning_buffer()));
OX (*error_code = wb->get_err_code());
OX (*sql_state = wb->get_sql_state());
OX (*str_len = STRLEN(*sql_state));
if (OB_FAIL(ret)) {
} else if (-1 == *error_code) {
*type = SQL_STATE;
} else {
*type = ERROR_CODE;
}
} else {
if (oberr < 0) {
*error_code = ob_mysql_errno(oberr);
if (-1 == *error_code) {
*type = SQL_STATE;
} else {
*type = ERROR_CODE;
}
} else {
*error_code = oberr;
*type = SQL_STATE;
}
}
}
}
return ret;
}
ObPLException::ObPLException(int64_t error_code)
{
body_.exception_class = ObPLEHService::get_exception_class();
body_.exception_cleanup = NULL;
body_.private_1 = 0;
body_.private_2 = 0;
new(&type_)ObPLConditionValue(ERROR_CODE, error_code);
}
#ifdef OB_BUILD_ORACLE_PL
int ObPLEH::eh_adjust_call_stack(ObPLContext &pl_ctx, uint64_t location, int err_code)
{
int ret = OB_SUCCESS;
int64_t stack_depth = 0;
if (lib::is_oracle_mode() && OB_NOT_NULL(pl_ctx.get_call_stack_trace())) {
CK ((stack_depth = pl_ctx.get_exec_stack().count()) > 0);
CK (OB_NOT_NULL(pl_ctx.get_exec_stack().at(stack_depth - 1)));
OX (pl_ctx.get_exec_stack().at(stack_depth - 1)->set_loc(location));
OZ (pl_ctx.get_call_stack_trace()->format_exception_stack(err_code, pl_ctx.get_exec_stack()));
}
return ret;
}
#endif
ObUnwindException *ObPLEH::eh_create_exception(int64_t pl_context,
int64_t pl_function,
int64_t loc,
int64_t allocator,
const ObPLConditionValue *value)
{
ObUnwindException *unwind = NULL;
UNUSED (allocator);
if (NULL != value) {
int ret = OB_SUCCESS;
ObPLContext *pl_ctx = reinterpret_cast<ObPLContext *>(pl_context);
ObPLExecState *frame = NULL;
ObIAllocator *pl_allocator = NULL;
CK (OB_NOT_NULL(pl_ctx));
CK (pl_ctx->get_exec_stack().count() > 0);
CK (OB_NOT_NULL(frame = pl_ctx->get_exec_stack().at(0)));
CK (frame->is_top_call());
CK (OB_NOT_NULL(pl_allocator = &(frame->get_exec_ctx().expr_alloc_)));
if (OB_FAIL(ret)) {
} else if (OB_ALLOCATE_MEMORY_FAILED == value->error_code_) {
unwind = &pre_reserved_e.body_;
} else {
ObPLException *exception
= static_cast<ObPLException *>(pl_allocator->alloc(sizeof(ObPLException)));
if (NULL != exception) {
exception = new(exception)ObPLException();
unwind = &exception->body_;
unwind->exception_class = ObPLEHService::get_exception_class();
unwind->exception_cleanup = NULL;
exception->type_.type_ = value->type_;
exception->type_.error_code_ = value->error_code_;
if (NULL == value->sql_state_ || 0 == value->str_len_) {
exception->type_.sql_state_ = value->sql_state_;
} else {
char* str = static_cast<char*>(pl_allocator->alloc(value->str_len_));
if (NULL != str) {
STRNCPY(str, value->sql_state_, value->str_len_);
exception->type_.sql_state_ = str;
} else {
pl_allocator->free(exception);
unwind = &pre_reserved_e.body_;
}
}
exception->type_.str_len_ = value->str_len_;
exception->type_.stmt_id_ = value->stmt_id_;
exception->type_.signal_ = value->signal_;
} else {
unwind = &pre_reserved_e.body_;
}
}
tl_eptr = unwind;
#ifdef OB_BUILD_ORACLE_PL
CK (OB_NOT_NULL(pl_ctx));
OZ (eh_adjust_call_stack(*pl_ctx, loc,
value->error_code_ < 0
? value->error_code_
:(value->error_code_ & ~(UINT64_MAX << OB_PACKAGE_ID_SHIFT))));
#endif
}
return unwind;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////Runtime Library functions///////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Type_>
uintptr_t ObPLEH::ReadType(const uint8_t *&p)
{
Type_ value;
memcpy(&value, p, sizeof(Type_));
p += sizeof(Type_);
return static_cast<uintptr_t>(value);
}
uintptr_t ObPLEH::readULEB128(const uint8_t **data)
{
uintptr_t result = 0;
uintptr_t shift = 0;
unsigned char byte;
const uint8_t *p = *data;
do {
byte = *p++;
result |= (byte & 0x7f) << shift;
shift += 7;
}
while (byte & 0x80);
*data = p;
return result;
}
uintptr_t ObPLEH::readSLEB128(const uint8_t **data)
{
uintptr_t result = 0;
uintptr_t shift = 0;
unsigned char byte;
const uint8_t *p = *data;
do {
byte = *p++;
result |= (byte & 0x7f) << shift;
shift += 7;
}
while (byte & 0x80);
*data = p;
if ((byte & 0x40) && (shift < (sizeof(result) << 3))) {
result |= (~0ULL << shift);
}
return result;
}
uintptr_t ObPLEH::readEncodedPointer(const uint8_t **data, uint8_t encoding)
{
uintptr_t result = 0;
const uint8_t *p = *data;
if (encoding == DW_EH_PE_omit)
return(result);
// first get value
switch (encoding & 0x0F) {
case DW_EH_PE_absptr:
result = ReadType<uintptr_t>(p);
break;
case DW_EH_PE_uleb128:
result = readULEB128(&p);
break;
// Note: This case has not been tested
case DW_EH_PE_sleb128:
result = readSLEB128(&p);
break;
case DW_EH_PE_udata2:
result = ReadType<uint16_t>(p);
break;
case DW_EH_PE_udata4:
result = ReadType<uint32_t>(p);
break;
case DW_EH_PE_udata8:
result = ReadType<uint64_t>(p);
break;
case DW_EH_PE_sdata2:
result = ReadType<int16_t>(p);
break;
case DW_EH_PE_sdata4:
result = ReadType<int32_t>(p);
break;
case DW_EH_PE_sdata8:
result = ReadType<int64_t>(p);
break;
default:
// not supported
ob_abort();
break;
}
// then add relative offset
switch (encoding & 0x70) {
case DW_EH_PE_absptr:
// do nothing
break;
case DW_EH_PE_pcrel:
result += (uintptr_t)(*data);
break;
case DW_EH_PE_textrel:
case DW_EH_PE_datarel:
case DW_EH_PE_funcrel:
case DW_EH_PE_aligned:
default:
// not supported
ob_abort();
break;
}
// then apply indirection
if (0 != (encoding & DW_EH_PE_indirect)) {
result = *((uintptr_t*)result);
}
*data = p;
return result;
}
unsigned ObPLEH::getEncodingSize(uint8_t Encoding)
{
if (Encoding == DW_EH_PE_omit)
return 0;
switch (Encoding & 0x0F) {
case DW_EH_PE_absptr:
return sizeof(uintptr_t);
case DW_EH_PE_udata2:
return sizeof(uint16_t);
case DW_EH_PE_udata4:
return sizeof(uint32_t);
case DW_EH_PE_udata8:
return sizeof(uint64_t);
case DW_EH_PE_sdata2:
return sizeof(int16_t);
case DW_EH_PE_sdata4:
return sizeof(int32_t);
case DW_EH_PE_sdata8:
return sizeof(int64_t);
default:
// not supported
ob_abort();
}
return 0;
}
bool ObPLEH::handleActionValue(int64_t *resultAction,
uint8_t TTypeEncoding,
const uint8_t *ClassInfo,
uintptr_t actionEntry,
uint64_t exceptionClass,
struct _Unwind_Exception *exceptionObject)
{
bool ret = false;
if (!resultAction || !exceptionObject || exceptionClass != ObPLEHService::get_exception_class())
return(ret);
ObPLException *excp = (ObPLException*)(((char*) exceptionObject) + ObPLEHService::get_exception_base_offset());
ObPLConditionValue &condition_value = excp->type_;
const uint8_t *actionPos = (uint8_t*) actionEntry,
*tempActionPos;
int64_t typeOffset = 0;
int64 actionOffset = 0;
int64_t precedence = MAX_TYPE;
for (int i = 0; true; ++i) {
// Each emitted dwarf action corresponds to a 2 tuple of
// type info address offset, and action offset to the next
// emitted action.
typeOffset = readSLEB128(&actionPos);
tempActionPos = actionPos;
actionOffset = readSLEB128(&tempActionPos);
assert((typeOffset >= 0) && "handleActionValue(...):filters are not supported.");
// Note: A typeOffset == 0 implies that a cleanup llvm.eh.selector
// argument has been matched.
if (typeOffset > 0) {
unsigned EncSize = getEncodingSize(TTypeEncoding);
const uint8_t *EntryP = ClassInfo - typeOffset * EncSize;
uintptr_t P = readEncodedPointer(&EntryP, TTypeEncoding);
ObPLConditionValue *ThisClassInfo = reinterpret_cast<ObPLConditionValue*>(P);
int64_t cur_pre = 0;
if (OB_SUCCESS !=match_action_value(ThisClassInfo, &condition_value, cur_pre)) {
LOG_WARN("Bug: Failed to match action value", K(ThisClassInfo), K(condition_value), K(ret));
} else if (cur_pre < 0) {
/*do nothing*/
} else if (cur_pre < precedence) {
precedence = cur_pre;
*resultAction = i + 1;
ret = true;
break; //这里其实不应break,应该寻找precedence最高的,但是我们在前面CG阶段已经把condition已经按precedence排过序了,这里可以break提升效率
} else { /*do nothing*/ }
}
if (!actionOffset)
break;
actionPos += actionOffset;
}
return ret;
}
int ObPLEH::match_action_value(const ObPLConditionValue *action, const ObPLConditionValue *exception, int64_t &precedence)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(action) || OB_ISNULL(exception)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("parse_tree is NULL", K(action), K(exception), K(ret));
} else if (ERROR_CODE != exception->type_ && SQL_STATE != exception->type_) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Unexpected exception type", K(exception->type_), K(ret));
} else {
switch (action->type_) {
case ERROR_CODE: {
precedence = action->error_code_ == exception->error_code_ ? ERROR_CODE : INVALID_TYPE;
break;
}
case SQL_STATE: {
precedence = exception->str_len_ == action->str_len_ ? (0 == STRNCMP(action->sql_state_, exception->sql_state_, exception->str_len_) ? SQL_STATE : INVALID_TYPE) : INVALID_TYPE;
break;
}
case SQL_EXCEPTION: {
precedence = (eh_classify_exception(exception->sql_state_) == SQL_EXCEPTION) && !is_internal_error(exception->error_code_) ? SQL_EXCEPTION : INVALID_TYPE;
break;
}
case SQL_WARNING: {
precedence = eh_classify_exception(exception->sql_state_) == SQL_WARNING ? SQL_WARNING : INVALID_TYPE;
break;
}
case NOT_FOUND: {
precedence = eh_classify_exception(exception->sql_state_) == NOT_FOUND ? NOT_FOUND : INVALID_TYPE;
break;
}
case OTHERS: {
if (ERROR_CODE == exception->type_) {
precedence = is_internal_error(exception->error_code_) ? INVALID_TYPE : OTHERS;
} else {
precedence = OTHERS;
}
break;
}
default: {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Unexpected exception type", K(action->type_), K(ret));
break;
}
}
}
return ret;
}
bool ObPLEH::is_internal_error(int error_code)
{
// these error code is oceanbase inner error, should not catched by exception handler.
return OB_TRY_LOCK_ROW_CONFLICT == error_code
|| OB_ERR_UNEXPECTED == error_code
|| OB_ALLOCATE_MEMORY_FAILED == error_code
|| OB_ERR_DEFENSIVE_CHECK == error_code
|| OB_TRANS_XA_BRANCH_FAIL == error_code
|| OB_TRANS_SQL_SEQUENCE_ILLEGAL == error_code
|| OB_ERR_SESSION_INTERRUPTED == error_code
|| OB_ERR_QUERY_INTERRUPTED == error_code
|| OB_TIMEOUT == error_code
|| OB_TRANS_TIMEOUT == error_code
|| OB_TRANS_STMT_TIMEOUT == error_code
|| OB_EAGAIN == error_code
|| OB_NOT_MASTER == error_code
|| OB_SNAPSHOT_DISCARDED == error_code;
}
ObPLConditionType ObPLEH::eh_classify_exception(const char *sql_state)
{
ObPLConditionType type = INVALID_TYPE;
if (NULL != sql_state) {
if ('0' == sql_state[0] && '0' == sql_state[1]) {
type = INVALID_TYPE;
} else if ('0' == sql_state[0] && '1' == sql_state[1]) {
type = SQL_WARNING;
} else if ('0' == sql_state[0] && '2' == sql_state[1]) {
type = NOT_FOUND;
} else {
type = SQL_EXCEPTION;
}
}
return type;
}
_Unwind_Reason_Code ObPLEH::handleLsda(int version,
const uint8_t *lsda,
_Unwind_Action actions,
_Unwind_Exception_Class exceptionClass,
struct _Unwind_Exception *exceptionObject,
struct _Unwind_Context *context)
{
UNUSED(version);
_Unwind_Reason_Code ret = _URC_CONTINUE_UNWIND;
if (NULL != lsda) {
uintptr_t pc = _Unwind_GetIP(context)-1;
uintptr_t funcStart = _Unwind_GetRegionStart(context);
uintptr_t pcOffset = pc - funcStart;
const uint8_t *ClassInfo = NULL;
uint8_t lpStartEncoding = *lsda++;
if (lpStartEncoding != DW_EH_PE_omit) {
readEncodedPointer(&lsda, lpStartEncoding);
}
uint8_t ttypeEncoding = *lsda++;
uintptr_t classInfoOffset;
if (ttypeEncoding != DW_EH_PE_omit) {
classInfoOffset = readULEB128(&lsda);
ClassInfo = lsda + classInfoOffset;
}
uint8_t callSiteEncoding = *lsda++;
uint32_t callSiteTableLength = static_cast<uint32_t>(readULEB128(&lsda));
const uint8_t *callSiteTableStart = lsda;
const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;
const uint8_t *actionTableStart = callSiteTableEnd;
const uint8_t *callSitePtr = callSiteTableStart;
while (callSitePtr < callSiteTableEnd) {
uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding);
uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding);
uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding);
// Note: Action value
uintptr_t actionEntry = readULEB128(&callSitePtr);
if (exceptionClass != ObPLEHService::get_exception_class()) {
// We have been notified of a foreign exception being thrown,
// and we therefore need to execute cleanup landing pads
actionEntry = 0;
}
if (0 == landingPad) {
continue; // no landing pad for this entry
}
if (0 != actionEntry) {
actionEntry += ((uintptr_t) actionTableStart) - 1;
}
bool exceptionMatched = false;
if ((start <= pcOffset) && (pcOffset < (start + length))) {
int64_t actionValue = 0;
if (0 != actionEntry) {
exceptionMatched = handleActionValue(&actionValue,
ttypeEncoding,
ClassInfo,
actionEntry,
exceptionClass,
exceptionObject);
}
if (!(actions & _UA_SEARCH_PHASE)) {
// Found landing pad for the PC.
// Set Instruction Pointer to so we re-enter function
// at landing pad. The landing pad is created by the
// compiler to take two parameters in registers.
_Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject);
// Note: this virtual register directly corresponds
// to the return of the llvm.eh.selector intrinsic
if (!actionEntry || !exceptionMatched) {
// We indicate cleanup only
_Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
} else {
// Matched type info index of llvm.eh.selector intrinsic
// passed here.
_Unwind_SetGR(context, __builtin_eh_return_data_regno(1), actionValue);
}
// To execute landing pad set here
_Unwind_SetIP(context, funcStart + landingPad);
ret = _URC_INSTALL_CONTEXT;
} else if (exceptionMatched) {
ret = _URC_HANDLER_FOUND;
} else {
// Note: Only non-clean up handlers are marked as
// found. Otherwise the clean up handlers will be
// re-found and executed during the clean up
// phase.
}
break;
}
}
}
return ret;
}
_Unwind_Reason_Code ObPLEH::eh_personality(int version, _Unwind_Action actions,
_Unwind_Exception_Class exceptionClass,
ObUnwindException *exceptionObject,
struct _Unwind_Context *context)
{
const uint8_t *lsda = reinterpret_cast<const uint8_t *>(_Unwind_GetLanguageSpecificData(context));
LOG_DEBUG(">>>>>>>>>>0", K(version), K(actions), K(exceptionClass), K(lsda));
return handleLsda(version, lsda, actions, exceptionClass, exceptionObject, context);
}
}
}