]> granicus.if.org Git - php/commitdiff
Add phpdbg generator command
authorBob Weinand <bobwei9@hotmail.com>
Wed, 28 Sep 2016 11:26:08 +0000 (13:26 +0200)
committerBob Weinand <bobwei9@hotmail.com>
Wed, 28 Sep 2016 11:26:08 +0000 (13:26 +0200)
NEWS
Zend/zend_generators.c
Zend/zend_generators.h
sapi/phpdbg/phpdbg_cmd.h
sapi/phpdbg/phpdbg_frame.c
sapi/phpdbg/phpdbg_frame.h
sapi/phpdbg/phpdbg_help.c
sapi/phpdbg/phpdbg_prompt.c
sapi/phpdbg/phpdbg_prompt.h

diff --git a/NEWS b/NEWS
index 50c862b64df0294f9d8ad8d054ae3de76876d8d3..ff6f5ec67a95ac00703dfa9d496ab89234bbe4a3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -46,6 +46,9 @@ PHP                                                                        NEWS
     in current rowset haven't been fetched). (Peter LeBrun)
   . Ignore potentially misleading dberr values. (Chris Kings-Lynne)
 
+- phpdbg:
+  . Added generator command for inspection of currently alive generators. (Bob)
+
 - Session:
   . Fixed bug #73100 (session_destroy null dereference in ps_files_path_create).
     (cmb)
index a238d5b22df1fa145aa089bcfb35c4fedf6291c3..1e6c770b74f5a21e17223d4686d93ff7a80915ae 100644 (file)
@@ -31,7 +31,7 @@ static zend_object_handlers zend_generator_handlers;
 
 static zend_object *zend_generator_create(zend_class_entry *class_type);
 
-static void zend_restore_call_stack(zend_generator *generator) /* {{{ */
+void zend_generator_restore_call_stack(zend_generator *generator) /* {{{ */
 {
        zend_execute_data *call, *new_call, *prev_call = NULL;
 
@@ -57,7 +57,7 @@ static void zend_restore_call_stack(zend_generator *generator) /* {{{ */
 }
 /* }}} */
 
-static zend_execute_data* zend_freeze_call_stack(zend_execute_data *execute_data) /* {{{ */
+zend_execute_data* zend_generator_freeze_call_stack(zend_execute_data *execute_data) /* {{{ */
 {
        size_t used_stack;
        zend_execute_data *call, *new_call, *prev_call = NULL;
@@ -106,7 +106,7 @@ static void zend_generator_cleanup_unfinished_execution(
                uint32_t op_num = execute_data->opline - execute_data->func->op_array.opcodes - 1;
 
                if (UNEXPECTED(generator->frozen_call_stack)) {
-                       zend_restore_call_stack(generator);
+                       zend_generator_restore_call_stack(generator);
                }
                zend_cleanup_unfinished_execution(execute_data, op_num, 0);
        }
@@ -808,7 +808,7 @@ try_again:
 
                if (UNEXPECTED(generator->frozen_call_stack)) {
                        /* Restore frozen call-stack */
-                       zend_restore_call_stack(generator);
+                       zend_generator_restore_call_stack(generator);
                }
 
                /* Resume execution */
@@ -820,7 +820,7 @@ try_again:
                if (EXPECTED(generator->execute_data) &&
                    UNEXPECTED(generator->execute_data->call)) {
                        /* Frize call-stack */
-                       generator->frozen_call_stack = zend_freeze_call_stack(generator->execute_data);
+                       generator->frozen_call_stack = zend_generator_freeze_call_stack(generator->execute_data);
                }
 
                /* Restore executor globals */
index 6aabcc6a4d2b7c12523f6f968fa66c8aa1355aad..b74dcca919167e89ae56c925073119f5da18bfd4 100644 (file)
@@ -105,6 +105,9 @@ void zend_register_generator_ce(void);
 ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution);
 ZEND_API void zend_generator_resume(zend_generator *generator);
 
+void zend_generator_restore_call_stack(zend_generator *generator);
+zend_execute_data* zend_generator_freeze_call_stack(zend_execute_data *execute_data);
+
 void zend_generator_yield_from(zend_generator *generator, zend_generator *from);
 ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_data *ptr);
 
index cbf2a8793d6eb2011344ebff06d4272489561e9a..1ecfd627051f8c9de6e16bc8e4b64e0e8fe3f838 100644 (file)
@@ -22,6 +22,7 @@
 #define PHPDBG_CMD_H
 
 #include "TSRM.h"
+#include "zend_generators.h"
 
 /* {{{ Command and Parameter */
 enum {
@@ -113,6 +114,7 @@ struct _phpdbg_command_t {
 
 typedef struct {
        int num;
+       zend_generator *generator;
        zend_execute_data *execute_data;
 } phpdbg_frame_t;
 /* }}} */
index f64d18b4bbff0b7f93b97704a54a78db2f7fd74d..85f3341e17c856fd354d04fc5a0fb816514f1938 100644 (file)
 #include "phpdbg_utils.h"
 #include "phpdbg_frame.h"
 #include "phpdbg_list.h"
+#include "zend_smart_str.h"
 
 ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
 
+static inline void phpdbg_append_individual_arg(smart_str *s, uint32_t i, zend_function *func, zval *arg) {
+       const zend_arg_info *arginfo = func->common.arg_info;
+       char *arg_name = NULL;
+
+       if (i) {
+               smart_str_appends(s, ", ");
+       }
+       if (i < func->common.num_args) {
+               if (arginfo) {
+                       if (func->type == ZEND_INTERNAL_FUNCTION) {
+                               arg_name = (char *) ((zend_internal_arg_info *) &arginfo[i])->name;
+                       } else {
+                               arg_name = ZSTR_VAL(arginfo[i].name);
+                       }
+               }
+               smart_str_appends(s, arg_name ? arg_name : "?");
+               smart_str_appendc(s, '=');
+       }
+       {
+               char *arg_print = phpdbg_short_zval_print(arg, 40);
+               smart_str_appends(s, arg_print);
+               efree(arg_print);
+       }
+}
+
+zend_string *phpdbg_compile_stackframe(zend_execute_data *ex) {
+       smart_str s = {0};
+       zend_op_array *op_array = &ex->func->op_array;
+       uint32_t i = 0, first_extra_arg = op_array->num_args, num_args = ZEND_CALL_NUM_ARGS(ex);
+       zval *p = ZEND_CALL_ARG(ex, 1);
+
+       if (op_array->scope) {
+               smart_str_append(&s, op_array->scope->name);
+               smart_str_appends(&s, "::");
+       }
+       smart_str_append(&s, op_array->function_name);
+       smart_str_appendc(&s, '(');
+       if (ZEND_CALL_NUM_ARGS(ex) > first_extra_arg) {
+               while (i < first_extra_arg) {
+                       phpdbg_append_individual_arg(&s, i, ex->func, p);
+                       p++;
+                       i++;
+               }
+               p = ZEND_CALL_VAR_NUM(ex, op_array->last_var + op_array->T);
+       }
+       while (i < num_args) {
+               phpdbg_append_individual_arg(&s, i, ex->func, p);
+               p++;
+               i++;
+       }
+       smart_str_appendc(&s, ')');
+
+       if (ex->func->type == ZEND_USER_FUNCTION) {
+               smart_str_appends(&s, " at ");
+               smart_str_append(&s, op_array->filename);
+               smart_str_appendc(&s, ':');
+               smart_str_append_unsigned(&s, ex->opline->lineno);
+       } else {
+               smart_str_appends(&s, " [internal function]");
+       }
+
+       return s.s;
+}
+
+void phpdbg_print_cur_frame_info() {
+       const char *file_chr = zend_get_executed_filename();
+       zend_string *file = zend_string_init(file_chr, strlen(file_chr), 0);
+
+       phpdbg_list_file(file, 3, zend_get_executed_lineno() - 1, zend_get_executed_lineno());
+       efree(file);
+}
+
 void phpdbg_restore_frame(void) /* {{{ */
 {
        if (PHPDBG_FRAME(num) == 0) {
                return;
        }
 
+       if (PHPDBG_FRAME(generator)) {
+               if (PHPDBG_FRAME(generator)->execute_data->call) {
+                       PHPDBG_FRAME(generator)->frozen_call_stack = zend_generator_freeze_call_stack(PHPDBG_FRAME(generator)->execute_data);
+               }
+               PHPDBG_FRAME(generator) = NULL;
+       }
+
        PHPDBG_FRAME(num) = 0;
 
        /* move things back */
@@ -40,7 +120,7 @@ void phpdbg_restore_frame(void) /* {{{ */
 
 void phpdbg_switch_frame(int frame) /* {{{ */
 {
-       zend_execute_data *execute_data = PHPDBG_FRAME(num)?PHPDBG_FRAME(execute_data):EG(current_execute_data);
+       zend_execute_data *execute_data = PHPDBG_FRAME(num) ? PHPDBG_FRAME(execute_data) : EG(current_execute_data);
        int i = 0;
 
        if (PHPDBG_FRAME(num) == frame) {
@@ -78,14 +158,15 @@ void phpdbg_switch_frame(int frame) /* {{{ */
                EG(current_execute_data) = execute_data;
        }
 
-       phpdbg_notice("frame", "id=\"%d\"", "Switched to frame #%d", frame);
+       phpdbg_try_access {
+               zend_string *s = phpdbg_compile_stackframe(EG(current_execute_data));
+               phpdbg_notice("frame", "id=\"%d\" frameinfo=\"%.*s\"", "Switched to frame #%d: %.*s", frame, (int) ZSTR_LEN(s), ZSTR_VAL(s));
+               zend_string_release(s);
+       } phpdbg_catch_access {
+               phpdbg_notice("frame", "id=\"%d\"", "Switched to frame #%d", frame);
+       } phpdbg_end_try_access();
 
-       {
-               const char *file_chr = zend_get_executed_filename();
-               zend_string *file = zend_string_init(file_chr, strlen(file_chr), 0);
-               phpdbg_list_file(file, 3, zend_get_executed_lineno() - 1, zend_get_executed_lineno());
-               efree(file);
-       }
+       phpdbg_print_cur_frame_info();
 } /* }}} */
 
 static void phpdbg_dump_prototype(zval *tmp) /* {{{ */
@@ -238,3 +319,27 @@ void phpdbg_dump_backtrace(size_t num) /* {{{ */
 
        PHPDBG_OUTPUT_BACKUP_RESTORE();
 } /* }}} */
+
+void phpdbg_open_generator_frame(zend_generator *gen) {
+       zend_string *s;
+
+       if (EG(current_execute_data) == gen->execute_data) {
+               return;
+       }
+
+       phpdbg_restore_frame();
+
+       PHPDBG_FRAME(num) = -1;
+       PHPDBG_FRAME(generator) = gen;
+
+       EG(current_execute_data) = gen->execute_data;
+       if (gen->frozen_call_stack) {
+               zend_generator_restore_call_stack(gen);
+       }
+       gen->execute_data->prev_execute_data = NULL;
+
+       s = phpdbg_compile_stackframe(EG(current_execute_data));
+       phpdbg_notice("frame", "handle=\"%d\" frameinfo=\"%.*s\"", "Switched to generator with handle #%d: %.*s", gen->std.handle, (int) ZSTR_LEN(s), ZSTR_VAL(s));
+       zend_string_release(s);
+       phpdbg_print_cur_frame_info();
+}
index 237848c29b87a527d2d3733bd37845161dbcbaea..d0baf4b2e0a2ffa8577cf03d837a89c67dbf2071 100644 (file)
 
 #include "TSRM.h"
 
+zend_string *phpdbg_compile_stackframe(zend_execute_data *);
 void phpdbg_restore_frame(void);
 void phpdbg_switch_frame(int);
 void phpdbg_dump_backtrace(size_t);
+void phpdbg_open_generator_frame(zend_generator *);
 
 #endif /* PHPDBG_FRAME_H */
index d01184b102d32e74b7ee251c4512099637ad2808..40716299d487e14462609cae82a2b2dae3402191 100644 (file)
@@ -329,34 +329,35 @@ phpdbg_help_text_t phpdbg_help_text[] = {
 "It supports the following commands:" CR CR
 
 "**Information**" CR
-"  **list**     list PHP source" CR
-"  **info**     displays information on the debug session" CR
-"  **print**    show opcodes" CR
-"  **frame**    select a stack frame and print a stack frame summary" CR
-"  **back**     shows the current backtrace" CR
-"  **help**     provide help on a topic" CR CR
+"  **list**      list PHP source" CR
+"  **info**      displays information on the debug session" CR
+"  **print**     show opcodes" CR
+"  **frame**     select a stack frame and print a stack frame summary" CR
+"  **generator** show active generators or select a generator frame" CR
+"  **back**      shows the current backtrace" CR
+"  **help**      provide help on a topic" CR CR
 
 "**Starting and Stopping Execution**" CR
-"  **exec**     set execution context" CR
-"  **run**      attempt execution" CR
-"  **step**     continue execution until other line is reached" CR
-"  **continue** continue execution" CR
-"  **until**    continue execution up to the given location" CR
-"  **next**     continue execution up to the given location and halt on the first line after it" CR
-"  **finish**   continue up to end of the current execution frame" CR
-"  **leave**    continue up to end of the current execution frame and halt after the calling instruction" CR
-"  **break**    set a breakpoint at the specified target" CR
-"  **watch**    set a watchpoint on $variable" CR
-"  **clear**    clear one or all breakpoints" CR
-"  **clean**    clean the execution environment" CR CR
+"  **exec**      set execution context" CR
+"  **run**       attempt execution" CR
+"  **step**      continue execution until other line is reached" CR
+"  **continue**  continue execution" CR
+"  **until**     continue execution up to the given location" CR
+"  **next**      continue execution up to the given location and halt on the first line after it" CR
+"  **finish**    continue up to end of the current execution frame" CR
+"  **leave**     continue up to end of the current execution frame and halt after the calling instruction" CR
+"  **break**     set a breakpoint at the specified target" CR
+"  **watch**     set a watchpoint on $variable" CR
+"  **clear**     clear one or all breakpoints" CR
+"  **clean**     clean the execution environment" CR CR
 
 "**Miscellaneous**" CR
-"  **set**      set the phpdbg configuration" CR
-"  **source**   execute a phpdbginit script" CR
-"  **register** register a phpdbginit function as a command alias" CR
-"  **sh**       shell a command" CR
-"  **ev**       evaluate some code" CR
-"  **quit**     exit phpdbg" CR CR
+"  **set**       set the phpdbg configuration" CR
+"  **source**    execute a phpdbginit script" CR
+"  **register**  register a phpdbginit function as a command alias" CR
+"  **sh**        shell a command" CR
+"  **ev**        evaluate some code" CR
+"  **quit**      exit phpdbg" CR CR
 
 "Type **help <command>** or (**help alias**) to get detailed help on any of the above commands, "
 "for example **help list** or **h l**.  Note that help will also match partial commands if unique "
@@ -648,8 +649,8 @@ phpdbg_help_text_t phpdbg_help_text[] = {
 },
 
 {"frame",
-"The **frame** takes an optional integer argument. If omitted, then the current frame is displayed "
-"If specified then the current scope is set to the corresponding frame listed in a **back** trace. "
+"The **frame** takes an optional integer argument. If omitted, then the current frame is displayed. "
+"If specified, then the current scope is set to the corresponding frame listed in a **back** trace. "
 "This can be used to allowing access to the variables in a higher stack frame than that currently being executed." CR CR
 
 "**Examples**" CR CR
@@ -658,7 +659,25 @@ phpdbg_help_text_t phpdbg_help_text[] = {
 "    Go to frame 2 and print out variable **$count** in that frame" CR CR
 
 "Note that this frame scope is discarded when execution continues, with the execution frame "
-"then reset to the lowest executiong frame."
+"then reset to the lowest executing frame."
+},
+
+{"generator",
+"The **generator** command takes an optional integer argument. If omitted, then a list of the "
+"currently active generators is displayed. If specified then the current scope is set to the frame "
+"of the generator with the corresponding object handle. This can be used to inspect any generators "
+"not in the current **back** trace." CR CR
+
+"**Examples**" CR CR
+"    $P generator" CR
+"    List of generators, with the #id being the object handle, e.g.:" CR
+"    #3: my_generator(argument=\"value\") at test.php:5" CR
+"    $P g 3" CR
+"    $P ev $i" CR
+"    Go to frame of generator with object handle 3 and print out variable **$i** in that frame" CR CR
+
+"Note that this frame scope is discarded when execution continues, with the execution frame "
+"then reset to the lowest executing frame."
 },
 
 {"info",
index 2a75dedc67c6218a35bb3aab4e18a40c116b4f08..c0ce007715efa1212dd51abf969e8ecbf86ce387 100644 (file)
@@ -67,33 +67,34 @@ extern int phpdbg_startup_run;
 
 /* {{{ command declarations */
 const phpdbg_command_t phpdbg_prompt_commands[] = {
-       PHPDBG_COMMAND_D(exec,    "set execution context",                    'e', NULL, "s", 0),
-       PHPDBG_COMMAND_D(step,    "step through execution",                   's', NULL, 0, PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(continue,"continue execution",                       'c', NULL, 0, PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(run,     "attempt execution",                        'r', NULL, "|s", 0),
-       PHPDBG_COMMAND_D(ev,      "evaluate some code",                        0 , NULL, "i", PHPDBG_ASYNC_SAFE), /* restricted ASYNC_SAFE */
-       PHPDBG_COMMAND_D(until,   "continue past the current line",           'u', NULL, 0, 0),
-       PHPDBG_COMMAND_D(finish,  "continue past the end of the stack",       'F', NULL, 0, 0),
-       PHPDBG_COMMAND_D(leave,   "continue until the end of the stack",      'L', NULL, 0, 0),
-       PHPDBG_COMMAND_D(print,   "print something",                          'p', phpdbg_print_commands, "|*c", 0),
-       PHPDBG_COMMAND_D(break,   "set breakpoint",                           'b', phpdbg_break_commands, "|*c", 0),
-       PHPDBG_COMMAND_D(back,    "show trace",                               't', NULL, "|n", PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(frame,   "switch to a frame",                        'f', NULL, "|n", PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(list,    "lists some code",                          'l', phpdbg_list_commands,  "*", PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(info,    "displays some informations",               'i', phpdbg_info_commands, "|s", PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(clean,   "clean the execution environment",          'X', NULL, 0, 0),
-       PHPDBG_COMMAND_D(clear,   "clear breakpoints",                        'C', NULL, 0, 0),
-       PHPDBG_COMMAND_D(help,    "show help menu",                           'h', phpdbg_help_commands, "|s", PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(set,     "set phpdbg configuration",                 'S', phpdbg_set_commands,   "s", PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(register,"register a function",                      'R', NULL, "s", 0),
-       PHPDBG_COMMAND_D(source,  "execute a phpdbginit",                     '<', NULL, "s", 0),
-       PHPDBG_COMMAND_D(export,  "export breaks to a .phpdbginit script",    '>', NULL, "s", PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(sh,      "shell a command",                           0 , NULL, "i", 0),
-       PHPDBG_COMMAND_D(quit,    "exit phpdbg",                              'q', NULL, 0, PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(wait,    "wait for other process",                   'W', NULL, 0, 0),
-       PHPDBG_COMMAND_D(watch,   "set watchpoint",                           'w', phpdbg_watch_commands, "|ss", 0),
-       PHPDBG_COMMAND_D(next,    "step over next line",                      'n', NULL, 0, PHPDBG_ASYNC_SAFE),
-       PHPDBG_COMMAND_D(eol,     "set EOL",                                  'E', NULL, "|s", 0),
+       PHPDBG_COMMAND_D(exec,      "set execution context",                    'e', NULL, "s", 0),
+       PHPDBG_COMMAND_D(step,      "step through execution",                   's', NULL, 0, PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(continue,  "continue execution",                       'c', NULL, 0, PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(run,       "attempt execution",                        'r', NULL, "|s", 0),
+       PHPDBG_COMMAND_D(ev,        "evaluate some code",                        0 , NULL, "i", PHPDBG_ASYNC_SAFE), /* restricted ASYNC_SAFE */
+       PHPDBG_COMMAND_D(until,     "continue past the current line",           'u', NULL, 0, 0),
+       PHPDBG_COMMAND_D(finish,    "continue past the end of the stack",       'F', NULL, 0, 0),
+       PHPDBG_COMMAND_D(leave,     "continue until the end of the stack",      'L', NULL, 0, 0),
+       PHPDBG_COMMAND_D(generator, "inspect or switch to a generator",         'g', NULL, "|n", 0),
+       PHPDBG_COMMAND_D(print,     "print something",                          'p', phpdbg_print_commands, "|*c", 0),
+       PHPDBG_COMMAND_D(break,     "set breakpoint",                           'b', phpdbg_break_commands, "|*c", 0),
+       PHPDBG_COMMAND_D(back,      "show trace",                               't', NULL, "|n", PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(frame,     "switch to a frame",                        'f', NULL, "|n", PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(list,      "lists some code",                          'l', phpdbg_list_commands,  "*", PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(info,      "displays some informations",               'i', phpdbg_info_commands, "|s", PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(clean,     "clean the execution environment",          'X', NULL, 0, 0),
+       PHPDBG_COMMAND_D(clear,     "clear breakpoints",                        'C', NULL, 0, 0),
+       PHPDBG_COMMAND_D(help,      "show help menu",                           'h', phpdbg_help_commands, "|s", PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(set,       "set phpdbg configuration",                 'S', phpdbg_set_commands,   "s", PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(register,  "register a function",                      'R', NULL, "s", 0),
+       PHPDBG_COMMAND_D(source,    "execute a phpdbginit",                     '<', NULL, "s", 0),
+       PHPDBG_COMMAND_D(export,    "export breaks to a .phpdbginit script",    '>', NULL, "s", PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(sh,        "shell a command",                           0 , NULL, "i", 0),
+       PHPDBG_COMMAND_D(quit,      "exit phpdbg",                              'q', NULL, 0, PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(wait,      "wait for other process",                   'W', NULL, 0, 0),
+       PHPDBG_COMMAND_D(watch,     "set watchpoint",                           'w', phpdbg_watch_commands, "|ss", 0),
+       PHPDBG_COMMAND_D(next,      "step over next line",                      'n', NULL, 0, PHPDBG_ASYNC_SAFE),
+       PHPDBG_COMMAND_D(eol,       "set EOL",                                  'E', NULL, "|s", 0),
        PHPDBG_END_COMMAND
 }; /* }}} */
 
@@ -880,6 +881,56 @@ PHPDBG_COMMAND(back) /* {{{ */
        return SUCCESS;
 } /* }}} */
 
+PHPDBG_COMMAND(generator) /* {{{ */
+{
+       int i;
+
+       if (!PHPDBG_G(in_execution)) {
+               phpdbg_error("inactive", "type=\"noexec\"", "Not executing!");
+               return SUCCESS;
+       }
+
+       if (param) {
+               i = param->num;
+               zend_object **obj = EG(objects_store).object_buckets + i;
+               if (i < EG(objects_store).top && *obj && IS_OBJ_VALID(*obj) && (*obj)->ce == zend_ce_generator) {
+                       zend_generator *gen = (zend_generator *) *obj;
+                       if (gen->execute_data) {
+                               if (zend_generator_get_current(gen)->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
+                                       phpdbg_error("generator", "type=\"running\"", "Generator currently running");
+                               } else {
+                                       phpdbg_open_generator_frame(gen);
+                               }
+                       } else {
+                               phpdbg_error("generator", "type=\"closed\"", "Generator already closed");
+                       }
+               } else {
+                       phpdbg_error("invalidarg", "", "Invalid object handle");
+               }
+       } else {
+               for (i = 0; i < EG(objects_store).top; i++) {
+                       zend_object *obj = EG(objects_store).object_buckets[i];
+                       if (obj && IS_OBJ_VALID(obj) && obj->ce == zend_ce_generator) {
+                               zend_generator *gen = (zend_generator *) obj, *current = zend_generator_get_current(gen);
+                               if (gen->execute_data) {
+                                       zend_string *s = phpdbg_compile_stackframe(gen->execute_data);
+                                       phpdbg_out("#%d: %.*s", i, (int) ZSTR_LEN(s), ZSTR_VAL(s));
+                                       zend_string_release(s);
+                                       if (gen != current) {
+                                               if (gen->node.parent != current) {
+                                                       phpdbg_out(" with direct parent #%d and", gen->node.parent->std.handle);
+                                               }
+                                               phpdbg_out(" executing #%d currently", current->std.handle);
+                                       }
+                                       phpdbg_out("\n");
+                               }
+                       }
+               }
+       }
+
+       return SUCCESS;
+} /* }}} */
+
 PHPDBG_COMMAND(print) /* {{{ */
 {
        if (!param || param->type == EMPTY_PARAM) {
index c17e7185ce3f346b95d05db15da9fa071c4d0940..fbfbe0af1c8a33a247694a220c60ba934f89ac22 100644 (file)
@@ -50,6 +50,7 @@ PHPDBG_COMMAND(clear);
 PHPDBG_COMMAND(help);
 PHPDBG_COMMAND(sh);
 PHPDBG_COMMAND(dl);
+PHPDBG_COMMAND(generator);
 PHPDBG_COMMAND(set);
 PHPDBG_COMMAND(source);
 PHPDBG_COMMAND(export);