summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikic@php.net>2015-04-11 17:24:01 +0200
committerNikita Popov <nikic@php.net>2015-04-11 18:29:36 +0200
commit71ccbf77a02361ce4e8a5f82ab3a1867d32cc574 (patch)
tree51da63b1f95f06b0b663548b50aa729e22f6373c
parentcefad04f0d20b45d5cb9b572f7253b45f33bd737 (diff)
downloadphp-git-71ccbf77a02361ce4e8a5f82ab3a1867d32cc574.tar.gz
Add support for more debug output during CC
Compile with ZEND_DEBUG_GC=2
-rw-r--r--Zend/zend_gc.c118
-rw-r--r--Zend/zend_gc.h25
2 files changed, 101 insertions, 42 deletions
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
index 39695a042c..b5c0911cde 100644
--- a/Zend/zend_gc.c
+++ b/Zend/zend_gc.c
@@ -40,6 +40,64 @@ ZEND_API int (*gc_collect_cycles)(void);
#define GC_REMOVE_FROM_ROOTS(current) \
gc_remove_from_roots((current))
+#if ZEND_GC_DEBUG > 1
+# define GC_TRACE(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__);
+# define GC_TRACE_REF(ref, format, ...) \
+ do { \
+ gc_trace_ref((zend_refcounted *) ref); \
+ fprintf(stderr, format "\n", ##__VA_ARGS__); \
+ } while (0)
+# define GC_TRACE_SET_COLOR(ref, color) \
+ GC_TRACE_REF(ref, "->%s", gc_color_name(color))
+#else
+# define GC_TRACE_REF(ref, format, ...)
+# define GC_TRACE_SET_COLOR(ref, new_color)
+# define GC_TRACE(str)
+#endif
+
+#define GC_REF_SET_ADDRESS(ref, a) \
+ GC_INFO_SET_ADDRESS(GC_INFO(ref), a)
+#define GC_REF_GET_COLOR(ref) \
+ GC_INFO_GET_COLOR(GC_INFO(ref))
+#define GC_REF_SET_COLOR(ref, c) \
+ do { GC_TRACE_SET_COLOR(ref, c); GC_INFO_SET_COLOR(GC_INFO(ref), c); } while (0)
+#define GC_REF_SET_BLACK(ref) \
+ do { GC_TRACE_SET_COLOR(ref, GC_BLACK); GC_INFO_SET_BLACK(GC_INFO(ref)); } while (0)
+#define GC_REF_SET_PURPLE(ref) \
+ do { GC_TRACE_SET_COLOR(ref, GC_PURPLE); GC_INFO_SET_PURPLE(GC_INFO(ref)); } while (0)
+
+#if ZEND_GC_DEBUG > 1
+static const char *gc_color_name(uint32_t color) {
+ switch (color) {
+ case GC_BLACK: return "black";
+ case GC_WHITE: return "white";
+ case GC_GREY: return "grey";
+ case GC_PURPLE: return "purple";
+ default: return "unknown";
+ }
+}
+static void gc_trace_ref(zend_refcounted *ref) {
+ if (GC_TYPE(ref) == IS_OBJECT) {
+ zend_object *obj = (zend_object *) ref;
+ fprintf(stderr, "[%p] rc=%d addr=%d %s object(%s)#%d ",
+ ref, GC_REFCOUNT(ref), GC_ADDRESS(GC_INFO(ref)),
+ gc_color_name(GC_REF_GET_COLOR(ref)),
+ obj->ce->name->val, obj->handle);
+ } else if (GC_TYPE(ref) == IS_ARRAY) {
+ zend_array *arr = (zend_array *) ref;
+ fprintf(stderr, "[%p] rc=%d addr=%d %s array(%d) ",
+ ref, GC_REFCOUNT(ref), GC_ADDRESS(GC_INFO(ref)),
+ gc_color_name(GC_REF_GET_COLOR(ref)),
+ zend_hash_num_elements(arr));
+ } else {
+ fprintf(stderr, "[%p] rc=%d addr=%d %s %s ",
+ ref, GC_REFCOUNT(ref), GC_ADDRESS(GC_INFO(ref)),
+ gc_color_name(GC_REF_GET_COLOR(ref)),
+ zend_get_type_by_const(GC_TYPE(ref)));
+ }
+}
+#endif
+
static zend_always_inline void gc_remove_from_roots(gc_root_buffer *root)
{
root->next->prev = root->prev;
@@ -152,7 +210,7 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)
ZEND_ASSERT(GC_TYPE(ref) == IS_ARRAY || GC_TYPE(ref) == IS_OBJECT);
GC_BENCH_INC(zval_possible_root);
- if (EXPECTED(GC_GET_COLOR(GC_INFO(ref)) == GC_BLACK)) {
+ if (EXPECTED(GC_REF_GET_COLOR(ref) == GC_BLACK)) {
if (!GC_ADDRESS(GC_INFO(ref))) {
gc_root_buffer *newRoot = GC_G(unused);
@@ -181,13 +239,13 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)
GC_G(unused) = newRoot->prev;
}
- GC_SET_PURPLE(GC_INFO(ref));
+ GC_REF_SET_PURPLE(ref);
newRoot->next = GC_G(roots).next;
newRoot->prev = &GC_G(roots);
GC_G(roots).next->prev = newRoot;
GC_G(roots).next = newRoot;
- GC_SET_ADDRESS(GC_INFO(ref), newRoot - GC_G(buf));
+ GC_REF_SET_ADDRESS(ref, newRoot - GC_G(buf));
newRoot->ref = ref;
@@ -220,7 +278,7 @@ static void gc_scan_black(zend_refcounted *ref)
tail_call:
ht = NULL;
- GC_SET_BLACK(GC_INFO(ref));
+ GC_REF_SET_BLACK(ref);
if (GC_TYPE(ref) == IS_OBJECT) {
zend_object_get_gc_t get_gc;
@@ -245,7 +303,7 @@ tail_call:
if (Z_REFCOUNTED_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_REFCOUNT(ref)++;
- if (GC_GET_COLOR(GC_INFO(ref)) != GC_BLACK) {
+ if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
gc_scan_black(ref);
}
}
@@ -254,7 +312,7 @@ tail_call:
if (EXPECTED(!ht)) {
ref = Z_COUNTED_P(zv);
GC_REFCOUNT(ref)++;
- if (GC_GET_COLOR(GC_INFO(ref)) != GC_BLACK) {
+ if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
goto tail_call;
}
return;
@@ -272,7 +330,7 @@ tail_call:
if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
GC_REFCOUNT(ref)++;
- if (GC_GET_COLOR(GC_INFO(ref)) != GC_BLACK) {
+ if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
goto tail_call;
}
}
@@ -291,7 +349,7 @@ tail_call:
if (Z_REFCOUNTED(p->val)) {
ref = Z_COUNTED(p->val);
GC_REFCOUNT(ref)++;
- if (GC_GET_COLOR(GC_INFO(ref)) != GC_BLACK) {
+ if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
gc_scan_black(ref);
}
}
@@ -299,7 +357,7 @@ tail_call:
}
ref = Z_COUNTED(p->val);
GC_REFCOUNT(ref)++;
- if (GC_GET_COLOR(GC_INFO(ref)) != GC_BLACK) {
+ if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
goto tail_call;
}
}
@@ -310,10 +368,10 @@ static void gc_mark_grey(zend_refcounted *ref)
Bucket *p, *end;
tail_call:
- if (GC_GET_COLOR(GC_INFO(ref)) != GC_GREY) {
+ if (GC_REF_GET_COLOR(ref) != GC_GREY) {
ht = NULL;
GC_BENCH_INC(zval_marked_grey);
- GC_SET_COLOR(GC_INFO(ref), GC_GREY);
+ GC_REF_SET_COLOR(ref, GC_GREY);
if (GC_TYPE(ref) == IS_OBJECT) {
zend_object_get_gc_t get_gc;
@@ -352,7 +410,7 @@ tail_call:
}
} else if (GC_TYPE(ref) == IS_ARRAY) {
if (((zend_array*)ref) == &EG(symbol_table)) {
- GC_SET_BLACK(GC_INFO(ref));
+ GC_REF_SET_BLACK(ref);
return;
} else {
ht = (zend_array*)ref;
@@ -408,7 +466,7 @@ static void gc_mark_roots(void)
gc_root_buffer *current = GC_G(roots).next;
while (current != &GC_G(roots)) {
- if (GC_GET_COLOR(GC_INFO(current->ref)) == GC_PURPLE) {
+ if (GC_REF_GET_COLOR(current->ref) == GC_PURPLE) {
gc_mark_grey(current->ref);
}
current = current->next;
@@ -421,11 +479,11 @@ static void gc_scan(zend_refcounted *ref)
Bucket *p, *end;
tail_call:
- if (GC_GET_COLOR(GC_INFO(ref)) == GC_GREY) {
+ if (GC_REF_GET_COLOR(ref) == GC_GREY) {
if (GC_REFCOUNT(ref) > 0) {
gc_scan_black(ref);
} else {
- GC_SET_COLOR(GC_INFO(ref), GC_WHITE);
+ GC_REF_SET_COLOR(ref, GC_WHITE);
if (GC_TYPE(ref) == IS_OBJECT) {
zend_object_get_gc_t get_gc;
zend_object *obj = (zend_object*)ref;
@@ -461,7 +519,7 @@ tail_call:
}
} else if (GC_TYPE(ref) == IS_ARRAY) {
if ((zend_array*)ref == &EG(symbol_table)) {
- GC_SET_BLACK(GC_INFO(ref));
+ GC_REF_SET_BLACK(ref);
return;
} else {
ht = (zend_array*)ref;
@@ -512,9 +570,9 @@ static int gc_collect_white(zend_refcounted *ref)
Bucket *p, *end;
tail_call:
- if (GC_GET_COLOR(GC_INFO(ref)) == GC_WHITE) {
+ if (GC_REF_GET_COLOR(ref) == GC_WHITE) {
ht = NULL;
- GC_SET_BLACK(GC_INFO(ref));
+ GC_REF_SET_BLACK(ref);
/* don't count references for compatibility ??? */
if (GC_TYPE(ref) != IS_REFERENCE) {
@@ -549,7 +607,7 @@ tail_call:
buf->prev = &GC_G(roots);
GC_G(roots).next->prev = buf;
GC_G(roots).next = buf;
- GC_SET_ADDRESS(GC_INFO(ref), buf - GC_G(buf));
+ GC_REF_SET_ADDRESS(ref, buf - GC_G(buf));
}
}
#endif
@@ -647,8 +705,8 @@ static int gc_collect_roots(void)
/* remove non-garbage from the list */
while (current != &GC_G(roots)) {
- if (GC_GET_COLOR(GC_INFO(current->ref)) != GC_WHITE) {
- GC_SET_ADDRESS(GC_INFO(current->ref), 0);
+ if (GC_REF_GET_COLOR(current->ref) != GC_WHITE) {
+ GC_REF_SET_ADDRESS(current->ref, 0);
GC_REMOVE_FROM_ROOTS(current);
}
current = current->next;
@@ -656,7 +714,7 @@ static int gc_collect_roots(void)
current = GC_G(roots).next;
while (current != &GC_G(roots)) {
- if (GC_GET_COLOR(GC_INFO(current->ref)) == GC_WHITE) {
+ if (GC_REF_GET_COLOR(current->ref) == GC_WHITE) {
GC_REFCOUNT(current->ref)++;
count += gc_collect_white(current->ref);
}
@@ -692,6 +750,7 @@ static void gc_remove_nested_data_from_buffer(zend_refcounted *ref)
tail_call:
if (GC_ADDRESS(GC_INFO(ref)) != 0) {
+ GC_TRACE_REF(ref, "removing from buffer");
GC_REMOVE_FROM_BUFFER(ref);
if (GC_TYPE(ref) == IS_OBJECT) {
@@ -772,14 +831,22 @@ ZEND_API int zend_gc_collect_cycles(void)
if (GC_G(gc_active)) {
return 0;
}
+
+ GC_TRACE("Collecting cycles");
GC_G(gc_runs)++;
GC_G(gc_active) = 1;
+
+ GC_TRACE("Marking roots");
gc_mark_roots();
+ GC_TRACE("Scanning roots");
gc_scan_roots();
+
#if ZEND_GC_DEBUG
orig_gc_full = GC_G(gc_full);
GC_G(gc_full) = 0;
#endif
+
+ GC_TRACE("Collecting roots");
count = gc_collect_roots();
#if ZEND_GC_DEBUG
GC_G(gc_full) = orig_gc_full;
@@ -788,6 +855,7 @@ ZEND_API int zend_gc_collect_cycles(void)
if (GC_G(to_free).next == &GC_G(to_free)) {
/* nothing to free */
+ GC_TRACE("Nothing to free");
return 0;
}
@@ -816,6 +884,7 @@ ZEND_API int zend_gc_collect_cycles(void)
}
/* Call destructors */
+ GC_TRACE("Calling destructors");
if (EG(objects_store).object_buckets) {
current = to_free.next;
while (current != &to_free) {
@@ -826,7 +895,7 @@ ZEND_API int zend_gc_collect_cycles(void)
if (IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
!(GC_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) {
-
+ GC_TRACE_REF(obj, "calling destructor");
GC_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED;
if (obj->handlers->dtor_obj) {
GC_REFCOUNT(obj)++;
@@ -850,11 +919,13 @@ ZEND_API int zend_gc_collect_cycles(void)
}
/* Destroy zvals */
+ GC_TRACE("Destroying zvals");
GC_G(gc_active) = 1;
current = to_free.next;
while (current != &to_free) {
p = current->ref;
GC_G(next_to_free) = current->next;
+ GC_TRACE_REF(p, "destroying");
if (GC_TYPE(p) == IS_OBJECT) {
zend_object *obj = (zend_object*)p;
@@ -892,6 +963,7 @@ ZEND_API int zend_gc_collect_cycles(void)
current = next;
}
+ GC_TRACE("Collection finished");
GC_G(collected) += count;
GC_G(next_to_free) = orig_next_to_free;
#if ZEND_GC_DEBUG
diff --git a/Zend/zend_gc.h b/Zend/zend_gc.h
index 94a8332a68..1e458292ae 100644
--- a/Zend/zend_gc.h
+++ b/Zend/zend_gc.h
@@ -49,30 +49,17 @@
#define GC_ADDRESS(v) \
((v) & ~GC_COLOR)
-#define GC_SET_ADDRESS(v, a) \
- do {(v) = ((v) & GC_COLOR) | (a);} while (0)
-#define GC_GET_COLOR(v) \
+#define GC_INFO_GET_COLOR(v) \
(((zend_uintptr_t)(v)) & GC_COLOR)
-#define GC_SET_COLOR(v, c) \
+#define GC_INFO_SET_ADDRESS(v, a) \
+ do {(v) = ((v) & GC_COLOR) | (a);} while (0)
+#define GC_INFO_SET_COLOR(v, c) \
do {(v) = ((v) & ~GC_COLOR) | (c);} while (0)
-#define GC_SET_BLACK(v) \
+#define GC_INFO_SET_BLACK(v) \
do {(v) = (v) & ~GC_COLOR;} while (0)
-#define GC_SET_PURPLE(v) \
+#define GC_INFO_SET_PURPLE(v) \
do {(v) = (v) | GC_COLOR;} while (0)
-#define GC_ZVAL_ADDRESS(v) \
- GC_ADDRESS(Z_GC_INFO_P(v))
-#define GC_ZVAL_SET_ADDRESS(v, a) \
- GC_SET_ADDRESS(Z_GC_INFO_P(v), (a))
-#define GC_ZVAL_GET_COLOR(v) \
- GC_GET_COLOR(Z_GC_INFO_P(v))
-#define GC_ZVAL_SET_COLOR(v, c) \
- GC_SET_COLOR(Z_GC_INFO_P(v), (c))
-#define GC_ZVAL_SET_BLACK(v) \
- GC_SET_BLACK(Z_GC_INFO_P(v))
-#define GC_ZVAL_SET_PURPLE(v) \
- GC_SET_PURPLE(Z_GC_INFO_P(v))
-
typedef struct _gc_root_buffer {
zend_refcounted *ref;
struct _gc_root_buffer *next; /* double-linked list */