Last active
October 26, 2016 14:30
-
-
Save ericol/65666d3476a540218c9980b38824a51c to your computer and use it in GitHub Desktop.
Patch for PHP APD (Advanced PHP Debugger)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
+----------------------------------------------------------------------+ | |
| APD Profiler & Debugger | | |
+----------------------------------------------------------------------+ | |
| Copyright (c) 2001-2003 Community Connect Inc. | | |
+----------------------------------------------------------------------+ | |
| This source file is subject to version 2.02 of the PHP license, | | |
| that is bundled with this package in the file LICENSE, and is | | |
| available at through the world-wide-web at | | |
| http://www.php.net/license/2_02.txt. | | |
| If you did not receive a copy of the PHP license and are unable to | | |
| obtain it through the world-wide-web, please send a note to | | |
| [email protected] so we can mail you a copy immediately. | | |
+----------------------------------------------------------------------+ | |
| Authors: Daniel Cowgill <[email protected]> | | |
| George Schlossnagle <[email protected]> | | |
| Sterling Hughes <[email protected]> | | |
+----------------------------------------------------------------------+ | |
*/ | |
#include "php_apd.h" | |
#ifdef PHP_WIN32 | |
#include "win32compat.h" | |
# define APD_IS_INVALID_SOCKET(a) (a == INVALID_SOCKET) | |
#else | |
#include <sys/time.h> | |
#include <sys/resource.h> | |
#include <sys/times.h> | |
#include <unistd.h> | |
# define APD_IS_INVALID_SOCKET(a) (a < 0) | |
#endif | |
#include "apd_lib.h" | |
#include "zend_API.h" | |
#include "zend_hash.h" | |
#include "zend_alloc.h" | |
#include "zend_operators.h" | |
#include "zend_globals.h" | |
#include "zend_compile.h" | |
#include <assert.h> | |
#include <stdarg.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#ifndef SUN_LEN | |
#define SUN_LEN(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) | |
#endif | |
#ifndef PF_INET | |
#define PF_INET AF_INET | |
#endif | |
/** | |
* Tracng calls to zend_compile_file | |
*/ | |
#undef TRACE_ZEND_COMPILE /* define to trace all calls to zend_compile_file */ | |
ZEND_DLEXPORT zend_op_array* apd_compile_file(zend_file_handle* TSRMLS_DC); | |
ZEND_DLEXPORT zend_op_array* (*old_compile_file)(zend_file_handle* TSRMLS_DC); | |
ZEND_DLEXPORT void apd_execute(zend_op_array *op_array TSRMLS_DC); | |
#if ZEND_EXTENSION_API_NO >= 20020731 | |
ZEND_DLEXPORT void apd_execute_internal(zend_execute_data *execute_data_ptr, int return_value_used TSRMLS_DC); | |
#endif | |
ZEND_DLEXPORT void (*old_execute)(zend_op_array *op_array TSRMLS_DC); | |
ZEND_DLEXPORT void onStatement(zend_op_array *op_array TSRMLS_DC); | |
ZEND_DECLARE_MODULE_GLOBALS(apd); | |
/* This comes from php install tree. */ | |
#include "ext/standard/info.h" | |
/* List of exported functions. */ | |
function_entry apd_functions[] = { | |
PHP_FE(override_function, NULL) | |
PHP_FE(rename_function, NULL) | |
PHP_FE(apd_set_pprof_trace, NULL) | |
PHP_FE(apd_set_browser_trace, NULL) | |
PHP_FE(apd_set_session_trace_socket, NULL) | |
PHP_FE(apd_breakpoint, NULL) | |
PHP_FE(apd_continue, NULL) | |
PHP_FE(apd_echo, NULL) | |
{NULL, NULL, NULL} | |
}; | |
void apd_pprof_fprintf(const char* fmt, ...); | |
static void log_time(TSRMLS_D); | |
long diff_times(struct timeval a, struct timeval b) | |
{ | |
long retval = (a.tv_sec - b.tv_sec) * 1000000L; | |
retval += (a.tv_usec - b.tv_usec); | |
return retval; | |
} | |
/** | |
* Output to be analyzed by pprof | |
*/ | |
static void | |
apd_pprof_output_header(void) | |
{ | |
TSRMLS_FETCH(); | |
apd_pprof_fprintf("#Pprof [APD] v1.0.1\n"); | |
apd_pprof_fprintf("caller=%s\n",zend_get_executed_filename(TSRMLS_C)); | |
apd_pprof_fprintf("\nEND_HEADER\n"); | |
} | |
static void | |
apd_pprof_output_file_reference(int id, const char *filename) | |
{ | |
apd_pprof_fprintf("! %d %s\n", id, filename); | |
} | |
static void | |
apd_pprof_output_elapsed_time(int filenum, int linenum, int usert, int systemt, int realt) | |
{ | |
apd_pprof_fprintf("@ %d %d %ld %ld %ld\n", filenum, linenum, usert, systemt, realt); | |
} | |
static void | |
apd_pprof_output_declare_function(int index, const char *fname, int type) | |
{ | |
apd_pprof_fprintf("& %d %s %d\n", index, fname, type); | |
} | |
static void | |
apd_pprof_output_enter_function(int index, int filenum, int linenum) | |
{ | |
apd_pprof_fprintf("+ %d %d %d\n", index, filenum, linenum); | |
} | |
static void | |
apd_pprof_output_exit_function(int index, int mem) | |
{ | |
if (mem == -1) { | |
apd_pprof_fprintf("- %d\n", index); | |
} else { | |
apd_pprof_fprintf("- %d %d\n", index, mem); | |
} | |
} | |
static void | |
apd_pprof_output_footer(void) | |
{ | |
struct rusage end_ru; | |
struct timeval end_clock; | |
TSRMLS_FETCH(); | |
if (!APD_GLOBALS(pprof_file)) { | |
return; | |
} | |
gettimeofday(&end_clock, NULL); | |
getrusage(RUSAGE_SELF, &end_ru); | |
apd_pprof_fprintf("END_TRACE\n"); | |
apd_pprof_fprintf("total_user=%ld\ntotal_sys=%ld\ntotal_wall=%ld\n", | |
diff_times(end_ru.ru_utime, APD_GLOBALS(first_ru).ru_utime), | |
diff_times(end_ru.ru_stime, APD_GLOBALS(first_ru).ru_stime), | |
diff_times(end_clock, APD_GLOBALS(first_clock))); | |
apd_pprof_fprintf("END_FOOTER\n"); | |
} | |
/* --------------------------------------------------------------------------- | |
apd_dump_fprintf - Outputer - either to file or socket | |
--------------------------------------------------------------------------- */ | |
void apd_dump_fprintf(const char* fmt, ...) | |
{ | |
va_list args; | |
char* newStr; | |
TSRMLS_FETCH(); | |
va_start(args, fmt); | |
newStr = apd_sprintf_real(fmt, args); | |
va_end(args); | |
if (APD_GLOBALS(dump_file) != NULL) { | |
fprintf(APD_GLOBALS(dump_file), "%s", newStr); | |
} else if ( APD_GLOBALS(dump_sock_id) > 0) { | |
#ifndef PHP_WIN32 | |
write(APD_GLOBALS(dump_sock_id), newStr, strlen (newStr) + 1); | |
#else | |
send(APD_GLOBALS(dump_sock_id), newStr, strlen (newStr) + 1, 0); | |
#endif | |
} | |
efree(newStr); | |
} | |
void apd_pprof_fprintf(const char* fmt, ...) | |
{ | |
va_list args; | |
char* newStr; | |
TSRMLS_FETCH(); | |
if(!APD_GLOBALS(pproftrace)) { | |
zend_error(E_ERROR, "pproftrace is unset"); | |
return; | |
} | |
va_start(args, fmt); | |
newStr = apd_sprintf_real(fmt, args); | |
va_end(args); | |
if (APD_GLOBALS(pprof_file) != NULL) { | |
fprintf(APD_GLOBALS(pprof_file), newStr); | |
} | |
/* | |
else if ( APD_GLOBALS(prof_sock_id) > 0) { | |
#ifndef PHP_WIN32 | |
write(APD_GLOBALS(pprof_sock_id), newStr, strlen (newStr) + 1); | |
#else | |
send(APD_GLOBALS(pprof_sock_id), newStr, strlen (newStr) + 1, 0); | |
#endif | |
} | |
*/ | |
efree(newStr); | |
} | |
/* --------------------------------------------------------------------------------- | |
Interactive Mode | |
--------------------------------------------------------------------------------- */ | |
/* interactive execution - \n continues, otherwise the data is eval'ed */ | |
void apd_interactive () { | |
char *tmpbuf=NULL,*tmp=NULL; | |
char *compiled_string_description; | |
zval retval; | |
int length = 1024; /* the maximum command length that can be accepted! */ | |
int recv_len; | |
TSRMLS_FETCH(); | |
if (APD_GLOBALS(interactive_mode) == 0) return; | |
if (APD_GLOBALS(ignore_interactive) == 1) return; | |
/* only available to sockets */ | |
if (APD_GLOBALS(dump_sock_id) < 1) return; | |
/* send the prompt! */ | |
write(APD_GLOBALS(dump_sock_id), ">\n", 3); | |
/* loop until \n is recieved */ | |
tmpbuf = emalloc(length + 1); | |
recv_len = recv(APD_GLOBALS(dump_sock_id), tmpbuf , length, 0) ; | |
if (recv_len == -1) { | |
php_error(E_WARNING, "apd debugger failed to recieve data: turning off debugger"); | |
efree(tmpbuf); | |
APD_GLOBALS(interactive_mode) = 0; | |
return; | |
} | |
tmpbuf = erealloc(tmpbuf, recv_len + 1); | |
tmpbuf[ recv_len ] = '\0' ; | |
if (strcmp(tmpbuf,"\n")==0) { | |
efree(tmpbuf); | |
return; | |
} | |
/* stop interactive debugging of it'self! */ | |
APD_GLOBALS(ignore_interactive) =1; | |
/* evaluate sring */ | |
apd_dump_fprintf("EXEC: %s",tmpbuf); | |
tmp = "apd_debugger"; | |
compiled_string_description = zend_make_compiled_string_description("Apd Debugger" TSRMLS_CC); | |
if (zend_eval_string(tmpbuf, &retval, compiled_string_description TSRMLS_CC) == FAILURE) { | |
efree(compiled_string_description); | |
zend_error(E_ERROR, "Failure evaluating code:\n%s\n", tmpbuf); | |
} | |
APD_GLOBALS(ignore_interactive) =0; | |
efree(tmpbuf); | |
/* call myself again! = recursive! */ | |
apd_interactive(); | |
} | |
/* --------------------------------------------------------------------------- | |
Call Tracing (Application-specific Code) | |
--------------------------------------------------------------------------- */ | |
char *apd_get_active_function_name(zend_op_array *op_array TSRMLS_DC) | |
{ | |
char *funcname = NULL; | |
int curSize = 0; | |
zend_execute_data *execd = NULL; | |
char *tmpfname; | |
char *classname; | |
int classnameLen; | |
int tmpfnameLen; | |
execd = EG(current_execute_data); | |
if(execd) { | |
tmpfname = execd->function_state.function->common.function_name; | |
if(tmpfname) { | |
tmpfnameLen = strlen(tmpfname); | |
if(execd->object) { | |
classname = Z_OBJCE(*execd->object)->name; | |
classnameLen = strlen(classname); | |
funcname = (char *)emalloc(classnameLen + tmpfnameLen + 3); | |
snprintf(funcname, classnameLen + tmpfnameLen + 3, "%s->%s", | |
classname, tmpfname); | |
} | |
else if(execd->function_state.function->common.scope) { | |
classname = execd->function_state.function->common.scope->name; | |
classnameLen = strlen(classname); | |
funcname = (char *)emalloc(classnameLen + tmpfnameLen + 3); | |
snprintf(funcname, classnameLen + tmpfnameLen + 3, "%s::%s", | |
classname, tmpfname); | |
} | |
else { | |
funcname = estrdup(tmpfname); | |
} | |
} | |
else { | |
switch (execd->opline->op2.u.constant.value.lval) { | |
case ZEND_EVAL: | |
funcname = estrdup("eval"); | |
break; | |
case ZEND_INCLUDE: | |
funcname = estrdup("include"); | |
break; | |
case ZEND_REQUIRE: | |
funcname = estrdup("require"); | |
break; | |
case ZEND_INCLUDE_ONCE: | |
funcname = estrdup("include_once"); | |
break; | |
case ZEND_REQUIRE_ONCE: | |
funcname = estrdup("require_once"); | |
break; | |
default: | |
funcname = estrdup("???"); | |
break; | |
} | |
} | |
} | |
else { | |
funcname = estrdup("main"); | |
} | |
return funcname; | |
} | |
static void trace_function_entry(HashTable *func_table, const char *fname, int type, const char *filename, int linenum) | |
{ | |
int i = 0; | |
int *function_index; | |
int *filenum; | |
TSRMLS_FETCH(); | |
if (!APD_GLOBALS(pproftrace)) { | |
return; | |
} | |
if (zend_hash_find(APD_GLOBALS(file_summary), (char *) filename, strlen(filename) + 1, (void *) &filenum) == FAILURE) { | |
filenum = (int *) emalloc(sizeof(int)); | |
*filenum = ++APD_GLOBALS(file_index); | |
APD_GLOBALS(output).file_reference(*filenum, filename); | |
zend_hash_add(APD_GLOBALS(file_summary), (char *) filename, strlen(filename) + 1, filenum, sizeof(int), NULL); | |
} | |
APD_GLOBALS(current_file_index) = *filenum; | |
if (zend_hash_find(APD_GLOBALS(function_summary), (char *) fname, strlen(fname)+1, (void *) &function_index) == SUCCESS) { | |
APD_GLOBALS(output).enter_function(*function_index, *filenum, linenum); | |
} else { | |
function_index = (int *) emalloc(sizeof(int)); | |
*function_index = ++APD_GLOBALS(function_index); | |
zend_hash_add(APD_GLOBALS(function_summary), (char *) fname, strlen(fname)+1, function_index, sizeof(int), NULL); | |
APD_GLOBALS(output).declare_function(*function_index, fname, type); | |
APD_GLOBALS(output).enter_function(*function_index, *filenum, linenum); | |
} | |
} | |
static void trace_function_exit(char *fname) | |
{ | |
struct rusage wall_ru; | |
struct timeval clock; | |
long utime, stime, rtime; | |
int *function_index; | |
int allocated = -1; | |
TSRMLS_FETCH(); | |
if (!APD_GLOBALS(pproftrace)) { | |
return; | |
} | |
#if MEMORY_LIMIT | |
allocated = AG(memory_limit); | |
#endif | |
log_time(TSRMLS_C); | |
if (zend_hash_find(APD_GLOBALS(function_summary), fname, strlen(fname) + 1, (void *) &function_index) == SUCCESS) { | |
APD_GLOBALS(output).exit_function(*function_index, allocated); | |
} else { | |
function_index = (int *) emalloc(sizeof(int)); | |
*function_index = ++APD_GLOBALS(function_index); | |
zend_hash_add(APD_GLOBALS(function_summary), fname, strlen(fname)+1, function_index, sizeof(int), NULL); | |
APD_GLOBALS(output).exit_function(*function_index, allocated); | |
} | |
} | |
static void log_time(TSRMLS_D) | |
{ | |
struct timeval clock; | |
struct rusage wall_ru; | |
gettimeofday(&clock, NULL); | |
getrusage(RUSAGE_SELF, &wall_ru); | |
if (APD_GLOBALS(function_index) > 1) { | |
long utime, stime, rtime; | |
utime = diff_times(wall_ru.ru_utime, APD_GLOBALS(last_ru).ru_utime); | |
stime = diff_times(wall_ru.ru_stime, APD_GLOBALS(last_ru).ru_stime); | |
rtime = diff_times(clock, APD_GLOBALS(last_clock)); | |
if(utime || stime || rtime) { | |
APD_GLOBALS(output).elapsed_time(APD_GLOBALS(current_file_index), zend_get_executed_lineno(TSRMLS_C), utime, stime, rtime); | |
} | |
} | |
APD_GLOBALS(last_ru) = wall_ru; | |
APD_GLOBALS(last_clock) = clock; | |
} | |
/* -------------------------------------------------------------------------- | |
Module Entry | |
--------------------------------------------------------------------------- */ | |
zend_module_entry apd_module_entry = { | |
STANDARD_MODULE_HEADER, | |
"APD", | |
apd_functions, | |
PHP_MINIT(apd), | |
NULL, | |
PHP_RINIT(apd), | |
PHP_RSHUTDOWN(apd), | |
PHP_MINFO(apd), | |
NO_VERSION_YET, /* extension version number (string) */ | |
STANDARD_MODULE_PROPERTIES | |
}; | |
#if COMPILE_DL_APD | |
ZEND_GET_MODULE(apd) | |
#endif | |
/* --------------------------------------------------------------------------- | |
PHP Configuration Functions | |
--------------------------------------------------------------------------- */ | |
static PHP_INI_MH(set_dumpdir) | |
{ | |
struct stat sb; | |
if (new_value == NULL || stat(new_value, &sb)) { | |
APD_GLOBALS(dumpdir) = NULL; | |
} | |
else { | |
APD_GLOBALS(dumpdir) = new_value; | |
} | |
return SUCCESS; | |
} | |
PHP_INI_BEGIN() | |
PHP_INI_ENTRY("apd.dumpdir", NULL, PHP_INI_ALL, set_dumpdir) | |
STD_PHP_INI_ENTRY("apd.statement_tracing", "0", PHP_INI_ALL, OnUpdateLong, statement_tracing, zend_apd_globals, apd_globals) | |
PHP_INI_END() | |
/* --------------------------------------------------------------------------- | |
Module Startup and Shutdown Function Definitions | |
--------------------------------------------------------------------------- */ | |
static void php_apd_init_globals(zend_apd_globals *apd_globals) | |
{ | |
memset(apd_globals, 0, sizeof(zend_apd_globals)); | |
apd_globals->function_summary = (HashTable *) malloc(sizeof(HashTable)); | |
apd_globals->file_summary = (HashTable *) malloc(sizeof(HashTable)); | |
zend_hash_init(apd_globals->function_summary, 0, NULL, NULL, 1); | |
zend_hash_init(apd_globals->file_summary, 0, NULL, NULL, 1); | |
} | |
static void php_apd_free_globals(zend_apd_globals *apd_globals) | |
{ | |
free(apd_globals->function_summary); | |
free(apd_globals->file_summary); | |
} | |
PHP_MINIT_FUNCTION(apd) | |
{ | |
ZEND_INIT_MODULE_GLOBALS(apd, php_apd_init_globals, php_apd_free_globals); | |
REGISTER_INI_ENTRIES(); | |
old_execute = zend_execute; | |
zend_execute = apd_execute; | |
zend_execute_internal = apd_execute_internal; | |
return SUCCESS; | |
} | |
ZEND_API void apd_execute(zend_op_array *op_array TSRMLS_DC) | |
{ | |
char *fname = NULL; | |
char *tmp; | |
void **p; | |
int argCount; | |
zval **object_ptr_ptr; | |
fname = apd_get_active_function_name(op_array TSRMLS_CC); | |
trace_function_entry(EG(function_table), fname, ZEND_USER_FUNCTION, | |
zend_get_executed_filename(TSRMLS_C), | |
zend_get_executed_lineno(TSRMLS_C)); | |
old_execute(op_array TSRMLS_CC); | |
trace_function_exit(fname); | |
efree(fname); | |
apd_interactive(); | |
} | |
ZEND_API void apd_execute_internal(zend_execute_data *execute_data_ptr, int return_value_used TSRMLS_DC) | |
{ | |
char *fname = NULL; | |
char *tmp; | |
void **p; | |
int argCount; | |
zval **object_ptr_ptr; | |
zend_execute_data *execd; | |
execd = EG(current_execute_data); | |
fname = apd_get_active_function_name(execd->op_array TSRMLS_CC); | |
trace_function_entry(EG(function_table), fname, ZEND_INTERNAL_FUNCTION, | |
zend_get_executed_filename(TSRMLS_C), | |
zend_get_executed_lineno(TSRMLS_C)); | |
execute_internal(execute_data_ptr, return_value_used TSRMLS_CC); | |
trace_function_exit(fname); | |
efree(fname); | |
apd_interactive(); | |
} | |
PHP_RINIT_FUNCTION(apd) | |
{ | |
APD_GLOBALS(output).header = apd_pprof_output_header; | |
APD_GLOBALS(output).footer = apd_pprof_output_footer; | |
APD_GLOBALS(output).file_reference = apd_pprof_output_file_reference; | |
APD_GLOBALS(output).elapsed_time = apd_pprof_output_elapsed_time; | |
APD_GLOBALS(output).declare_function = apd_pprof_output_declare_function; | |
APD_GLOBALS(output).enter_function = apd_pprof_output_enter_function; | |
APD_GLOBALS(output).exit_function = apd_pprof_output_exit_function; | |
APD_GLOBALS(dump_file) = stderr; | |
APD_GLOBALS(dump_sock_id) = 0; | |
APD_GLOBALS(interactive_mode) = 0; | |
APD_GLOBALS(ignore_interactive) = 0; | |
gettimeofday(&APD_GLOBALS(last_clock), NULL); | |
getrusage(RUSAGE_SELF, &APD_GLOBALS(last_ru)); | |
APD_GLOBALS(first_ru) = APD_GLOBALS(last_ru); | |
APD_GLOBALS(first_clock) = APD_GLOBALS(last_clock); | |
/** | |
* These values must start from 1 because they are stored in the hashtable, and | |
* Zend will try to free them, and die freeing a NULL pointer otherwise. | |
*/ | |
APD_GLOBALS(file_index) = 1; | |
APD_GLOBALS(function_index) = 1; | |
return SUCCESS; | |
} | |
PHP_RSHUTDOWN_FUNCTION(apd) | |
{ | |
// APD_GLOBALS(output).footer(); | |
if (APD_GLOBALS(pprof_file)) { | |
fclose(APD_GLOBALS(pprof_file)); | |
} | |
if (APD_GLOBALS(dump_sock_id)) { | |
close(APD_GLOBALS(dump_sock_id)); | |
/* bit academic - but may as well */ | |
APD_GLOBALS(dump_sock_id)=0; | |
} | |
zend_hash_clean(APD_GLOBALS(function_summary)); | |
zend_hash_clean(APD_GLOBALS(file_summary)); | |
APD_GLOBALS(counter)++; | |
return SUCCESS; | |
} | |
PHP_MINFO_FUNCTION(apd) | |
{ | |
php_info_print_table_start(); | |
php_info_print_table_header(2, "Advanced PHP Debugger (APD)", "Enabled"); | |
php_info_print_table_row(2, "APD Version", APD_VERSION); | |
php_info_print_table_end(); | |
} | |
/* --------------------------------------------------------------------------- | |
PHP Extension Functions | |
--------------------------------------------------------------------------- */ | |
#define TEMP_OVRD_FUNC_NAME "__overridden__" | |
PHP_FUNCTION(override_function) | |
{ | |
char *eval_code,*eval_name, *temp_function_name; | |
int eval_code_length, retval, temp_function_name_length; | |
zval **z_function_name, **z_function_args, **z_function_code; | |
if (ZEND_NUM_ARGS() != 3 || | |
zend_get_parameters_ex(3, &z_function_name, &z_function_args, | |
&z_function_code) == FAILURE) | |
{ | |
ZEND_WRONG_PARAM_COUNT(); | |
} | |
convert_to_string_ex(z_function_name); | |
convert_to_string_ex(z_function_args); | |
convert_to_string_ex(z_function_code); | |
eval_code_length = sizeof("function " TEMP_OVRD_FUNC_NAME) | |
+ Z_STRLEN_PP(z_function_args) | |
+ 2 /* parentheses */ | |
+ 2 /* curlies */ | |
+ Z_STRLEN_PP(z_function_code); | |
eval_code = (char *) emalloc(eval_code_length); | |
sprintf(eval_code, "function " TEMP_OVRD_FUNC_NAME "(%s){%s}", | |
Z_STRVAL_PP(z_function_args), Z_STRVAL_PP(z_function_code)); | |
eval_name = zend_make_compiled_string_description("runtime-created override function" TSRMLS_CC); | |
retval = zend_eval_string(eval_code, NULL, eval_name TSRMLS_CC); | |
efree(eval_code); | |
efree(eval_name); | |
if (retval == SUCCESS) { | |
zend_function *func; | |
if (zend_hash_find(EG(function_table), TEMP_OVRD_FUNC_NAME, | |
sizeof(TEMP_OVRD_FUNC_NAME), (void **) &func) == FAILURE) | |
{ | |
zend_error(E_ERROR, "%s() temporary function name not present in global function_table", get_active_function_name(TSRMLS_C)); | |
RETURN_FALSE; | |
} | |
function_add_ref(func); | |
zend_hash_del(EG(function_table), Z_STRVAL_PP(z_function_name), | |
Z_STRLEN_PP(z_function_name) + 1); | |
if(zend_hash_add(EG(function_table), Z_STRVAL_PP(z_function_name), | |
Z_STRLEN_PP(z_function_name) + 1, func, sizeof(zend_function), | |
NULL) == FAILURE) | |
{ | |
RETURN_FALSE; | |
} | |
RETURN_TRUE; | |
} | |
else { | |
RETURN_FALSE; | |
} | |
} | |
PHP_FUNCTION(rename_function) | |
{ | |
zval **z_orig_fname, **z_new_fname; | |
zend_function *func, *dummy_func; | |
if( ZEND_NUM_ARGS() != 2 || | |
zend_get_parameters_ex(2, &z_orig_fname, &z_new_fname) == FAILURE ) | |
{ | |
ZEND_WRONG_PARAM_COUNT(); | |
} | |
convert_to_string_ex(z_orig_fname); | |
convert_to_string_ex(z_new_fname); | |
if(zend_hash_find(EG(function_table), Z_STRVAL_PP(z_orig_fname), | |
Z_STRLEN_PP(z_orig_fname) + 1, (void **) &func) == FAILURE) | |
{ | |
zend_error(E_WARNING, "%s(%s, %s) failed: %s does not exist!", | |
get_active_function_name(TSRMLS_C), | |
Z_STRVAL_PP(z_orig_fname), Z_STRVAL_PP(z_new_fname), | |
Z_STRVAL_PP(z_orig_fname)); | |
RETURN_FALSE; | |
} | |
if(zend_hash_find(EG(function_table), Z_STRVAL_PP(z_new_fname), | |
Z_STRLEN_PP(z_new_fname) + 1, (void **) &dummy_func) == SUCCESS) | |
{ | |
zend_error(E_WARNING, "%s(%s, %s) failed: %s already exists!", | |
get_active_function_name(TSRMLS_C), | |
Z_STRVAL_PP(z_orig_fname), Z_STRVAL_PP(z_new_fname), | |
Z_STRVAL_PP(z_new_fname)); | |
RETURN_FALSE; | |
} | |
if(zend_hash_add(EG(function_table), Z_STRVAL_PP(z_new_fname), | |
Z_STRLEN_PP(z_new_fname) + 1, func, sizeof(zend_function), | |
NULL) == FAILURE) | |
{ | |
zend_error(E_WARNING, "%s() failed to insert %s into EG(function_table)", | |
get_active_function_name(TSRMLS_C), | |
Z_STRVAL_PP(z_new_fname)); | |
RETURN_FALSE; | |
} | |
if(zend_hash_del(EG(function_table), Z_STRVAL_PP(z_orig_fname), | |
Z_STRLEN_PP(z_orig_fname) + 1) == FAILURE) | |
{ | |
zend_error(E_WARNING, "%s() failed to remove %s from function table", | |
get_active_function_name(TSRMLS_C), | |
Z_STRVAL_PP(z_orig_fname)); | |
zend_hash_del(EG(function_table), Z_STRVAL_PP(z_new_fname), | |
Z_STRLEN_PP(z_new_fname) + 1); | |
RETURN_FALSE; | |
} | |
RETURN_TRUE; | |
} | |
void apd_pprof_header(char *ent_fname TSRMLS_DC) { | |
char *fname = "main"; | |
char *filename; | |
int linenum, *filenum, *fnum; | |
APD_GLOBALS(output).header(); | |
filename = zend_get_executed_filename(TSRMLS_C); | |
linenum = zend_get_executed_lineno(TSRMLS_C); | |
fnum = (int *) emalloc(sizeof(int)); | |
*fnum = APD_GLOBALS(function_index)++; | |
zend_hash_add(APD_GLOBALS(function_summary), fname, strlen(fname)+1, fnum, sizeof(int), NULL); | |
filenum = (int *) emalloc(sizeof(int)); | |
*filenum = APD_GLOBALS(file_index)++; | |
zend_hash_add(APD_GLOBALS(file_summary), (char *) filename, strlen(filename)+1, filenum, sizeof(int), NULL); | |
APD_GLOBALS(output).file_reference(*filenum, filename); | |
APD_GLOBALS(output).declare_function(*fnum, fname, ZEND_USER_FUNCTION); | |
APD_GLOBALS(output).enter_function(*fnum, *filenum, linenum); | |
fnum = (int *) emalloc(sizeof(int)); | |
*fnum = APD_GLOBALS(function_index)++; | |
zend_hash_add(APD_GLOBALS(function_summary), ent_fname, strlen(ent_fname)+1, fnum, sizeof(int), NULL); | |
APD_GLOBALS(output).declare_function(*fnum, ent_fname, ZEND_USER_FUNCTION); | |
APD_GLOBALS(output).enter_function(*fnum, *filenum, linenum); | |
} | |
PHP_FUNCTION(apd_set_browser_trace) | |
{ | |
if (ZEND_NUM_ARGS() > 1) { | |
ZEND_WRONG_PARAM_COUNT(); | |
} | |
APD_GLOBALS(pproftrace) = 1; | |
APD_GLOBALS(output).header = apd_summary_output_header; | |
APD_GLOBALS(output).footer = apd_summary_output_footer; | |
APD_GLOBALS(output).file_reference = apd_summary_output_file_reference; | |
APD_GLOBALS(output).elapsed_time = apd_summary_output_elapsed_time; | |
APD_GLOBALS(output).declare_function = apd_summary_output_declare_function; | |
APD_GLOBALS(output).enter_function = apd_summary_output_enter_function; | |
APD_GLOBALS(output).exit_function = apd_summary_output_exit_function; | |
apd_pprof_header("apd_set_broswer_trace" TSRMLS_CC); | |
} | |
PHP_FUNCTION(apd_set_pprof_trace) | |
{ | |
int issock =0; | |
int socketd = 0; | |
int path_len; | |
char *dumpdir; | |
char path[MAXPATHLEN]; | |
zval **z_dumpdir; | |
if(ZEND_NUM_ARGS() > 1 ) | |
{ | |
ZEND_WRONG_PARAM_COUNT(); | |
} | |
if(ZEND_NUM_ARGS() == 0) | |
{ | |
if(APD_GLOBALS(dumpdir)) { | |
dumpdir = APD_GLOBALS(dumpdir); | |
} | |
else { | |
zend_error(E_WARNING, "%s() no dumpdir specified", | |
get_active_function_name(TSRMLS_C)); | |
RETURN_FALSE; | |
} | |
APD_GLOBALS(pproftrace) = 1; | |
} | |
else { | |
if(zend_get_parameters_ex(1, &z_dumpdir) == FAILURE) | |
{ | |
ZEND_WRONG_PARAM_COUNT(); | |
} | |
APD_GLOBALS(pproftrace) = 1; | |
convert_to_string_ex(z_dumpdir); | |
dumpdir = Z_STRVAL_PP(z_dumpdir); | |
} | |
snprintf(path, MAXPATHLEN, "%s/pprof.%05d.%d", dumpdir, getpid(), APD_GLOBALS(counter)); | |
if((APD_GLOBALS(pprof_file) = fopen(path, "a")) == NULL) { | |
zend_error(E_ERROR, "%s() failed to open %s for tracing", get_active_function_name(TSRMLS_C), path); | |
} | |
apd_pprof_header("apd_set_pprof_trace" TSRMLS_CC); | |
} | |
/* {{{ proto bool apd_set_session_trace_socket(string ip_or_filename, int domain, int port, int mask) | |
Connects to a socket either unix domain or tcpip and sends data there*/ | |
PHP_FUNCTION(apd_set_session_trace_socket) | |
{ | |
char *address; | |
int address_len; | |
int sock_domain; | |
int sock_port; | |
int connect_result; | |
struct sockaddr_in si; | |
struct sockaddr_un su; | |
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, | |
"slll", &address,&address_len, &sock_domain, &sock_port) == FAILURE) | |
{ | |
return; | |
} | |
APD_GLOBALS(dump_file)=NULL; | |
/* now for the socket stuff */ | |
/* is it already connected ? */ | |
if (APD_GLOBALS(dump_sock_id) > 0) { | |
RETURN_TRUE; | |
} | |
if (sock_domain != AF_UNIX && sock_domain != AF_INET) { | |
php_error(E_WARNING, "%s() invalid socket domain [%d] specified for argument 2, assuming AF_INET", get_active_function_name(TSRMLS_C), sock_domain); | |
sock_domain = AF_INET; | |
} | |
APD_GLOBALS(dump_sock_id) = socket(sock_domain, SOCK_STREAM, 0); | |
if (APD_IS_INVALID_SOCKET(APD_GLOBALS(dump_sock_id))) { | |
/* this may not work = it assumes that 0 would be an invalid socket! */ | |
APD_GLOBALS(dump_sock_id)=0; | |
RETURN_FALSE; | |
} | |
/* now connect it */ | |
switch(sock_domain) { | |
case AF_UNIX: | |
{ | |
su.sun_family = AF_UNIX; | |
strncpy (su.sun_path, address, sizeof (su.sun_path)); | |
if (connect(APD_GLOBALS(dump_sock_id), (struct sockaddr *) &su, SUN_LEN(&su)) < 0) | |
{ | |
php_error(E_WARNING, "%s() failed to connect to [%s]", get_active_function_name(TSRMLS_C), address); | |
APD_GLOBALS(dump_sock_id)=0; | |
RETURN_FALSE; | |
} | |
break; | |
} | |
case AF_INET: | |
{ | |
struct hostent *hp; | |
si.sin_family = AF_INET; | |
si.sin_port = htons (sock_port); | |
hp = gethostbyname (address); | |
if (hp == NULL) | |
{ | |
php_error(E_WARNING, "%s() failed to get host by name [%s]", get_active_function_name(TSRMLS_C), address); | |
APD_GLOBALS(dump_sock_id)=0; | |
RETURN_FALSE; | |
} | |
si.sin_addr = *(struct in_addr *) hp->h_addr; | |
connect_result = connect(APD_GLOBALS(dump_sock_id), (struct sockaddr *) &si, sizeof (si)); | |
if (0 > connect_result) | |
{ | |
php_error(E_WARNING, "%s() failed to connect to [%s:%d] %d", get_active_function_name(TSRMLS_C), address,sock_port,connect_result); | |
APD_GLOBALS(dump_sock_id)=0; | |
RETURN_FALSE; | |
} | |
break; | |
} | |
} | |
apd_summary_output_header(); | |
RETURN_TRUE; | |
} | |
/* }}} */ | |
/* {{{ proto void apd_breakpoint() | |
Starts interactive mode*/ | |
PHP_FUNCTION(apd_breakpoint) | |
{ | |
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, | |
"l") == FAILURE) { | |
return; | |
} | |
APD_GLOBALS(interactive_mode) = 1; | |
RETURN_TRUE; | |
} | |
/* }}} */ | |
/* {{{ proto void apd_continue() | |
Stops interactive mode - normally called by interactive interpreter! */ | |
PHP_FUNCTION(apd_continue) | |
{ | |
APD_GLOBALS(interactive_mode) = 0; | |
RETURN_TRUE; | |
} | |
/* }}} */ | |
/* {{{ proto apd_echo(string output) | |
sends string to debugger - normally called by interactive interpreter!*/ | |
PHP_FUNCTION(apd_echo) | |
{ | |
char *str; | |
int str_len; | |
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str,&str_len) == FAILURE) { | |
return; | |
} | |
if (str_len > 0) { | |
/* php_error(E_WARNING, "apd echoing %s", str); */ | |
if ( APD_GLOBALS(dump_sock_id) > 0) { | |
#ifndef PHP_WIN32 | |
/* Unix bit */ | |
write(APD_GLOBALS(dump_sock_id), str, str_len); | |
write(APD_GLOBALS(dump_sock_id), "\n", 2); | |
#else | |
/* Win32 bit */ | |
send(APD_GLOBALS(dump_sock_id), str, str_len, 0); | |
send(APD_GLOBALS(dump_sock_id), "\n", 2, 0); | |
#endif | |
} | |
apd_dump_fprintf("%s\n",str); | |
/* efree(str); */ | |
} | |
RETURN_TRUE; | |
} | |
/* }}} */ | |
// --------------------------------------------------------------------------- | |
// Zend Extension Functions | |
// --------------------------------------------------------------------------- | |
ZEND_DLEXPORT void onStatement(zend_op_array *op_array) | |
{ | |
TSRMLS_FETCH(); | |
if(APD_GLOBALS(pproftrace) && APD_GLOBALS(statement_tracing)) { | |
log_time(TSRMLS_C); | |
} | |
} | |
int apd_zend_startup(zend_extension *extension) | |
{ | |
TSRMLS_FETCH(); | |
CG(extended_info) = 1; /* XXX: this is ridiculous */ | |
return zend_startup_module(&apd_module_entry); | |
} | |
ZEND_DLEXPORT void apd_zend_shutdown(zend_extension *extension) | |
{ | |
/* Do nothing. */ | |
} | |
#ifndef ZEND_EXT_API | |
#define ZEND_EXT_API ZEND_DLEXPORT | |
#endif | |
ZEND_EXTENSION(); | |
ZEND_DLEXPORT zend_extension zend_extension_entry = { | |
"Advanced PHP Debugger (APD)", | |
APD_VERSION, | |
"George Schlossnagle", | |
"http://pear.php.net/", | |
"", | |
apd_zend_startup, | |
apd_zend_shutdown, | |
NULL, // activate_func_t | |
NULL, // deactivate_func_t | |
NULL, // message_handler_func_t | |
NULL, // op_array_handler_func_t | |
onStatement, // statement_handler_func_t | |
NULL, // fcall_begin_handler_func_t | |
NULL, // fcall_end_handler_func_t | |
NULL, // op_array_ctor_func_t | |
NULL, // op_array_dtor_func_t | |
NULL, // api_no_check | |
COMPAT_ZEND_EXTENSION_PROPERTIES | |
}; | |
/** | |
* Local Variables: | |
* indent-tabs-mode: t | |
* tab-width: 4 | |
* c-basic-offset: 4 | |
* End: | |
* vim600:fdm=marker | |
* vim:noet:sw=4:ts=4 | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment