From 57c3848c8f588dfa8524f18b1225dc65b3794cc3 Mon Sep 17 00:00:00 2001 From: Pawel Filipczak Date: Tue, 10 Sep 2024 15:24:02 +0200 Subject: [PATCH] Class interface instrumentation (#87) --- .../code/InternalFunctionInstrumentation.cpp | 33 +++++++++++++-- .../phpt/tests/instrumentation_interface.phpt | 42 +++++++++++++++++++ prod/native/libcommon/code/LoggerInterface.h | 2 +- 3 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 prod/native/extension/phpt/tests/instrumentation_interface.phpt diff --git a/prod/native/extension/code/InternalFunctionInstrumentation.cpp b/prod/native/extension/code/InternalFunctionInstrumentation.cpp index b1cfbe3..34d1786 100644 --- a/prod/native/extension/code/InternalFunctionInstrumentation.cpp +++ b/prod/native/extension/code/InternalFunctionInstrumentation.cpp @@ -352,7 +352,7 @@ bool instrumentFunction(LoggerInterface *log, std::string_view cName, std::strin zend_ulong funcHash = ZSTR_HASH(func->common.function_name); zend_ulong hash = classHash ^ (funcHash << 1); - ELOG_DEBUG(log, "instrumentFunction 0x%X " PRsv "::" PRsv " type: %s is instrumented", hash, PRsvArg(className), PRsvArg(functionName), func->common.type == ZEND_INTERNAL_FUNCTION ? "internal" : "user"); + ELOG_DEBUG(log, "instrumentFunction 0x%X " PRsv "::" PRsv " type: %s is marked to be instrumented", hash, PRsvArg(className), PRsvArg(functionName), func->common.type == ZEND_INTERNAL_FUNCTION ? "internal" : "user"); reinterpret_cast(EAPM_GL(hooksStorage_).get())->store(hash, AutoZval{callableOnEntry}, AutoZval{callableOnExit}); @@ -432,11 +432,38 @@ zend_observer_fcall_handlers elasticRegisterObserver(zend_execute_data *execute_ if (!callbacks) { if (EAPM_GL(logger_)->doesMeetsLevelCondition(LogLevel::logLevel_trace)) { auto [cls, func] = getClassAndFunctionName(execute_data); - ELOG_TRACE(EAPM_GL(logger_), "elasticRegisterObserver hash: 0x%X " PRsv "::" PRsv ", not instrumented", hash, PRsvArg(cls), PRsvArg(func)); + ELOG_TRACE(EAPM_GL(logger_), "elasticRegisterObserver hash: 0x%X " PRsv "::" PRsv ", not marked to be instrumented", hash, PRsvArg(cls), PRsvArg(func)); } + + auto ce = execute_data->func->common.scope; + if (!ce) { + return {nullptr, nullptr}; + } + + // lookup for class interfaces + for (uint32_t i = 0; i < ce->num_interfaces; ++i) { + auto classHash = ZSTR_HASH(ce->interfaces[i]->name); + zend_ulong funcHash = ZSTR_HASH(execute_data->func->common.function_name); + zend_ulong ifaceHash = classHash ^ (funcHash << 1); + + callbacks = reinterpret_cast(EAPM_GL(hooksStorage_).get())->find(ifaceHash); + if (callbacks) { + if (EAPM_GL(logger_)->doesMeetsLevelCondition(LogLevel::logLevel_trace)) { + auto [cls, func] = getClassAndFunctionName(execute_data); + ELOG_TRACE(EAPM_GL(logger_), "elasticRegisterObserver hash: 0x%X " PRsv "::" PRsv ", will be instrumented because interface 0x%X '" PRsv "' was marked to be instrumented", hash, PRsvArg(cls), PRsvArg(func), ifaceHash, PRzsArg(ce->interfaces[i]->name)); + } + // copy callbacks from interface storage hash to implementation hash + for (auto &item : *callbacks) { + reinterpret_cast(EAPM_GL(hooksStorage_).get())->store(hash, AutoZval(item.first.get()), AutoZval(item.second.get())); + } + break; + } + } + } + + if (!callbacks) { return {nullptr, nullptr}; } - ELOG_TRACE(EAPM_GL(logger_), "elasticRegisterObserver hash: 0x%X", hash); bool havePreHook = false; bool havePostHook = false; diff --git a/prod/native/extension/phpt/tests/instrumentation_interface.phpt b/prod/native/extension/phpt/tests/instrumentation_interface.phpt new file mode 100644 index 0000000..a1aa46c --- /dev/null +++ b/prod/native/extension/phpt/tests/instrumentation_interface.phpt @@ -0,0 +1,42 @@ +--TEST-- +instrumentation - interface +--ENV-- +ELASTIC_OTEL_LOG_LEVEL_STDERR=info +--INI-- +extension=/elastic/elastic_otel_php.so +elastic_otel.bootstrap_php_part_file={PWD}/includes/bootstrap_mock.inc +--FILE-- +testfunc(); + +echo "Test completed\n"; +?> +--EXPECTF-- +*** prehook() called from impl +TestFunc +*** posthook() called from impl +Test completed diff --git a/prod/native/libcommon/code/LoggerInterface.h b/prod/native/libcommon/code/LoggerInterface.h index e033986..e83f424 100644 --- a/prod/native/libcommon/code/LoggerInterface.h +++ b/prod/native/libcommon/code/LoggerInterface.h @@ -38,7 +38,7 @@ class LoggerInterface { #define PRsv "%.*s" #define PRsvArg(strv) static_cast(strv.length()), strv.data() #define PRcsvArg(str, len) len, str - +#define PRzsArg(strv) ZSTR_LEN(strv), ZSTR_VAL(strv) #define ELOG_CRITICAL(logger, format, ...) do { if (!logger || !logger->doesMeetsLevelCondition(LogLevel::logLevel_critical)) break; logger->printf(LogLevel::logLevel_critical, format, ##__VA_ARGS__); } while(false); #define ELOG_ERROR(logger, format, ...) do { if (!logger || !logger->doesMeetsLevelCondition(LogLevel::logLevel_error)) break; logger->printf(LogLevel::logLevel_error, format, ##__VA_ARGS__); } while(false);