Release address space to the OS at gencgc_release_granularity
[sbcl.git] / src / runtime / save.c
index f6742c3..b3addc9 100644 (file)
 #include "genesis/static-symbols.h"
 #include "genesis/symbol.h"
 
+#if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_SB_LUTEX)
+#include "genesis/lutex.h"
+#endif
+
+/* write_runtime_options uses a simple serialization scheme that
+ * consists of one word of magic, one word indicating whether options
+ * are actually saved, and one word per struct field. */
+static void
+write_runtime_options(FILE *file, struct runtime_options *options)
+{
+    size_t optarray[RUNTIME_OPTIONS_WORDS];
+
+    memset(&optarray, 0, sizeof(optarray));
+    optarray[0] = RUNTIME_OPTIONS_MAGIC;
+
+    if (options != NULL) {
+        /* optarray[1] is a flag indicating that options are present */
+        optarray[1] = 1;
+        optarray[2] = options->dynamic_space_size;
+        optarray[3] = options->thread_control_stack_size;
+    }
+
+    if (RUNTIME_OPTIONS_WORDS !=
+        fwrite(optarray, sizeof(size_t), RUNTIME_OPTIONS_WORDS, file)) {
+        perror("Error writing runtime options to file");
+    }
+}
+
 static void
 write_lispobj(lispobj obj, FILE *file)
 {
-    fwrite(&obj, sizeof(lispobj), 1, file);
+    if (1 != fwrite(&obj, sizeof(lispobj), 1, file)) {
+        perror("Error writing to file");
+    }
 }
 
 static long
@@ -76,10 +106,83 @@ write_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset)
     return ((data - file_offset) / os_vm_page_size) - 1;
 }
 
+#if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_SB_LUTEX)
+/* saving lutexes in the core */
+static void **lutex_addresses;
+static long n_lutexes = 0;
+static long max_lutexes = 0;
+
+static long
+default_scan_action(lispobj *obj)
+{
+    return (sizetab[widetag_of(*obj)])(obj);
+}
+
+static long
+lutex_scan_action(lispobj *obj)
+{
+    /* note the address of the lutex */
+    if(n_lutexes >= max_lutexes) {
+        max_lutexes *= 2;
+        lutex_addresses = realloc(lutex_addresses, max_lutexes * sizeof(void *));
+        gc_assert(lutex_addresses);
+    }
+
+    lutex_addresses[n_lutexes++] = obj;
+
+    return (*sizetab[widetag_of(*obj)])(obj);
+}
+
+typedef long (*scan_table[256])(lispobj *obj);
+
+static void
+scan_objects(lispobj *start, long n_words, scan_table table)
+{
+    lispobj *end = start + n_words;
+    lispobj *object_ptr;
+    long n_words_scanned;
+    for (object_ptr = start;
+         object_ptr < end;
+         object_ptr += n_words_scanned) {
+        lispobj obj = *object_ptr;
+
+        n_words_scanned = (table[widetag_of(obj)])(object_ptr);
+    }
+}
+
+static void
+scan_for_lutexes(lispobj *addr, long n_words)
+{
+    static int initialized = 0;
+    static scan_table lutex_scan_table;
+
+    if (!initialized) {
+        int i;
+
+        /* allocate a little space to get started */
+        lutex_addresses = malloc(16*sizeof(void *));
+        gc_assert(lutex_addresses);
+        max_lutexes = 16;
+
+        /* initialize the mapping table */
+        for(i = 0; i < ((sizeof lutex_scan_table)/(sizeof lutex_scan_table[0])); ++i) {
+            lutex_scan_table[i] = default_scan_action;
+        }
+
+        lutex_scan_table[LUTEX_WIDETAG] = lutex_scan_action;
+
+        initialized = 1;
+    }
+
+    /* do the scan */
+    scan_objects(addr, n_words, lutex_scan_table);
+}
+#endif
+
 static void
 output_space(FILE *file, int id, lispobj *addr, lispobj *end, os_vm_offset_t file_offset)
 {
-    int words, bytes, data;
+    size_t words, bytes, data;
     static char *names[] = {NULL, "dynamic", "static", "read-only"};
 
     write_lispobj(id, file);
@@ -88,8 +191,13 @@ output_space(FILE *file, int id, lispobj *addr, lispobj *end, os_vm_offset_t fil
 
     bytes = words * sizeof(lispobj);
 
-    printf("writing %d bytes from the %s space at 0x%08lx\n",
-           bytes, names[id], (unsigned long)addr);
+#if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_SB_LUTEX)
+    printf("scanning space for lutexes...\n");
+    scan_for_lutexes((char *)addr, words);
+#endif
+
+    printf("writing %lu bytes from the %s space at 0x%08lx\n",
+           (unsigned long)bytes, names[id], (unsigned long)addr);
 
     data = write_bytes(file, (char *)addr, bytes, file_offset);
 
@@ -110,10 +218,11 @@ open_core_for_saving(char *filename)
 
 boolean
 save_to_filehandle(FILE *file, char *filename, lispobj init_function,
-                   boolean make_executable)
+                   boolean make_executable,
+                   boolean save_runtime_options)
 {
     struct thread *th;
-    os_vm_offset_t core_start_pos, core_end_pos, core_size;
+    os_vm_offset_t core_start_pos;
 
     /* Smash the enclosing state. (Once we do this, there's no good
      * way to go back, which is a sufficient reason that this ends up
@@ -148,7 +257,7 @@ save_to_filehandle(FILE *file, char *filename, lispobj init_function,
          file);
     {
         unsigned char *p;
-        for (p = build_id; *p; ++p)
+        for (p = (unsigned char *)build_id; *p; ++p)
             write_lispobj(*p, file);
     }
 
@@ -201,34 +310,69 @@ save_to_filehandle(FILE *file, char *filename, lispobj init_function,
     {
         size_t size = (last_free_page*sizeof(long)+os_vm_page_size-1)
             &~(os_vm_page_size-1);
-        long *data = calloc(size, 1);
+        unsigned long *data = calloc(size, 1);
         if (data) {
+            unsigned long word;
             long offset;
             int i;
             for (i = 0; i < last_free_page; i++) {
-                data[i] = page_table[i].first_object_offset;
+                /* Thanks to alignment requirements, the two low bits
+                 * are always zero, so we can use them to store the
+                 * allocation type -- region is always closed, so only
+                 * the two low bits of allocation flags matter. */
+                word = page_table[i].region_start_offset;
+                gc_assert((word & 0x03) == 0);
+                data[i] = word | (0x03 & page_table[i].allocated);
             }
             write_lispobj(PAGE_TABLE_CORE_ENTRY_TYPE_CODE, file);
             write_lispobj(4, file);
             write_lispobj(size, file);
-            offset = write_bytes(file, (char *) data, size, core_start_pos);
+            offset = write_bytes(file, (char *)data, size, core_start_pos);
             write_lispobj(offset, file);
         }
     }
 #endif
 
+#if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_SB_LUTEX)
+    if(n_lutexes > 0) {
+        long offset;
+        printf("writing %ld lutexes to the core...\n", n_lutexes);
+        write_lispobj(LUTEX_TABLE_CORE_ENTRY_TYPE_CODE, file);
+        /* word count of the entry */
+        write_lispobj(4, file);
+        /* indicate how many lutexes we saved */
+        write_lispobj(n_lutexes, file);
+        /* save the lutexes */
+        offset = write_bytes(file, (char *) lutex_addresses,
+                             n_lutexes * sizeof(*lutex_addresses),
+                             core_start_pos);
+
+        write_lispobj(offset, file);
+    }
+#endif
+
     write_lispobj(END_CORE_ENTRY_TYPE_CODE, file);
 
     /* Write a trailing header, ignored when parsing the core normally.
      * This is used to locate the start of the core when the runtime is
      * prepended to it. */
     fseek(file, 0, SEEK_END);
-    core_end_pos = ftell(file);
-    core_size = core_end_pos - core_start_pos;
 
-    fwrite(&core_size, sizeof(os_vm_offset_t), 1, file);
-    write_lispobj(CORE_MAGIC, file);
-    fclose(file);
+    /* If NULL runtime options are passed to write_runtime_options,
+     * command-line processing is performed as normal in the SBCL
+     * executable. Otherwise, the saved runtime options are used and
+     * all command-line arguments are available to Lisp in
+     * SB-EXT:*POSIX-ARGV*. */
+    write_runtime_options(file,
+                          (save_runtime_options ? runtime_options : NULL));
+
+    if (1 != fwrite(&core_start_pos, sizeof(os_vm_offset_t), 1, file)) {
+        perror("Error writing core starting position to file");
+        fclose(file);
+    } else {
+        write_lispobj(CORE_MAGIC, file);
+        fclose(file);
+    }
 
 #ifndef LISP_FEATURE_WIN32
     if (make_executable)
@@ -239,6 +383,25 @@ save_to_filehandle(FILE *file, char *filename, lispobj init_function,
     exit(0);
 }
 
+/* Check if the build_id for the current runtime is present in a
+ * buffer. */
+int
+check_runtime_build_id(void *buf, size_t size)
+{
+    size_t idlen;
+    char *pos;
+
+    idlen = strlen(build_id) - 1;
+    while ((pos = memchr(buf, build_id[0], size)) != NULL) {
+        size -= (pos + 1) - (char *)buf;
+        buf = (pos + 1);
+        if (idlen <= size && memcmp(buf, build_id + 1, idlen) == 0)
+            return 1;
+    }
+
+    return 0;
+}
+
 /* Slurp the executable portion of the runtime into a malloced buffer
  * and return it.  Places the size in bytes of the runtime into
  * 'size_out'.  Returns NULL if the runtime cannot be loaded from
@@ -270,6 +433,12 @@ load_runtime(char *runtime_path, size_t *size_out)
         goto lose;
     }
 
+    if (!check_runtime_build_id(buf, size)) {
+        fprintf(stderr, "Failed to locate current build_id in runtime: %s\n",
+            runtime_path);
+        goto lose;
+    }
+
     fclose(input);
     *size_out = size;
     return buf;
@@ -288,13 +457,20 @@ save_runtime_to_filehandle(FILE *output, void *runtime, size_t runtime_size)
     size_t padding;
     void *padbytes;
 
-    fwrite(runtime, 1, runtime_size, output);
+    if (runtime_size != fwrite(runtime, 1, runtime_size, output)) {
+        perror("Error saving runtime");
+        return 0;
+    }
 
     padding = (os_vm_page_size - (runtime_size % os_vm_page_size)) & ~os_vm_page_size;
     if (padding > 0) {
         padbytes = successful_malloc(padding);
         memset(padbytes, 0, padding);
-        fwrite(padbytes, 1, padding, output);
+        if (padding != fwrite(padbytes, 1, padding, output)) {
+            perror("Error saving runtime");
+            free(padbytes);
+            return 0;
+        }
         free(padbytes);
     }
 
@@ -309,15 +485,19 @@ prepare_to_save(char *filename, boolean prepend_runtime, void **runtime_bytes,
     char *runtime_path;
 
     if (prepend_runtime) {
-        runtime_path = os_get_runtime_executable_path();
+        runtime_path = os_get_runtime_executable_path(0);
 
-        if (runtime_path == NULL) {
+        if (runtime_path == NULL && saved_runtime_path == NULL) {
             fprintf(stderr, "Unable to get default runtime path.\n");
             return NULL;
         }
 
-        *runtime_bytes = load_runtime(runtime_path, runtime_size);
-        free(runtime_path);
+        if (runtime_path == NULL)
+            *runtime_bytes = load_runtime(saved_runtime_path, runtime_size);
+        else {
+            *runtime_bytes = load_runtime(runtime_path, runtime_size);
+            free(runtime_path);
+        }
 
         if (*runtime_bytes == NULL)
             return 0;
@@ -334,7 +514,8 @@ prepare_to_save(char *filename, boolean prepend_runtime, void **runtime_bytes,
 }
 
 boolean
-save(char *filename, lispobj init_function, boolean prepend_runtime)
+save(char *filename, lispobj init_function, boolean prepend_runtime,
+     boolean save_runtime_options)
 {
     FILE *file;
     void *runtime_bytes = NULL;
@@ -347,5 +528,6 @@ save(char *filename, lispobj init_function, boolean prepend_runtime)
     if (prepend_runtime)
         save_runtime_to_filehandle(file, runtime_bytes, runtime_size);
 
-    return save_to_filehandle(file, filename, init_function, prepend_runtime);
+    return save_to_filehandle(file, filename, init_function, prepend_runtime,
+                              save_runtime_options);
 }