in prod/native/extension/code/Hooking.cpp [43:150]
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{};
#else
void elastic_observer_error_cb(int type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) {
std::string_view fileName = error_filename ? std::string_view{ZSTR_VAL(error_filename), ZSTR_LEN(error_filename)} : std::string_view{};
#endif
std::string_view msg = message && ZSTR_VAL(message) ? std::string_view{ZSTR_VAL(message), ZSTR_LEN(message)} : std::string_view{};
ELOGF_DEBUG(ELASTICAPM_G(globals)->logger_, HOOKS, "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) {
ELOGF_WARNING(ELASTICAPM_G(globals)->logger_, HOOKS, "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)) {
auto hash = getClassAndFunctionHashFromExecuteData(EG(current_execute_data));
if (hash) {
if (ELASTICAPM_G(globals)->logger_ && ELASTICAPM_G(globals)->logger_->doesMeetsLevelCondition(LogLevel::logLevel_debug)) {
auto [cls, fun] = getClassAndFunctionName(EG(current_execute_data));
ELOGF_DEBUG(ELASTICAPM_G(globals)->logger_, HOOKS, "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) {
ELOGF_DEBUG(ELASTICAPM_G(globals)->logger_, HOOKS, "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 {
ELOGF_WARNING(ELASTICAPM_G(globals)->logger_, HOOKS, "elastic_observer_error_cb currentED: %p currentEXception: %p func null, msg: " PRsv, EG(current_execute_data), EG(exception), PRsvArg(msg));
}
}
errorHandling = true;
ELASTICAPM_G(globals)->requestScope_->handleError(type, fileName, error_lineno, msg, static_cast<bool>(EG(current_execute_data)));
errorHandling = false;
}
static void elastic_interrupt_function(zend_execute_data *execute_data) {
ELOGF_DEBUG(ELASTICAPM_G(globals)->logger_, HOOKS, "%s: interrupt", __FUNCTION__);
ELASTICAPM_G(globals)->inferredSpans_->attachBacktraceIfInterrupted();
zend_try {
if (Hooking::getInstance().getOriginalZendInterruptFunction()) {
Hooking::getInstance().getOriginalZendInterruptFunction()(execute_data);
}
}
zend_catch {
ELOGF_DEBUG(ELASTICAPM_G(globals)->logger_, HOOKS, "%s: original call error", __FUNCTION__);
}
zend_end_try();
}
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 {
ELOGF_DEBUG(ELASTICAPM_G(globals)->logger_, HOOKS, "%s: original call error", __FUNCTION__);
}
zend_end_try();
ELASTICAPM_G(globals)->inferredSpans_->attachBacktraceIfInterrupted();
}
static zend_op_array *elastic_compile_file(zend_file_handle *file_handle, int type) {
std::string_view file = file_handle->opened_path ? std::string_view(ZSTR_VAL(file_handle->opened_path), ZSTR_LEN(file_handle->opened_path)) : std::string_view(ZSTR_VAL(file_handle->filename), ZSTR_LEN(file_handle->filename));
if (ELASTICAPM_G(globals)->dependencyAutoLoaderGuard_->shouldDiscardFileCompilation(file)) {
return nullptr;
}
zend_op_array *ret = nullptr;
zend_try {
if (Hooking::getInstance().getOriginalZendCompileFile()) {
ret = Hooking::getInstance().getOriginalZendCompileFile()(file_handle, type);
}
}
zend_catch {
ELOGF_DEBUG(ELASTICAPM_G(globals)->logger_, HOOKS, "%s: original call error", __FUNCTION__);
ret = nullptr;
}
zend_end_try();
return ret;
}
void Hooking::replaceHooks(bool enableInferredSpansHooks, bool enableDepenecyAutoloaderGuard) {
zend_observer_error_register(elastic_observer_error_cb);
if (enableInferredSpansHooks) {
ELOGF_DEBUG(ELASTICAPM_G(globals)->logger_, HOOKS, "Hooked into zend_execute_internal and zend_interrupt_function");
zend_execute_internal = elastic_execute_internal;
zend_interrupt_function = elastic_interrupt_function;
}
if (enableDepenecyAutoloaderGuard) {
ELOGF_DEBUG(ELASTICAPM_G(globals)->logger_, HOOKS, "Hooked into zend_compile_file, original ptr: %p, new ptr: %p", zend_compile_file, elastic_compile_file);
zend_compile_file = elastic_compile_file;
}
}
} // namespace elasticapm::php