@@ -133,6 +133,16 @@ const phpdbg_command_t phpdbg_watch_commands[] = {
133133#define HT_WATCH_HT (watch ) HT_PTR_HT((watch)->addr.ptr)
134134
135135/* ### PRINTING POINTER DIFFERENCES ### */
136+ static bool phpdbg_check_zval_watch_diff (zval * oldPtr , zval * newPtr ) {
137+ if (Z_TYPE_INFO_P (oldPtr ) != Z_TYPE_INFO_P (newPtr )) {
138+ return true;
139+ }
140+ if (Z_TYPE_P (oldPtr ) < IS_LONG ) {
141+ return false;
142+ }
143+ return memcmp (oldPtr , newPtr , sizeof (zend_value )) != 0 ;
144+ }
145+
136146bool phpdbg_check_watch_diff (phpdbg_watchtype type , void * oldPtr , void * newPtr ) {
137147 switch (type ) {
138148 case WATCH_ON_BUCKET :
@@ -142,7 +152,7 @@ bool phpdbg_check_watch_diff(phpdbg_watchtype type, void *oldPtr, void *newPtr)
142152 /* Fall through to also compare the value from the bucket. */
143153 ZEND_FALLTHROUGH ;
144154 case WATCH_ON_ZVAL :
145- return memcmp ( oldPtr , newPtr , sizeof ( zend_value ) + sizeof ( uint32_t ) /* value + typeinfo */ ) != 0 ;
155+ return phpdbg_check_zval_watch_diff (( zval * ) oldPtr , ( zval * ) newPtr ) ;
146156 case WATCH_ON_HASHTABLE :
147157 return zend_hash_num_elements (HT_PTR_HT (oldPtr )) != zend_hash_num_elements (HT_PTR_HT (newPtr ));
148158 case WATCH_ON_REFCOUNTED :
@@ -568,9 +578,28 @@ phpdbg_watch_element *phpdbg_add_watch_element(phpdbg_watchpoint_t *watch, phpdb
568578 return element ;
569579}
570580
581+ static bool phpdbg_zval_is_bucket_of (HashTable * ht , zval * zv ) {
582+ uintptr_t off ;
583+
584+ if (!ht || HT_IS_PACKED (ht )) {
585+ return false;
586+ }
587+ if ((uintptr_t ) zv < (uintptr_t ) ht -> arData
588+ || (uintptr_t ) zv >= (uintptr_t ) (ht -> arData + ht -> nNumUsed )) {
589+ return false;
590+ }
591+ off = (uintptr_t ) zv - (uintptr_t ) ht -> arData ;
592+ return off % sizeof (Bucket ) == 0 ;
593+ }
594+
571595phpdbg_watch_element * phpdbg_add_bucket_watch_element (Bucket * bucket , phpdbg_watch_element * element , bool * is_new ) {
572596 phpdbg_watchpoint_t watch ;
573- phpdbg_set_bucket_watchpoint (bucket , & watch );
597+
598+ if (phpdbg_zval_is_bucket_of (element -> parent_container , (zval * ) bucket )) {
599+ phpdbg_set_bucket_watchpoint (bucket , & watch );
600+ } else {
601+ phpdbg_set_zval_watchpoint ((zval * ) bucket , & watch );
602+ }
574603 bool added_new ;
575604 phpdbg_watch_element * added = phpdbg_add_watch_element (& watch , element , & added_new );
576605 if (added_new ) {
@@ -697,7 +726,7 @@ void phpdbg_recurse_watch_element(phpdbg_watch_element *element) {
697726}
698727
699728void phpdbg_watch_parent_ht (phpdbg_watch_element * element ) {
700- if (element -> watch -> type == WATCH_ON_BUCKET ) {
729+ if (element -> watch -> type == WATCH_ON_BUCKET || element -> watch -> type == WATCH_ON_ZVAL ) {
701730 phpdbg_btree_result * res ;
702731 phpdbg_watch_ht_info * hti ;
703732 ZEND_ASSERT (element -> parent_container );
@@ -716,14 +745,17 @@ void phpdbg_watch_parent_ht(phpdbg_watch_element *element) {
716745 hti = (phpdbg_watch_ht_info * ) res -> ptr ;
717746 }
718747
719- zend_hash_add_ptr (& hti -> watches , element -> name_in_parent , element );
748+ if (zend_hash_add_ptr (& hti -> watches , element -> name_in_parent , element )) {
749+ element -> flags |= PHPDBG_WATCH_HT_REGISTERED ;
750+ }
720751 }
721752}
722753
723754void phpdbg_unwatch_parent_ht (phpdbg_watch_element * element ) {
724- if (element -> watch && element -> watch -> type == WATCH_ON_BUCKET ) {
755+ if (element -> flags & PHPDBG_WATCH_HT_REGISTERED ) {
725756 phpdbg_btree_result * res = phpdbg_btree_find (& PHPDBG_G (watch_HashTables ), (zend_ulong ) element -> parent_container );
726757 ZEND_ASSERT (element -> parent_container );
758+ element -> flags &= ~PHPDBG_WATCH_HT_REGISTERED ;
727759 if (res ) {
728760 phpdbg_watch_ht_info * hti = res -> ptr ;
729761
@@ -1120,6 +1152,32 @@ void phpdbg_check_watchpoint(phpdbg_watchpoint_t *watch) {
11201152 default :
11211153 comparePtr = & watch -> backup ;
11221154 }
1155+ if (watch -> type == WATCH_ON_BUCKET || watch -> type == WATCH_ON_ZVAL ) {
1156+ phpdbg_watch_element * first = NULL ;
1157+ ZEND_HASH_MAP_FOREACH_PTR (& watch -> elements , first ) {
1158+ break ;
1159+ } ZEND_HASH_FOREACH_END ();
1160+ if (first && first -> parent_container && HT_IS_PACKED (first -> parent_container )) {
1161+ zval * new = zend_symtable_find (first -> parent_container , first -> name_in_parent );
1162+ if (!new || Z_TYPE_P (new ) == IS_UNDEF ) {
1163+ phpdbg_remove_watchpoint (watch );
1164+ return ;
1165+ }
1166+ if (new != watch -> addr .zv ) {
1167+ phpdbg_remove_watchpoint_btree (watch );
1168+ phpdbg_deactivate_watchpoint (watch );
1169+ watch -> addr .zv = new ;
1170+ phpdbg_store_watchpoint_btree (watch );
1171+ phpdbg_activate_watchpoint (watch );
1172+ }
1173+ if (!phpdbg_check_watch_diff (WATCH_ON_ZVAL , & watch -> backup .bucket .val , watch -> addr .ptr )) {
1174+ phpdbg_watch_backup_data (watch );
1175+ return ;
1176+ }
1177+ goto changed ;
1178+ }
1179+ }
1180+
11231181 if (!phpdbg_check_watch_diff (watch -> type , comparePtr , watch -> addr .ptr )) {
11241182 return ;
11251183 }
@@ -1160,8 +1218,14 @@ void phpdbg_check_watchpoint(phpdbg_watchpoint_t *watch) {
11601218 phpdbg_remove_watchpoint (watch );
11611219 return ;
11621220 }
1221+ } else if (watch -> type == WATCH_ON_ZVAL && !watch -> coll
1222+ && zend_hash_num_elements (& watch -> elements ) > 0
1223+ && Z_TYPE_P (watch -> addr .zv ) == IS_UNDEF ) {
1224+ phpdbg_remove_watchpoint (watch );
1225+ return ;
11631226 }
11641227
1228+ changed :
11651229 name = phpdbg_watchpoint_change_collision_name (watch );
11661230
11671231 if (name ) {
0 commit comments