Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 14 additions & 5 deletions .github/workflows/linux-x64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ jobs:
path: build/integration-*

phpt:
name: PHPT - PHP ${{ matrix.php }} ZTS
name: PHPT - PHP ${{ matrix.php }} ZTS${{ matrix.sanitize && format(' ({0} sanitizer)', matrix.sanitize) || '' }}
if: ${{ startsWith(github.ref, 'refs/tags/') || !contains(github.event.head_commit.message || '', '--skip-tests') }}
runs-on: ubuntu-22.04
timeout-minutes: 180
Expand All @@ -377,6 +377,14 @@ jobs:
fail-fast: false
matrix:
php: ["8.4", "8.5"]
# Empty means no sanitizer. The include below overrides it, so GitHub
# adds a separate job instead of merging into the PHP 8.5 one.
sanitize: [""]
include:
# Undefined behavior in the generated C++ (division by zero, shift
# counts, signed overflow, misaligned access...) aborts the test binary.
- php: "8.5"
sanitize: undefined
env:
PHPX_HOME: ${{ github.workspace }}/third_party/phpx
NO_INTERACTION: 1
Expand Down Expand Up @@ -489,6 +497,7 @@ jobs:
mkdir -p build/phpt-metrics
.github/scripts/observe-command.sh build/phpt-metrics -- \
php run-tests.php -q -j8 --compiler .github/scripts/observe-tpc.sh \
${{ matrix.sanitize && format('--sanitize {0}', matrix.sanitize) || '' }} \
-w build/failed-tests.txt -W build/test-results.txt tests/compiler

- name: Summarize PHPT compiler metrics
Expand All @@ -503,13 +512,13 @@ jobs:
if: always()
uses: actions/upload-artifact@v4
with:
name: phpt-metrics-linux-x64-php-${{ matrix.php }}-zts
name: phpt-metrics-linux-x64-php-${{ matrix.php }}-zts${{ matrix.sanitize && format('-{0}', matrix.sanitize) || '' }}
if-no-files-found: warn
retention-days: 14
path: build/phpt-metrics

- name: Package tested Linux compiler
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/') && !matrix.sanitize
shell: bash
run: |
composer install --no-dev --prefer-dist --no-progress --classmap-authoritative
Expand All @@ -521,7 +530,7 @@ jobs:
test "$(find . -maxdepth 1 -name 'tpc_v*_linux_x64_php${{ matrix.php }}.*-zts.tar.gz' -type f | wc -l)" -eq 1

- name: Upload Linux release package
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/') && !matrix.sanitize
uses: actions/upload-artifact@v4
with:
name: release-linux-x64-php-${{ matrix.php }}-zts
Expand All @@ -533,7 +542,7 @@ jobs:
if: failure()
uses: actions/upload-artifact@v4
with:
name: phpt-failures-linux-x64-php-${{ matrix.php }}-zts
name: phpt-failures-linux-x64-php-${{ matrix.php }}-zts${{ matrix.sanitize && format('-{0}', matrix.sanitize) || '' }}
if-no-files-found: ignore
retention-days: 7
path: |
Expand Down
36 changes: 35 additions & 1 deletion run-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ function show_usage(): void

--no-aot Run tests without AOT compilation (plain PHP mode).

--sanitize <list>
Compile every test binary with the given sanitizers (passed to
the compiler as --sanitize), e.g. undefined or address,undefined.
A sanitizer report aborts the binary, so the test fails.

--compiler <path>
Use specified compiler binary (default: ./bin/tpc.php).
For bootstrap testing, use: --compiler ./tpc
Expand Down Expand Up @@ -169,7 +174,7 @@ function main(): void
$temp_source, $temp_target, $test_cnt,
$test_files, $test_idx, $test_results, $testfile,
$valgrind, $sum_results, $shuffle, $file_cache, $num_repeats,
$show_progress, $aot_parallel_root, $test_target;
$show_progress, $aot_parallel_root, $test_target, $aot_sanitize;
// Parallel testing
global $workers, $workerID;
global $context_line_count;
Expand Down Expand Up @@ -610,6 +615,15 @@ function main(): void
. ':print_suppressions=0';
}
break;
case '--sanitize':
$aot_sanitize = parse_sanitize_option($argv[++$i] ?? '');
// A report must fail the test instead of scrolling past in its output.
$environment['UBSAN_OPTIONS'] = 'halt_on_error=1:abort_on_error=0:print_stacktrace=1';
if (in_array('address', $aot_sanitize, true)) {
$environment['ASAN_OPTIONS'] = 'halt_on_error=1:detect_leaks=0';
$environment['SKIP_ASAN'] = 1;
}
break;
case '--repeat':
$num_repeats = (int) $argv[++$i];
$environment['SKIP_REPEAT'] = 1;
Expand Down Expand Up @@ -2497,6 +2511,10 @@ function run_test(string $php, $file, array $env): string
if (!$no_aot) {
try {
$aot_args = $test->hasSection('AOT_ARGS') ? trim($test->getSection('AOT_ARGS')) : '';
global $aot_sanitize;
if (!empty($aot_sanitize)) {
$aot_args = trim($aot_args . ' --sanitize ' . implode(',', $aot_sanitize));
}
if ($test_target === 'native') {
$bin_file = compile_php_file($test_file, $aot_args);
} else {
Expand Down Expand Up @@ -4534,6 +4552,22 @@ function normalize_wasm_test_output(string $output): string
return str_replace("\r\n", "\n", trim($output));
}

/**
* Parse --sanitize: a comma-separated subset of the sanitizers the compiler supports.
*
* @return list<string>
*/
function parse_sanitize_option(string $value): array
{
$sanitizers = array_values(array_unique(array_filter(array_map('trim', explode(',', $value)))));
$unknown = array_diff($sanitizers, ['address', 'undefined']);
if ($sanitizers === [] || $unknown !== []) {
fwrite(STDERR, "--sanitize expects address and/or undefined, got: " . $value . "\n");
exit(1);
}
return $sanitizers;
}

function compile_php_file(string $file, string $compiler_args = ''): string
{
global $compiler_path, $workerID, $aot_parallel_root;
Expand Down
Loading