2013-12-13 22:57:36 +00:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2015-03-13 09:09:42 +00:00
| PHP Version 7 |
2013-12-13 22:57:36 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2016-01-01 17:19:27 +00:00
| Copyright ( c ) 1997 - 2016 The PHP Group |
2013-12-13 22:57:36 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| This source file is subject to version 3.01 of the PHP license , |
| that is bundled with this package in the file LICENSE , and is |
| available through the world - wide - web at the following url : |
| http : //www.php.net/license/3_01.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 |
| license @ php . net so we can mail you a copy immediately . |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Authors : Felipe Pena < felipe @ php . net > |
| Authors : Joe Watkins < joe . watkins @ live . co . uk > |
| Authors : Bob Weinand < bwoebi @ php . net > |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
2015-04-19 10:53:06 +00:00
/* Some information for the reader...
*
* Watchpoints are either simple , recursive or implicit ( PHPDBG_WATCH_ * flags )
* Simple means that a particular watchpoint was explicitely defined
* Recursive watchpoints are created recursively and substitute simple watchpoints
* Implicit watchpoints are implicitely created on all ancestors of simple or recursive watchpoints
* Recursive and ( simple or implicit ) watchpoints are mutually exclusive
*
* PHPDBG_G ( watchpoint_tree ) contains all watchpoints identified by the watch target address
* PHPDBG_G ( watch_HashTables ) contains the dtors of the HashTables to call in our custom dtor ( we substitute the dtor of HashTables containing watched zvals by our own dtor )
* PHPDBG_G ( watchpoints ) contains all watchpoints ( except the ones managed by watch collision )
* PHPDBG_G ( watch_collisions ) is indexed by a zend_reference * pointer . It stores information about collisions ( everything which contains a zend_reference * may be referenced by multiple watches )
*
* Creating a watchpoint :
* * Create watchpoints with PHPDBG_WATCH_IMPLICIT set on each zval and HashTable in hierarchy except the last zval or HashTable fetch . ( if already existing PHPDBG_WATCH_IMPLICIT flag is added )
* * Create a PHPDBG_WATCH_SIMPLE watch for simple watches or a PHPDBG_WATCH_RECURSIVE watch for recursive watches
* * When the target zval is an IS_REFERENCE , create a watchpoint on it too
* * Each time a watchpoints parent is a zval and it is Z_REFCOUNTED ( ) , put a watchpoint ( WATCH_ON_REFCOUNTED ) on it and add a watchpoint collision
* * When in recursive mode and encountering a not - refcounted PHPDBG_WATCH_SIMPLE , remove it and recreate it with a PHPDBG_WATCH_RECURSIVE ( handled via watch collision )
* * Make attention to not add something twice or iterate over it twice
*
* Deleting a watchpoint :
* * Only allow deletion of recursive watches at their root and simple watches
* * Go to referenced variable . And remove watch collision on * parent * ( if there is a parent )
* * If it is Z_REFCOUNTED ( ) , remove that watch collision
*
* Watch collisions :
* * hold a counter for recursive , if it is incremented from 0 to 1 , create recursive watchpoint
* * holds a HashTable for normal ( not implicit ) watchpoints . . . it is used to get the fetch type of the HashTable ( depending on whether it is empty or not )
* * holds a HashTable for implicit watchpoints . . . ( some sort of a refcounter , but ensure that there are no duplicates )
* * if normal and implicit watchpoints are empty , drop that watch collision and remove WATCH_ON_REFCOUNT alongside with watchpoint on an eventual reference
*
* Watching on addresses :
* * Address and size are transformed into memory page aligned address and size
* * mprotect ( ) enables or disables them ( depending on flags ) - Windows has a transparent compatibility layer in phpdbg_win . c
* * segfault handler dumps watched memory segment and deactivates watchpoint
* * later watches inside these memory segments are compared against their current value and eventually reactivated ( or deleted )
*
* A watched zval was removed :
* * trigger a memory copy ( in segv handler ) and an automatic deactivation
* * change a type flag _zval_struct . u1 . v . type_flags ( add PHPDBG_DESTRUCTED_ZVAL flag ) in memory dump
*
* A watched zval was changed :
* * check if parent container has a different reference for referenced zval - recursively update all watches and drop them if necessary
* * if _zval_struct . u1 . v . type_flags & PHPDBG_DESTRUCTED_ZVAL , add it to a list of zvals to be handled at the end ( if location was not changed , remove it finally )
* * display changes if watch - > flags & PHPDBG_WATCH_NORMAL ( means : not implicit )
* * handle case where Z_RECOUNTED ( ) or Z_PTR ( ) changed ( remove / add collison ( s ) )
* * if necessary . . . on zvals : handle references , if recursive also objects and arrays . . . on arrays : if recursive , add new elements
* * drop destructed zval watchpoints which were not updated
*/
2013-12-11 20:31:22 +00:00
# include "zend.h"
# include "phpdbg.h"
2013-12-15 14:05:50 +00:00
# include "phpdbg_btree.h"
2013-12-11 20:31:22 +00:00
# include "phpdbg_watch.h"
2013-12-19 16:05:38 +00:00
# include "phpdbg_utils.h"
2015-03-21 22:19:12 +00:00
# include "phpdbg_prompt.h"
2014-03-16 18:39:02 +00:00
# ifndef _WIN32
# include <unistd.h>
# include <sys / mman.h>
# endif
2013-12-11 20:31:22 +00:00
2016-02-16 22:47:37 +00:00
ZEND_EXTERN_MODULE_GLOBALS ( phpdbg )
2013-12-11 20:31:22 +00:00
2014-10-24 17:29:50 +00:00
const phpdbg_command_t phpdbg_watch_commands [ ] = {
2015-03-21 22:19:12 +00:00
PHPDBG_COMMAND_D_EX ( array , " create watchpoint on an array " , ' a ' , watch_array , & phpdbg_prompt_commands [ 24 ] , " s " , 0 ) ,
PHPDBG_COMMAND_D_EX ( delete , " delete watchpoint " , ' d ' , watch_delete , & phpdbg_prompt_commands [ 24 ] , " s " , 0 ) ,
PHPDBG_COMMAND_D_EX ( recursive , " create recursive watchpoints " , ' r ' , watch_recursive , & phpdbg_prompt_commands [ 24 ] , " s " , 0 ) ,
2014-10-24 17:29:50 +00:00
PHPDBG_END_COMMAND
} ;
2014-12-13 22:06:14 +00:00
//#define HT_FROM_WATCH(watch) (watch->type == WATCH_ON_OBJECT ? watch->addr.obj->handlers->get_properties(watch->parent_container.zv) : watch->type == WATCH_ON_ARRAY ? &watch->addr.arr->ht : NULL)
2014-10-24 17:29:50 +00:00
# define HT_FROM_ZVP(zvp) (Z_TYPE_P(zvp) == IS_OBJECT ? Z_OBJPROP_P(zvp) : Z_TYPE_P(zvp) == IS_ARRAY ? Z_ARRVAL_P(zvp) : NULL)
2013-12-11 20:31:22 +00:00
2015-04-19 10:53:06 +00:00
# define HT_WATCH_OFFSET (sizeof(zend_refcounted *) + sizeof(uint32_t)) /* we are not interested in gc and flags */
# define HT_PTR_HT(ptr) ((HashTable *) (((char *) (ptr)) - HT_WATCH_OFFSET))
# define HT_WATCH_HT(watch) HT_PTR_HT((watch)->addr.ptr)
2013-12-13 22:57:36 +00:00
typedef struct {
void * page ;
size_t size ;
2014-03-11 10:14:32 +00:00
char reenable_writing ;
2013-12-13 22:57:36 +00:00
/* data must be last element */
void * data ;
} phpdbg_watch_memdump ;
# define MEMDUMP_SIZE(size) (sizeof(phpdbg_watch_memdump) - sizeof(void *) + (size))
2014-12-13 22:06:14 +00:00
static phpdbg_watchpoint_t * phpdbg_check_for_watchpoint ( void * addr ) {
2013-12-20 03:21:08 +00:00
phpdbg_watchpoint_t * watch ;
phpdbg_btree_result * result = phpdbg_btree_find_closest ( & PHPDBG_G ( watchpoint_tree ) , ( zend_ulong ) phpdbg_get_page_boundary ( addr ) + phpdbg_pagesize - 1 ) ;
if ( result = = NULL ) {
return NULL ;
}
watch = result - > ptr ;
2013-12-14 10:39:09 +00:00
/* check if that addr is in a mprotect()'ed memory area */
2014-10-24 17:29:50 +00:00
if ( ( char * ) phpdbg_get_page_boundary ( watch - > addr . ptr ) > ( char * ) addr | | ( char * ) phpdbg_get_page_boundary ( watch - > addr . ptr ) + phpdbg_get_total_page_size ( watch - > addr . ptr , watch - > size ) < ( char * ) addr ) {
2013-12-13 22:57:36 +00:00
/* failure */
return NULL ;
}
return watch ;
2013-12-11 20:31:22 +00:00
}
2014-12-13 22:06:14 +00:00
static void phpdbg_change_watchpoint_access ( phpdbg_watchpoint_t * watch , int access ) {
2013-12-16 15:29:31 +00:00
/* pagesize is assumed to be in the range of 2^x */
2014-10-24 17:29:50 +00:00
mprotect ( phpdbg_get_page_boundary ( watch - > addr . ptr ) , phpdbg_get_total_page_size ( watch - > addr . ptr , watch - > size ) , access ) ;
2013-12-16 15:29:31 +00:00
}
2014-12-13 22:06:14 +00:00
static inline void phpdbg_activate_watchpoint ( phpdbg_watchpoint_t * watch ) {
phpdbg_change_watchpoint_access ( watch , PROT_READ ) ;
2013-12-16 15:29:31 +00:00
}
2014-12-13 22:06:14 +00:00
static inline void phpdbg_deactivate_watchpoint ( phpdbg_watchpoint_t * watch ) {
phpdbg_change_watchpoint_access ( watch , PROT_READ | PROT_WRITE ) ;
2013-12-16 15:29:31 +00:00
}
2014-12-13 22:06:14 +00:00
static inline void phpdbg_store_watchpoint ( phpdbg_watchpoint_t * watch ) {
2014-10-24 17:29:50 +00:00
phpdbg_btree_insert ( & PHPDBG_G ( watchpoint_tree ) , ( zend_ulong ) watch - > addr . ptr , watch ) ;
2014-03-11 10:14:32 +00:00
}
2014-12-13 22:06:14 +00:00
static inline void phpdbg_remove_watchpoint ( phpdbg_watchpoint_t * watch ) {
2014-10-24 17:29:50 +00:00
phpdbg_btree_delete ( & PHPDBG_G ( watchpoint_tree ) , ( zend_ulong ) watch - > addr . ptr ) ;
2014-03-11 10:14:32 +00:00
}
2013-12-16 15:29:31 +00:00
void phpdbg_create_addr_watchpoint ( void * addr , size_t size , phpdbg_watchpoint_t * watch ) {
watch - > addr . ptr = addr ;
watch - > size = size ;
}
void phpdbg_create_zval_watchpoint ( zval * zv , phpdbg_watchpoint_t * watch ) {
phpdbg_create_addr_watchpoint ( zv , sizeof ( zval ) , watch ) ;
watch - > type = WATCH_ON_ZVAL ;
}
2014-01-10 18:54:07 +00:00
void phpdbg_create_ht_watchpoint ( HashTable * ht , phpdbg_watchpoint_t * watch ) {
2015-04-19 10:53:06 +00:00
phpdbg_create_addr_watchpoint ( ( ( char * ) ht ) + HT_WATCH_OFFSET , sizeof ( HashTable ) - HT_WATCH_OFFSET , watch ) ;
2014-01-10 18:54:07 +00:00
watch - > type = WATCH_ON_HASHTABLE ;
2015-04-19 10:53:06 +00:00
watch - > implicit_ht_count = 0 ;
2014-01-10 18:54:07 +00:00
}
2015-04-19 10:53:06 +00:00
static int phpdbg_create_recursive_ht_watch ( phpdbg_watchpoint_t * watch ) ;
static int phpdbg_create_recursive_zval_watch ( phpdbg_watchpoint_t * watch ) ;
2014-10-24 17:29:50 +00:00
void phpdbg_watch_HashTable_dtor ( zval * ptr ) ;
2015-04-19 10:53:06 +00:00
static void phpdbg_free_watch ( phpdbg_watchpoint_t * watch ) {
zend_string_release ( watch - > str ) ;
2015-07-15 14:22:39 +00:00
zend_string_release ( watch - > name_in_parent ) ;
2015-04-19 10:53:06 +00:00
}
2014-12-13 22:06:14 +00:00
static int phpdbg_delete_watchpoint ( phpdbg_watchpoint_t * tmp_watch ) ;
static void phpdbg_delete_ht_watchpoints_recursive ( phpdbg_watchpoint_t * watch ) ;
static void phpdbg_delete_zval_watchpoints_recursive ( phpdbg_watchpoint_t * watch ) ;
static void phpdbg_delete_watchpoints_recursive ( phpdbg_watchpoint_t * watch ) ;
2014-10-24 17:29:50 +00:00
2015-04-19 10:53:06 +00:00
/* Store all the possible watches the refcounted may refer to (for displaying & deleting by identifier) [collision] */
2014-10-24 17:29:50 +00:00
static phpdbg_watchpoint_t * phpdbg_create_refcounted_watchpoint ( phpdbg_watchpoint_t * parent , zend_refcounted * ref ) {
phpdbg_watchpoint_t * watch = emalloc ( sizeof ( phpdbg_watchpoint_t ) ) ;
watch - > flags = parent - > flags ;
watch - > parent = parent ;
2015-04-19 10:53:06 +00:00
watch - > str = parent - > str ;
+ + GC_REFCOUNT ( parent - > str ) ;
2015-08-13 10:56:29 +00:00
phpdbg_create_addr_watchpoint ( & GC_REFCOUNT ( ref ) , sizeof ( uint32_t ) , watch ) ;
2014-10-24 17:29:50 +00:00
watch - > type = WATCH_ON_REFCOUNTED ;
return watch ;
}
2015-04-19 10:53:06 +00:00
/* Must prevent duplicates ... if there are duplicates, replace new by old! */
2014-12-13 22:06:14 +00:00
static void phpdbg_add_watch_collision ( phpdbg_watchpoint_t * watch ) {
2014-10-24 17:29:50 +00:00
phpdbg_watch_collision * cur ;
2015-04-19 10:53:06 +00:00
/* Check for either recursive or (simple and/or implicit) */
ZEND_ASSERT ( ( ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) = = 0 ) ^ ( ( watch - > flags & ( PHPDBG_WATCH_IMPLICIT | PHPDBG_WATCH_SIMPLE ) ) = = 0 ) ) ;
2014-10-24 17:29:50 +00:00
if ( ( cur = zend_hash_index_find_ptr ( & PHPDBG_G ( watch_collisions ) , ( zend_ulong ) watch - > addr . ref ) ) ) {
2015-04-19 10:53:06 +00:00
phpdbg_watchpoint_t * old ;
int flags = 0 ;
if ( ( old = zend_hash_find_ptr ( & cur - > watches , watch - > str ) ) | | ( old = zend_hash_find_ptr ( & cur - > implicit_watches , watch - > str ) ) ) {
if ( ( ( old - > flags ^ watch - > flags ) & ( PHPDBG_WATCH_NORMAL | PHPDBG_WATCH_IMPLICIT ) ) = = 0 ) {
return ; /* there was no change ... */
}
flags = old - > flags ;
if ( flags & PHPDBG_WATCH_RECURSIVE ) {
if ( ! ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) & & ! - - cur - > refs ) {
phpdbg_delete_watchpoints_recursive ( watch ) ;
}
}
if ( flags & PHPDBG_WATCH_NORMAL ) {
zend_hash_del ( & cur - > watches , watch - > str ) ;
if ( zend_hash_num_elements ( & cur - > watches ) > 0 ) {
cur - > watch = Z_PTR_P ( zend_hash_get_current_data_ex ( & cur - > watches , NULL ) ) ;
} else {
cur - > watch = Z_PTR_P ( zend_hash_get_current_data_ex ( & cur - > implicit_watches , NULL ) ) ;
}
}
if ( flags & PHPDBG_WATCH_IMPLICIT ) {
zend_hash_del ( & cur - > implicit_watches , watch - > str ) ;
}
2015-07-15 14:22:39 +00:00
2015-04-19 10:53:06 +00:00
old - > flags = watch - > flags ;
phpdbg_free_watch ( watch ) ;
efree ( watch ) ;
watch = old ;
}
if ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) {
if ( ! ( flags & PHPDBG_WATCH_RECURSIVE ) & & ! cur - > refs + + ) {
phpdbg_create_recursive_zval_watch ( watch - > parent ) ;
}
2014-10-24 17:29:50 +00:00
}
} else {
phpdbg_watch_collision coll ;
2015-04-19 10:53:06 +00:00
coll . refs = ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) ! = 0 ;
coll . watch = watch ;
zend_hash_init ( & coll . watches , 8 , arghs , NULL , 0 ) ;
zend_hash_init ( & coll . implicit_watches , 8 , . . . , NULL , 0 ) ;
2014-10-24 17:29:50 +00:00
cur = zend_hash_index_add_mem ( & PHPDBG_G ( watch_collisions ) , ( zend_ulong ) watch - > addr . ref , & coll , sizeof ( phpdbg_watch_collision ) ) ;
2015-04-19 10:53:06 +00:00
phpdbg_store_watchpoint ( cur - > watch ) ;
phpdbg_activate_watchpoint ( cur - > watch ) ;
if ( coll . refs ) {
phpdbg_create_recursive_zval_watch ( watch - > parent ) ;
}
2014-10-24 17:29:50 +00:00
}
2015-04-19 10:53:06 +00:00
if ( watch - > flags & PHPDBG_WATCH_NORMAL ) {
cur - > watch = watch ;
zend_hash_add_ptr ( & cur - > watches , watch - > str , watch - > parent ) ;
}
if ( watch - > flags & PHPDBG_WATCH_IMPLICIT ) {
zend_hash_add_ptr ( & cur - > implicit_watches , watch - > str , watch - > parent ) ;
}
2014-10-24 17:29:50 +00:00
}
2015-04-19 10:53:06 +00:00
static void phpdbg_remove_watch_collision ( phpdbg_watchpoint_t * watch ) {
2014-10-24 17:29:50 +00:00
phpdbg_watch_collision * cur ;
2015-04-19 10:53:06 +00:00
if ( ( cur = zend_hash_index_find_ptr ( & PHPDBG_G ( watch_collisions ) , ( zend_ulong ) Z_COUNTED_P ( watch - > addr . zv ) ) ) ) {
if ( cur - > refs & & ! - - cur - > refs ) {
2014-12-13 22:06:14 +00:00
phpdbg_delete_watchpoints_recursive ( watch ) ;
2014-10-24 17:29:50 +00:00
}
2015-03-21 19:36:30 +00:00
zend_hash_del ( & cur - > watches , watch - > str ) ;
2015-04-19 10:53:06 +00:00
zend_hash_del ( & cur - > implicit_watches , watch - > str ) ;
2014-10-24 17:29:50 +00:00
2015-04-19 10:53:06 +00:00
if ( zend_hash_num_elements ( & cur - > watches ) > 0 ) {
cur - > watch = Z_PTR_P ( zend_hash_get_current_data_ex ( & cur - > watches , NULL ) ) ;
} else if ( zend_hash_num_elements ( & cur - > implicit_watches ) > 0 ) {
cur - > watch = Z_PTR_P ( zend_hash_get_current_data_ex ( & cur - > implicit_watches , NULL ) ) ;
} else {
phpdbg_deactivate_watchpoint ( cur - > watch ) ;
phpdbg_remove_watchpoint ( cur - > watch ) ;
2014-10-24 17:29:50 +00:00
2015-04-19 10:53:06 +00:00
zend_hash_index_del ( & PHPDBG_G ( watch_collisions ) , ( zend_ulong ) Z_COUNTED_P ( watch - > addr . zv ) ) ;
2014-10-24 17:29:50 +00:00
}
}
}
2014-03-22 13:39:39 +00:00
2015-04-19 10:53:06 +00:00
static phpdbg_watchpoint_t * phpdbg_create_watchpoint ( phpdbg_watchpoint_t * watch ) ;
static phpdbg_watchpoint_t * phpdbg_create_reference_watch ( phpdbg_watchpoint_t * watch ) {
2015-03-21 19:36:30 +00:00
phpdbg_watchpoint_t * ref = emalloc ( sizeof ( phpdbg_watchpoint_t ) ) ;
watch - > reference = ref ;
ref - > flags = watch - > flags ;
ref - > str = watch - > str ;
+ + GC_REFCOUNT ( ref - > str ) ;
ref - > parent = watch ;
ref - > parent_container = NULL ;
phpdbg_create_zval_watchpoint ( Z_REFVAL_P ( watch - > addr . zv ) , ref ) ;
2015-04-19 10:53:06 +00:00
phpdbg_create_watchpoint ( ref ) ;
return ref ;
2015-03-21 19:36:30 +00:00
}
2015-04-19 10:53:06 +00:00
static phpdbg_watchpoint_t * phpdbg_get_refcount_watch ( phpdbg_watchpoint_t * parent ) {
zend_refcounted * ref ;
phpdbg_btree_result * res ;
if ( parent - > type = = WATCH_ON_HASHTABLE ) {
parent = parent - > parent ;
if ( ! parent ) {
return NULL ;
}
}
ZEND_ASSERT ( parent - > type = = WATCH_ON_ZVAL ) ;
ref = Z_COUNTED_P ( parent - > addr . zv ) ;
res = phpdbg_btree_find ( & PHPDBG_G ( watchpoint_tree ) , ( zend_ulong ) ref ) ;
if ( res ) {
return res - > ptr ;
}
return NULL ;
}
static phpdbg_watchpoint_t * phpdbg_create_watchpoint ( phpdbg_watchpoint_t * watch ) {
phpdbg_watchpoint_t * ret = watch ;
2015-08-12 10:42:40 +00:00
if ( watch - > type = = WATCH_ON_ZVAL ) {
switch ( Z_TYPE_P ( watch - > addr . zv ) ) {
case IS_NULL :
case IS_UNDEF :
case IS_TRUE :
case IS_FALSE :
memset ( watch - > addr . zv , 0 , sizeof ( zend_value ) ) ;
}
}
2015-04-19 10:53:06 +00:00
/* exclude references & refcounted */
if ( ! watch - > parent | | watch - > parent - > type ! = WATCH_ON_ZVAL | | watch - > type = = WATCH_ON_HASHTABLE ) {
phpdbg_watchpoint_t * old_watch = zend_hash_find_ptr ( & PHPDBG_G ( watchpoints ) , watch - > str ) ;
if ( old_watch ) {
# define return_and_free_watch(x) { \
phpdbg_watchpoint_t * ref = phpdbg_get_refcount_watch ( old_watch ) ; \
if ( ref ) { \
phpdbg_add_watch_collision ( ref ) ; \
} \
2015-07-15 14:22:39 +00:00
if ( watch ! = old_watch ) { \
phpdbg_free_watch ( watch ) ; \
efree ( watch ) ; \
} \
2015-04-19 10:53:06 +00:00
return ( x ) ; \
}
if ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) {
if ( old_watch - > flags & PHPDBG_WATCH_RECURSIVE ) {
return_and_free_watch ( NULL ) ;
} else {
old_watch - > flags & = ~ ( PHPDBG_WATCH_SIMPLE | PHPDBG_WATCH_IMPLICIT ) ;
old_watch - > flags | = PHPDBG_WATCH_RECURSIVE ;
return_and_free_watch ( old_watch ) ;
}
} else {
if ( ! ( old_watch - > flags & PHPDBG_WATCH_RECURSIVE ) ) {
old_watch - > flags | = watch - > flags & ( PHPDBG_WATCH_IMPLICIT | PHPDBG_WATCH_SIMPLE ) ;
}
return_and_free_watch ( NULL ) ;
}
} else {
if ( watch - > parent & & watch - > parent - > type = = WATCH_ON_HASHTABLE ) {
watch - > parent - > implicit_ht_count + + ;
}
zend_hash_add_ptr ( & PHPDBG_G ( watchpoints ) , watch - > str , watch ) ;
}
}
2014-03-11 13:11:27 +00:00
2014-12-13 22:06:14 +00:00
phpdbg_store_watchpoint ( watch ) ;
2014-10-24 17:29:50 +00:00
if ( watch - > parent & & watch - > parent - > type = = WATCH_ON_ZVAL & & Z_REFCOUNTED_P ( watch - > parent - > addr . zv ) ) {
2014-12-13 22:06:14 +00:00
phpdbg_add_watch_collision ( phpdbg_create_refcounted_watchpoint ( watch , Z_COUNTED_P ( watch - > parent - > addr . zv ) ) ) ;
2014-10-24 17:29:50 +00:00
}
2013-12-19 16:05:38 +00:00
2014-03-22 13:39:39 +00:00
if ( watch - > type = = WATCH_ON_ZVAL ) {
2014-10-24 17:29:50 +00:00
if ( watch - > parent_container ) {
2015-04-19 10:53:06 +00:00
HashTable * ht_watches ;
phpdbg_btree_result * find ;
if ( ! ( find = phpdbg_btree_find ( & PHPDBG_G ( watch_HashTables ) , ( zend_ulong ) watch - > parent_container ) ) ) {
phpdbg_watch_ht_info * hti = emalloc ( sizeof ( * hti ) ) ;
hti - > dtor = watch - > parent_container - > pDestructor ;
ht_watches = & hti - > watches ;
zend_hash_init ( ht_watches , 0 , grrrrr , ZVAL_PTR_DTOR , 0 ) ;
phpdbg_btree_insert ( & PHPDBG_G ( watch_HashTables ) , ( zend_ulong ) watch - > parent_container , hti ) ;
watch - > parent_container - > pDestructor = ( dtor_func_t ) phpdbg_watch_HashTable_dtor ;
} else {
ht_watches = & ( ( phpdbg_watch_ht_info * ) find - > ptr ) - > watches ;
}
zend_hash_add_ptr ( ht_watches , watch - > name_in_parent , watch ) ;
2014-10-24 17:29:50 +00:00
}
if ( Z_ISREF_P ( watch - > addr . zv ) ) {
2015-04-19 10:53:06 +00:00
ret = phpdbg_create_reference_watch ( watch ) ;
2014-10-24 17:29:50 +00:00
}
2014-03-22 13:39:39 +00:00
}
2014-12-13 22:06:14 +00:00
phpdbg_activate_watchpoint ( watch ) ;
2013-12-19 16:05:38 +00:00
2015-04-19 10:53:06 +00:00
return ret ;
}
static int phpdbg_create_simple_watchpoint ( phpdbg_watchpoint_t * watch ) {
watch - > flags | = PHPDBG_WATCH_SIMPLE ;
phpdbg_create_watchpoint ( watch ) ;
2013-12-19 16:05:38 +00:00
return SUCCESS ;
}
2014-12-13 22:06:14 +00:00
static int phpdbg_create_array_watchpoint ( phpdbg_watchpoint_t * zv_watch ) {
2014-10-24 17:29:50 +00:00
zval * zv = zv_watch - > addr . zv ;
phpdbg_watchpoint_t * watch = emalloc ( sizeof ( phpdbg_watchpoint_t ) ) ;
HashTable * ht = HT_FROM_ZVP ( zv ) ;
2014-01-10 18:54:07 +00:00
2014-10-24 17:29:50 +00:00
watch - > parent = zv_watch ;
if ( ! ht ) {
return FAILURE ;
2014-01-10 18:54:07 +00:00
}
2014-04-03 19:53:58 +00:00
phpdbg_create_ht_watchpoint ( ht , watch ) ;
2014-01-10 18:54:07 +00:00
2015-04-19 10:53:06 +00:00
if ( phpdbg_create_watchpoint ( watch ) = = NULL ) {
return SUCCESS ;
}
2014-01-10 18:54:07 +00:00
2014-10-24 17:29:50 +00:00
if ( Z_TYPE_P ( zv ) = = IS_ARRAY ) {
watch - > flags | = PHPDBG_WATCH_ARRAY ;
} else {
watch - > flags | = PHPDBG_WATCH_OBJECT ;
2014-04-14 13:45:15 +00:00
}
2014-10-24 17:29:50 +00:00
2014-12-13 22:06:14 +00:00
phpdbg_add_watch_collision ( phpdbg_create_refcounted_watchpoint ( watch , Z_COUNTED_P ( zv ) ) ) ;
2014-10-24 17:29:50 +00:00
2014-01-10 18:54:07 +00:00
return SUCCESS ;
2014-04-14 13:45:15 +00:00
}
2014-12-13 22:06:14 +00:00
static int phpdbg_create_recursive_watchpoint ( phpdbg_watchpoint_t * watch ) {
2013-12-31 18:14:36 +00:00
if ( watch - > type ! = WATCH_ON_ZVAL ) {
return FAILURE ;
}
2014-03-11 13:11:27 +00:00
watch - > flags | = PHPDBG_WATCH_RECURSIVE ;
2015-04-19 10:53:06 +00:00
watch = phpdbg_create_watchpoint ( watch ) ;
2014-10-24 17:29:50 +00:00
2015-04-19 10:53:06 +00:00
return SUCCESS ;
}
2013-12-31 18:14:36 +00:00
2015-04-19 10:53:06 +00:00
static int phpdbg_create_recursive_ht_watch ( phpdbg_watchpoint_t * watch ) {
zval * zv ;
zend_string * key ;
zend_long h ;
2014-01-10 18:54:07 +00:00
2015-04-19 10:53:06 +00:00
ZEND_ASSERT ( watch - > type = = WATCH_ON_HASHTABLE ) ;
2013-12-31 18:14:36 +00:00
2015-04-19 10:53:06 +00:00
ZEND_HASH_FOREACH_KEY_VAL ( HT_WATCH_HT ( watch ) , h , key , zv ) {
phpdbg_watchpoint_t * new_watch = emalloc ( sizeof ( phpdbg_watchpoint_t ) ) ;
2013-12-31 18:14:36 +00:00
2015-04-19 10:53:06 +00:00
new_watch - > flags = PHPDBG_WATCH_RECURSIVE ;
new_watch - > parent = watch ;
new_watch - > parent_container = HT_WATCH_HT ( watch ) ;
2013-12-31 18:14:36 +00:00
2015-04-19 10:53:06 +00:00
if ( key ) {
new_watch - > name_in_parent = key ;
+ + GC_REFCOUNT ( key ) ;
} else {
2015-07-23 12:23:24 +00:00
new_watch - > name_in_parent = strpprintf ( 0 , ZEND_LONG_FMT , h ) ;
2015-04-19 10:53:06 +00:00
}
2014-10-24 17:29:50 +00:00
2015-07-23 12:23:24 +00:00
new_watch - > str = strpprintf ( 0 , " %.*s%s%s%s " , ( int ) ZSTR_LEN ( watch - > str ) - 2 , ZSTR_VAL ( watch - > str ) , ( watch - > flags & PHPDBG_WATCH_ARRAY ) ? " [ " : " -> " , phpdbg_get_property_key ( ZSTR_VAL ( new_watch - > name_in_parent ) ) , ( watch - > flags & PHPDBG_WATCH_ARRAY ) ? " ] " : " " ) ;
2013-12-31 18:14:36 +00:00
2015-04-19 10:53:06 +00:00
while ( Z_TYPE_P ( zv ) = = IS_INDIRECT ) {
zv = Z_INDIRECT_P ( zv ) ;
}
phpdbg_create_zval_watchpoint ( zv , new_watch ) ;
new_watch = phpdbg_create_watchpoint ( new_watch ) ;
phpdbg_create_recursive_zval_watch ( new_watch ) ;
} ZEND_HASH_FOREACH_END ( ) ;
return SUCCESS ;
}
static int phpdbg_create_recursive_zval_watch ( phpdbg_watchpoint_t * watch ) {
HashTable * ht ;
zval * zvp ;
ZEND_ASSERT ( watch - > type = = WATCH_ON_ZVAL ) ;
2013-12-31 18:14:36 +00:00
2015-04-19 10:53:06 +00:00
zvp = watch - > addr . zv ;
ZVAL_DEREF ( zvp ) ;
if ( ( ht = HT_FROM_ZVP ( zvp ) ) ) {
2014-01-10 18:54:07 +00:00
phpdbg_watchpoint_t * new_watch = emalloc ( sizeof ( phpdbg_watchpoint_t ) ) ;
2015-03-21 19:36:30 +00:00
new_watch - > flags = PHPDBG_WATCH_RECURSIVE ;
2014-01-10 18:54:07 +00:00
new_watch - > parent = watch ;
2014-03-11 10:14:32 +00:00
new_watch - > parent_container = watch - > parent_container ;
2015-03-21 19:36:30 +00:00
new_watch - > name_in_parent = watch - > name_in_parent ;
+ + GC_REFCOUNT ( new_watch - > name_in_parent ) ;
2015-07-23 12:23:24 +00:00
new_watch - > str = strpprintf ( 0 , " %.*s[] " , ( int ) ZSTR_LEN ( watch - > str ) , ZSTR_VAL ( watch - > str ) ) ;
2014-01-10 18:54:07 +00:00
2014-10-24 17:29:50 +00:00
if ( Z_TYPE_P ( zvp ) = = IS_ARRAY ) {
new_watch - > flags | = PHPDBG_WATCH_ARRAY ;
} else {
new_watch - > flags | = PHPDBG_WATCH_OBJECT ;
}
2014-01-10 18:54:07 +00:00
phpdbg_create_ht_watchpoint ( ht , new_watch ) ;
2015-04-19 10:53:06 +00:00
phpdbg_create_recursive_ht_watch ( new_watch ) ;
2014-12-13 22:06:14 +00:00
phpdbg_create_watchpoint ( new_watch ) ;
2014-01-10 18:54:07 +00:00
}
2013-12-31 18:14:36 +00:00
return SUCCESS ;
}
2015-04-19 10:53:06 +00:00
static void phpdbg_delete_implicit_parents ( phpdbg_watchpoint_t * watch ) {
phpdbg_watchpoint_t * parent = watch - > parent ;
if ( ! parent ) {
return ;
}
ZEND_ASSERT ( ! ( parent - > flags & PHPDBG_WATCH_RECURSIVE ) ) ;
ZEND_ASSERT ( parent - > flags & PHPDBG_WATCH_IMPLICIT ) ;
if ( parent - > type = = WATCH_ON_HASHTABLE & & - - parent - > implicit_ht_count ) {
return ;
}
parent - > flags & = ~ PHPDBG_WATCH_IMPLICIT ;
if ( ! ( parent - > flags & PHPDBG_WATCH_SIMPLE ) ) {
if ( parent - > type = = WATCH_ON_ZVAL & & Z_REFCOUNTED_P ( watch - > addr . zv ) ) {
phpdbg_remove_watch_collision ( parent ) ;
}
zend_hash_del ( & PHPDBG_G ( watchpoints ) , parent - > str ) ;
}
}
/* delete watchpoint, recursively (and inclusively) */
2014-12-13 22:06:14 +00:00
static int phpdbg_delete_watchpoint_recursive ( phpdbg_watchpoint_t * watch , zend_bool user_request ) {
2014-10-24 17:29:50 +00:00
if ( watch - > type = = WATCH_ON_HASHTABLE ) {
2015-04-19 10:53:06 +00:00
if ( user_request ) {
2014-12-13 22:06:14 +00:00
phpdbg_delete_ht_watchpoints_recursive ( watch ) ;
2014-03-11 13:11:27 +00:00
} else {
2015-04-19 10:53:06 +00:00
HashTable * ht ;
phpdbg_btree_result * result ;
2014-10-24 17:29:50 +00:00
ht = HT_FROM_ZVP ( watch - > addr . zv ) ;
2014-03-11 13:11:27 +00:00
if ( ( result = phpdbg_btree_find ( & PHPDBG_G ( watchpoint_tree ) , ( zend_ulong ) ht ) ) ) {
2014-12-13 22:06:14 +00:00
phpdbg_delete_watchpoint_recursive ( ( phpdbg_watchpoint_t * ) result - > ptr , user_request ) ;
2014-03-11 13:11:27 +00:00
}
}
2014-10-24 17:29:50 +00:00
} else if ( watch - > type = = WATCH_ON_ZVAL ) {
2014-12-13 22:06:14 +00:00
phpdbg_delete_zval_watchpoints_recursive ( watch ) ;
2014-03-11 13:11:27 +00:00
}
2015-03-21 19:36:30 +00:00
return zend_hash_del ( & PHPDBG_G ( watchpoints ) , watch - > str ) ;
2014-10-24 17:29:50 +00:00
}
2014-12-13 22:06:14 +00:00
static void phpdbg_delete_ht_watchpoints_recursive ( phpdbg_watchpoint_t * watch ) {
2015-07-23 12:23:24 +00:00
zend_string * str , * strkey ;
2014-10-24 17:29:50 +00:00
zend_long numkey ;
phpdbg_watchpoint_t * watchpoint ;
2015-04-19 10:53:06 +00:00
ZEND_HASH_FOREACH_KEY ( HT_WATCH_HT ( watch ) , numkey , strkey ) {
2014-10-24 17:29:50 +00:00
if ( strkey ) {
2015-07-23 12:23:24 +00:00
str = strpprintf ( 0 , " %.*s%s%s%s " , ( int ) ZSTR_LEN ( watch - > str ) , ZSTR_VAL ( watch - > str ) , ( watch - > flags & PHPDBG_WATCH_ARRAY ) ? " [ " : " -> " , phpdbg_get_property_key ( ZSTR_VAL ( strkey ) ) , ( watch - > flags & PHPDBG_WATCH_ARRAY ) ? " ] " : " " ) ;
2014-10-24 17:29:50 +00:00
} else {
2015-07-23 12:23:24 +00:00
str = strpprintf ( 0 , " %.*s%s " ZEND_LONG_FMT " %s " , ( int ) ZSTR_LEN ( watch - > str ) , ZSTR_VAL ( watch - > str ) , ( watch - > flags & PHPDBG_WATCH_ARRAY ) ? " [ " : " -> " , numkey , ( watch - > flags & PHPDBG_WATCH_ARRAY ) ? " ] " : " " ) ;
2014-10-24 17:29:50 +00:00
}
2015-07-23 12:23:24 +00:00
if ( ( watchpoint = zend_hash_find_ptr ( & PHPDBG_G ( watchpoints ) , str ) ) ) {
2014-12-13 22:06:14 +00:00
phpdbg_delete_watchpoint_recursive ( watchpoint , 1 ) ;
2014-10-24 17:29:50 +00:00
}
2015-03-21 19:36:30 +00:00
2015-07-23 12:23:24 +00:00
zend_string_release ( str ) ;
2014-10-24 17:29:50 +00:00
} ZEND_HASH_FOREACH_END ( ) ;
}
2014-12-13 22:06:14 +00:00
static void phpdbg_delete_zval_watchpoints_recursive ( phpdbg_watchpoint_t * watch ) {
2014-10-24 17:29:50 +00:00
if ( Z_REFCOUNTED_P ( watch - > addr . zv ) ) {
2015-04-19 10:53:06 +00:00
phpdbg_remove_watch_collision ( watch ) ;
2014-03-11 13:11:27 +00:00
}
2014-10-24 17:29:50 +00:00
}
2014-03-11 13:11:27 +00:00
2015-04-19 10:53:06 +00:00
/* delete watchpoints recusively (exclusively!) */
2014-12-13 22:06:14 +00:00
static void phpdbg_delete_watchpoints_recursive ( phpdbg_watchpoint_t * watch ) {
2014-10-24 17:29:50 +00:00
if ( watch - > type = = WATCH_ON_ZVAL ) {
2014-12-13 22:06:14 +00:00
phpdbg_delete_zval_watchpoints_recursive ( watch ) ;
2014-10-24 17:29:50 +00:00
} else if ( watch - > type = = WATCH_ON_HASHTABLE ) {
2014-12-13 22:06:14 +00:00
phpdbg_delete_ht_watchpoints_recursive ( watch ) ;
2014-10-24 17:29:50 +00:00
}
2014-03-11 13:11:27 +00:00
}
2014-12-13 22:06:14 +00:00
static int phpdbg_delete_watchpoint ( phpdbg_watchpoint_t * tmp_watch ) {
2014-03-11 13:11:27 +00:00
int ret ;
phpdbg_watchpoint_t * watch ;
phpdbg_btree_result * result ;
2014-10-24 17:29:50 +00:00
if ( ( result = phpdbg_btree_find ( & PHPDBG_G ( watchpoint_tree ) , ( zend_ulong ) tmp_watch - > addr . ptr ) ) = = NULL ) {
2014-03-11 13:11:27 +00:00
return FAILURE ;
}
watch = result - > ptr ;
2015-04-19 10:53:06 +00:00
if ( ! ( watch - > flags & PHPDBG_WATCH_NORMAL ) | | ( watch - > parent & & ( watch - > parent - > flags & PHPDBG_WATCH_RECURSIVE ) ) ) {
return FAILURE ; /* TODO: better error message for recursive (??) */
}
2014-03-11 13:11:27 +00:00
if ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) {
2014-12-13 22:06:14 +00:00
ret = phpdbg_delete_watchpoint_recursive ( watch , 1 ) ;
2014-03-11 13:11:27 +00:00
} else {
2015-03-21 19:36:30 +00:00
ret = zend_hash_del ( & PHPDBG_G ( watchpoints ) , watch - > str ) ;
2014-03-11 13:11:27 +00:00
}
2015-04-19 10:53:06 +00:00
phpdbg_free_watch ( tmp_watch ) ;
2014-03-11 13:11:27 +00:00
efree ( tmp_watch ) ;
2013-12-19 16:05:38 +00:00
return ret ;
}
2015-03-21 19:36:30 +00:00
static int phpdbg_watchpoint_parse_wrapper ( char * name , size_t namelen , char * key , size_t keylen , HashTable * parent , zval * zv , int ( * callback ) ( phpdbg_watchpoint_t * ) ) {
2014-10-03 10:43:32 +00:00
int ret ;
2015-03-21 19:36:30 +00:00
phpdbg_watchpoint_t * watch = ecalloc ( 1 , sizeof ( phpdbg_watchpoint_t ) ) ;
watch - > str = zend_string_init ( name , namelen , 0 ) ;
watch - > name_in_parent = zend_string_init ( key , keylen , 0 ) ;
2014-10-03 10:43:32 +00:00
watch - > parent_container = parent ;
2014-10-24 17:29:50 +00:00
phpdbg_create_zval_watchpoint ( zv , watch ) ;
2014-10-03 10:43:32 +00:00
2014-12-13 22:06:14 +00:00
ret = callback ( watch ) ;
2014-10-03 10:43:32 +00:00
2015-03-21 19:36:30 +00:00
efree ( name ) ;
efree ( key ) ;
2014-10-03 10:43:32 +00:00
if ( ret ! = SUCCESS ) {
2015-04-19 10:53:06 +00:00
phpdbg_free_watch ( watch ) ;
2014-10-03 10:43:32 +00:00
efree ( watch ) ;
2013-12-18 07:56:20 +00:00
}
2015-04-19 10:53:06 +00:00
PHPDBG_G ( watch_tmp ) = NULL ;
2013-12-19 16:05:38 +00:00
return ret ;
2014-10-03 10:43:32 +00:00
}
2014-12-13 22:06:14 +00:00
PHPDBG_API int phpdbg_watchpoint_parse_input ( char * input , size_t len , HashTable * parent , size_t i , int ( * callback ) ( phpdbg_watchpoint_t * ) , zend_bool silent ) {
2015-04-19 10:53:06 +00:00
return phpdbg_parse_variable_with_arg ( input , len , parent , i , ( phpdbg_parse_var_with_arg_func ) phpdbg_watchpoint_parse_wrapper , NULL , 0 , callback ) ;
}
static int phpdbg_watchpoint_parse_step ( char * name , size_t namelen , char * key , size_t keylen , HashTable * parent , zval * zv , int ( * callback ) ( phpdbg_watchpoint_t * ) ) {
phpdbg_watchpoint_t * watch ;
if ( ( watch = zend_hash_str_find_ptr ( & PHPDBG_G ( watchpoints ) , name , namelen ) ) ) {
watch - > flags | = PHPDBG_WATCH_IMPLICIT ;
PHPDBG_G ( watch_tmp ) = watch ;
return SUCCESS ;
}
watch = ecalloc ( 1 , sizeof ( phpdbg_watchpoint_t ) ) ;
watch - > flags = PHPDBG_WATCH_IMPLICIT ;
watch - > str = zend_string_init ( name , namelen , 0 ) ;
watch - > name_in_parent = zend_string_init ( key , keylen , 0 ) ;
watch - > parent_container = parent ;
watch - > parent = PHPDBG_G ( watch_tmp ) ;
phpdbg_create_zval_watchpoint ( zv , watch ) ;
phpdbg_create_watchpoint ( watch ) ;
efree ( name ) ;
efree ( key ) ;
PHPDBG_G ( watch_tmp ) = watch ;
return SUCCESS ;
2013-12-18 07:56:20 +00:00
}
2014-12-13 22:06:14 +00:00
static int phpdbg_watchpoint_parse_symtables ( char * input , size_t len , int ( * callback ) ( phpdbg_watchpoint_t * ) ) {
2014-10-24 17:29:50 +00:00
if ( EG ( scope ) & & len > = 5 & & ! memcmp ( " $this " , input , 5 ) ) {
2015-02-13 19:20:39 +00:00
zend_hash_str_add ( EG ( current_execute_data ) - > symbol_table , ZEND_STRL ( " this " ) , & EG ( current_execute_data ) - > This ) ;
2014-04-14 13:45:15 +00:00
}
2015-02-13 19:20:39 +00:00
if ( phpdbg_is_auto_global ( input , len ) & & phpdbg_watchpoint_parse_input ( input , len , & EG ( symbol_table ) , 0 , callback , 1 ) ! = FAILURE ) {
2014-04-14 14:26:27 +00:00
return SUCCESS ;
2014-04-14 13:45:15 +00:00
}
2015-04-19 10:53:06 +00:00
return phpdbg_parse_variable_with_arg ( input , len , EG ( current_execute_data ) - > symbol_table , 0 , ( phpdbg_parse_var_with_arg_func ) phpdbg_watchpoint_parse_wrapper , ( phpdbg_parse_var_with_arg_func ) phpdbg_watchpoint_parse_step , 0 , callback ) ;
2014-04-14 13:45:15 +00:00
}
2013-12-19 16:05:38 +00:00
PHPDBG_WATCH ( delete ) /* {{{ */
{
switch ( param - > type ) {
case STR_PARAM :
2014-12-13 22:06:14 +00:00
if ( phpdbg_delete_var_watchpoint ( param - > str , param - > len ) = = FAILURE ) {
2014-09-21 02:17:19 +00:00
phpdbg_error ( " watchdelete " , " type= \" nowatch \" " , " Nothing was deleted, no corresponding watchpoint found " ) ;
2014-04-13 15:34:27 +00:00
} else {
2014-09-21 02:17:19 +00:00
phpdbg_notice ( " watchdelete " , " variable= \" %.*s \" " , " Removed watchpoint %.*s " , ( int ) param - > len , param - > str ) ;
2013-12-19 16:05:38 +00:00
}
break ;
phpdbg_default_switch_case ( ) ;
}
return SUCCESS ;
} /* }}} */
2013-12-31 18:14:36 +00:00
PHPDBG_WATCH ( recursive ) /* {{{ */
{
2014-12-13 22:06:14 +00:00
if ( phpdbg_rebuild_symtable ( ) = = FAILURE ) {
2013-12-31 18:14:36 +00:00
return SUCCESS ;
}
switch ( param - > type ) {
case STR_PARAM :
2014-12-13 22:06:14 +00:00
if ( phpdbg_watchpoint_parse_symtables ( param - > str , param - > len , phpdbg_create_recursive_watchpoint ) ! = FAILURE ) {
2014-09-21 02:17:19 +00:00
phpdbg_notice ( " watchrecursive " , " variable= \" %.*s \" " , " Set recursive watchpoint on %.*s " , ( int ) param - > len , param - > str ) ;
2014-04-13 15:34:27 +00:00
}
2013-12-31 18:14:36 +00:00
break ;
phpdbg_default_switch_case ( ) ;
}
return SUCCESS ;
} /* }}} */
2014-01-10 18:54:07 +00:00
PHPDBG_WATCH ( array ) /* {{{ */
{
2014-12-13 22:06:14 +00:00
if ( phpdbg_rebuild_symtable ( ) = = FAILURE ) {
2014-01-10 18:54:07 +00:00
return SUCCESS ;
}
switch ( param - > type ) {
case STR_PARAM :
2014-12-13 22:06:14 +00:00
if ( phpdbg_watchpoint_parse_symtables ( param - > str , param - > len , phpdbg_create_array_watchpoint ) ! = FAILURE ) {
2014-09-21 02:17:19 +00:00
phpdbg_notice ( " watcharray " , " variable= \" %.*s \" " , " Set array watchpoint on %.*s " , ( int ) param - > len , param - > str ) ;
2014-04-13 15:34:27 +00:00
}
2014-01-10 18:54:07 +00:00
break ;
phpdbg_default_switch_case ( ) ;
}
return SUCCESS ;
} /* }}} */
2014-10-24 17:29:50 +00:00
void phpdbg_watch_HashTable_dtor ( zval * zv ) {
2014-03-22 13:39:39 +00:00
phpdbg_btree_result * result ;
2015-04-19 10:53:06 +00:00
zval * orig_zv = zv ;
2014-04-03 19:53:58 +00:00
2015-03-21 19:36:30 +00:00
while ( Z_TYPE_P ( zv ) = = IS_INDIRECT ) {
zv = Z_INDIRECT_P ( zv ) ;
}
2014-10-24 17:29:50 +00:00
if ( ( result = phpdbg_btree_find ( & PHPDBG_G ( watchpoint_tree ) , ( zend_ulong ) zv ) ) ) {
2014-03-22 13:39:39 +00:00
phpdbg_watchpoint_t * watch = result - > ptr ;
2015-04-19 10:53:06 +00:00
if ( watch - > flags & PHPDBG_WATCH_NORMAL ) {
PHPDBG_G ( watchpoint_hit ) = 1 ;
2014-03-22 13:39:39 +00:00
2015-06-30 10:59:27 +00:00
phpdbg_notice ( " watchdelete " , " variable= \" %.*s \" recursive= \" %s \" " , " %.*s was removed, removing watchpoint%s " , ( int ) ZSTR_LEN ( watch - > str ) , ZSTR_VAL ( watch - > str ) , ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) ? " recursively " : " " ) ;
2015-04-19 10:53:06 +00:00
}
2014-03-22 13:39:39 +00:00
2015-04-19 10:53:06 +00:00
if ( ( result = phpdbg_btree_find ( & PHPDBG_G ( watch_HashTables ) , ( zend_ulong ) watch - > parent_container ) ) ) {
phpdbg_watch_ht_info * hti = result - > ptr ;
hti - > dtor ( orig_zv ) ;
zend_hash_del ( & hti - > watches , watch - > name_in_parent ) ;
if ( zend_hash_num_elements ( & hti - > watches ) = = 0 ) {
watch - > parent_container - > pDestructor = hti - > dtor ;
zend_hash_destroy ( & hti - > watches ) ;
2015-07-15 14:22:39 +00:00
phpdbg_btree_delete ( & PHPDBG_G ( watch_HashTables ) , ( zend_ulong ) watch - > parent_container ) ;
2015-04-19 10:53:06 +00:00
efree ( hti ) ;
}
} else {
zval_ptr_dtor_wrapper ( orig_zv ) ;
}
2015-07-15 14:22:39 +00:00
if ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) {
phpdbg_delete_watchpoint_recursive ( watch , 0 ) ;
} else {
zend_hash_del ( & PHPDBG_G ( watchpoints ) , watch - > str ) ;
}
2014-03-22 13:39:39 +00:00
}
}
2014-12-13 22:06:14 +00:00
int phpdbg_create_var_watchpoint ( char * input , size_t len ) {
if ( phpdbg_rebuild_symtable ( ) = = FAILURE ) {
2014-04-13 15:34:27 +00:00
return FAILURE ;
2013-12-16 15:29:31 +00:00
}
2015-04-19 10:53:06 +00:00
return phpdbg_watchpoint_parse_symtables ( input , len , phpdbg_create_simple_watchpoint ) ;
2013-12-19 16:05:38 +00:00
}
2013-12-16 15:29:31 +00:00
2014-12-13 22:06:14 +00:00
int phpdbg_delete_var_watchpoint ( char * input , size_t len ) {
if ( phpdbg_rebuild_symtable ( ) = = FAILURE ) {
2014-04-13 15:34:27 +00:00
return FAILURE ;
2013-12-16 15:29:31 +00:00
}
2015-04-19 10:53:06 +00:00
return phpdbg_watchpoint_parse_input ( input , len , EG ( current_execute_data ) - > symbol_table , 0 , phpdbg_delete_watchpoint , 0 ) ;
2013-12-16 15:29:31 +00:00
}
2014-03-16 18:39:02 +00:00
# ifdef _WIN32
2014-12-13 22:06:14 +00:00
int phpdbg_watchpoint_segfault_handler ( void * addr ) {
2014-03-16 18:39:02 +00:00
# else
2014-12-13 22:06:14 +00:00
int phpdbg_watchpoint_segfault_handler ( siginfo_t * info , void * context ) {
2014-03-16 18:39:02 +00:00
# endif
2013-12-13 22:57:36 +00:00
void * page ;
phpdbg_watch_memdump * dump ;
phpdbg_watchpoint_t * watch ;
size_t size ;
2014-03-16 18:39:02 +00:00
watch = phpdbg_check_for_watchpoint (
# ifdef _WIN32
addr
# else
info - > si_addr
# endif
2014-12-14 13:07:59 +00:00
) ;
2013-12-13 22:57:36 +00:00
if ( watch = = NULL ) {
return FAILURE ;
}
2014-01-19 01:36:56 +00:00
page = phpdbg_get_page_boundary ( watch - > addr . ptr ) ;
size = phpdbg_get_total_page_size ( watch - > addr . ptr , watch - > size ) ;
2013-12-13 22:57:36 +00:00
/* re-enable writing */
2014-03-11 10:14:32 +00:00
mprotect ( page , size , PROT_READ | PROT_WRITE ) ;
2013-12-13 22:57:36 +00:00
2014-03-08 14:52:57 +00:00
dump = malloc ( MEMDUMP_SIZE ( size ) ) ;
2013-12-14 10:39:09 +00:00
dump - > page = page ;
dump - > size = size ;
2015-07-15 14:22:39 +00:00
dump - > reenable_writing = 0 ;
2013-12-13 22:57:36 +00:00
memcpy ( & dump - > data , page , size ) ;
2013-12-14 10:39:09 +00:00
zend_llist_add_element ( & PHPDBG_G ( watchlist_mem ) , & dump ) ;
2013-12-13 22:57:36 +00:00
return SUCCESS ;
}
2014-12-13 22:06:14 +00:00
void phpdbg_watchpoints_clean ( void ) {
2013-12-16 15:29:31 +00:00
zend_hash_clean ( & PHPDBG_G ( watchpoints ) ) ;
}
2015-04-19 10:53:06 +00:00
/* due to implicit delete... MUST BE DESTROYED MANUALLY */
2014-10-24 17:29:50 +00:00
static void phpdbg_watch_dtor ( zval * pDest ) {
phpdbg_watchpoint_t * watch = ( phpdbg_watchpoint_t * ) Z_PTR_P ( pDest ) ;
2013-12-16 15:29:31 +00:00
2015-04-19 10:53:06 +00:00
if ( watch - > flags & PHPDBG_WATCH_IMPLICIT ) {
watch - > flags = PHPDBG_WATCH_SIMPLE ; // tiny hack for delete_implicit_parents
if ( watch - > type = = WATCH_ON_ZVAL ) {
phpdbg_delete_zval_watchpoints_recursive ( watch ) ;
} else if ( watch - > type = = WATCH_ON_HASHTABLE ) {
phpdbg_watchpoint_t * watchpoint ;
watch - > implicit_ht_count + + ;
ZEND_HASH_FOREACH_PTR ( & ( ( phpdbg_watch_ht_info * ) phpdbg_btree_find ( & PHPDBG_G ( watch_HashTables ) , ( zend_ulong ) HT_WATCH_HT ( watch ) ) - > ptr ) - > watches , watchpoint ) {
phpdbg_delete_watchpoint_recursive ( watchpoint , 1 ) ;
} ZEND_HASH_FOREACH_END ( ) ;
}
}
phpdbg_delete_implicit_parents ( watch ) ;
2014-12-13 22:06:14 +00:00
phpdbg_deactivate_watchpoint ( watch ) ;
phpdbg_remove_watchpoint ( watch ) ;
2013-12-16 15:29:31 +00:00
2015-04-19 10:53:06 +00:00
phpdbg_free_watch ( watch ) ;
2015-07-15 14:22:39 +00:00
efree ( watch ) ;
2013-12-16 15:29:31 +00:00
}
static void phpdbg_watch_mem_dtor ( void * llist_data ) {
2014-10-24 17:29:50 +00:00
phpdbg_watch_memdump * dump = * ( phpdbg_watch_memdump * * ) llist_data ;
2013-12-16 15:29:31 +00:00
/* Disble writing again */
2014-03-11 10:14:32 +00:00
if ( dump - > reenable_writing ) {
2014-03-16 18:39:02 +00:00
mprotect ( dump - > page , dump - > size , PROT_READ ) ;
2014-03-11 10:14:32 +00:00
}
2014-03-16 18:39:02 +00:00
2015-03-21 19:36:30 +00:00
free ( dump ) ;
2013-12-16 15:29:31 +00:00
}
2015-04-19 10:53:06 +00:00
static void phpdbg_watch_free_ptr_dtor ( zval * ptr ) {
efree ( Z_PTR_P ( ptr ) ) ;
}
2014-12-13 22:06:14 +00:00
void phpdbg_setup_watchpoints ( void ) {
2014-03-11 10:14:32 +00:00
# if _SC_PAGE_SIZE
2013-12-16 15:29:31 +00:00
phpdbg_pagesize = sysconf ( _SC_PAGE_SIZE ) ;
2014-03-11 10:14:32 +00:00
# elif _SC_PAGESIZE
2013-12-16 15:29:31 +00:00
phpdbg_pagesize = sysconf ( _SC_PAGESIZE ) ;
2014-03-11 10:14:32 +00:00
# elif _SC_NUTC_OS_PAGESIZE
2013-12-16 15:29:31 +00:00
phpdbg_pagesize = sysconf ( _SC_NUTC_OS_PAGESIZE ) ;
2014-03-11 10:14:32 +00:00
# else
phpdbg_pagesize = 4096 ; /* common pagesize */
2013-12-16 15:29:31 +00:00
# endif
2014-03-08 14:52:57 +00:00
zend_llist_init ( & PHPDBG_G ( watchlist_mem ) , sizeof ( void * ) , phpdbg_watch_mem_dtor , 1 ) ;
2013-12-16 15:29:31 +00:00
phpdbg_btree_init ( & PHPDBG_G ( watchpoint_tree ) , sizeof ( void * ) * 8 ) ;
2014-03-22 13:39:39 +00:00
phpdbg_btree_init ( & PHPDBG_G ( watch_HashTables ) , sizeof ( void * ) * 8 ) ;
2014-10-24 17:29:50 +00:00
zend_hash_init ( & PHPDBG_G ( watchpoints ) , 8 , NULL , phpdbg_watch_dtor , 0 ) ;
2015-04-19 10:53:06 +00:00
zend_hash_init ( & PHPDBG_G ( watch_collisions ) , 8 , NULL , phpdbg_watch_free_ptr_dtor , 0 ) ;
2013-12-16 15:29:31 +00:00
}
2014-12-13 22:06:14 +00:00
static void phpdbg_print_changed_zval ( phpdbg_watch_memdump * dump ) {
2013-12-13 22:57:36 +00:00
/* fetch all changes between dump->page and dump->page + dump->size */
2014-09-21 02:17:19 +00:00
phpdbg_btree_position pos = phpdbg_btree_find_between ( & PHPDBG_G ( watchpoint_tree ) , ( zend_ulong ) dump - > page , ( zend_ulong ) dump - > page + dump - > size ) ;
2014-10-24 17:29:50 +00:00
phpdbg_btree_result * result ;
2014-01-10 18:54:07 +00:00
int elementDiff ;
2014-03-17 10:19:14 +00:00
void * curTest ;
2014-03-11 10:14:32 +00:00
dump - > reenable_writing = 0 ;
2013-12-14 10:39:09 +00:00
2013-12-15 14:05:50 +00:00
while ( ( result = phpdbg_btree_next ( & pos ) ) ) {
2014-10-24 17:29:50 +00:00
phpdbg_watchpoint_t * watch = result - > ptr ;
void * oldPtr = ( char * ) & dump - > data + ( ( size_t ) watch - > addr . ptr - ( size_t ) dump - > page ) ;
2014-03-11 10:14:32 +00:00
char reenable = 1 ;
2014-10-24 17:29:50 +00:00
int removed = 0 ;
2014-03-10 13:10:27 +00:00
2014-10-24 17:29:50 +00:00
if ( ( size_t ) watch - > addr . ptr < ( size_t ) dump - > page | | ( size_t ) watch - > addr . ptr + watch - > size > ( size_t ) dump - > page + dump - > size ) {
2014-03-22 13:39:39 +00:00
continue ;
}
2015-03-21 19:36:30 +00:00
/* Test if the zval was separated or replaced and if necessary move the watchpoint */
2014-10-24 17:29:50 +00:00
if ( ( watch - > type = = WATCH_ON_HASHTABLE | | watch - > type = = WATCH_ON_ZVAL ) & & watch - > parent_container ) {
2015-03-21 19:36:30 +00:00
if ( ( curTest = zend_symtable_find ( watch - > parent_container , watch - > name_in_parent ) ) ) {
2014-10-24 17:29:50 +00:00
while ( Z_TYPE_P ( ( zval * ) curTest ) = = IS_INDIRECT ) {
curTest = Z_INDIRECT_P ( ( zval * ) curTest ) ;
2014-03-17 10:19:14 +00:00
}
2014-03-11 10:14:32 +00:00
2014-10-24 17:29:50 +00:00
if ( watch - > type = = WATCH_ON_HASHTABLE ) {
switch ( Z_TYPE_P ( ( zval * ) curTest ) ) {
case IS_ARRAY :
curTest = ( void * ) Z_ARRVAL_P ( ( zval * ) curTest ) ;
break ;
case IS_OBJECT :
curTest = ( void * ) Z_OBJPROP_P ( ( zval * ) curTest ) ;
break ;
}
}
2014-03-11 10:14:32 +00:00
2014-10-24 17:29:50 +00:00
if ( curTest ! = watch - > addr . ptr ) {
2014-12-13 22:06:14 +00:00
phpdbg_deactivate_watchpoint ( watch ) ;
phpdbg_remove_watchpoint ( watch ) ;
2014-10-24 17:29:50 +00:00
watch - > addr . ptr = curTest ;
2014-12-13 22:06:14 +00:00
phpdbg_store_watchpoint ( watch ) ;
phpdbg_activate_watchpoint ( watch ) ;
2014-10-24 17:29:50 +00:00
reenable = 0 ;
}
} else {
removed = 1 ;
2014-03-10 13:10:27 +00:00
}
}
/* Show to the user what changed and delete watchpoint upon removal */
2015-08-12 10:42:40 +00:00
{
2014-10-24 17:29:50 +00:00
zend_bool do_break = 0 ;
2015-08-12 10:42:40 +00:00
switch ( watch - > type ) {
case WATCH_ON_ZVAL :
2015-10-13 13:51:23 +00:00
do_break = memcmp ( oldPtr , watch - > addr . zv , sizeof ( zend_value ) + sizeof ( uint32_t ) /* value + typeinfo */ ) ! = 0 ;
2015-08-12 10:42:40 +00:00
if ( ! do_break ) {
goto end ;
}
break ;
case WATCH_ON_HASHTABLE :
do_break = zend_hash_num_elements ( HT_PTR_HT ( oldPtr ) ) ! = zend_hash_num_elements ( HT_WATCH_HT ( watch ) ) ;
if ( ! do_break ) {
goto end ;
}
break ;
case WATCH_ON_REFCOUNTED :
2015-10-13 13:51:23 +00:00
do_break = memcmp ( oldPtr , watch - > addr . ref , sizeof ( uint32_t ) /* no zend_refcounted metadata info */ ) ! = 0 ;
2015-08-12 10:42:40 +00:00
if ( ! do_break ) {
goto end ;
}
if ( ! ( PHPDBG_G ( flags ) & PHPDBG_SHOW_REFCOUNTS ) ) {
do_break = 0 ;
}
break ;
}
if ( ! ( watch - > flags & PHPDBG_WATCH_NORMAL ) ) {
do_break = 0 ;
2014-10-24 17:29:50 +00:00
}
2014-09-21 02:17:19 +00:00
if ( do_break ) {
2014-04-14 17:37:31 +00:00
PHPDBG_G ( watchpoint_hit ) = 1 ;
2015-06-30 10:59:27 +00:00
phpdbg_notice ( " watchhit " , " variable= \" %s \" " , " Breaking on watchpoint %.*s " , ( int ) ZSTR_LEN ( watch - > str ) , ZSTR_VAL ( watch - > str ) ) ;
2014-10-09 10:24:52 +00:00
phpdbg_xml ( " <watchdata %r> " ) ;
2014-04-14 17:37:31 +00:00
}
2013-12-14 10:39:09 +00:00
switch ( watch - > type ) {
2014-03-16 21:37:33 +00:00
case WATCH_ON_ZVAL : {
2015-10-13 13:51:23 +00:00
zend_bool show_value = memcmp ( oldPtr , watch - > addr . zv , sizeof ( zend_value ) + sizeof ( uint32_t ) /* no metadata info */ ) ! = 0 ;
2014-04-14 08:46:52 +00:00
2015-04-19 10:53:06 +00:00
if ( ( watch - > flags & PHPDBG_WATCH_NORMAL ) & & ( removed | | show_value ) ) {
/* TODO: Merge with refcounting watches, store if watched ref value is to be dropped etc. [for example: manually increment refcount transparently for displaying and drop it if it decrements to 1] */
if ( Z_REFCOUNTED_P ( ( zval * ) oldPtr ) ) {
phpdbg_writeln ( " watchvalue " , " type= \" old \" inaccessible= \" inaccessible \" " , " Old value inaccessible or destroyed " ) ;
2014-04-14 08:46:52 +00:00
} else {
2014-09-21 02:17:19 +00:00
phpdbg_out ( " Old value: " ) ;
2014-10-09 10:24:52 +00:00
phpdbg_xml ( " <watchvalue %r type= \" old \" > " ) ;
2014-12-13 22:06:14 +00:00
zend_print_flat_zval_r ( ( zval * ) oldPtr ) ;
2014-09-21 02:17:19 +00:00
phpdbg_xml ( " </watchvalue> " ) ;
2014-10-19 18:09:39 +00:00
phpdbg_out ( " \n " ) ;
2014-04-14 08:46:52 +00:00
}
}
2013-12-16 15:29:31 +00:00
/* check if zval was removed */
2014-03-16 21:37:33 +00:00
if ( removed ) {
2015-04-19 10:53:06 +00:00
if ( watch - > flags & PHPDBG_WATCH_NORMAL ) {
2015-06-30 10:59:27 +00:00
phpdbg_notice ( " watchdelete " , " variable= \" %.*s \" " , " Watchpoint %.*s was unset, removing watchpoint " , ( int ) ZSTR_LEN ( watch - > str ) , ZSTR_VAL ( watch - > str ) ) ;
2015-04-19 10:53:06 +00:00
}
2015-03-21 19:36:30 +00:00
zend_hash_del ( & PHPDBG_G ( watchpoints ) , watch - > str ) ;
2014-01-10 18:54:07 +00:00
2014-03-11 10:14:32 +00:00
reenable = 0 ;
2014-10-24 17:29:50 +00:00
if ( Z_REFCOUNTED_P ( ( zval * ) oldPtr ) ) {
2015-04-19 10:53:06 +00:00
phpdbg_remove_watch_collision ( watch ) ;
2014-01-10 18:54:07 +00:00
}
2013-12-16 15:29:31 +00:00
break ;
}
2015-04-19 10:53:06 +00:00
if ( ( watch - > flags & PHPDBG_WATCH_NORMAL ) & & show_value ) {
2015-03-21 19:36:30 +00:00
phpdbg_out ( " New value%s: " , Z_ISREF_P ( watch - > addr . zv ) ? " (reference) " : " " ) ;
phpdbg_xml ( " <watchvalue %r%s type= \" new \" > " , Z_ISREF_P ( watch - > addr . zv ) ? " reference= \" reference \" " : " " ) ;
2014-12-13 22:06:14 +00:00
zend_print_flat_zval_r ( watch - > addr . zv ) ;
2014-09-21 02:17:19 +00:00
phpdbg_xml ( " </watchvalue> " ) ;
2014-10-19 18:09:39 +00:00
phpdbg_out ( " \n " ) ;
2014-04-14 08:46:52 +00:00
}
2014-01-10 18:54:07 +00:00
2014-10-24 17:29:50 +00:00
/* add new watchpoints if necessary */
2015-03-21 19:36:30 +00:00
if ( Z_PTR_P ( watch - > addr . zv ) ! = Z_PTR_P ( ( zval * ) oldPtr ) | | Z_TYPE_P ( watch - > addr . zv ) ! = Z_TYPE_P ( ( zval * ) oldPtr ) ) {
2014-10-24 17:29:50 +00:00
if ( Z_REFCOUNTED_P ( ( zval * ) oldPtr ) ) {
2015-04-19 10:53:06 +00:00
zval * new_zv = watch - > addr . zv ;
watch - > addr . ptr = oldPtr ;
phpdbg_remove_watch_collision ( watch ) ;
watch - > addr . zv = new_zv ;
2014-10-24 17:29:50 +00:00
}
if ( Z_REFCOUNTED_P ( watch - > addr . zv ) ) {
2015-04-19 10:53:06 +00:00
if ( ( watch - > flags & PHPDBG_WATCH_NORMAL ) & & ( PHPDBG_G ( flags ) & PHPDBG_SHOW_REFCOUNTS ) ) {
2015-08-13 10:56:29 +00:00
phpdbg_writeln ( " watchrefcount " , " type= \" new \" refcount= \" %d \" " , " New refcount: %d " , Z_REFCOUNT_P ( watch - > addr . zv ) ) ;
2014-10-24 17:29:50 +00:00
}
if ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) {
2014-12-13 22:06:14 +00:00
phpdbg_create_recursive_watchpoint ( watch ) ;
2015-03-21 19:36:30 +00:00
} else if ( Z_ISREF_P ( watch - > addr . zv ) ) {
phpdbg_create_reference_watch ( watch ) ;
2014-10-24 17:29:50 +00:00
}
2014-04-03 19:53:58 +00:00
}
2014-01-10 18:54:07 +00:00
}
2014-04-03 19:53:58 +00:00
2014-01-10 18:54:07 +00:00
break ;
2014-03-16 21:37:33 +00:00
}
2014-01-10 18:54:07 +00:00
case WATCH_ON_HASHTABLE :
2015-04-19 10:53:06 +00:00
/* We should be safely able to assume the HashTable to be consistent (inconsistent HashTables should have been caught by phpdbg_watch_efree() */
elementDiff = zend_hash_num_elements ( HT_PTR_HT ( oldPtr ) ) - zend_hash_num_elements ( HT_WATCH_HT ( watch ) ) ;
if ( ( watch - > flags & PHPDBG_WATCH_NORMAL ) & & elementDiff ) {
2014-03-11 10:14:32 +00:00
if ( elementDiff > 0 ) {
2014-09-21 02:17:19 +00:00
phpdbg_writeln ( " watchsize " , " removed= \" %d \" " , " %d elements were removed from the array " , elementDiff ) ;
2014-01-10 18:54:07 +00:00
} else {
2014-09-21 02:17:19 +00:00
phpdbg_writeln ( " watchsize " , " added= \" %d \" " , " %d elements were added to the array " , - elementDiff ) ;
2014-01-10 18:54:07 +00:00
}
}
2015-04-19 10:53:06 +00:00
/* add new watchpoints if necessary */
if ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) {
phpdbg_create_recursive_ht_watch ( watch ) ;
}
if ( ( watch - > flags & PHPDBG_WATCH_NORMAL ) & & HT_WATCH_HT ( watch ) - > nInternalPointer ! = HT_PTR_HT ( oldPtr ) - > nInternalPointer ) {
2014-09-21 02:17:19 +00:00
phpdbg_writeln ( " watcharrayptr " , " " , " Internal pointer of array was changed " ) ;
2014-01-10 18:54:07 +00:00
}
2013-12-14 10:39:09 +00:00
break ;
2014-10-24 17:29:50 +00:00
case WATCH_ON_REFCOUNTED : {
2015-04-19 10:53:06 +00:00
if ( ( watch - > flags & PHPDBG_WATCH_NORMAL ) & & ( PHPDBG_G ( flags ) & PHPDBG_SHOW_REFCOUNTS ) ) {
2015-08-13 10:56:29 +00:00
phpdbg_writeln ( " watchrefcount " , " type= \" old \" refcount= \" %d \" " , " Old refcount: %d " , GC_REFCOUNT ( ( zend_refcounted * ) oldPtr ) ) ;
phpdbg_writeln ( " watchrefcount " , " type= \" new \" refcount= \" %d \" " , " New refcount: %d " , GC_REFCOUNT ( watch - > addr . ref ) ) ;
2014-01-10 18:54:07 +00:00
}
2013-12-14 10:39:09 +00:00
break ;
2014-10-24 17:29:50 +00:00
}
2013-12-14 10:39:09 +00:00
}
2014-09-21 02:17:19 +00:00
if ( do_break ) {
phpdbg_xml ( " </watchdata> " ) ;
2013-12-14 10:39:09 +00:00
}
2015-08-12 10:42:40 +00:00
} end :
2014-03-11 10:14:32 +00:00
dump - > reenable_writing = dump - > reenable_writing | reenable ;
2013-12-13 22:57:36 +00:00
}
}
2014-12-13 22:06:14 +00:00
int phpdbg_print_changed_zvals ( void ) {
2014-03-10 13:10:27 +00:00
zend_llist_position pos ;
phpdbg_watch_memdump * * dump ;
2014-03-22 13:39:39 +00:00
int ret ;
2014-03-10 13:10:27 +00:00
2013-12-13 22:57:36 +00:00
if ( zend_llist_count ( & PHPDBG_G ( watchlist_mem ) ) = = 0 ) {
return FAILURE ;
}
2014-09-21 02:17:19 +00:00
dump = ( phpdbg_watch_memdump * * ) zend_llist_get_last_ex ( & PHPDBG_G ( watchlist_mem ) , & pos ) ;
2014-03-10 13:10:27 +00:00
do {
2014-12-13 22:06:14 +00:00
phpdbg_print_changed_zval ( * dump ) ;
2014-09-21 02:17:19 +00:00
} while ( ( dump = ( phpdbg_watch_memdump * * ) zend_llist_get_prev_ex ( & PHPDBG_G ( watchlist_mem ) , & pos ) ) ) ;
2014-03-10 13:10:27 +00:00
zend_llist_clean ( & PHPDBG_G ( watchlist_mem ) ) ;
2013-12-13 22:57:36 +00:00
2014-09-21 02:17:19 +00:00
ret = PHPDBG_G ( watchpoint_hit ) ? SUCCESS : FAILURE ;
2014-03-22 13:39:39 +00:00
PHPDBG_G ( watchpoint_hit ) = 0 ;
return ret ;
2013-12-14 10:39:09 +00:00
}
2013-12-18 07:56:20 +00:00
2014-12-13 22:06:14 +00:00
void phpdbg_list_watchpoints ( void ) {
2014-10-24 17:29:50 +00:00
phpdbg_watchpoint_t * watch ;
2013-12-18 07:56:20 +00:00
2014-10-09 10:24:52 +00:00
phpdbg_xml ( " <watchlist %r> " ) ;
2014-09-21 02:17:19 +00:00
2014-10-24 17:29:50 +00:00
ZEND_HASH_FOREACH_PTR ( & PHPDBG_G ( watchpoints ) , watch ) {
2015-04-19 10:53:06 +00:00
if ( watch - > flags & PHPDBG_WATCH_NORMAL ) {
2015-06-30 10:59:27 +00:00
phpdbg_writeln ( " watchvariable " , " variable= \" %.*s \" on= \" %s \" type= \" %s \" " , " %.*s (%s, %s) " , ( int ) ZSTR_LEN ( watch - > str ) , ZSTR_VAL ( watch - > str ) , watch - > type = = WATCH_ON_HASHTABLE ? " array " : watch - > type = = WATCH_ON_REFCOUNTED ? " refcount " : " variable " , watch - > flags = = PHPDBG_WATCH_RECURSIVE ? " recursive " : " simple " ) ;
2015-04-19 10:53:06 +00:00
}
2014-10-24 17:29:50 +00:00
} ZEND_HASH_FOREACH_END ( ) ;
2014-09-21 02:17:19 +00:00
phpdbg_xml ( " </watchlist> " ) ;
2013-12-18 07:56:20 +00:00
}
2014-01-10 18:54:07 +00:00
void phpdbg_watch_efree ( void * ptr ) {
2014-04-27 15:57:38 +00:00
phpdbg_btree_result * result ;
2014-04-03 19:53:58 +00:00
2014-09-21 02:17:19 +00:00
result = phpdbg_btree_find_closest ( & PHPDBG_G ( watchpoint_tree ) , ( zend_ulong ) ptr ) ;
2014-04-27 15:57:38 +00:00
2014-01-10 18:54:07 +00:00
if ( result ) {
phpdbg_watchpoint_t * watch = result - > ptr ;
2014-10-24 17:29:50 +00:00
if ( ( size_t ) watch - > addr . ptr + watch - > size > ( size_t ) ptr ) {
2015-04-19 10:53:06 +00:00
if ( watch - > type = = WATCH_ON_REFCOUNTED ) {
/* remove watchpoint here from btree, zval watchpoint will remove it via remove_watch_collison */
phpdbg_deactivate_watchpoint ( watch ) ;
phpdbg_remove_watchpoint ( watch ) ;
} else {
if ( watch - > type = = WATCH_ON_ZVAL ) {
phpdbg_remove_watch_collision ( watch ) ;
}
if ( watch - > type = = WATCH_ON_HASHTABLE & & ( watch - > flags & PHPDBG_WATCH_SIMPLE ) ) {
/* when a HashTable is freed, we can safely assume the other zvals all were dtor'ed */
2015-06-30 10:59:27 +00:00
phpdbg_notice ( " watchdelete " , " variable= \" %.*s \" recursive= \" %s \" " , " Array %.*s was removed, removing watchpoint%s " , ( int ) ZSTR_LEN ( watch - > str ) , ZSTR_VAL ( watch - > str ) , ( watch - > flags & PHPDBG_WATCH_RECURSIVE ) ? " recursively " : " " ) ;
2015-04-19 10:53:06 +00:00
}
if ( watch - > type = = WATCH_ON_HASHTABLE | | watch - > parent = = NULL | | watch - > parent - > type ! = WATCH_ON_ZVAL ) { /* no references */
zend_hash_del ( & PHPDBG_G ( watchpoints ) , watch - > str ) ;
}
2015-03-21 19:36:30 +00:00
}
2014-01-10 18:54:07 +00:00
}
}
2015-08-11 13:33:47 +00:00
if ( PHPDBG_G ( original_free_function ) ) {
PHPDBG_G ( original_free_function ) ( ptr ) ;
}
2014-03-17 10:19:14 +00:00
}