about summary refs log tree commit diff stats
path: root/src/libtools
diff options
context:
space:
mode:
authorptitSeb <sebastien.chev@gmail.com>2021-11-18 14:21:16 +0100
committerptitSeb <sebastien.chev@gmail.com>2021-11-18 14:21:16 +0100
commite7290d78d5047662c49a8558d1709ed912346ff7 (patch)
tree10ecdc9b7b9a994087980cf5e65cfefdb48602db /src/libtools
parent4f0e98ade5d37b8647cb5d39b6e5a3aded55fa2f (diff)
downloadbox64-e7290d78d5047662c49a8558d1709ed912346ff7.tar.gz
box64-e7290d78d5047662c49a8558d1709ed912346ff7.zip
[DYNAREC] Improved JIT handling, and added a HotPage detection to temporarily disable Dynarec when write occurs on the same page of some Dynablocks (help speedup some C#/Unity3D programs)
Diffstat (limited to 'src/libtools')
-rwxr-xr-xsrc/libtools/signals.c42
1 files changed, 38 insertions, 4 deletions
diff --git a/src/libtools/signals.c b/src/libtools/signals.c
index 3cb84d7d..5ac76342 100755
--- a/src/libtools/signals.c
+++ b/src/libtools/signals.c
@@ -706,6 +706,9 @@ void my_sigactionhandler_oldcode(int32_t sig, int simple, siginfo_t* info, void
 }
 
 extern __thread void* current_helper;
+#ifdef DYNAREC
+static pthread_mutex_t mutex_dynarec_prot;
+#endif
 
 void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
 {
@@ -745,12 +748,23 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
     dynablock_t* db = NULL;
     int db_searched = 0;
     if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && (prot&PROT_DYNAREC)) {
-        // access error, unprotect the block (and mark them dirty)
-        unprotectDB((uintptr_t)addr, 1);    // unprotect 1 byte... But then, the whole page will be unprotected
+        pthread_mutex_lock(&mutex_dynarec_prot);
         // check if SMC inside block
         db = FindDynablockFromNativeAddress(pc);
         db_searched = 1;
-        dynarec_log(LOG_DEBUG, "SIGSEGV with Access error on %p for %p , db=%p(%p), prot=0x%x\n", pc, addr, db, db?((void*)db->x64_addr):NULL, prot);
+        static uintptr_t repeated_page = 0;
+        dynarec_log(LOG_DEBUG, "SIGSEGV with Access error on %p for %p , db=%p(%p), prot=0x%x (old page=%p)\n", pc, addr, db, db?((void*)db->x64_addr):NULL, prot, (void*)repeated_page);
+        static int repeated_count = 0;
+        if(repeated_page == ((uintptr_t)addr&~0xfff)) {
+            ++repeated_count;   // Access eoor multiple time on same page, disable dynarec on this page a few time...
+            dynarec_log(LOG_DEBUG, "Detecting a Hotpage at %p (%d)\n", (void*)repeated_page, repeated_count);
+            AddHotPage(repeated_page);
+        } else {
+            repeated_page = (uintptr_t)addr&~0xfff;
+            repeated_count = 0;
+        }
+        // access error, unprotect the block (and mark them dirty)
+        unprotectDB((uintptr_t)addr, 1);    // unprotect 1 byte... But then, the whole page will be unprotected
         if(db && ((addr>=db->x64_addr && addr<(db->x64_addr+db->x64_size)) || db->need_test)) {
             // dynablock got auto-dirty! need to get out of it!!!
             emu_jmpbuf_t* ejb = GetJmpBuf();
@@ -785,17 +799,21 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
                     dynarec_log(LOG_INFO, "Dynablock unprotected, getting out!\n");
                 }
                 relockMutex(Locks);
+                pthread_mutex_unlock(&mutex_dynarec_prot);
                 siglongjmp(ejb->jmpbuf, 2);
             }
             dynarec_log(LOG_INFO, "Warning, Auto-SMC (%p for db %p/%p) detected, but jmpbuffer not ready!\n", (void*)addr, db, (void*)db->x64_addr);
         }
         // done
-        if(prot&PROT_WRITE) {
+        if((prot&PROT_WRITE) || (prot&PROT_DYNAREC)) {
+            pthread_mutex_unlock(&mutex_dynarec_prot);
             // if there is no write permission, don't return and continue to program signal handling
             relockMutex(Locks);
             return;
         }
+        pthread_mutex_unlock(&mutex_dynarec_prot);
     } else if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && (prot&(PROT_READ|PROT_WRITE))) {
+        pthread_mutex_lock(&mutex_dynarec_prot);
         db = FindDynablockFromNativeAddress(pc);
         db_searched = 1;
         if(db && db->x64_addr>= addr && (db->x64_addr+db->x64_size)<addr) {
@@ -812,6 +830,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
                 glitch_addr = addr;
                 glitch_prot = prot;
                 relockMutex(Locks);
+                pthread_mutex_unlock(&mutex_dynarec_prot);
                 return; // try again
             }
 dynarec_log(/*LOG_DEBUG*/LOG_INFO, "Repeated SIGSEGV with Access error on %p for %p, db=%p, prot=0x%x\n", pc, addr, db, prot);
@@ -819,6 +838,7 @@ dynarec_log(/*LOG_DEBUG*/LOG_INFO, "Repeated SIGSEGV with Access error on %p for
             glitch_addr = NULL;
             glitch_prot = 0;
         }
+        pthread_mutex_unlock(&mutex_dynarec_prot);
     }
 #else
     void* db = NULL;
@@ -1281,7 +1301,17 @@ EXPORT int my_swapcontext(x64emu_t* emu, void* ucp1, void* ucp2)
     my_setcontext(emu, ucp2);
     return 0;
 }
+#ifdef DYNAREC
+static void atfork_child_dynarec_prot(void)
+{
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+    pthread_mutex_init(&mutex_dynarec_prot, &attr);
 
+    pthread_mutexattr_destroy(&attr);
+}
+#endif
 void init_signal_helper(box64context_t* context)
 {
     // setup signal handling
@@ -1300,6 +1330,10 @@ void init_signal_helper(box64context_t* context)
     sigaction(SIGILL, &action, NULL);
 
 	pthread_once(&sigstack_key_once, sigstack_key_alloc);
+#ifdef DYNAREC
+    atfork_child_dynarec_prot();
+    pthread_atfork(NULL, NULL, atfork_child_dynarec_prot);
+#endif
 }
 
 void fini_signal_helper()