Skip to content
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

FrankenPHP support #258

Closed
SpartakusMd opened this issue Aug 13, 2024 · 23 comments
Closed

FrankenPHP support #258

SpartakusMd opened this issue Aug 13, 2024 · 23 comments
Assignees

Comments

@SpartakusMd
Copy link

Is there any way to make it work under FrankenPHP worker mode?

@NoiseByNorthwest
Copy link
Owner

NoiseByNorthwest commented Aug 16, 2024

It looks similar to this use case https://github.com/NoiseByNorthwest/php-spx?tab=readme-ov-file#handle-long-living--daemon-processes

With custom metadata for request related information https://github.com/NoiseByNorthwest/php-spx?tab=readme-ov-file#add-custom-metadata-to-the-current-full-report

With these 2 features it will work although it won't be perfect.

@SpartakusMd
Copy link
Author

SpartakusMd commented Aug 24, 2024

Thank you for the direction. Unfortunately, when I try to build the extension, I get the following error:

SPX does not work with ZTS PHP build

So, I guess frankenphp is not supported.

@NoiseByNorthwest
Copy link
Owner

Sorry I forgot that FrankenPHP embeds a ZTS PHP.
ZTS is currently not supported because back in 2017 it was especially used on windows and supporting ZTS would have required some extra reviews (use of thread local everywhere, no use of NTS things, touching ZE hooks at MINIT/MSHUTDOWN only...).
But that said SPX is theorically ZTS-compliant if these extra reviews are OK. So I could allow it (maybe with some warnings around the use of other extensions touching global hooks).
Adding the support for the ZE's observer api (as an alternative and optional instrumentation system) could also help in ZTS context.

@SpartakusMd
Copy link
Author

Unfortunately, I cannot help with reviews but willing to test locally and in the staging environment if it works ok.

@NoiseByNorthwest
Copy link
Owner

@SpartakusMd could you please test with this branch #260 ?

@SpartakusMd
Copy link
Author

Hello, sorry for late reply. The build runs successfully but I receive the following when running php -m.

/usr/local/lib/php/extensions/no-debug-zts-20230831/spx.so doesn't appear to be a valid Zend extension

@NoiseByNorthwest
Copy link
Owner

I looks like it is loaded as a Zend extension (i.e. zend_extension=... instead of extension=...).

@SpartakusMd
Copy link
Author

My bad 🫣 Got it running. But it crashes the process whenever I open a report from the list: ex http://localhost/?SPX_UI_URI=/report.html&key=spx-full-20241008_052212-bbe7aff0909d-1-910913925

How can I extract something useful to investigate the issue?

@NoiseByNorthwest
Copy link
Owner

How can I extract something useful to investigate the issue?

A stack trace obtained from coredump + gdb ?

@tigitz
Copy link

tigitz commented Nov 19, 2024

@NoiseByNorthwest As requested, I've created a repro repository: https://github.com/tigitz/franken-php-spx, including the Dockerfile.

On my side, I don't encounter any crashes when visiting https://localhost/?SPX_KEY=dev&SPX_UI_URI=/. However, nothing seems to happen.

Looking forward to the subtle tweaks that will make SPX fully enjoyable in a FrankenPHP environment!

@NoiseByNorthwest
Copy link
Owner

Thanks @tigitz, I currently reproduce the "nothing happen" behavior, I will spend more time on it this weekend. I'll also have to make some changes to your reproducer since the SPX config is incomplete as you can see on the phpinfo() screenshot below
image

@NoiseByNorthwest
Copy link
Owner

I reproduce the segfault.

Here is the backtrace

(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x00007c1dd6215992 in tls_hook_free (ptr=0x7c1db6859020) at /tmp/php-spx/src/spx_php.c:1041
#2  0x00007c1e2168fa78 in virtual_file_ex () from /usr/local/lib/libphp.so
#3  0x00007c1e2168fc7a in virtual_chdir () from /usr/local/lib/libphp.so
#4  0x00007c1e2158fc28 in php_execute_script () from /usr/local/lib/libphp.so
#5  0x00000000016ed449 in ?? ()
#6  0x00000000016ead97 in ?? ()
#7  0x00000000004823e4 in ?? ()
#8  0x00000000000002b8 in ?? ()
#9  0x000000c00033fa40 in ?? ()
#10 0x00007c1dc1fffa10 in ?? ()
#11 0x00007c1e216a572b in zend_max_execution_timer_init () from /usr/local/lib/libphp.so
#12 0x00000000016ea77a in go_handle_request ()
#13 0x00000000016ec786 in ?? ()
#14 0x00007c1e20ff01c4 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#15 0x00007c1e2106fac0 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:100

So ze_hooked_func.free is called at a time it is NULL.

This function pointer is nullified by spx_php_execution_shutdown() called at RSHUTDOWN which is broken in ZTS context.

Below is what it causes (nullified at the end of a request and called right after by another thread / request processing):

Thread=29 -> tls_hook_free():1036 ze_hooked_func.block_size = 0x7c1dd6215674
Thread=29 -> tls_hook_free():1040 ze_hooked_func.free = 0x7c1dd62156cd
Thread=29 -> tls_hook_malloc():1020 ze_hooked_func.malloc = 0x7c1dd62156a8
Thread=29 -> tls_hook_malloc():1025 ze_hooked_func.block_size = 0x7c1dd6215674
Thread=28 -> spx_php_execution_shutdown():701
Thread=29 -> tls_hook_free():1036 ze_hooked_func.block_size = 0x7c1dd6215674
Thread=29 -> tls_hook_free():1040 ze_hooked_func.free = (nil)

BTW somewhat related to xdebug/xdebug#958

Regarding these hooks, this seems at first glance to be an oversight as other hooks are correctly touched by spx_php_global_hooks_set() / spx_php_global_hooks_unset() which means at MINIT / MSHUTDOWN, but this is actually not the case since these hooks are related to a Zend Heap object which is thread-bound.

The issue seems to be more related to the fact that the various states related to these hooks are stored in ze_hooked_func which is global, while a thread-local storage is required for these states.

I'll try to fix it with a thread-local storage.

@NoiseByNorthwest
Copy link
Owner

@SpartakusMd @tigitz the segfault on HTTP side is fixed.

@tigitz
Copy link

tigitz commented Nov 24, 2024

@NoiseByNorthwest Thanks a lot! I'll give it a try in an hour or two and confirm.

Are there any specific SPX directives I should configure for my tests? Or is it just about accessing https://localhost/?SPX_KEY=dev&SPX_UI_URI=/ ?

@NoiseByNorthwest
Copy link
Owner

@tigitz I'll make a PR to your reproducer

@tigitz
Copy link

tigitz commented Nov 24, 2024

@NoiseByNorthwest I can confirm it now works as expected in non-worker mode, thank you! This mode aligns with my development and profiling workflow so it suits my use case perfectly. However those looking to profile in worker mode (commonly used for production profiling) might still encounter an issue where https://localhost:8500/?SPX_KEY=dev&SPX_UI_URI=/ fails to display the web UI.

I've provided a reproducer here: https://github.com/tigitz/frankenphp-demo-spx

@NoiseByNorthwest
Copy link
Owner

@tigitz using https://github.com/tigitz/franken-php-spx I've no issue for profiling the Symfony demo app and accessing the web UI

@tigitz
Copy link

tigitz commented Nov 24, 2024

@tigitz using https://github.com/tigitz/franken-php-spx I've no issue for profiling the Symfony demo app and accessing the web UI

@NoiseByNorthwest While using FrankenPHP's worker mode that I mentioned ? Because otherwise yes it works fine in non worker mode. I’ve created a separate reproduction based on FrankenPHP’s production demo to ensure my minimal app isn’t overly simplistic for worker mode.

@NoiseByNorthwest
Copy link
Owner

Sorry I've missed that point, I'll try to debug the worker mode case next week end.

@NoiseByNorthwest
Copy link
Owner

@tigitz the web UI cannot be served by FrankenPHP in worker mode since this mode is an execution model where a single PHP script serves many HTTP requests. SPX needs an execution model where a single script execution (RINIT/RSHUTDOWN boundaries) serves one request.

So it works with FPM, mod_php or even with PHP's embedded server, but it cannot work with runtimes such as FrankenPHP, Swoole, Roadrunner...

As a workaround, you can use php-fpm to serve the web UI, and make sure that SPX's data directory is shared between php-fpm and FrankenPHP.

@tigitz
Copy link

tigitz commented Dec 1, 2024

That makes sense. Honestly, the easiest way would be to temporarily disable worker mode and run the profiler on it. If I decide to profile in production, that’s exactly what I’ll do.

Thanks for making it possible to profile outside worker mode in the first place, it’s a huge improvement for tackling my day-to-day performance issues! I think issue can be closed now. 🙂

@NoiseByNorthwest
Copy link
Owner

@SpartakusMd Is everything OK for you ? Can this issue be closed ?

@SpartakusMd
Copy link
Author

@NoiseByNorthwest yeah, all good. Thank you for the improvements made. I will also disable worker mode when doing debugging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants