352 lines
10 KiB
PHP
352 lines
10 KiB
PHP
--TEST--
|
|
Memory Leakage Test
|
|
--DESCRIPTION--
|
|
Checks for memory leaks using memory_get_usage(). memory_get_usage() only tracks the memory that is allocated using
|
|
emalloc (which only allocate memory in the memory space allocated for the PHP process).
|
|
--ENV--
|
|
PHPT_EXEC=true
|
|
--SKIPIF--
|
|
<?php require('skipif_versions_old.inc'); ?>
|
|
<?php require('skipif_azure_dw.inc'); ?>
|
|
--FILE--
|
|
<?php
|
|
require_once('MsCommon.inc');
|
|
|
|
const _NUM_PASSES = 20;
|
|
const _NUM_ROWS1 = 10;
|
|
const _NUM_ROWS2 = 15;
|
|
|
|
function memCheck($noPasses, $noRows1, $noRows2, $startStep, $endStep)
|
|
{
|
|
$testName = "Memory Leakage Check";
|
|
|
|
startTest($testName);
|
|
|
|
setup();
|
|
|
|
trace("Execution setup: $noPasses passes over a table with $noRows1 => ".($noRows1 + $noRows2)." rows.\n");
|
|
|
|
// The data added into the table has some UTF-8 characters in it.
|
|
// The fetch functions in the switch block below fail if we don't
|
|
// set the encoding to UTF-8. We can set the UTF-8 option elsewhere
|
|
// (in the options for sqlsrv_fetch for example) but it is easier
|
|
// to simply call connect(array( 'CharacterSet'=>'UTF-8' )).
|
|
$conn1 = AE\connect(array( 'CharacterSet'=>'UTF-8' ));
|
|
setUTF8Data(true);
|
|
|
|
$tableName = 'TC81test';
|
|
AE\createTestTable($conn1, $tableName);
|
|
$noRowsInserted = AE\insertTestRows($conn1, $tableName, $noRows1);
|
|
|
|
// Calibration
|
|
// when fetching DateTime in the test, the DateTime PHP extension is used, and memory is allocated when this
|
|
// is extension is first used. Thus create a new DateTime and release it in the calibration step so it won't
|
|
// appear to be a leak in the testing step.
|
|
$date = new DateTime();
|
|
unset($date);
|
|
$phpLeak = runTest($noPasses, 0, $tableName, $conn1, false, true, 0);
|
|
trace("\n0. Calibration\t - PHP memory leak: $phpLeak bytes\n");
|
|
|
|
// Preliminary Execution
|
|
trace("\nPreliminary Execution:\n");
|
|
$drvLeak = execTest(1, $noRows1, $startStep, $endStep, $tableName, $conn1, false, true, $phpLeak);
|
|
$totalLeak = 0;
|
|
|
|
// Connection & Query
|
|
$start = Max($startStep, 1);
|
|
$end = Min($endStep, 3);
|
|
trace("\nConnection & Direct Query Execution:\n");
|
|
$leak = execTest($noPasses, $noRows1, $start, $end, $tableName, $conn1, false, true, $phpLeak) - $drvLeak;
|
|
if ($leak > $totalLeak) {
|
|
$totalLeak = $leak;
|
|
}
|
|
|
|
trace("\nPrepared Query Execution:\n");
|
|
$start = Max($startStep, 2);
|
|
$leak = execTest($noPasses, $noRows1, $start, $end, $tableName, $conn1, true, true, $phpLeak) - $drvLeak;
|
|
if ($leak > $totalLeak) {
|
|
$totalLeak = $leak;
|
|
}
|
|
|
|
// Execution
|
|
$noRows = $noRows1;
|
|
$start = Max($startStep, 4);
|
|
$end = Min($endStep, 7);
|
|
$prepared = false;
|
|
$release = false;
|
|
for ($j = 0; $j < 8; $j++) {
|
|
switch ($j) {
|
|
case 0:
|
|
$prepared = false;
|
|
$release = true;
|
|
break;
|
|
|
|
case 1:
|
|
$prepared = true;
|
|
$release = true;
|
|
break;
|
|
|
|
case 2:
|
|
$prepared = false;
|
|
$release = false;
|
|
break;
|
|
|
|
case 3:
|
|
$prepared = true;
|
|
$release = false;
|
|
break;
|
|
|
|
case 4:
|
|
AE\insertTestRows($conn1, $tableName, $noRows2);
|
|
$noRows = $noRows1 + $noRows2;
|
|
$prepared = false;
|
|
$release = false;
|
|
break;
|
|
|
|
case 5:
|
|
$prepared = true;
|
|
$release = false;
|
|
break;
|
|
|
|
case 6:
|
|
$prepared = false;
|
|
$release = true;
|
|
break;
|
|
|
|
case 7:
|
|
$prepared = true;
|
|
$release = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
if ($prepared) {
|
|
trace("\nPrepared Query");
|
|
} else {
|
|
trace("\nDirect Query");
|
|
}
|
|
if ($release) {
|
|
trace(" with statement release:\n");
|
|
} else {
|
|
trace(" without statement release:\n");
|
|
}
|
|
$leak = execTest($noPasses, $noRows, $start, $end, $tableName, $conn1, $prepared, $release, $phpLeak) - $drvLeak;
|
|
if ($leak > $totalLeak) {
|
|
$totalLeak = $leak;
|
|
}
|
|
}
|
|
|
|
sqlsrv_close($conn1);
|
|
|
|
$conn2 = AE\connect();
|
|
dropTable($conn2, $tableName);
|
|
sqlsrv_close($conn2);
|
|
setUTF8Data(false);
|
|
|
|
if ($totalLeak > 0) {
|
|
die("Memory leaks detected: $totalLeak bytes\n");
|
|
}
|
|
|
|
endTest($testName);
|
|
}
|
|
|
|
function getConnection()
|
|
{
|
|
$conn = AE\connect();
|
|
return ($conn);
|
|
}
|
|
|
|
function execQuery($conn, $tableName, $prepared)
|
|
{
|
|
$selectQuery = "SELECT * FROM [$tableName]";
|
|
$stmt = null;
|
|
|
|
if (AE\isColEncrypted()){
|
|
$stmt = AE\executeQuery($conn, $selectQuery);
|
|
} else {
|
|
if ($prepared) {
|
|
$stmt = sqlsrv_prepare($conn, $selectQuery);
|
|
} else {
|
|
$stmt = sqlsrv_query($conn, $selectQuery);
|
|
}
|
|
if ($stmt === false) {
|
|
fatalError("Query execution failed: $selectQuery");
|
|
}
|
|
if ($prepared) {
|
|
if (!sqlsrv_execute($stmt)) {
|
|
fatalError("Query execution failed: $selectQuery");
|
|
}
|
|
}
|
|
}
|
|
return ($stmt);
|
|
}
|
|
|
|
function execTest($noPasses, $noRows, $startStep, $endStep, $tableName, $conn, $prepared, $release, $phpLeak)
|
|
{
|
|
$leak = 0;
|
|
|
|
// Execution
|
|
for ($i = $startStep; $i <= $endStep; $i++) {
|
|
switch ($i) {
|
|
case 1: // connection only
|
|
trace("$i. Connection\t - ");
|
|
break;
|
|
|
|
case 2: // query with no release
|
|
trace("$i. Query\t - ");
|
|
break;
|
|
|
|
case 3: // query with release
|
|
trace("$i. Query Freed\t - ");
|
|
break;
|
|
|
|
case 4: // fetch
|
|
trace("$i. Simple Fetch\t - ");
|
|
break;
|
|
|
|
case 5: // fetch fields
|
|
trace("$i. Fetch Fields\t - ");
|
|
break;
|
|
|
|
case 6: // fetch array
|
|
trace("$i. Fetch Array\t - ");
|
|
break;
|
|
|
|
case 7: // fetch object
|
|
trace("$i. Fetch Object\t - ");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
$memLeak = runTest($noPasses, $noRows, $tableName, $conn, $prepared, $release, $i) - $phpLeak;
|
|
trace("Driver memory leak: $memLeak bytes\n");
|
|
if ($memLeak > 0) {
|
|
if ($leak <= 0) {
|
|
$leak = $memLeak;
|
|
echo intval($leak) . " leaking\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
return ($leak);
|
|
}
|
|
|
|
function runTest($noPasses, $noRows, $tableName, $conn, $prepared, $release, $mode)
|
|
{
|
|
$memStart = memory_get_usage();
|
|
for ($k = 1; $k <= $noPasses; $k++) {
|
|
$conn2 = null;
|
|
$stmt = null;
|
|
$fld = null;
|
|
$rowCount = 0;
|
|
$numFields = 0;
|
|
$i = 0;
|
|
|
|
switch ($mode) {
|
|
case 0: // calibration
|
|
break;
|
|
|
|
case 1: // no release
|
|
$conn2 = getConnection();
|
|
sqlsrv_close($conn2);
|
|
break;
|
|
|
|
case 2: // query with no release
|
|
$stmt = execQuery($conn, $tableName, $prepared);
|
|
break;
|
|
|
|
case 3: // query with release
|
|
$stmt = execQuery($conn, $tableName, $prepared);
|
|
sqlsrv_free_stmt($stmt);
|
|
break;
|
|
|
|
case 4: // fetch
|
|
$stmt = execQuery($conn, $tableName, $prepared);
|
|
while (sqlsrv_fetch($stmt)) {
|
|
$rowCount++;
|
|
}
|
|
if ($release) {
|
|
sqlsrv_free_stmt($stmt);
|
|
}
|
|
if ($rowCount != $noRows) {
|
|
die("$rowCount rows retrieved instead of $noRows\n");
|
|
}
|
|
break;
|
|
|
|
case 5: // fetch fields
|
|
$stmt = execQuery($conn, $tableName, $prepared);
|
|
$numFields = sqlsrv_num_fields($stmt);
|
|
while (sqlsrv_fetch($stmt)) {
|
|
$rowCount++;
|
|
for ($i = 0; $i < $numFields; $i++) {
|
|
$fld = sqlsrv_get_field($stmt, $i);
|
|
$col = $i + 1;
|
|
|
|
if ($fld === false) {
|
|
fatalError("Field $i of row $rowCount is missing");
|
|
}
|
|
unset($fld);
|
|
}
|
|
}
|
|
if ($release) {
|
|
sqlsrv_free_stmt($stmt);
|
|
}
|
|
if ($rowCount != $noRows) {
|
|
die("$rowCount rows retrieved instead of $noRows\n");
|
|
}
|
|
unset($errState);
|
|
unset($errMessage);
|
|
break;
|
|
|
|
case 6: // fetch array
|
|
$stmt = execQuery($conn, $tableName, $prepared);
|
|
while (sqlsrv_fetch_array($stmt)) {
|
|
$rowCount++;
|
|
}
|
|
if ($release) {
|
|
sqlsrv_free_stmt($stmt);
|
|
}
|
|
if ($rowCount != $noRows) {
|
|
die("$rowCount rows retrieved instead of $noRows\n");
|
|
}
|
|
break;
|
|
|
|
case 7: // fetch object
|
|
$stmt = execQuery($conn, $tableName, $prepared);
|
|
while (sqlsrv_fetch_object($stmt)) {
|
|
$rowCount++;
|
|
}
|
|
if ($release) {
|
|
sqlsrv_free_stmt($stmt);
|
|
}
|
|
if ($rowCount != $noRows) {
|
|
die("$rowCount rows retrieved instead of $noRows\n");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
// need unset to trigger the destruction of a zval with refcount of 0
|
|
unset($conn2);
|
|
unset($stmt);
|
|
}
|
|
$memEnd = memory_get_usage();
|
|
trace(intval($memEnd) . " - " . intval($memStart) . "\n");
|
|
return ($memEnd - $memStart);
|
|
}
|
|
|
|
try {
|
|
memCheck(_NUM_PASSES, _NUM_ROWS1, _NUM_ROWS2, 1, 7);
|
|
} catch (Exception $e) {
|
|
echo $e->getMessage();
|
|
}
|
|
|
|
?>
|
|
--EXPECT--
|
|
Test "Memory Leakage Check" completed successfully.
|