]> git.saurik.com Git - apple/objc4.git/blobdiff - test/debuggerMode.m
objc4-437.tar.gz
[apple/objc4.git] / test / debuggerMode.m
diff --git a/test/debuggerMode.m b/test/debuggerMode.m
new file mode 100644 (file)
index 0000000..2836a4d
--- /dev/null
@@ -0,0 +1,157 @@
+#include "test.h"
+#include <objc/objc.h>
+#include <mach/mach.h>
+#include <pthread.h>
+#define _OBJC_PRIVATE_H_
+#include <objc/objc-gdb.h>
+
+extern void _objc_flush_caches(Class cls, BOOL flushMeta);
+
+
+@interface Super { id isa; } @end
+
+@implementation Super
++(void)initialize { } 
++class { return self; }
++(int)method { return 1; }
++(int)method2 { return 1; }
+@end
+
+
+semaphore_t sema;
+
+void *thread(void *arg __unused)
+{
+    objc_registerThreadWithCollector();
+
+    semaphore_signal(sema);
+    testprintf("hi\n");
+    while (1) {
+        [Super method];
+        _objc_flush_caches(0, YES);
+    }
+}
+
+
+void stopAllThreads(void)
+{
+    mach_msg_type_number_t count, i;
+    thread_act_array_t list;
+
+    task_threads(mach_task_self(), &list, &count);
+    for (i = 0; i < count; i++) {
+        if (list[i] == mach_thread_self()) continue;
+        thread_suspend(list[i]);
+        mach_port_deallocate(mach_task_self(), list[i]);
+    }
+}
+
+void startAllThreads(void)
+{
+    mach_msg_type_number_t count, i;
+    thread_act_array_t list;
+
+    task_threads(mach_task_self(), &list, &count);
+    for (i = 0; i < count; i++) {
+        if (list[i] == mach_thread_self()) continue;
+        thread_resume(list[i]);
+        mach_port_deallocate(mach_task_self(), list[i]);
+    }
+}
+
+
+static void cycle(int mode, int *good, int *bad)
+{
+    stopAllThreads();
+    if (gdb_objc_startDebuggerMode(mode)) {
+        testprintf("good\n");
+        [Super method];
+        [Super method2];
+        if (mode == OBJC_DEBUGMODE_FULL) {
+            // will crash without full write locks
+            _objc_flush_caches(0, YES);
+        }
+        gdb_objc_endDebuggerMode();
+        ++*good;
+    } else {
+        testprintf("bad\n");
+        ++*bad;
+    }
+    startAllThreads();
+    sched_yield();
+}
+
+
+int main()
+{
+#define STOPS 10000
+#define THREADS 1
+    int i;
+
+    [Super class];
+
+    testassert(STOPS > 200);
+
+    // Uncontended debugger mode
+    testassert(gdb_objc_startDebuggerMode(0));
+    gdb_objc_endDebuggerMode();
+
+    // Uncontended full debugger mode
+    testassert(gdb_objc_startDebuggerMode(OBJC_DEBUGMODE_FULL));
+    gdb_objc_endDebuggerMode();
+
+    // Nested debugger mode
+    testassert(gdb_objc_startDebuggerMode(0));
+    testassert(gdb_objc_startDebuggerMode(0));
+    gdb_objc_endDebuggerMode();
+    gdb_objc_endDebuggerMode();
+
+    // Nested full debugger mode
+    testassert(gdb_objc_startDebuggerMode(OBJC_DEBUGMODE_FULL));
+    testassert(gdb_objc_startDebuggerMode(OBJC_DEBUGMODE_FULL));
+    gdb_objc_endDebuggerMode();
+    gdb_objc_endDebuggerMode();
+
+    // Check that debugger mode sometimes works and sometimes doesn't
+    // when contending with another runtime-manipulating thread.
+
+    semaphore_create(mach_task_self(), &sema, 0, 0);
+
+    for (i = 0; i < THREADS; i++) {
+        pthread_t th;
+        pthread_create(&th, NULL, &thread, NULL);
+        semaphore_wait(sema);
+    }
+
+    testprintf("go\n");
+
+    int good0 = 0, bad0 = 0;
+    for (i = 0; i < STOPS; i++) {
+        cycle(0, &good0, &bad0);
+    }
+    testprintf("good0 %d, bad0 %d\n", good0, bad0);
+
+    int goodF = 0, badF = 0;
+    for (i = 0; i < STOPS; i++) {
+        cycle(OBJC_DEBUGMODE_FULL, &goodF, &badF);
+    }
+    testprintf("goodF %d, badF %d\n", goodF, badF);
+
+    // Require at least 1% each of good and bad. 
+    // Also require more than one each (exactly one is likely 
+    // a bug wherein the locks got stuck the first time).
+    // Also require that FULL worked less often.
+
+    if (good0 > STOPS/100  &&  bad0 > STOPS/100  &&  good0 > 1  &&  bad0 > 1 &&
+        goodF > STOPS/100  &&  badF > STOPS/100  &&  goodF > 1  &&  badF > 1
+#ifdef __OBJC2__
+        && good0 > goodF  /* not reliable enough in old runtime */
+#endif
+        )
+    {
+        succeed(__FILE__);
+    }
+
+    fail("good0=%d/%d bad0=%d/%d goodF=%d/%d badF=%d/%d (required at least %d/%d good)", 
+         good0, STOPS, bad0, STOPS, goodF, STOPS, badF, STOPS, STOPS/100, STOPS);
+}