From f2212c05c784e98189075ef4c501bcaea39ff4fe Mon Sep 17 00:00:00 2001 From: krakjoe Date: Thu, 14 Nov 2013 21:10:44 +0000 Subject: [PATCH] add opline logging and setup including command line option --- phpdbg.c | 46 +++++++++++++++++++++++++++------ phpdbg.h | 1 + phpdbg_help.c | 33 ++++++++++++++++++------ phpdbg_help.h | 2 ++ phpdbg_prompt.c | 67 ++++++++++++++++++++++++++++++++++++++++++++----- phpdbg_utils.c | 45 ++++++++++++++++++++------------- phpdbg_utils.h | 20 ++++++++++----- 7 files changed, 168 insertions(+), 46 deletions(-) diff --git a/phpdbg.c b/phpdbg.c index 443b603074..30a06e8a50 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -37,6 +37,7 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */ pg->last_params = NULL; pg->last_params_len = 0; pg->flags = PHPDBG_DEFAULT_FLAGS; + pg->oplog = NULL; } /* }}} */ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ @@ -103,6 +104,12 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ efree(PHPDBG_G(exec)); PHPDBG_G(exec) = NULL; } + + if (PHPDBG_G(oplog)) { + fclose( + PHPDBG_G(oplog)); + PHPDBG_G(oplog) = NULL; + } if (PHPDBG_G(ops)) { destroy_op_array(PHPDBG_G(ops) TSRMLS_CC); @@ -293,11 +300,12 @@ const opt_struct OPTIONS[] = { /* {{{ */ {'z', 1, "load zend_extension"}, /* phpdbg options */ {'e', 1, "exec"}, - {'v', 0, "verbose"}, - {'s', 0, "step"}, + {'v', 0, "disable quietness"}, + {'s', 0, "enable stepping"}, {'b', 0, "boring colours"}, - {'i', 1, "init"}, - {'I', 0, "ignore-init"}, + {'i', 1, "specify init"}, + {'I', 0, "ignore init"}, + {'O', 1, "opline log"}, {'-', 0, NULL} }; /* }}} */ @@ -333,6 +341,8 @@ int main(int argc, char **argv) /* {{{ */ char *init_file; size_t init_file_len; zend_bool init_file_default; + char *oplog_file; + size_t oplog_file_len; zend_ulong flags; char *php_optarg; int php_optind; @@ -364,6 +374,8 @@ phpdbg_main: init_file = NULL; init_file_len = 0; init_file_default = 1; + oplog_file = NULL; + oplog_file_len = 0; flags = PHPDBG_DEFAULT_FLAGS; php_optarg = NULL; php_optind = 1; @@ -412,24 +424,33 @@ phpdbg_main: case 'z': zend_load_extension(php_optarg); break; + + /* begin phpdbg options */ - case 'e': /* set execution context */ + case 'e': { /* set execution context */ exec_len = strlen(php_optarg); if (exec_len) { exec = strdup(php_optarg); } - break; + } break; case 'I': { /* ignore .phpdbginit */ init_file_default = 0; } break; - case 'i': /* set init file */ + case 'i': { /* set init file */ init_file_len = strlen(php_optarg); if (init_file_len) { init_file = strdup(php_optarg); } - break; + } break; + + case 'O': { /* set oplog output */ + oplog_file_len = strlen(php_optarg); + if (oplog_file_len) { + oplog_file = strdup(php_optarg); + } + } break; case 'v': /* set quietness off */ flags &= ~PHPDBG_IS_QUIET; @@ -485,6 +506,15 @@ phpdbg_main: free(exec); } + + if (oplog_file) { /* open oplog */ + PHPDBG_G(oplog) = fopen(oplog_file, "w+"); + if (!PHPDBG_G(oplog)) { + phpdbg_error( + "Failed to open oplog %s", oplog_file); + } + free(oplog_file); + } /* set flags from command line */ PHPDBG_G(flags) = flags; diff --git a/phpdbg.h b/phpdbg.h index 48b3ab44c9..8577624eb9 100644 --- a/phpdbg.h +++ b/phpdbg.h @@ -109,6 +109,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) const char *last_params; /* last expression */ size_t last_params_len; /* last expression length */ zend_ulong flags; /* phpdbg flags */ + FILE *oplog; /* opline log */ ZEND_END_MODULE_GLOBALS(phpdbg) #endif /* PHPDBG_H */ diff --git a/phpdbg_help.c b/phpdbg_help.c index fb987d131a..244b435df0 100644 --- a/phpdbg_help.c +++ b/phpdbg_help.c @@ -26,7 +26,7 @@ ZEND_EXTERN_MODULE_GLOBALS(phpdbg); PHPDBG_HELP(exec) /* {{{ */ { - phpdbg_writeln("Will attempt execution, if compilation has not yet taken place, it occurs now."); + phpdbg_writeln("Will attempt execution, if compilation has not yet taken place, it occurs now"); phpdbg_writeln("The execution context must be set before execution can take place"); return SUCCESS; } /* }}} */ @@ -53,7 +53,7 @@ PHPDBG_HELP(compile) /* {{{ */ { phpdbg_writeln("Pre-compilation of the execution context provides the opportunity to inspect the opcodes before they are executed"); phpdbg_writeln("The execution context must be set for compilation to succeed"); - phpdbg_writeln("If errors occur during compilation they must be resolved before execution can take place."); + phpdbg_writeln("If errors occur during compilation they must be resolved before execution can take place"); phpdbg_writeln("It is a good idea to clean the environment between each compilation with the clean command"); phpdbg_writeln("You do not need to exit phpdbg to retry compilation"); return SUCCESS; @@ -99,13 +99,13 @@ PHPDBG_HELP(run) /* {{{ */ PHPDBG_HELP(eval) /* {{{ */ { phpdbg_writeln("Access to eval() allows you to change the environment during execution, careful though !!"); - phpdbg_writeln("Note: When using eval in phpdbg do not prefix the code with return."); + phpdbg_writeln("Note: When using eval in phpdbg do not prefix the code with return"); return SUCCESS; } /* }}} */ PHPDBG_HELP(break) /* {{{ */ { - phpdbg_writeln("Setting a breakpoint stops execution at a specific stage."); + phpdbg_writeln("Setting a breakpoint stops execution at a specific stage"); phpdbg_writeln(EMPTY); phpdbg_writeln("Examples:"); phpdbg_writeln("\t%sbreak test.php:1", PROMPT); @@ -131,8 +131,8 @@ PHPDBG_HELP(break) /* {{{ */ PHPDBG_HELP(clean) /* {{{ */ { - phpdbg_writeln("While debugging you may experience errors because of attempts to redeclare classes, constants or functions."); - phpdbg_writeln("Cleaning the environment cleans these tables, so that files can be recompiled without exiting phpdbg."); + phpdbg_writeln("While debugging you may experience errors because of attempts to redeclare classes, constants or functions"); + phpdbg_writeln("Cleaning the environment cleans these tables, so that files can be recompiled without exiting phpdbg"); return SUCCESS; } /* }}} */ @@ -159,7 +159,7 @@ PHPDBG_HELP(quiet) /* {{{ */ PHPDBG_HELP(back) /* {{{ */ { - phpdbg_writeln("The backtrace is gathered with the default debug_backtrace functionality."); + phpdbg_writeln("The backtrace is gathered with the default debug_backtrace functionality"); phpdbg_writeln(EMPTY); phpdbg_writeln("Examples:"); phpdbg_writeln("You can set the limit on the trace"); @@ -170,7 +170,7 @@ PHPDBG_HELP(back) /* {{{ */ PHPDBG_HELP(list) /* {{{ */ { - phpdbg_writeln("The list command displays N line from current context file."); + phpdbg_writeln("The list command displays N line from current context file"); phpdbg_writeln(EMPTY); phpdbg_writeln("Examples:"); phpdbg_writeln("\t%slist 2", PROMPT); @@ -183,3 +183,20 @@ PHPDBG_HELP(list) /* {{{ */ phpdbg_writeln("Note: before listing functions you must have a populated function table, try compile !!"); return SUCCESS; } /* }}} */ + +PHPDBG_HELP(oplog) /* {{{ */ +{ + phpdbg_writeln("Even when quietness is enabled you may wish to save opline logs to a file"); + phpdbg_writeln("Setting a new oplog closes the previously open log"); + phpdbg_writeln("The log includes a high resolution timestamp on each entry"); + phpdbg_writeln(EMPTY); + phpdbg_writeln("Example:"); + phpdbg_writeln("\t%soplog /path/to/my.oplog", PROMPT); + phpdbg_writeln("Will open the file /path/to/my.oplog for writing, creating it if it does not exist"); + phpdbg_writeln("Example:"); + phpdbg_writeln("\t%soplog 0", PROMPT); + phpdbg_writeln("Will open the currently open log file, disabling oplog"); + phpdbg_writeln(EMPTY); + phpdbg_writeln("Note: upon failure to open a new oplog, the last oplog is held open"); + return SUCCESS; +} /* }}} */ diff --git a/phpdbg_help.h b/phpdbg_help.h index 4e15bda224..c5706f65f3 100644 --- a/phpdbg_help.h +++ b/phpdbg_help.h @@ -47,6 +47,7 @@ PHPDBG_HELP(clear); PHPDBG_HELP(back); PHPDBG_HELP(quiet); PHPDBG_HELP(list); +PHPDBG_HELP(oplog); /** * Commands @@ -65,6 +66,7 @@ static const phpdbg_command_t phpdbg_help_commands[] = { PHPDBG_HELP_D(back, "show debug backtrace information during execution"), PHPDBG_HELP_D(quiet, "be quiet during execution"), PHPDBG_HELP_D(list, "listing code gives you quick access to code while executing"), + PHPDBG_HELP_D(oplog, "sets a file for, or disables oplog"), {NULL, 0, 0} }; diff --git a/phpdbg_prompt.c b/phpdbg_prompt.c index 7b2c603668..bf016ea8a3 100644 --- a/phpdbg_prompt.c +++ b/phpdbg_prompt.c @@ -46,6 +46,7 @@ static PHPDBG_COMMAND(clear); static PHPDBG_COMMAND(help); static PHPDBG_COMMAND(quiet); static PHPDBG_COMMAND(aliases); +static PHPDBG_COMMAND(oplog); static PHPDBG_COMMAND(quit); /* }}} */ /* {{{ command declarations */ @@ -65,6 +66,7 @@ static const phpdbg_command_t phpdbg_prompt_commands[] = { PHPDBG_COMMAND_EX_D(help, "show help menu", 'h'), PHPDBG_COMMAND_EX_D(quiet, "silence some output", 'Q'), PHPDBG_COMMAND_EX_D(aliases, "show alias list", 'a'), + PHPDBG_COMMAND_EX_D(oplog, "sets oplog output", 'O'), PHPDBG_COMMAND_EX_D(quit, "exit phpdbg", 'q'), {NULL, 0, 0} }; /* }}} */ @@ -496,6 +498,45 @@ static PHPDBG_COMMAND(aliases) /* {{{ */ return SUCCESS; } /* }}} */ +static PHPDBG_COMMAND(oplog) /* {{{ */ +{ + if (expr && expr_len > 0L) { + /* disable oplog */ + if (expr[0] == '0' && expr_len == 1) { + if (PHPDBG_G(oplog)) { + phpdbg_notice("Disabling oplog"); + fclose( + PHPDBG_G(oplog)); + return SUCCESS; + } else { + phpdbg_error("No oplog currently open"); + return FAILURE; + } + } else { + /* open oplog */ + FILE *old = PHPDBG_G(oplog); + + PHPDBG_G(oplog) = fopen(expr, "w+"); + if (!PHPDBG_G(oplog)) { + phpdbg_error("Failed to open %s for oplog", expr); + PHPDBG_G(oplog) = old; + return FAILURE; + } else { + if (old) { + phpdbg_notice("Closing previously open oplog"); + fclose(old); + } + phpdbg_notice("Successfully opened oplog"); + + return SUCCESS; + } + } + } else { + phpdbg_error("No expression provided"); + return FAILURE; + } +} /* }}} */ + static PHPDBG_COMMAND(help) /* {{{ */ { phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", @@ -538,6 +579,7 @@ static PHPDBG_COMMAND(help) /* {{{ */ phpdbg_writeln("\t-b\tN/A\t\t\tDisable the use of colours"); phpdbg_writeln("\t-i\t-imy.init\t\tSet the phpdbginit file"); phpdbg_writeln("\t-I\tN/A\t\t\tDisable loading .phpdbginit"); + phpdbg_writeln("\t-O\t-Omy.oplog\t\tSets oplog output file"); } phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES); @@ -687,14 +729,27 @@ void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags /* force out a line while stepping so the user knows what is happening */ if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || - (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) { + (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) || + (PHPDBG_G(oplog)))) { zend_op *opline = execute_data->opline; - /* output line info */ - phpdbg_notice("#%lu %p %s %s", - opline->lineno, - opline, phpdbg_decode_opcode(opline->opcode), - execute_data->op_array->filename ? execute_data->op_array->filename : "unknown"); + + if (ignore_flags || + (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || + (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) { + /* output line info */ + phpdbg_notice("#%lu %p %s %s", + opline->lineno, + opline, phpdbg_decode_opcode(opline->opcode), + execute_data->op_array->filename ? execute_data->op_array->filename : "unknown"); + } + + if (!ignore_flags && PHPDBG_G(oplog)) { + phpdbg_log_ex(PHPDBG_G(oplog), "#%lu %p %s %s", + opline->lineno, + opline, phpdbg_decode_opcode(opline->opcode), + execute_data->op_array->filename ? execute_data->op_array->filename : "unknown"); + } } } /* }}} */ diff --git a/phpdbg_utils.c b/phpdbg_utils.c index 91e7ec2b50..7538c774dc 100644 --- a/phpdbg_utils.c +++ b/phpdbg_utils.c @@ -159,7 +159,7 @@ void phpdbg_clear_param(int type, phpdbg_param_t *param TSRMLS_DC) /* {{{ */ } } /* }}} */ -int phpdbg_print(int type TSRMLS_DC, const char *format, ...) /* {{{ */ +int phpdbg_print(int type TSRMLS_DC, FILE *fp, const char *format, ...) /* {{{ */ { int rc = 0; char *buffer = NULL; @@ -175,36 +175,45 @@ int phpdbg_print(int type TSRMLS_DC, const char *format, ...) /* {{{ */ switch (type) { case P_ERROR: - rc = printf("%s%s%s\n", - ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[1;31m[" : "["), - buffer, - ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "]\033[0m" : "]")); + rc = fprintf(fp, "%s%s%s\n", + ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[1;31m[" : "["), + buffer, + ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "]\033[0m" : "]")); break; case P_NOTICE: - rc = printf("%s%s%s\n", - ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[1;64m[" : "["), - buffer, - ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "]\033[0m" : "]")); + rc = fprintf(fp, "%s%s%s\n", + ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[1;64m[" : "["), + buffer, + ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "]\033[0m" : "]")); break; case P_WRITELN: { if (buffer) { - rc = printf("%s%s%s\n", - ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[37m" : ""), - buffer, - ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[0m" : "")); + rc = fprintf(fp, "%s%s%s\n", + ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[37m" : ""), + buffer, + ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[0m" : "")); } else { - rc = printf("\n"); + rc = fprintf(fp, "\n"); } } break; case P_WRITE: if (buffer) { - rc = printf("%s%s%s", - ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[37m" : ""), - buffer, - ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[0m" : "")); + rc = fprintf(fp, "%s%s%s", + ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[37m" : ""), + buffer, + ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED) ? "\033[0m" : "")); } break; + + /* no formatting on logging output */ + case P_LOG: if (buffer) { + struct timeval tp; + if (gettimeofday(&tp, NULL) == SUCCESS) { + rc = fprintf( + fp, "[%ld %.8F]: %s\n", tp.tv_sec, tp.tv_usec / 1000000.00, buffer); + } else rc = FAILURE; + } break; } if (buffer) { diff --git a/phpdbg_utils.h b/phpdbg_utils.h index 9ae3f8d136..57fa23c509 100644 --- a/phpdbg_utils.h +++ b/phpdbg_utils.h @@ -66,15 +66,23 @@ enum { P_ERROR = 1, P_NOTICE, P_WRITELN, - P_WRITE + P_WRITE, + P_LOG }; -int phpdbg_print(int TSRMLS_DC, const char*, ...); +int phpdbg_print(int TSRMLS_DC, FILE*, const char*, ...); -#define phpdbg_error(fmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, fmt, ##__VA_ARGS__) -#define phpdbg_notice(fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, fmt, ##__VA_ARGS__) -#define phpdbg_writeln(fmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, fmt, ##__VA_ARGS__) -#define phpdbg_write(fmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, fmt, ##__VA_ARGS__) +#define phpdbg_error(fmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, stderr, fmt, ##__VA_ARGS__) +#define phpdbg_notice(fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, stderr, fmt, ##__VA_ARGS__) +#define phpdbg_writeln(fmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, stderr, fmt, ##__VA_ARGS__) +#define phpdbg_write(fmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, stderr, fmt, ##__VA_ARGS__) +#define phpdbg_log(fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, stderr, fmt, ##__VA_ARGS__) + +#define phpdbg_error_ex(out, fmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, out, fmt, ##__VA_ARGS__) +#define phpdbg_notice_ex(out, fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, out, fmt, ##__VA_ARGS__) +#define phpdbg_writeln_ex(out, fmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, out, fmt, ##__VA_ARGS__) +#define phpdbg_write_ex(out, fmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, out, fmt, ##__VA_ARGS__) +#define phpdbg_log_ex(out, fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, out, fmt, ##__VA_ARGS__) /* {{{ For writing blank lines */ #define EMPTY "" /* }}} */ -- 2.40.0