diff --git a/agent/lib_mongodb.c b/agent/lib_mongodb.c index 51e979abd..9b5ca9c99 100644 --- a/agent/lib_mongodb.c +++ b/agent/lib_mongodb.c @@ -108,6 +108,8 @@ void nr_mongodb_get_host_and_port_path_or_id(zval* server, } } +#if ZEND_MODULE_API_NO < ZEND_8_0_X_API_NO \ + || defined OVERWRITE_ZEND_EXECUTE_DATA NR_PHP_WRAPPER(nr_mongodb_operation) { const char* this_klass = "MongoDB\\Operation\\Executable"; zval* collection = NULL; @@ -173,7 +175,169 @@ NR_PHP_WRAPPER(nr_mongodb_operation) { } NR_PHP_WRAPPER_END -void nr_mongodb_enable(TSRMLS_D) { +#else + +NR_PHP_WRAPPER(nr_mongodb_operation_before) { + (void)wraprec; + nr_segment_t* segment = NULL; + segment = nr_segment_start(NRPRG(txn), NULL, NULL); + if (NULL != segment) { + segment->wraprec = auto_segment->wraprec; + } +} +NR_PHP_WRAPPER_END + +NR_PHP_WRAPPER(nr_mongodb_operation_after) { + const char* this_klass = "MongoDB\\Operation\\Executable"; + zval* collection = NULL; + zval* database = NULL; + zval* server = NULL; + zval* this_var = NULL; + bool discard_segment = false; + nr_datastore_instance_t instance = { + .host = NULL, + .port_path_or_id = NULL, + .database_name = NULL, + }; + + // tell the compiler to ignore the cast from const char * to char * + // to save having to do a strdup operation +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + nr_segment_datastore_params_t params = { + .collection = NULL, + .datastore = { + .type = NR_DATASTORE_MONGODB, + }, + .operation = (char *)wraprec->extra, + .instance = &instance, + .callbacks = { + .backtrace = nr_php_backtrace_callback, + }, + }; +#pragma GCC diagnostic pop + /* + * We check for the interface all Collection operations extend, rather than + * their specific class. Not all operations have the properties we need but + * the ones we hook do (as of mongo-php-library v.1.1). + */ + this_var = nr_php_scope_get(NR_EXECUTE_ORIG_ARGS); + if (!nr_php_object_instanceof_class(this_var, this_klass)) { + nrl_verbosedebug(NRL_FRAMEWORK, "%s: operation is not %s", __func__, + this_klass); + discard_segment = true; + goto leave; + } + + collection = nr_php_get_zval_object_property(this_var, "collectionName"); + if (nr_php_is_zval_valid_string(collection)) { + params.collection = Z_STRVAL_P(collection); + } + + database = nr_php_get_zval_object_property(this_var, "databaseName"); + if (nr_php_is_zval_valid_string(database)) { + instance.database_name = Z_STRVAL_P(database); + } + + server = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS); + nr_mongodb_get_host_and_port_path_or_id(server, &instance.host, + &instance.port_path_or_id); + +leave: + if (discard_segment) { + nr_segment_discard(&auto_segment); + } else { + nr_segment_datastore_end(&auto_segment, ¶ms); + } + nr_php_arg_release(&server); + nr_php_scope_release(&this_var); + nr_free(instance.host); + nr_free(instance.port_path_or_id); +} +NR_PHP_WRAPPER_END + +#endif /* OAPI */ + +void nr_mongodb_enable() { +#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ + && !defined OVERWRITE_ZEND_EXECUTE_DATA + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Aggregate::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "aggregate"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\BulkWrite::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "bulkWrite"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Count::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "count"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\CreateIndexes::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "createIndexes"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Delete::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "delete"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Distinct::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "distinct"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\DropCollection::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "dropCollection"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\DropIndexes::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "dropIndexes"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Find::execute"), nr_mongodb_operation_before, + nr_mongodb_operation_after, nr_mongodb_operation_after, "find"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\FindAndModify::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "findAndModify"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\InsertMany::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "insertMany"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\InsertOne::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "insertOne"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\ListIndexes::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "listIndexes"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Update::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "update"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\DatabaseCommand::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "databaseCommand"); + +#else /* Non-OAPI */ + /* * We instrument interesting methods on the MongoDB\Collection class via their * associated MongoDB\Operation classes. @@ -265,6 +429,8 @@ void nr_mongodb_enable(TSRMLS_D) { NR_PSTR("MongoDB\\Operation\\DatabaseCommand::execute"), nr_mongodb_operation, "databaseCommand" TSRMLS_CC); +#endif /* OAPI */ + if (NRINI(vulnerability_management_package_detection_enabled)) { nr_txn_add_php_package(NRPRG(txn), "mongodb/mongodb", PHP_PACKAGE_VERSION_UNKNOWN); diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index daa01bb4f..6631c06d2 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -73,6 +73,27 @@ nruserfn_t* nr_php_wrap_user_function_before_after_clean( return wraprec; } +nruserfn_t* nr_php_wrap_user_function_before_after_clean_extra( + const char* name, + size_t namelen, + nrspecialfn_t before_callback, + nrspecialfn_t after_callback, + nrspecialfn_t clean_callback, + const char* extra) { + nruserfn_t* wraprec = nr_php_wrap_user_function_before_after_clean( + name, namelen, before_callback, after_callback, clean_callback); + + if (nrunlikely(NULL == wraprec)) { + nrl_warning(NRL_INSTRUMENT, "%s: unable to wrap '%s'", __func__, + NRSAFESTR(name)); + return wraprec; + } + + wraprec->extra = extra; + + return wraprec; +} + nruserfn_t* nr_php_wrap_callable_before_after_clean( zend_function* callable, nrspecialfn_t before_callback, diff --git a/agent/php_wrapper.h b/agent/php_wrapper.h index 27e0dba91..058e06e08 100644 --- a/agent/php_wrapper.h +++ b/agent/php_wrapper.h @@ -150,6 +150,14 @@ extern nruserfn_t* nr_php_wrap_callable_before_after_clean( nrspecialfn_t before_callback, nrspecialfn_t after_callback, nrspecialfn_t clean_callback); + +extern nruserfn_t* nr_php_wrap_user_function_before_after_clean_extra( + const char* name, + size_t namelen, + nrspecialfn_t before_callback, + nrspecialfn_t after_callback, + nrspecialfn_t clean_callback, + const char* extra); #endif extern nruserfn_t* nr_php_wrap_user_function(const char* name, size_t namelen, diff --git a/docker-compose.yaml b/docker-compose.yaml index 4626f2523..4d57753ff 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -36,11 +36,6 @@ services: ports: - "11211:11211" container_name: memcached -# mongodb: -# image: mongo:latest -# restart: always -# ports: -# - "27019:27019" postgres: image: postgres restart: always @@ -57,8 +52,6 @@ services: environment: MEMCACHE_HOST: memcached -# MONGO_HOST: mongodb - MYSQL_DB: database MYSQL_USER: admin MYSQL_PASSWD: admin @@ -86,8 +79,6 @@ services: environment: MEMCACHE_HOST: memcached -# MONGO_HOST: mongodb - MYSQL_DB: database MYSQL_USER: admin MYSQL_PASSWD: admin diff --git a/tests/include/config.php b/tests/include/config.php index 8f29f3faf..4cde7cfb2 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -24,14 +24,6 @@ function isset_or($check, $alternate = NULL) $MYSQL_SERVER = $MYSQL_HOST . ":" . $MYSQL_PORT; } -if (class_exists('MongoClient')) { - $MONGO_HOST = isset_or('MONGO_HOST', MongoClient::DEFAULT_HOST); - $MONGO_PORT = isset_or('MONGO_PORT', MongoClient::DEFAULT_PORT); -} else { - $MONGO_HOST = null; - $MONGO_PORT = null; -} - $MEMCACHE_HOST = isset_or('MEMCACHE_HOST', '127.0.0.1'); $MEMCACHE_PORT = isset_or('MEMCACHE_PORT', '11211'); diff --git a/tests/integration/mongo/mongo.inc b/tests/integration/mongo/mongo.inc deleted file mode 100644 index ddc83282f..000000000 --- a/tests/integration/mongo/mongo.inc +++ /dev/null @@ -1,14 +0,0 @@ -close(); -} catch (MongoConnectionException $e) { - die('skip: ' . $e->getMessage() . "\n"); -} diff --git a/tests/integration/mongo/test_execute.php b/tests/integration/mongo/test_execute.php deleted file mode 100644 index 245e92036..000000000 --- a/tests/integration/mongo/test_execute.php +++ /dev/null @@ -1,55 +0,0 @@ - -*/ - -/*INI -*/ - -/*EXPECT_METRICS -[ - "?? agent run id", - "?? start time", - "?? stop time", - [ - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/operation/MongoDB/execute"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/operation/MongoDB/execute", - "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]] - ] -] -*/ - - - - -/*EXPECT_TRACED_ERRORS -null -*/ - -require_once(realpath(dirname(__FILE__ )) . '/mongo.inc'); - -$client = new MongoClient(mongo_server()); -$db = $client->selectDB('test'); -$db->execute("17.0;"); diff --git a/tests/integration/mongo/test_execute_logging_off.php b/tests/integration/mongo/test_execute_logging_off.php deleted file mode 100644 index 29661c7c7..000000000 --- a/tests/integration/mongo/test_execute_logging_off.php +++ /dev/null @@ -1,56 +0,0 @@ - -*/ - -/*INI -newrelic.application_logging.enabled = false -newrelic.application_logging.forwarding.enabled = false -newrelic.application_logging.metrics.enabled = false -*/ - -/*EXPECT_METRICS -[ - "?? agent run id", - "?? start time", - "?? stop time", - [ - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/operation/MongoDB/execute"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/operation/MongoDB/execute", - "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Forwarding/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Metrics/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]] - ] -] -*/ - - -/*EXPECT_TRACED_ERRORS -null -*/ - -require_once(realpath(dirname(__FILE__ )) . '/mongo.inc'); - -$client = new MongoClient(mongo_server()); -$db = $client->selectDB('test'); -$db->execute("17.0;"); diff --git a/tests/integration/mongo/test_find.php b/tests/integration/mongo/test_find.php deleted file mode 100644 index 00d023364..000000000 --- a/tests/integration/mongo/test_find.php +++ /dev/null @@ -1,71 +0,0 @@ - -*/ - -/*INI -*/ - -/*EXPECT_METRICS -[ - "?? agent run id", - "?? start time", - "?? stop time", - [ - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/operation/MongoDB/find"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/statement/MongoDB/test.produce/find"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/statement/MongoDB/test.produce/find", - "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]] - ] -] -*/ - - - - -/*EXPECT_TRACED_ERRORS -null -*/ - -require_once(realpath (dirname ( __FILE__ )) . '/mongo.inc'); - -/* See http://php.net/manual/en/mongocollection.find.php */ -function test_find($db) { - $produce = new MongoCollection($db, 'produce'); - $fruitQuery = array('Type' => 'Fruit'); - $cursor = $produce->find($fruitQuery); - foreach ($cursor as $doc) { - var_dump($doc); - } -} - -function main() { - $client = new MongoClient(mongo_server()); - $db = $client->selectDB('test'); - - test_find($db); -} - -main();