Closed yoeunes closed 5 years ago
I can't replicate this (php 7.3, mac os), but it would mean that something pretty bad has gone wrong. If you are using a parallel value of 2, it means at least one of the two forks has exited but not written any data to the output file. I assume this means it died early, but I'm not sure why. It could be that the temp output file couldn't be written for some reason.
Without adding debug information into the runner, I'm not really sure how to figure out where it is dying. But there are some things to try:
-v
command line argument to see debug output and ensure it is at least checking filesIf you can try those things and post your findings, maybe something might show up.
Alternatively, if you'd like to add some debug code, I'd start in src/Runner.php
at about line 497 to see if it is getting to the place where the file is written. Something like this:
$output .= ";\n?".'>';
echo "ABOUT TO WRITE TO $childOutFilename\n";
file_put_contents($childOutFilename, $output);
echo "ABOUT TO EXIT\n";
exit($pid);
Thank you @gsherwood for your response.
This is the output with -v
argument:
$ ./vendor/bin/phpcs -v
Registering sniffs in the Tamtam coding standard standard... DONE (42 sniffs registered)
Creating file list... DONE (1 files in queue)
I get the error in this line: https://github.com/squizlabs/PHP_CodeSniffer/blob/ab6caa0a7e23bbe48d8a12c5c7a3c2f1ab1dad9e/src/Runner.php#L702-L709
The existence of $childOutput
variable was checked for another bloc above and not for line 705:
adding and isset
condition arround that bloc fixed my problem
I had the same issue :(
adding and
isset
condition arround that bloc fixed my problem
I figured it would, but I asked for more testing because it would be good to find out why your child processes are not outputting data. If they aren't producing output, it is quite likely that you are not seeing errors that are being generated.
This is why I asked if you could do some test runs over a single file, then 2 identical files. You need to be sure that the results you are getting are still valid, or else you are just hiding a very serious error.
I have the same error when a use --parallel value >1. PHP 7.3.0 (cli)
@gsherwood You're right, this is more complicated. The child processes aren't outputting any data. It dies quietly on this line, and then continues the loop, therefore never reaching the code that outputs the file contents: https://github.com/squizlabs/PHP_CodeSniffer/blob/master/src/Runner.php#L454
More info: this function call successfully runs, but anything placed after this function call does not: https://github.com/squizlabs/PHP_CodeSniffer/blob/master/src/Files/LocalFile.php#L74
Adding logging to the setContent
function demonstrates that it is successfully completing the function call without an error getting caught, but nothing after that is doing anything.
Due to the absence of error/warning/notice output and the fact that the script seems to mysteriously die, this feels to me like a possible race condition, in which the thing that is causing the script to stop is the fatal itself.
I have the same problem with php 7.3. One Project ist okey and another have the problem. I can it reproduce only on mac and on alpine is fine.
I have the same issue on mac using brew install php
version 7.3.1:
❯ php -v
PHP 7.3.1 (cli) (built: Jan 10 2019 13:15:37) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.1, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.3.1, Copyright (c) 1999-2018, by Zend Technologies
Downgrading back to v7.1.23 works correctly
Getting these too in PHPStorm with CodeSniffer enabled since updating to 7.3 (brew). Don't have the parallel option set in my config, but maybe PHPStorm sets it.
phpcs: PHP Fatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined variable: childOutput in …/vendor/squizlabs/php_codesniffer/src/Runner.php on line 712
Yep, crash in PHP 7.3.1 (cli) (built: Jan 10 2019 13:15:37) ( NTS )
(homebrew)
The only way I can get the error to happen is if the child processes run out of memory.
Is it possible that the fatal error is being suppressed for anyone, and that the child processes are actually dying due to memory limits?
PHP 7.3.1 and 7.3.2 PHPCS 3.4.0
Taking it in a different direction, I added an echo 'FINISHED LOOP
after the loops had finished, right before the $this->processChildProcs($childProcs);
, and some debugging inside the loop right before and after each file was supposedly written.
Here's my extra debug code (starting [here])(https://github.com/squizlabs/PHP_CodeSniffer/blob/a982ecd9b62742887817b176431b9a36d64ede72/src/Runner.php#L533):
$output .= ";\n?".'>';
echo 'About to write file ' . $childOutFilename . "\n";
file_put_contents($childOutFilename, $output);
//var_dump( file_get_contents( $childOutFilename ) );
exit($pid);
}//end if
}//end for
echo 'FINISHED LOOP' . "\n";
And my output:
Batch: 0
Child process created. PID = 40174
Batch: 1
Child process created. PID = 40175
Batch: 2
About to write file /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-child4FQCyE
Child process created. PID = 40176
Batch: 3
About to write file /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childedI8Ol
Child process created. PID = 40177
Batch: 4
About to write file /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childE3LXir
Child process created. PID = 40178
FINISHED LOOP
About to write file /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-child6DuKPE
About to write file /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childujhpCQ
File /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-child4FQCyE exists.
(That last "File ... exists" message is in processChildProcs()
.)
In this case, the FINISHED LOOP was appearing, before all of the the var_dump()
was output - suggesting that some processing is hitting it, before all loops have actually finished (back to the race condition possibility). That in turns means the processChildProcs()
call (where the reported undefined variable is), is being called before all child process temp files have been written.
With PHP 7.2, my output is:
Batch: 0
Child process created. PID = 46277
Batch: 1
Child process created. PID = 46278
Batch: 2
Child process created. PID = 46279
Batch: 3
Child process created. PID = 46280
Batch: 4
Child process created. PID = 46281
FINISHED LOOP
About to write file /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childr37H17
File /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childr37H17 exists.
.About to write file /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childRI5nsH
File /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childRI5nsH exists.
EAbout to write file /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childjTwoSr
File /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childjTwoSr exists.
.About to write file /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childvQ8Fjk
File /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childvQ8Fjk exists.
.About to write file /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childe4qV6Z
File /private/var/folders/3_/nz1_2g_927sf2hp3k2xb3mr40000gn/T/phpcs-childe4qV6Z exists.
. 5 / 5 (100%)
Which again suggests the same regarding initial file-writing, but it is in a different order, but more importantly, each file exists before it is processed.
Both PHP 7.3 and 7.2 have been compiled with --enable-pcntl
.
If I echo $res;
near the top of processChildProcs()
, for PHP 7.2, there are screens and screens of zeros, even for the first file. With 7.3, there are considerably fewer for the first one. Maybe a speed optimisation for PHP 7.3 has revealed a race condition? If there was a way to not have $this->processChildProcs($childProcs);
called until all child processes had been created and their temp files written to, then I think this would resolve the issue.
In this case, the FINISHED LOOP was appearing, before all of the the
var_dump()
was output - suggesting that some processing is hitting it, before all loops have actually finished (back to the race condition possibility). That in turns means theprocessChildProcs()
call (where the reported undefined variable is), is being called before all child process temp files have been written.
The way this is supposed to work means that FINISHED LOOP
should almost always appear before any files are written. The call to processChildProcs()
would normally happen before any of the child processes have completed their processing because they are normally checking a large number of files each. Not always, but often.
The processChildProcs()
method uses pcntl_waitpid()
with the WNOHANG
flag to see if any of the child processes have completed. Once it finds one, it goes ahead and opens the file that the child process should have written. If the file doesn't exist, it doesn't try to open anything. If it does exist, it assumes there will be a var inside called $childOutput
. It shouldn't matter when this loop starts as it is waiting for specific process IDs to be completed. If they are already done by the time the loop starts, they will be processed immediately. If not, the loop will wait.
Given that the error here is $childOutput
does not exist, it means that the file_exists()
call inside processChildProcs()
passed, and so the pcntl_waitpid()
call also found that the child process had ended. The file content itself was empty though. So this means that the child process did start correctly and was able to make a call to tempnam()
to create the temp file (the one that is later empty) but pcntl_waitpid()
tells the parent process that the child has completed before the file was written.
So either the child died before it could write to the file, or the pcntl_waitpid()
function is saying that the child process has ended before it actually ended.
Debug code right before the exit($pid);
line in the child would be good. If the processChildProcs()
loop thinks the process has ended before the exit() call is made, something has gone wrong. The debug output you have commented out is probably the most important bit. Are you able to put that back in and see if you are able to retrieve the file contents before the process ends?
Thank you for that explanation. It would be useful for future readers to have some of that added to DocBlocks / inline comments, for those of us who aren't familiar with child processes in PHP.
Are you able to put that back in and see if you are able to retrieve the file contents before the process ends?
Yes. For brevity, I commented out the var_dump() and hadn't been including it by dumps here, but I thought that yes, the file was being written and I could retrieve the contents. However, I've just tried it again, and the results are different:
For PHP 7.2, all looks good:
For PHP 7.3, it doesn't get to the var_dump call:
There's a debug line after ob_end_clean();
that is not getting called for PHP 7.3.
If I comment out the block of code from ob_start()
down to ob_end_clean()
, and add a $debugOutput = '';
, then it seems to work:
I can't find any reference to what's intentionally changed regarding output buffering for PHP 7.3. I use brew install php
for my version of PHP 7.3 (and 7.2), and php -i | grep ouput_
shows it has the same values as with PHP 7.2.
I commented out the ob_*()
start, get and end lines, and it still didn't give file outputs, so it doesn't appear to be anything to do with OB itself, only something within that block.
I commented out the following, it worked:
$file = $todo->current();
if ($file->ignored === true) {
continue;
}
...
$this->processFile($file);
If any of them was uncommented, it failed again (that latter two because $file
was undefined).
I looked more into FileList::current() - if I returned early from this, it worked.
If it helps I had the same issue in PHP 7.3.1, but it stopped when I upgraded to PHP 7.3.2.
Actually that may be a false positive, it started happening again after the first run, so upon php upgrade the first run was ok, the second and all since then, not.
Brought to this issue upon searching for the error message. Using OSX with Homebrew, PHPCS version 3.4.0 with PHP 7.3.2 Here's more info for debugging:
Just an update, after switching to php 7.2.15, error disappeared.
$ brew unlink php
$ brew link php@7.2 --force
I am also running into this this issue running macOS Mojave (10.14.3)
❯ php --version
PHP 7.3.2 (cli) (built: Feb 5 2019 22:19:09) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.2, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.3.2, Copyright (c) 1999-2018, by Zend Technologies
❯ phpcs --version
PHP_CodeSniffer version 3.4.0 (stable) by Squiz (http://www.squiz.net)
❯ phpcs .
PHP Fatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined variable: childOutput in /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php on line 712 in /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php:569
Stack trace:
#0 /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php(712): PHP_CodeSniffer\Runner->handleErrors(8, 'Undefined varia...', '/Users/ryan/.co...', 712, Array)
#1 /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php(509): PHP_CodeSniffer\Runner->processChildProcs(Array)
#2 /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php(114): PHP_CodeSniffer\Runner->run()
#3 /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/bin/phpcs(18): PHP_CodeSniffer\Runner->runPHPCS()
#4 {main}
thrown in /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php on line 569
Fatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined variable: childOutput in /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php on line 712 in /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php:569
Stack trace:
#0 /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php(712): PHP_CodeSniffer\Runner->handleErrors(8, 'Undefined varia...', '/Users/ryan/.co...', 712, Array)
#1 /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php(509): PHP_CodeSniffer\Runner->processChildProcs(Array)
#2 /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php(114): PHP_CodeSniffer\Runner->run()
#3 /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/bin/phpcs(18): PHP_CodeSniffer\Runner->runPHPCS()
#4 {main}
thrown in /Users/ryan/.composer/vendor/squizlabs/php_codesniffer/src/Runner.php on line 569
Update on this?
Update on this?
I'm still unable to replicate this issue, even using the same OS and PHP versions as stated here. If anyone can provide a repo or container or anything to help replicate the problem, I'd be very grateful.
had the same issue on a Mac with php 7.3.3. downgraded to php 7.2.16 and its working fine again.
We had the same issue and for us it seems related to "symlinked" files and "parallel" execution?
PHP Fatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: in phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Files/FileList.php on line 2 in phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Runner.php:2
Stack trace:
#0 phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Files/FileList.php(2): PHP_CodeSniffer\Runner->handleErrors(8, 'Undefined index...', 'phar:///home/co...', 2, Array)
#1 phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Runner.php(2): PHP_CodeSniffer\Files\FileList->current()
#2 phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Runner.php(2): PHP_CodeSniffer\Runner->run()
#3 /home/USER/PROJECT/Framework/thirdparty/phpcs.phar(6): PHP_CodeSniffer\Runner->runPHPCS()
#4 {main}
thrown in phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Runner.php on line 2
PHP Fatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined variable: childOutput in phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Runner.php on line 2 in phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Runner.php:2
Stack trace:
#0 phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Runner.php(2): PHP_CodeSniffer\Runner->handleErrors(8, 'Undefined varia...', 'phar:///home/co...', 2, Array)
#1 phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Runner.php(2): PHP_CodeSniffer\Runner->processChildProcs(Array)
#2 phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Runner.php(2): PHP_CodeSniffer\Runner->run()
#3 /home/USER/PROJECT/Framework/thirdparty/phpcs.phar(6): PHP_CodeSniffer\Runner->runPHPCS()
#4 {main}
thrown in phar:///home/USER/PROJECT/Framework/thirdparty/phpcs.phar/src/Runner.php on line 2
We had the same issue and for us it seems related to "symlinked" files and "parallel" execution?
I've just tried running over symlinked files with parallel execution of PHP 7.3 and still can't replicate any failures.
@gsherwood How did you install your PHP 7.3? I'm wondering if that could be a difference here.
@gsherwood I saw the following changes on the server at the same day, where I saw this error the first time, maybe it's related to "symlink" + "parallel"? Or maybe it's a timing thing, because on that day the server was very busy?
fs.inotify.max_queued_events
: 1048576
-> 2097152
fs.inotify.max_user_instances
: 1048576
-> 2097152
fs.inotify.max_user_watches
: 1048576
-> 2097152
vm.max_map_count
: 262144
-> 524288
kernel.keys.maxkeys
: 2048
<- /etc/sysctl.d/90-lxc-host.conf
@GaryJones I use phpbrew to run multiple PHP versions on mac OS.
Edit: also via a docker container running 7.3 - but same result.
@gsherwood
Here's a sample repo on which you'll see the errors we've mentioned: https://github.com/WordPress/wordpress-develop
@gagan0123 Thanks for posting this repo, but I couldn't replicate any issues using php 7.3.
I cloned the repo, composer install, vendor/bin/phpcs and it ran 20 processes. It found A TOTAL OF 2467 ERRORS AND 964 WARNINGS WERE FOUND IN 480 FILES
without generating any child process errors.
I've managed to replicate this with a new brew installed version of PHP 7.3.4. Comparing that to my known working versions of 7.3, I've found that this php.ini setting appears to be the problem:
; Enables or disables JIT compilation of patterns. This requires the PCRE
; library to be compiled with JIT support.
;pcre.jit=1
Note that it is commented out, but the default value is obviously to enable this. Disabling this fixes the problems for me, and reflects what my working 7.3 installs have already done by default:
; Enables or disables JIT compilation of patterns. This requires the PCRE
; library to be compiled with JIT support.
pcre.jit=0
I debugged PHPCS as far as I could to determine why the child processes were failing, but it looked like the process just died between function calls. But this is a regex extension and I did notice that leaving the EOL char of files empty did allow the script to continue longer, but die from other reasons.
I'll keep looking into this to see if there is a particular regex that the jit is breaking. If I can't find one, I'll see if I can disable or detect this ini setting during a PHPCS run, or just detect that the child process has died and fail the entire run (means you wont be able to use the parallel option with the jit enabled).
From the Generic standard, these are the sniffs that each cause errors when the jit is enabled:
Generic.PHP.Syntax
Generic.Commenting.Todo
Generic.WhiteSpace.DisallowTabIndent
Generic.PHP.DisallowAlternativePHPTags
Generic.WhiteSpace.DisallowSpaceIndent
Generic.Commenting.Fixme
I thought this was caused by complex regular expressions, but changing the TODO code to just be:
$matches = [];
preg_match('/(.*)/', 'foo', $matches);
Also causes the process to die. I'm not sure that I can actually fix this. I might need to disable the jit during PHPCS runs.
I ended up just disabling the pcre jit for PHPCS (c09a4e1a2f3f6fe4a628e72b5e61ad35b309ed02). I couldn't figure out why the processes were dying and why a simple regex would kill it.
I've also changed the process handling code so that PHPCS will die with a more specific error message rather than just an undefined var error.
If anyone is able to test this fix, I'd be very grateful. Or even just disabling the jit in your php.ini to confirm that is the cause for you as well.
I can confirm that turning off JIT for PHP 7.3, allows PHPCS to run with parallel > 1.
And the fix seems to work too:
(That's with my php.ini set back to ;pcre.jit=1
)
@GaryJones Thanks for testing that for me.
It Works 😃
@gsherwood Thanks for being so patient with this bug and helping us out
🤔 Still wondering why it works with JIT enabled on PHP 7.2 but not on PHP 7.3
Found this on PHP 7.3 Migration notes, worth looking into to resolve this without disabling JIT performance benefits
https://www.php.net/manual/en/migration73.other-changes.php#migration73.other-changes.pcre
So in PHP 7.3 they upgraded PCRE to PCRE2. This has caused issues in many other projects as well.
worth looking into to resolve this without disabling JIT performance benefits
That's what I was trying to do, but it appeared that simple calling preg_match was enough to silently segfault or something, no matter what regex or string I used. But only when running after forking, and even then it wasn't the first regex that PHPCS was using. So I'm a bit lost on this.
Thanks @GaryJones and @gagan0123 for testing. I'm going to close this so this change can be released in 3.4.2.
If anyone is continuing to have issues that this fix does not resolve, then it is likely to be a different problem and a new issue should be opened. If anyone has any idea how to get the parallel option working with the jit, please open an issue to discuss that as well.
Thanks to everyone who provided info to help track this down.
This may need to be investigated by PHP itself - has anyone checked if there are bugs reported against PHP 7.3 for PCRE2 vs the pcre.jit
option ?
@jrfnl Yes there are several projects that started reporting issues because of PCRE2 update, but most of them simply disabled pcre.jit
for the time being, and haven't resolved the underlying issue.
@gsherwood Creating new issue for support of pcre.jit
. Having it disabled is quite a performance hit, as it optimises the performance of same regular expressions running again and again which in PHPCS provides a performance boost.
@gagan0123 Thanks for checking.
If disabling pcre.jit
solves the issue, I doubt the issue is a regex being incompatible with PCRE2 as in that case, the regex wouldn't (shouldn't) work on PHP 7.3 with pcre.jit
turned off either.
This, to me, still sounds more like a problem with the PCRE2 implementation in PHP 7.3 itself and possibly support for pcre.jit
not being fully implemented, not so much something which can be solved in userland code.
@jrfnl You're right, its not compatibility with PCRE2 thats causing the issue. I profiled the command using xdebug and with some luck found the exact line causing the issue.
Currently child processes in PHPCS are failing at https://github.com/squizlabs/PHP_CodeSniffer/blob/master/src/Util/Common.php#L143
But I doubt this line if of any concern, since child processes are quitting at any preg_match statement in a forked process with Segmentation Fault signal(SIGSEGV).
Its an issue localised to PHP 7.3.x running on MacOS. I've tested on Ubuntu 18.04 with PHP 7.3.3-1+ubuntu18.04.1+deb.sury.org+1 and it works perfectly fine.
So I don't think we should hamper the performance benefits of having pcre.jit
enabled, for everyone.
Right now building docker image with PHP 7.3 on alpine, once its built, will test on that as well. https://travis-ci.com/gagan0123/wp-testing-docker/jobs/191527120
Update: It even works with PHP 7.3.4 on Alpine as well.
@gagan0123 Oh wow! Well done with the debugging!
If it helps: I was never able to reproduce the issue on Windows, so can confirm, based on one user testing, that things work fine on Windows.
https://github.com/squizlabs/PHP_CodeSniffer/blob/master/src/Util/Common.php#L143
Just had a quick look at that regex, even though it may not be the actual issue. I know PCRE2 is stricter regarding syntax and the |
should really be within parentheses, so I'd be interested to see if changing the line to the below would make a difference:
if (preg_match("/(?:\r\n?|\n)/", $contents, $matches) !== 1) {
Thanks for confirming it works on Windows 😃
I tested with parentheses as well, but it didn't work either, thing is, on MacOS, any statement with preg_match
within a forked child process results in segmentation fault, no matter what you put inside.
Learnt a lot while debugging this issue, thanks to @yoeunes for bringing this up 👍
Description
I get this exception when i run phpcs using a parallel value greater than 1
this is my phpcs.xml file: