Skip to content

Commit

Permalink
Class interface instrumentation (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
intuibase authored Sep 10, 2024
1 parent 160467c commit 57c3848
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 4 deletions.
33 changes: 30 additions & 3 deletions prod/native/extension/code/InternalFunctionInstrumentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<InstrumentedFunctionHooksStorage_t *>(EAPM_GL(hooksStorage_).get())->store(hash, AutoZval{callableOnEntry}, AutoZval{callableOnExit});

Expand Down Expand Up @@ -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<InstrumentedFunctionHooksStorage_t *>(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<InstrumentedFunctionHooksStorage_t *>(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;
Expand Down
42 changes: 42 additions & 0 deletions prod/native/extension/phpt/tests/instrumentation_interface.phpt
Original file line number Diff line number Diff line change
@@ -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--
<?php
declare(strict_types=1);

interface ImplementationInterface
{
public function testfunc();
}

class Implementation implements ImplementationInterface
{
public function testfunc()
{
echo "TestFunc".PHP_EOL;
}
}


elastic_otel_hook("ImplementationInterface", "testfunc", function () {
echo "*** prehook() called from impl\n";
}, function () {
echo "*** posthook() called from impl\n";
});

$impl = new Implementation;

$impl->testfunc();

echo "Test completed\n";
?>
--EXPECTF--
*** prehook() called from impl
TestFunc
*** posthook() called from impl
Test completed
2 changes: 1 addition & 1 deletion prod/native/libcommon/code/LoggerInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class LoggerInterface {
#define PRsv "%.*s"
#define PRsvArg(strv) static_cast<int>(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);
Expand Down

0 comments on commit 57c3848

Please sign in to comment.