in prod/native/extension/code/InternalFunctionInstrumentation.cpp [300:369]
bool instrumentFunction(LoggerInterface *log, std::string_view cName, std::string_view fName, zval *callableOnEntry, zval *callableOnExit) {
//TODO if called from other place that MINIT - make it thread safe in ZTS
//TODO use hash struct instead of combined to prevent collisions
std::string className{cName.data(), cName.length()};
std::string functionName{fName.data(), fName.length()};
std::transform(className.begin(), className.end(), className.begin(), [](unsigned char c){ return std::tolower(c); });
std::transform(functionName.begin(), functionName.end(), functionName.begin(), [](unsigned char c){ return std::tolower(c); });
HashTable *table = nullptr;
zend_ulong classHash = 0;
if (className.empty()) { // looking for function
table = EG(function_table);
} else {
if (!EG(class_table)) {
ELOGF_DEBUG(log, INSTRUMENTATION, "instrumentFunction Class table is empty. Function " PRsv "::" PRsv " not found and cannot be instrumented.", PRsvArg(className), PRsvArg(functionName));
return false;
}
auto ce = static_cast<zend_class_entry *>(zend_hash_str_find_ptr(EG(class_table), className.data(), className.length()));
if (!ce) {
ELOGF_DEBUG(log, INSTRUMENTATION, "instrumentFunction Class not found. Function " PRsv "::" PRsv " not found and cannot be instrumented.", PRsvArg(className), PRsvArg(functionName));
if (log->doesMeetsLevelCondition(logLevel_trace)) {
zend_string *argStrKey = nullptr;
ZEND_HASH_FOREACH_STR_KEY(EG(class_table), argStrKey) {
if (argStrKey) {
ELOGF_DEBUG(log, INSTRUMENTATION, "instrumentFunction Class not found. Function " PRsv "::" PRsv " not found and cannot be instrumented. %s", PRsvArg(className), PRsvArg(functionName), ZSTR_VAL(argStrKey));
}
}
ZEND_HASH_FOREACH_END();
}
return false;
}
table = &ce->function_table;
classHash = ZSTR_HASH(ce->name);
}
if (!table) {
return false;
}
zend_function *func = reinterpret_cast<zend_function *>(zend_hash_str_find_ptr(table, functionName.data(), functionName.length()));
if (!func) {
ELOGF_DEBUG(log, INSTRUMENTATION, "instrumentFunction " PRsv "::" PRsv " not found and cannot be instrumented.", PRsvArg(className), PRsvArg(functionName));
return false;
}
zend_ulong funcHash = ZSTR_HASH(func->common.function_name);
zend_ulong hash = classHash ^ (funcHash << 1);
ELOGF_DEBUG(log, INSTRUMENTATION, "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});
// we only keep original handler for internal (native) functions
if (func->common.type == ZEND_INTERNAL_FUNCTION) {
if (func->internal_function.handler != internal_function_handler) {
InternalStorage_t::getInstance().store(hash, func->internal_function.handler);
func->internal_function.handler = internal_function_handler;
}
ELOGF_DEBUG(log, INSTRUMENTATION, PRsv "::" PRsv " instrumented, key: 0x%X", PRsvArg(className), PRsvArg(functionName), hash);
}
return true;
}