Skip to content

Commit

Permalink
Support xdebug (#4915)
Browse files Browse the repository at this point in the history
* Support xdebug

* Fix build error under PHP 8.1

* Free fiber_context when coroutine exit

* Optimize code

call zend_observer_fiber_destroy_notify in fiber_context_destroy

* Remove useless code

* Move state = STATE_RUNNING into Coroutine::run
  • Loading branch information
huanghantao authored Nov 29, 2022
1 parent f81ad1b commit a941e94
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 2 deletions.
17 changes: 17 additions & 0 deletions ext-src/php_swoole.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "ext/pcre/php_pcre.h"
#endif
#include "zend_exceptions.h"
#include "zend_extensions.h"

BEGIN_EXTERN_C()
#include "ext/json/php_json.h"
Expand Down Expand Up @@ -704,6 +705,22 @@ PHP_MINIT_FUNCTION(swoole) {

zend::known_strings_init();

/* Debug extensions check */
static const char *debug_zend_extension_names[] = { "Xdebug" };
static const char *debug_php_extension_names[] = { "ddtrace" };
for (size_t i = 0; i < sizeof(debug_zend_extension_names); i++) {
const char *name = debug_zend_extension_names[i];
if (zend_get_extension(name) != NULL) {
SWOOLE_G(has_debug_extension) = 1;
}
}
for (size_t i = 0; i < sizeof(debug_php_extension_names); i++) {
const char *name = debug_php_extension_names[i];
if (zend_hash_str_find_ptr(&module_registry, name, strlen(name))) {
SWOOLE_G(has_debug_extension) = 1;
}
}

return SUCCESS;
}
/* }}} */
Expand Down
28 changes: 27 additions & 1 deletion ext-src/php_swoole_coroutine.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
#include "zend_vm.h"
#include "zend_closures.h"

#if PHP_VERSION_ID >= 80100
#define SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT 1
#include "zend_fibers.h"
#include "zend_observer.h"
#endif

#include <stack>
#include <thread>

Expand Down Expand Up @@ -68,6 +74,10 @@ struct PHPContext {
int ori_error_reporting;
int tmp_error_reporting;
Coroutine *co;
#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT
zend_fiber_context *fiber_context;
bool fiber_init_notified;
#endif
std::stack<zend::Function *> *defer_tasks;
SwapCallback *on_yield;
SwapCallback *on_resume;
Expand Down Expand Up @@ -214,6 +224,14 @@ class PHPCoroutine {
return sw_likely(activated) ? Coroutine::get_execute_time(cid) : -1;
}

static inline void init_main_task() {
main_task.co = Coroutine::init_main_coroutine();
#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT
main_task.fiber_context = EG(main_fiber_context);
main_task.fiber_init_notified = true;
#endif
}

protected:
static bool activated;
static PHPContext main_task;
Expand All @@ -240,7 +258,15 @@ class PHPCoroutine {
static void on_resume(void *arg);
static void on_close(void *arg);
static void main_func(void *arg);

#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT
static zend_fiber_status get_fiber_status(PHPContext *task);
static void fiber_context_init(PHPContext *task);
static void fiber_context_try_init(PHPContext *task);
static void fiber_context_destroy(PHPContext *task);
static void fiber_context_try_destroy(PHPContext *task);
static void fiber_context_switch_notify(PHPContext *from, PHPContext *to);
static void fiber_context_switch_try_notify(PHPContext *from, PHPContext *to);
#endif
static void interrupt_thread_start();
static void record_last_msec(PHPContext *task) {
if (interrupt_thread_running) {
Expand Down
111 changes: 110 additions & 1 deletion ext-src/swoole_coroutine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
#include "zend_builtin_functions.h"
#include "ext/spl/spl_array.h"

#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT
#include "zend_observer.h"
#endif

#include <unordered_map>
#include <chrono>

Expand Down Expand Up @@ -278,12 +282,13 @@ void PHPCoroutine::activate() {
return;
}

#ifndef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT
if (zend_hash_str_find_ptr(&module_registry, ZEND_STRL("xdebug"))) {
php_swoole_fatal_error(
E_WARNING,
"Using Xdebug in coroutines is extremely dangerous, please notice that it may lead to coredump!");
}

#endif
zval *enable_library = zend_get_constant_str(ZEND_STRL("SWOOLE_LIBRARY"));
if (enable_library == NULL || !zval_is_true(enable_library)) {
php_swoole_load_library();
Expand Down Expand Up @@ -508,6 +513,10 @@ void PHPCoroutine::restore_task(PHPContext *task) {
void PHPCoroutine::on_yield(void *arg) {
PHPContext *task = (PHPContext *) arg;
PHPContext *origin_task = get_origin_context(task);

#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT
fiber_context_switch_try_notify(task, origin_task);
#endif
save_task(task);
restore_task(origin_task);

Expand All @@ -521,6 +530,10 @@ void PHPCoroutine::on_yield(void *arg) {
void PHPCoroutine::on_resume(void *arg) {
PHPContext *task = (PHPContext *) arg;
PHPContext *current_task = get_context();

#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT
fiber_context_switch_try_notify(current_task, task);
#endif
save_task(current_task);
restore_task(task);
record_last_msec(task);
Expand Down Expand Up @@ -570,6 +583,11 @@ void PHPCoroutine::on_close(void *arg) {
concurrency--;
}

#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT
fiber_context_switch_try_notify(task, origin_task);
fiber_context_try_destroy(task);
#endif

vm_stack_destroy();
restore_task(origin_task);

Expand Down Expand Up @@ -658,6 +676,11 @@ void PHPCoroutine::main_func(void *arg) {
task->on_close = nullptr;
task->enable_scheduler = true;

#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT
fiber_context_try_init(task);
task->fiber_init_notified = false;
#endif

save_vm_stack(task);
record_last_msec(task);

Expand Down Expand Up @@ -697,6 +720,9 @@ void PHPCoroutine::main_func(void *arg) {
// TODO: enhancement it, separate execute data is necessary, but we lose the backtrace
EG(current_execute_data) = nullptr;
zend_init_func_execute_data(call, &func->op_array, retval);
#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT
fiber_context_switch_try_notify(get_origin_context(task), get_context());
#endif
zend_execute_ex(EG(current_execute_data));
} else { /* ZEND_INTERNAL_FUNCTION */
ZVAL_NULL(retval);
Expand Down Expand Up @@ -788,6 +814,87 @@ void PHPCoroutine::defer(zend::Function *fci) {
task->defer_tasks->push(fci);
}

#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT

void PHPCoroutine::fiber_context_init(PHPContext *task)
{
zend_fiber_context *fiber_context = (zend_fiber_context *) emalloc(sizeof(*fiber_context));
fiber_context->handle = (void *) -1;
fiber_context->kind = (void *) -1;
fiber_context->function = (zend_fiber_coroutine) -1;
fiber_context->stack = (zend_fiber_stack *) -1;
task->fiber_context = fiber_context;

zend_observer_fiber_init_notify(fiber_context);
}

void PHPCoroutine::fiber_context_try_init(PHPContext *task)
{
if (EXPECTED(!SWOOLE_G(has_debug_extension))) {
return;
}
fiber_context_init(task);
}

void PHPCoroutine::fiber_context_destroy(PHPContext *task)
{
zend_observer_fiber_destroy_notify(task->fiber_context);

if (task->fiber_context != NULL) {
efree(task->fiber_context);
}
}

void PHPCoroutine::fiber_context_try_destroy(PHPContext *task)
{
if (EXPECTED(!SWOOLE_G(has_debug_extension))) {
return;
}
fiber_context_destroy(task);
}

zend_fiber_status PHPCoroutine::get_fiber_status(PHPContext *task) {
switch (task->co->get_state()) {
case Coroutine::STATE_INIT:
return ZEND_FIBER_STATUS_INIT;
case Coroutine::STATE_WAITING:
return ZEND_FIBER_STATUS_SUSPENDED;
case Coroutine::STATE_RUNNING:
return ZEND_FIBER_STATUS_RUNNING;
case Coroutine::STATE_END:
return ZEND_FIBER_STATUS_DEAD;
default:
php_swoole_fatal_error(E_ERROR, "Unexpected state when get fiber status");
return ZEND_FIBER_STATUS_DEAD;
}
}

void PHPCoroutine::fiber_context_switch_notify(PHPContext *from, PHPContext *to) {
zend_fiber_context *from_context = from->fiber_context;
zend_fiber_context *to_context = to->fiber_context;

from_context->status = get_fiber_status(from);
to_context->status = get_fiber_status(to);

if (!to->fiber_init_notified) {
to_context->status = ZEND_FIBER_STATUS_INIT;
zend_observer_fiber_switch_notify(from_context, to_context);
to_context->status = get_fiber_status(to);
to->fiber_init_notified = true;
} else {
zend_observer_fiber_switch_notify(from_context, to_context);
}
}

void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext *to)
{
if (EXPECTED(!SWOOLE_G(has_debug_extension))) {
return;
}
fiber_context_switch_notify(from, to);
}
#endif /* SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT */

void php_swoole_coroutine_minit(int module_number) {
PHPCoroutine::init();

Expand Down Expand Up @@ -827,6 +934,8 @@ void php_swoole_coroutine_rinit() {
ori_end_silence_handler = zend_get_user_opcode_handler(ZEND_END_SILENCE);
zend_set_user_opcode_handler(ZEND_END_SILENCE, coro_end_silence_handler);
}

PHPCoroutine::init_main_task();
}

void php_swoole_coroutine_rshutdown() {
Expand Down
11 changes: 11 additions & 0 deletions include/swoole_coroutine.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ class Coroutine {
#endif
}

static inline Coroutine *init_main_coroutine() {
Coroutine *co = new Coroutine(0, nullptr, nullptr);
co->state = STATE_RUNNING;
return co;
}

static void activate();
static void deactivate();

Expand Down Expand Up @@ -264,11 +270,16 @@ class Coroutine {
}
}

Coroutine(long _cid, const CoroutineFunc &fn, void *private_data): ctx(stack_size, fn, private_data) {
cid = _cid;
}

inline long run() {
long cid = this->cid;
origin = current;
current = this;
CALC_EXECUTE_USEC(origin, nullptr);
state = STATE_RUNNING;
ctx.swap_in();
check_end();
return cid;
Expand Down
1 change: 1 addition & 0 deletions php_swoole.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ ZEND_BEGIN_MODULE_GLOBALS(swoole)
zend_bool enable_coroutine;
zend_bool enable_preemptive_scheduler;
zend_bool enable_library;
zend_bool has_debug_extension;
long socket_buffer_size;
int req_status;
ZEND_END_MODULE_GLOBALS(swoole)
Expand Down

0 comments on commit a941e94

Please sign in to comment.