static int phpdbg_create_array_watchpoint(phpdbg_watchpoint_t *zv_watch) { zval *zv = zv_watch->addr.zv; phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t)); HashTable *ht = HT_FROM_ZVP(zv); watch->parent = zv_watch; if (!ht) { return FAILURE; } phpdbg_create_ht_watchpoint(ht, watch); if (phpdbg_create_watchpoint(watch) == NULL) { return SUCCESS; } if (Z_TYPE_P(zv) == IS_ARRAY) { watch->flags |= PHPDBG_WATCH_ARRAY; } else { watch->flags |= PHPDBG_WATCH_OBJECT; } phpdbg_add_watch_collision(phpdbg_create_refcounted_watchpoint(watch, Z_COUNTED_P(zv))); return SUCCESS; }
static void zend_persist_zval_const(zval *z) { zend_uchar flags; void *new_ptr; switch (Z_TYPE_P(z)) { case IS_STRING: case IS_CONSTANT: flags = Z_GC_FLAGS_P(z) & ~ (IS_STR_PERSISTENT | IS_STR_INTERNED | IS_STR_PERMANENT); zend_accel_memdup_interned_string(Z_STR_P(z)); Z_GC_FLAGS_P(z) |= flags; Z_TYPE_FLAGS_P(z) &= ~(IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE); break; case IS_ARRAY: new_ptr = zend_shared_alloc_get_xlat_entry(Z_ARR_P(z)); if (new_ptr) { Z_ARR_P(z) = new_ptr; Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE; } else { if (Z_IMMUTABLE_P(z)) { Z_ARR_P(z) = zend_accel_memdup(Z_ARR_P(z), sizeof(zend_array)); zend_hash_persist_immutable(Z_ARRVAL_P(z)); } else { GC_REMOVE_FROM_BUFFER(Z_ARR_P(z)); zend_accel_store(Z_ARR_P(z), sizeof(zend_array)); zend_hash_persist(Z_ARRVAL_P(z), zend_persist_zval); /* make immutable array */ Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE; GC_REFCOUNT(Z_COUNTED_P(z)) = 2; Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; } } break; case IS_REFERENCE: new_ptr = zend_shared_alloc_get_xlat_entry(Z_REF_P(z)); if (new_ptr) { Z_REF_P(z) = new_ptr; } else { zend_accel_store(Z_REF_P(z), sizeof(zend_reference)); zend_persist_zval(Z_REFVAL_P(z)); } break; case IS_CONSTANT_AST: new_ptr = zend_shared_alloc_get_xlat_entry(Z_AST_P(z)); if (new_ptr) { Z_AST_P(z) = new_ptr; } else { zend_accel_store(Z_AST_P(z), sizeof(zend_ast_ref)); Z_ASTVAL_P(z) = zend_persist_ast(Z_ASTVAL_P(z)); } break; } }
static void phpdbg_remove_watch_collision(phpdbg_watchpoint_t *watch) { phpdbg_watch_collision *cur; if ((cur = zend_hash_index_find_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) Z_COUNTED_P(watch->addr.zv)))) { if (cur->refs && !--cur->refs) { phpdbg_delete_watchpoints_recursive(watch); } zend_hash_del(&cur->watches, watch->str); zend_hash_del(&cur->implicit_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 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); zend_hash_index_del(&PHPDBG_G(watch_collisions), (zend_ulong) Z_COUNTED_P(watch->addr.zv)); } } }
ZEND_API void zval_internal_ptr_dtor(zval *zval_ptr) /* {{{ */ { if (Z_REFCOUNTED_P(zval_ptr)) { zend_refcounted *ref = Z_COUNTED_P(zval_ptr); if (GC_DELREF(ref) == 0) { if (Z_TYPE_P(zval_ptr) == IS_STRING) { zend_string *str = (zend_string*)ref; CHECK_ZVAL_STRING(str); ZEND_ASSERT(!ZSTR_IS_INTERNED(str)); ZEND_ASSERT((GC_FLAGS(str) & IS_STR_PERSISTENT)); free(str); } else { zend_error_noreturn(E_CORE_ERROR, "Internal zval's can't be arrays, objects, resources or reference"); } } } }
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; /* 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); \ } \ if (watch != old_watch) { \ phpdbg_free_watch(watch); \ efree(watch); \ } \ 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); } } phpdbg_store_watchpoint(watch); if (watch->parent && watch->parent->type == WATCH_ON_ZVAL && Z_REFCOUNTED_P(watch->parent->addr.zv)) { phpdbg_add_watch_collision(phpdbg_create_refcounted_watchpoint(watch, Z_COUNTED_P(watch->parent->addr.zv))); } if (watch->type == WATCH_ON_ZVAL) { if (watch->parent_container) { 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); } if (Z_ISREF_P(watch->addr.zv)) { ret = phpdbg_create_reference_watch(watch); } } phpdbg_activate_watchpoint(watch); return ret; }
static void gc_scan(zend_refcounted *ref, gc_stack *stack) { HashTable *ht = NULL; Bucket *p, *end; zval *zv; GC_STACK_DCL(stack); tail_call: if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { if (GC_REFCOUNT(ref) > 0) { if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) { GC_REF_SET_BLACK(ref); if (UNEXPECTED(!_stack->next)) { gc_stack_next(_stack); } /* Split stack and reuse the tail */ _stack->next->prev = NULL; gc_scan_black(ref, _stack->next); _stack->next->prev = _stack; } } else { if (GC_TYPE(ref) == IS_OBJECT) { zend_object *obj = (zend_object*)ref; if (EXPECTED(!(OBJ_FLAGS(ref) & IS_OBJ_FREE_CALLED))) { int n; zval *zv, *end; ht = obj->handlers->get_gc(obj, &zv, &n); end = zv + n; if (EXPECTED(!ht)) { if (!n) goto next; while (!Z_REFCOUNTED_P(--end)) { if (zv == end) goto next; } } while (zv != end) { if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); GC_STACK_PUSH(ref); } } zv++; } if (EXPECTED(!ht)) { ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); goto tail_call; } goto next; } } else { goto next; } } else if (GC_TYPE(ref) == IS_ARRAY) { if ((zend_array*)ref == &EG(symbol_table)) { GC_REF_SET_BLACK(ref); goto next; } else { ht = (zend_array*)ref; } } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); goto tail_call; } } goto next; } else { goto next; } if (!ht->nNumUsed) goto next; p = ht->arData; end = p + ht->nNumUsed; while (1) { end--; zv = &end->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { break; } if (p == end) goto next; } while (p != end) { zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); GC_STACK_PUSH(ref); } } p++; } zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); goto tail_call; } } } next: ref = GC_STACK_POP(); if (ref) { goto tail_call; } }
static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) { HashTable *ht = NULL; Bucket *p, *end; zval *zv; GC_STACK_DCL(stack); do { GC_BENCH_INC(zval_marked_grey); if (GC_TYPE(ref) == IS_OBJECT) { zend_object *obj = (zend_object*)ref; if (EXPECTED(!(OBJ_FLAGS(ref) & IS_OBJ_FREE_CALLED))) { int n; zval *zv, *end; ht = obj->handlers->get_gc(obj, &zv, &n); end = zv + n; if (EXPECTED(!ht)) { if (!n) goto next; while (!Z_REFCOUNTED_P(--end)) { if (zv == end) goto next; } } while (zv != end) { if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); GC_STACK_PUSH(ref); } } zv++; } if (EXPECTED(!ht)) { ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); continue; } goto next; } } else { goto next; } } else if (GC_TYPE(ref) == IS_ARRAY) { if (((zend_array*)ref) == &EG(symbol_table)) { GC_REF_SET_BLACK(ref); goto next; } else { ht = (zend_array*)ref; } } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); continue; } } goto next; } else { goto next; } if (!ht->nNumUsed) goto next; p = ht->arData; end = p + ht->nNumUsed; while (1) { end--; zv = &end->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { break; } if (p == end) goto next; } while (p != end) { zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); GC_STACK_PUSH(ref); } } p++; } zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); continue; } next: ref = GC_STACK_POP(); } while (ref); }
static void gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffer *root) { HashTable *ht = NULL; Bucket *p, *end; zval *zv; tail_call: if (root || (GC_REF_ADDRESS(ref) != 0 && GC_REF_CHECK_COLOR(ref, GC_BLACK))) { GC_TRACE_REF(ref, "removing from buffer"); if (root) { gc_remove_from_roots(root); GC_REF_SET_INFO(ref, 0); root = NULL; } else { GC_REMOVE_FROM_BUFFER(ref); } if (GC_TYPE(ref) == IS_OBJECT) { zend_object *obj = (zend_object*)ref; if (EXPECTED(!(OBJ_FLAGS(ref) & IS_OBJ_FREE_CALLED))) { int n; zval *zv, *end; ht = obj->handlers->get_gc(obj, &zv, &n); end = zv + n; if (EXPECTED(!ht)) { if (!n) return; while (!Z_REFCOUNTED_P(--end)) { if (zv == end) return; } } while (zv != end) { if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); gc_remove_nested_data_from_buffer(ref, NULL); } zv++; } if (EXPECTED(!ht)) { ref = Z_COUNTED_P(zv); goto tail_call; } } else { return; } } else if (GC_TYPE(ref) == IS_ARRAY) { ht = (zend_array*)ref; } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); goto tail_call; } return; } else { return; } if (!ht->nNumUsed) return; p = ht->arData; end = p + ht->nNumUsed; while (1) { end--; zv = &end->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { break; } if (p == end) return; } while (p != end) { zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); gc_remove_nested_data_from_buffer(ref, NULL); } p++; } zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } ref = Z_COUNTED_P(zv); goto tail_call; } }
static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *stack) { int count = 0; HashTable *ht = NULL; Bucket *p, *end; zval *zv; GC_STACK_DCL(stack); do { /* don't count references for compatibility ??? */ if (GC_TYPE(ref) != IS_REFERENCE) { count++; } if (GC_TYPE(ref) == IS_OBJECT) { zend_object *obj = (zend_object*)ref; if (EXPECTED(!(OBJ_FLAGS(ref) & IS_OBJ_FREE_CALLED))) { int n; zval *zv, *end; /* optimization: color is GC_BLACK (0) */ if (!GC_INFO(ref)) { gc_add_garbage(ref); } if (obj->handlers->dtor_obj != zend_objects_destroy_object || obj->ce->destructor != NULL) { *flags |= GC_HAS_DESTRUCTORS; } ht = obj->handlers->get_gc(obj, &zv, &n); end = zv + n; if (EXPECTED(!ht)) { if (!n) goto next; while (!Z_REFCOUNTED_P(--end)) { /* count non-refcounted for compatibility ??? */ if (Z_TYPE_P(zv) != IS_UNDEF) { count++; } if (zv == end) goto next; } } while (zv != end) { if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); GC_STACK_PUSH(ref); } /* count non-refcounted for compatibility ??? */ } else if (Z_TYPE_P(zv) != IS_UNDEF) { count++; } zv++; } if (EXPECTED(!ht)) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); continue; } goto next; } } else { goto next; } } else if (GC_TYPE(ref) == IS_ARRAY) { /* optimization: color is GC_BLACK (0) */ if (!GC_INFO(ref)) { gc_add_garbage(ref); } ht = (zend_array*)ref; } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); continue; } } goto next; } else { goto next; } if (!ht->nNumUsed) goto next; p = ht->arData; end = p + ht->nNumUsed; while (1) { end--; zv = &end->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { break; } /* count non-refcounted for compatibility ??? */ if (Z_TYPE_P(zv) != IS_UNDEF) { count++; } if (p == end) goto next; } while (p != end) { zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); GC_STACK_PUSH(ref); } /* count non-refcounted for compatibility ??? */ } else if (Z_TYPE_P(zv) != IS_UNDEF) { count++; } p++; } zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); continue; } next: ref = GC_STACK_POP(); } while (ref); return count; }
/* {{{ my_copy_zval */ static APC_HOTSPOT void my_copy_zval(zval* dst, const zval* src, apc_context_t* ctxt) { apc_pool* pool = ctxt->pool; assert(dst != NULL); assert(src != NULL); memcpy(dst, src, sizeof(zval)); if (Z_REFCOUNTED_P(src)) { if (zend_hash_num_elements(&ctxt->copied)) { zval *rc = zend_hash_index_find( &ctxt->copied, (uintptr_t) Z_COUNTED_P(src)); if (rc) { ZVAL_COPY(dst, rc); return; } } } switch (Z_TYPE_P(src)) { case IS_RESOURCE: case IS_TRUE: case IS_FALSE: case IS_LONG: case IS_DOUBLE: case IS_NULL: break; case IS_REFERENCE: Z_REF_P(dst) = my_copy_reference(Z_REF_P(src), ctxt); break; case IS_INDIRECT: my_copy_zval(dst, Z_INDIRECT_P(src), ctxt); break; case IS_CONSTANT: case IS_STRING: if (ctxt->copy == APC_COPY_OUT) { ZVAL_DUP(dst, src); } else { Z_TYPE_INFO_P(dst) = IS_STRING_EX; Z_STR_P(dst) = apc_pstrcpy(Z_STR_P(src), pool); } break; case IS_ARRAY: if(ctxt->serializer == NULL) { Z_ARRVAL_P(dst) = my_copy_hashtable(Z_ARRVAL_P(src), ctxt); break; } /* break intentionally omitted */ case IS_OBJECT: if(ctxt->copy == APC_COPY_IN) { dst = my_serialize_object(dst, src, ctxt); } else dst = my_unserialize_object(dst, src, ctxt); break; case IS_CALLABLE: /* XXX implement this */ assert(0); break; default: assert(0); } if (Z_REFCOUNTED_P(dst) && ctxt->copied.nTableSize) { zend_hash_index_update(&ctxt->copied, (uintptr_t) Z_COUNTED_P(src), dst); } }