zend_observer_fcall_handlers elasticRegisterObserver()

in prod/native/extension/code/InternalFunctionInstrumentation.cpp [420:489]


zend_observer_fcall_handlers elasticRegisterObserver(zend_execute_data *execute_data) {
    if (execute_data->func->common.type == ZEND_INTERNAL_FUNCTION) {
        return {nullptr, nullptr};
    }

    auto hash = getClassAndFunctionHashFromExecuteData(execute_data);
    if (hash == 0) {
        ELOGF_TRACE(EAPM_GL(logger_), INSTRUMENTATION, "elasticRegisterObserver main scope");
        return {nullptr, nullptr};
    }

    auto callbacks = reinterpret_cast<InstrumentedFunctionHooksStorage_t *>(EAPM_GL(hooksStorage_).get())->find(hash);
    if (!callbacks) {
        if (EAPM_GL(logger_)->doesMeetsLevelCondition(LogLevel::logLevel_trace)) {
            auto [cls, func] = getClassAndFunctionName(execute_data);
            ELOGF_TRACE(EAPM_GL(logger_), INSTRUMENTATION, "elasticRegisterObserver hash: 0x%X " PRsv "::" PRsv ", not marked to be instrumented", hash, PRsvArg(cls), PRsvArg(func));
        }

        // lookup for class interfaces
        auto ce = execute_data->func->common.scope;
        if (ce) {
            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);
                        ELOGF_TRACE(EAPM_GL(logger_), INSTRUMENTATION, "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 (EAPM_GL(config_)->get().debug_instrument_all && EAPM_GL(requestScope_)->isFunctional()) {
        std::string_view filename(ZSTR_VAL(execute_data->func->op_array.filename), ZSTR_LEN(execute_data->func->op_array.filename));
        if (!(execute_data->func->common.fn_flags & ZEND_ACC_CLOSURE) && filename.find("/ElasticOTel/") == std::string_view::npos && filename.find("/open-telemetry/") == std::string_view::npos) {
            callbacks = reinterpret_cast<InstrumentedFunctionHooksStorage_t *>(EAPM_GL(hooksStorage_).get())->storeFront(hash, AutoZval("Elastic\\OTel\\PhpPartFacade::debugPreHook"sv), AutoZval("Elastic\\OTel\\PhpPartFacade::debugPostHook"));
        }
    }

    if (!callbacks) {
        return {nullptr, nullptr};
    }

    bool havePreHook = false;
    bool havePostHook = false;
    for (auto const &item : *callbacks) {
        if (!item.first.isNull()) {
            havePreHook = true;
        }
        if (!item.second.isNull()) {
            havePostHook = true;
        }
        if (havePreHook && havePostHook) {
            break;
        }
    }
    ELOGF_TRACE(EAPM_GL(logger_), INSTRUMENTATION, "elasticRegisterObserver hash: 0x%X, havePreHooks: %d havePostHooks: %d", hash, havePreHook, havePostHook);

    return {havePreHook ? elasticObserverFcallBeginHandler : nullptr, havePostHook ? elasticObserverFcallEndHandler : nullptr};
}