Skip to content

Multiple JIT test improvements #12406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 39 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
35c0183
Add PHPSeclib, Psalm and PHPStan to nightly tests
danog Oct 12, 2023
2813621
Add JIT check
danog Oct 22, 2023
7d0f061
Add infection to testsuite, repeat JIT tests
danog Oct 22, 2023
d8997a5
Repeat windows JIT tests too
danog Oct 22, 2023
ce13272
Merge
danog Oct 22, 2023
7c02611
Merge remote-tracking branch 'origin/master' into test_repeat
danog Oct 23, 2023
5c12927
Fix
danog Nov 11, 2023
1d97fcf
Add phpseclib, Psalm, PHPStan nightly tests
danog Nov 15, 2023
a6dc102
Merge remote-tracking branch 'origin/PHP-8.2' into tests_psalm_phpsta…
danog Feb 1, 2024
1bb6a1b
Add infection to matrix
danog Feb 1, 2024
12b8a63
Bump config
danog Feb 1, 2024
8f64c9e
Cleanup
danog Feb 1, 2024
596d31a
Merge remote-tracking branch 'origin/master' into test_repeat
danog Feb 1, 2024
590323c
Parallelize nightly tests and add more community libraries
danog Feb 2, 2024
2727ab9
Bump
danog Feb 2, 2024
d3eca11
Bump
danog Feb 2, 2024
9626b10
Bump
danog Feb 2, 2024
826e8a3
Fix
danog Feb 2, 2024
3c9739d
Fix
danog Feb 2, 2024
c853e32
Fix
danog Feb 2, 2024
64ad873
Add --enable-rtld-deepbind configure flag
danog Nov 13, 2024
3bdc28b
Bump
danog Nov 13, 2024
066184f
Merge remote-tracking branch 'php/master' into tests_psalm_phpstan_ph…
danog Nov 13, 2024
030d1a4
Bump
danog Nov 13, 2024
40d5421
Bump
danog Nov 13, 2024
632cf7e
Merge branch 'tests_psalm_phpstan_phpseclib'
danog Nov 13, 2024
7ac12be
Merge branch 'test_repeat'
danog Nov 13, 2024
495749a
Bump
danog Nov 13, 2024
103fd82
bump
danog Nov 13, 2024
1f40a4c
Improve tests
danog Nov 15, 2024
5fe1dc1
Merge remote-tracking branch 'php/master'
danog Nov 15, 2024
b4abe9e
Merge remote-tracking branch 'php/master'
danog Nov 16, 2024
257d913
Always test psalm
danog Nov 16, 2024
31453aa
Fix
danog Nov 16, 2024
7ae5321
Parallelize
danog Nov 16, 2024
2db7e8b
Merge remote-tracking branch 'o/master' into
danog Dec 13, 2024
f9fb10c
Merge remote-tracking branch 'php/master' into tests_psalm_phpstan_ph…
danog May 4, 2025
1d84bdb
Cleanup
danog May 4, 2025
5124607
Split repeating into separate PR
danog May 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/jit_check.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

register_shutdown_function(function () {
$status = opcache_get_status(false);
var_dump($status);

if ($status["memory_usage"]["free_memory"] < 10*1024*1024) {
fwrite(STDERR, "Not enough free opcache memory!".PHP_EOL);
}
if ($status["interned_strings_usage"]["free_memory"] < 1*1024*1024) {
fwrite(STDERR, "Not enough free interned strings memory!".PHP_EOL);
}
if ($status["jit"]["buffer_free"] < 10*1024*1024) {
fwrite(STDERR, "Not enough free JIT memory!".PHP_EOL);
}
if (!$status["jit"]["on"]) {
fwrite(STDERR, "JIT is not enabled!".PHP_EOL);
}

unset($status);
while (gc_collect_cycles());
});

$argc--;
array_shift($argv);

$_SERVER['argc']--;
array_shift($_SERVER['argv']);

require $argv[0];
297 changes: 297 additions & 0 deletions .github/nightly.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
<?php

putenv("ASAN_OPTIONS=exitcode=139");
putenv("SYMFONY_DEPRECATIONS_HELPER=max[total]=999");
putenv("PHPSECLIB_ALLOW_JIT=1");

function printMutex(string $result): void {
flock(STDOUT, LOCK_EX);
fwrite(STDOUT, $result.PHP_EOL);
flock(STDOUT, LOCK_UN);
}

function e(string $cmd, string $extra = ''): string {
exec("bash -c ".escapeshellarg("$cmd 2>&1"), $result, $code);
$result = implode("\n", $result);
if ($code) {
printMutex("An error occurred while executing $cmd (status $code, extra info $extra): $result");
die(1);
}
return $result;
}

$parallel = (int) ($argv[1] ?? 0);
$parallel = $parallel ?: ((int)`nproc`);
$parallel = $parallel ?: 8;

$repos = [];

$repos["psalm"] = [
"https://github.com/vimeo/psalm",
"master",
null,
function (): iterable {
$it = new RecursiveDirectoryIterator("tests");
/** @var SplFileInfo $file */
foreach(new RecursiveIteratorIterator($it) as $file) {
if ($file->getExtension() == 'php' && ctype_upper($file->getBasename()[0])) {
yield [
getcwd()."/vendor/bin/phpunit",
$file->getRealPath(),
];
}
}
},
2
];

$repos["phpseclib"] = [
"https://github.com/phpseclib/phpseclib",
"master",
null,
function (): iterable {
$it = new RecursiveDirectoryIterator("tests");
/** @var SplFileInfo $file */
foreach(new RecursiveIteratorIterator($it) as $file) {
if ($file->getExtension() == 'php' && ctype_upper($file->getBasename()[0])) {
yield [
getcwd()."/vendor/bin/phpunit",
'-c',
getcwd()."/tests/phpunit.xml",
$file->getRealPath(),
];
}
}
},
2
];

$repos["phpunit"] = [
"https://github.com/sebastianbergmann/phpunit.git",
"main",
null,
["./phpunit"],
2
];

$repos["infection"] = [
"https://github.com/infection/infection",
"master",
null,
["vendor/bin/phpunit"],
2
];

$repos["wordpress"] = [
"https://github.com/WordPress/wordpress-develop.git",
"",
function (): void {
$f = file_get_contents('wp-tests-config-sample.php');
$f = str_replace('youremptytestdbnamehere', 'test', $f);
$f = str_replace('yourusernamehere', 'root', $f);
$f = str_replace('yourpasswordhere', 'root', $f);
file_put_contents('wp-tests-config.php', $f);
},
["vendor/bin/phpunit"],
2
];

foreach (['amp', 'cache', 'dns', 'file', 'http', 'parallel', 'parser', 'pipeline', 'process', 'serialization', 'socket', 'sync', 'websocket-client', 'websocket-server'] as $repo) {
$repos["amphp-$repo"] = ["https://github.com/amphp/$repo.git", "", null, ["vendor/bin/phpunit"], 2];
}

$repos["laravel"] = [
"https://github.com/laravel/framework.git",
"master",
function (): void {
$c = file_get_contents("tests/Filesystem/FilesystemTest.php");
$c = str_replace("public function testSharedGet()", "#[\\PHPUnit\\Framework\\Attributes\\Group('skip')]\n public function testSharedGet()", $c);
file_put_contents("tests/Filesystem/FilesystemTest.php", $c);
},
["vendor/bin/phpunit", "--exclude-group", "skip"],
2
];

foreach (['async', 'cache', 'child-process', 'datagram', 'dns', 'event-loop', 'promise', 'promise-stream', 'promise-timer', 'stream'] as $repo) {
$repos["reactphp-$repo"] = ["https://github.com/reactphp/$repo.git", "", null, ["vendor/bin/phpunit"], 2];
}

$repos["revolt"] = ["https://github.com/revoltphp/event-loop.git", "", null, ["vendor/bin/phpunit"], 2];

$repos["symfony"] = [
"https://github.com/symfony/symfony.git",
"",
function (): void {
e("php ./phpunit install");
},
function (): iterable {
$it = new RecursiveDirectoryIterator("src/Symfony");
/** @var SplFileInfo $file */
foreach(new RecursiveIteratorIterator($it) as $file) {
if ($file->getBasename() == 'phpunit.xml.dist') {
yield [
getcwd()."/phpunit",
dirname($file->getRealPath()),
"--exclude-group",
"tty,benchmark,intl-data,transient",
"--exclude-group",
"skip"
];
}
}
},
2
];

$finalStatus = 0;
$parentPids = [];

$waitOne = function () use (&$finalStatus, &$parentPids): void {
$res = pcntl_wait($status);
if ($res === -1) {
printMutex("An error occurred while waiting with waitpid!");
$finalStatus = $finalStatus ?: 1;
return;
}
if (!isset($parentPids[$res])) {
printMutex("Unknown PID $res returned!");
$finalStatus = $finalStatus ?: 1;
return;
}
$desc = $parentPids[$res];
unset($parentPids[$res]);
if (pcntl_wifexited($status)) {
$status = pcntl_wexitstatus($status);
printMutex("Child task $desc exited with status $status");
if ($status !== 0) {
$finalStatus = $status;
}
} elseif (pcntl_wifstopped($status)) {
$status = pcntl_wstopsig($status);
printMutex("Child task $desc stopped by signal $status");
$finalStatus = 1;
} elseif (pcntl_wifsignaled($status)) {
$status = pcntl_wtermsig($status);
printMutex("Child task $desc terminated by signal $status");
$finalStatus = 1;
}
};

$waitAll = function () use ($waitOne, &$parentPids): void {
while ($parentPids) {
$waitOne();
}
};

printMutex("Cloning repos...");

foreach ($repos as $dir => [$repo, $branch, $prepare, $command, $repeat]) {
$pid = pcntl_fork();
if ($pid) {
$parentPids[$pid] = "clone $dir";
continue;
}

chdir(sys_get_temp_dir());
if ($branch) {
$branch = "--branch $branch";
}
e("git clone $repo $branch --depth 1 $dir");

exit(0);
}

$waitAll();

printMutex("Done cloning repos!");

printMutex("Preparing repos (max $parallel processes)...");
foreach ($repos as $dir => [$repo, $branch, $prepare, $command, $repeat]) {
chdir(sys_get_temp_dir()."/$dir");
$rev = e("git rev-parse HEAD", $dir);

$pid = pcntl_fork();
if ($pid) {
$parentPids[$pid] = "prepare $dir ($rev)";
if (count($parentPids) >= $parallel) {
$waitOne();
}
continue;
}

e("composer i --ignore-platform-reqs", $dir);
if ($prepare) {
$prepare();
}

exit(0);
}
$waitAll();

printMutex("Done preparing repos!");

printMutex("Running tests (max $parallel processes)...");
foreach ($repos as $dir => [$repo, $branch, $prepare, $command, $repeat]) {
chdir(sys_get_temp_dir()."/$dir");
$rev = e("git rev-parse HEAD", $dir);

if ($command instanceof Closure) {
$commands = iterator_to_array($command());
} else {
$commands = [$command];
}

foreach ($commands as $idx => $cmd) {
$cmd = array_merge([
PHP_BINARY,
'--repeat',
$repeat,
'-f',
__DIR__.'/jit_check.php',
], $cmd);

$cmdStr = implode(" ", $cmd);

$pid = pcntl_fork();
if ($pid) {
$parentPids[$pid] = "test $dir ($rev): $cmdStr";
if (count($parentPids) >= $parallel) {
$waitOne();
}
continue;
}

$output = sys_get_temp_dir()."/out_{$dir}_$idx.txt";

$p = proc_open($cmd, [
["pipe", "r"],
["file", $output, "a"],
["file", $output, "a"]
], $pipes, sys_get_temp_dir()."/$dir");

if ($p === false) {
printMutex("Failure starting $cmdStr");
exit(1);
}

$final = 0;
$status = proc_close($p);
if ($status !== 0) {
if ($status > 128) {
$final = $status;
}
printMutex(
"$dir ($rev): $cmdStr terminated with status $status:".PHP_EOL
.file_get_contents($output).PHP_EOL
);
}

exit($final);
}
}

$waitAll();

printMutex("All done!");

die($finalStatus);
Loading