2006-08-31 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / rawbuffer.c
index be7fc2a7172b102c8e1262cbed669b8210f52687..4475c1271a3b26160fb401c9d7385062273de307 100644 (file)
@@ -12,7 +12,9 @@
 #endif
 
 #include <unistd.h>
+#include <errno.h>
 #ifdef USE_WIN32_API
+#include <winsock2.h>
 #include <windows.h>
 #include <io.h>
 #else
 
 static GHashTable *mmap_map = NULL;
 static size_t alignment = 0;
+#define mono_mmap_lock() EnterCriticalSection (&mmap_mutex)
+#define mono_mmap_unlock() LeaveCriticalSection (&mmap_mutex)
 static CRITICAL_SECTION mmap_mutex;
+static gboolean make_unreadable = FALSE;
+static guint32 n_pagefaults = 0;
 
 static void
 get_alignment (void)
@@ -80,7 +86,15 @@ mono_raw_buffer_init (void)
 
        get_alignment ();
 
-       mmap_map = g_hash_table_new (g_direct_hash, g_direct_equal);
+       mmap_map = g_hash_table_new (NULL, NULL);
+}
+
+void
+mono_raw_buffer_cleanup (void)
+{
+       g_hash_table_destroy (mmap_map);
+
+       DeleteCriticalSection (&mmap_mutex);
 }
 
 static void *
@@ -117,9 +131,9 @@ mono_raw_buffer_load_mmap (int fd, int is_writable, guint32 base, size_t size)
                return 0;
        }
 
-       EnterCriticalSection (&mmap_mutex);
+       mono_mmap_lock ();
        g_hash_table_insert (mmap_map, ptr, GINT_TO_POINTER (mapping));
-       LeaveCriticalSection (&mmap_mutex);
+       mono_mmap_unlock ();
        
        return ((char *)ptr) + (base - start);
 
@@ -146,10 +160,25 @@ mono_raw_buffer_load_mmap (int fd, int is_writable, guint32 base, size_t size)
 
        if (ptr == (void *) -1)
                return 0;
-       
-       EnterCriticalSection (&mmap_mutex);
+
+       /* 
+        * This seems to prevent segmentation faults on Fedora Linux, no
+        * idea why :). See
+        * http://bugzilla.ximian.com/show_bug.cgi?id=49499
+        * for more info.
+        */
+       if (mprotect (ptr, end - start, prot | PROT_EXEC) != 0)
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                                  ": mprotect failed: %s", g_strerror (errno));
+
+       if (make_unreadable) {
+               int res = mprotect (ptr, end - start, 0);
+               g_assert (res == 0);
+       }
+
+       mono_mmap_lock ();
        g_hash_table_insert (mmap_map, ptr, GINT_TO_POINTER (size));
-       LeaveCriticalSection (&mmap_mutex);
+       mono_mmap_unlock ();
 
        return ((char *)ptr) + (base - start);
 #endif
@@ -160,9 +189,9 @@ mono_raw_buffer_free_mmap (void *base)
 {
        int value;
 
-       EnterCriticalSection (&mmap_mutex);
+       mono_mmap_lock ();
        value = GPOINTER_TO_INT (g_hash_table_lookup (mmap_map, base));
-       LeaveCriticalSection (&mmap_mutex);
+       mono_mmap_unlock ();
 
 #ifdef USE_WIN32_API
        UnmapViewOfFile (base);
@@ -202,9 +231,9 @@ mono_raw_buffer_update (void *buffer, size_t size)
 
        mmap_base =  (gpointer)(ROUND_DOWN ((UINTPTR_TYPE) (buffer), alignment));
 
-       EnterCriticalSection (&mmap_mutex);
+       mono_mmap_lock ();
        exists = g_hash_table_lookup (mmap_map, mmap_base) != NULL;
-       LeaveCriticalSection (&mmap_mutex);
+       mono_mmap_unlock ();
        if (exists)
                mono_raw_buffer_update_mmap (mmap_base, size);
 }
@@ -224,3 +253,90 @@ mono_raw_buffer_free (void *buffer)
                mono_raw_buffer_free_malloc (buffer);
 }
 
+/*
+ * mono_raw_buffer_set_make_unreadable:
+ *
+ *   Set whenever to make all mmaped memory unreadable. In conjuction with a
+ * SIGSEGV handler, this is useful to find out which pages the runtime tries to read.
+ */
+void
+mono_raw_buffer_set_make_unreadable (gboolean unreadable)
+{
+       make_unreadable = unreadable;
+}
+
+typedef struct {
+       gboolean found;
+       void *ptr;
+} FindMapUserData;
+
+static void
+find_map (void *start, guint32 size, gpointer user_data)
+{
+       FindMapUserData *data = (FindMapUserData*)user_data;
+
+       if (!data->found)
+               if (((guint8*)data->ptr >= (guint8*)start) && ((guint8*)data->ptr < (guint8*)start + size))
+                       data->found = TRUE;
+}
+
+/*
+ * mono_raw_buffer_is_pagefault:
+ *
+ *   Should be called from a SIGSEGV signal handler to find out whenever @ptr is
+ * within memory allocated by this module.
+ */
+gboolean
+mono_raw_buffer_is_pagefault (void *ptr)
+{
+       FindMapUserData data;
+
+       if (!make_unreadable)
+               return FALSE;
+
+       data.found = FALSE;
+       data.ptr = ptr;
+
+       mono_mmap_lock ();
+       g_hash_table_foreach (mmap_map, (GHFunc)find_map, &data);
+       mono_mmap_unlock ();
+
+       return data.found;
+}
+
+/*
+ * mono_raw_buffer_handle_pagefault:
+ *
+ *   Handle a pagefault caused by an unreadable page by making it readable again.
+ */
+void
+mono_raw_buffer_handle_pagefault (void *ptr)
+{
+#ifndef PLATFORM_WIN32
+       guint8* start = (guint8*)ROUND_DOWN (((gssize)ptr), alignment);
+       int res;
+
+       mono_mmap_lock ();
+       res = mprotect (start, alignment, PROT_READ);
+       g_assert (res == 0);
+
+       n_pagefaults ++;
+       mono_mmap_unlock ();
+#endif
+}
+
+/*
+ * mono_raw_buffer_get_n_pagefaults:
+ *
+ *   Return the number of times handle_pagefault is called.
+ * To count the number of pagefaults caused by a block of code use code like this:
+ * 
+ *  int prev_pagefaults = mono_raw_buffer_get_n_pagefaults ();
+ *  <CODE>
+ *  int new_pagefaults = mono_raw_buffer_get_n_pagefaults () - prev_pagefaults;
+ */
+guint32
+mono_raw_buffer_get_n_pagefaults (void)
+{
+       return n_pagefaults;
+}