Skip to content

Commit

Permalink
Fixed error handler (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
intuibase authored Jun 5, 2024
1 parent 5c9c1ad commit 2e0f7c0
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 150 deletions.
123 changes: 24 additions & 99 deletions prod/native/extension/code/Hooking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@
* under the License.
*/


#include "Hooking.h"


#include "ModuleGlobals.h"

#include "PhpBridge.h"
Expand All @@ -33,81 +31,13 @@
#include "RequestScope.h"
#include "os/OsUtils.h"

#include <main/php_version.h>
#include <Zend/zend_API.h>
#include <Zend/zend_execute.h>
#include <Zend/zend_observer.h>

namespace elasticapm::php {


// void elastic_throw_exception_hook(zend_object *ex) {
// }

// #if PHP_VERSION_ID < 80000
// void elastic_apm_error_cb(int type, const char *error_filename, const Hooking::zend_error_cb_lineno_t error_lineno, const char *format, va_list args) { //<8.0
// #elif PHP_VERSION_ID < 80100
// void elastic_apm_error_cb(int type, const char *error_filename, const uint32_t error_lineno, zend_string *message) { // 8.0
// #else
// void elastic_apm_error_cb(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message) { // 8.1+
// #endif
// using namespace std::string_view_literals;

// ELOG_DEBUG(ELASTICAPM_G(globals)->logger_, __FUNCTION__);

// // if (ELASTICAPM_G(captureErrors)) {
// // #if PHP_VERSION_ID < 80100
// // ELASTICAPM_G(lastErrorData) = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? error_filename : ""sv, error_lineno, message ? std::string_view{ZSTR_VAL(message), ZSTR_LEN(message)} : ""sv);
// // #else
// // ELASTICAPM_G(lastErrorData) = nullptr;
// // ELASTICAPM_G(lastErrorData) = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? std::string_view{ZSTR_VAL(error_filename), ZSTR_LEN(error_filename)} : ""sv, error_lineno, message ? std::string_view{ZSTR_VAL(message), ZSTR_LEN(message)} : ""sv);
// // #endif

// // }

// auto original = Hooking::getInstance().getOriginalZendErrorCb();
// if (original == elastic_apm_error_cb) {
// ELOG_CRITICAL(ELASTICAPM_G(globals)->logger_, "originalZendErrorCallback == elasticApmZendErrorCallback dead loop detected");
// return;
// }

// if (original) {
// ELOG_DEBUG(ELASTICAPM_G(globals)->logger_, "elastic_apm_error_cb calling original error_cb %p", original);
// original(type, error_filename, error_lineno, message);
// } else {
// ELOG_DEBUG(ELASTICAPM_G(globals)->logger_, "elastic_apm_error_cb missing original error_cb");
// }
// }

// static void elastic_execute_internal(INTERNAL_FUNCTION_PARAMETERS) {

// zend_try {
// if (Hooking::getInstance().getOriginalExecuteInternal()) {
// Hooking::getInstance().getOriginalExecuteInternal()(INTERNAL_FUNCTION_PARAM_PASSTHRU);
// } else {
// execute_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
// }
// } zend_catch {
// ELASTIC_APM_LOG_DIRECT_DEBUG("%s: original call error; parent PID: %d", __FUNCTION__, static_cast<int>(elasticapm::osutils::getParentProcessId()));
// } zend_end_try();

// // ELASTICAPM_G(globals)->inferredSpans_->attachBacktraceIfInterrupted();
// }

// static void elastic_interrupt_function(zend_execute_data *execute_data) {
// ELOG_DEBUG(EAPM_GL(logger_), "%s: interrupt; parent PID: %d", __FUNCTION__, static_cast<int>(elasticapm::osutils::getParentProcessId()));

// // ELASTICAPM_G(globals)->inferredSpans_->attachBacktraceIfInterrupted();

// zend_try {
// if (Hooking::getInstance().getOriginalZendInterruptFuncption()) {
// Hooking::getInstance().getOriginalZendInterruptFunction()(execute_data);
// }
// }
// zend_catch {
// ELOG_DEBUG(EAPM_GL(logger_), "%s: original call error; parent PID: %d", __FUNCTION__, static_cast<int>(elasticapm::osutils::getParentProcessId()));
// }
// zend_end_try();
// }

#if PHP_VERSION_ID < 80100
void elastic_observer_error_cb(int type, const char *error_filename, uint32_t error_lineno, zend_string *message) {
std::string_view fileName = error_filename ? std::string_view{error_filename} : std::string_view{};
Expand All @@ -117,45 +47,40 @@ void elastic_observer_error_cb(int type, zend_string *error_filename, uint32_t e
#endif
std::string_view msg = message && ZSTR_VAL(message) ? std::string_view{ZSTR_VAL(message), ZSTR_LEN(message)} : std::string_view{};

ELOG_DEBUG(ELASTICAPM_G(globals)->logger_, "elastic_observer_error_cb currentED: %p", EG(current_execute_data));
ELOG_DEBUG(ELASTICAPM_G(globals)->logger_, "elastic_observer_error_cb type: %d, fn: " PRsv ":%d, msg: " PRsv " ED: %p", type, PRsvArg(fileName), error_lineno, PRsvArg(msg), EG(current_execute_data));
static bool errorHandling = false;
if (errorHandling) {
ELOG_WARNING(ELASTICAPM_G(globals)->logger_, "elastic_observer_error_cb detected error handler loop, skipping error handler");
return;
}

// we're looking if function (inside which error was thrown) is instrumented - if yes, w're skipping default error instrumentation and letting post hook to handler error.
if (EG(current_execute_data)) {
//TODO we will miss exception if there is no posthook!
auto hash = getClassAndFunctionHashFromExecuteData(EG(current_execute_data));

auto [cls, fun] = getClassAndFunctionName(EG(current_execute_data));

ELOG_DEBUG(ELASTICAPM_G(globals)->logger_, "elastic_observer_error_cb currentED: %p currentEXception: %p hash: 0x%X " PRsv "::" PRsv, EG(current_execute_data),EG(exception), hash, PRsvArg(cls), PRsvArg(fun));


auto callbacks = reinterpret_cast<InstrumentedFunctionHooksStorage_t *>(EAPM_GL(hooksStorage_).get())->find(hash);
if (callbacks) {
ELOG_DEBUG(ELASTICAPM_G(globals)->logger_, "elastic_observer_error_cb type: %d, fn: " PRsv ":%d, msg: " PRsv ". Skipping default error instrumentation because function is instrumented and error will be passed to posthook", type, PRsvArg(fileName), error_lineno, PRsvArg(msg));
return;
if (hash) {
if (ELASTICAPM_G(globals)->logger_ && ELASTICAPM_G(globals)->logger_->doesMeetsLevelCondition(LogLevel::logLevel_debug)) {
auto [cls, fun] = getClassAndFunctionName(EG(current_execute_data));
ELOG_DEBUG(ELASTICAPM_G(globals)->logger_, "elastic_observer_error_cb currentED: %p currentEXception: %p hash: 0x%X " PRsv "::" PRsv, EG(current_execute_data), EG(exception), hash, PRsvArg(cls), PRsvArg(fun));
}

auto callbacks = reinterpret_cast<InstrumentedFunctionHooksStorage_t *>(EAPM_GL(hooksStorage_).get())->find(hash);
if (callbacks) {
ELOG_DEBUG(ELASTICAPM_G(globals)->logger_, "elastic_observer_error_cb type: %d, fn: " PRsv ":%d, msg: " PRsv ". Skipping default error instrumentation because function is instrumented and error will be passed to posthook", type, PRsvArg(fileName), error_lineno, PRsvArg(msg));
return;
}
} else {
ELOG_WARNING(ELASTICAPM_G(globals)->logger_, "elastic_observer_error_cb currentED: %p currentEXception: %p func null, msg: " PRsv, EG(current_execute_data), EG(exception), PRsvArg(msg));
}
}

ELOG_DEBUG(ELASTICAPM_G(globals)->logger_, "elastic_observer_error_cb type: %d, fn: " PRsv ":%d, msg: " PRsv, type, PRsvArg(fileName), error_lineno, PRsvArg(msg));
static bool errorHandling = false;
if (errorHandling) {
ELOG_WARNING(ELASTICAPM_G(globals)->logger_, "elastic_observer_error_cb detected error handler loop, skipping error handler");
return;
}

errorHandling = true;
ELASTICAPM_G(globals)->requestScope_->handleError(type, fileName, error_lineno, msg);
errorHandling = false;

}

void Hooking::replaceHooks() {
// zend_execute_internal = elastic_execute_internal;
// zend_interrupt_function = elastic_interrupt_function;
// zend_error_cb = elastic_apm_error_cb;
// zend_throw_exception_hook = elastic_throw_exception_hook;

zend_observer_error_register(elastic_observer_error_cb);

zend_observer_error_register(elastic_observer_error_cb);
}

}
} // namespace elasticapm::php
49 changes: 0 additions & 49 deletions prod/native/extension/code/Hooking.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,75 +19,26 @@

#pragma once

#include <main/php_version.h>
#include <Zend/zend_execute.h>
#include <Zend/zend_exceptions.h>
#include <Zend/zend_types.h>

#include <optional>

namespace elasticapm::php {

class Hooking {
public:

#if PHP_VERSION_ID < 80100
using zend_error_cb_t = void (*)(int type, const char *error_filename, const uint32_t error_lineno, zend_string *message); // 8.0
#else
using zend_error_cb_t = void (*)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message); // 8.1+
#endif
using zend_throw_exception_hook_t = void(*)(zend_object *ex);


// using zend_execute_internal_t = void (*)(zend_execute_data *execute_data, zval *return_value);
using zend_interrupt_function_t = void (*)(zend_execute_data *execute_data);

static Hooking &getInstance() {
static Hooking instance;
return instance;
}

void fetchOriginalHooks() {
// original_execute_internal_ = zend_execute_internal;
// original_zend_interrupt_function_ = zend_interrupt_function;
// original_zend_error_cb_ = zend_error_cb;
// original_zend_throw_exception_hook_ = zend_throw_exception_hook;
}

void restoreOriginalHooks() {
// zend_execute_internal = original_execute_internal_;
// zend_interrupt_function = original_zend_interrupt_function_;
// zend_error_cb = original_zend_error_cb_;
// zend_throw_exception_hook = original_zend_throw_exception_hook_;
}

void replaceHooks();

// zend_execute_internal_t getOriginalExecuteInternal() {
// return original_execute_internal_;
// }

// zend_interrupt_function_t getOriginalZendInterruptFunction() {
// return original_zend_interrupt_function_;
// }

// zend_error_cb_t getOriginalZendErrorCb() {
// return original_zend_error_cb_;
// }

// zend_throw_exception_hook_t getOriginalZendThrowExceptionHook() {
// return original_zend_throw_exception_hook_;
// }

private:
Hooking(Hooking const &) = delete;
void operator=(Hooking const &) = delete;
Hooking() = default;

// zend_execute_internal_t original_execute_internal_ = nullptr;
// zend_interrupt_function_t original_zend_interrupt_function_ = nullptr;
// zend_error_cb_t original_zend_error_cb_ = nullptr;
// zend_throw_exception_hook_t original_zend_throw_exception_hook_ = nullptr;
};

} // namespace elasticapm::php
2 changes: 1 addition & 1 deletion prod/native/libcommon/code/RequestScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class RequestScope {
}

bool handleError(int type, std::string_view errorFilename, uint32_t errorLineno, std::string_view message) {
ELOG_INFO(log_, "handleError type: %d fn: %s:%d msg: %s\n", type, errorFilename.data(), errorLineno, message.data());
ELOG_INFO(log_, "RequestScope::handleError type: %d fn: %s:%d msg: %s\n", type, errorFilename.data(), errorLineno, message.data());

bridge_->callPHPSideErrorHandler(type, errorFilename, errorLineno, message);

Expand Down
2 changes: 1 addition & 1 deletion prod/native/libphpbridge/code/Helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ std::string_view zvalToStringView(zval *zv) {
}

zend_ulong getClassAndFunctionHashFromExecuteData(zend_execute_data *execute_data) {
if (!execute_data || !execute_data->func->common.function_name) {
if (!execute_data || !execute_data->func || !execute_data->func->common.function_name) {
return 0;
}

Expand Down

0 comments on commit 2e0f7c0

Please sign in to comment.