1.0.21.24: saving runtime options in executables
authorNikodemus Siivola <nikodemus@random-state.net>
Fri, 17 Oct 2008 12:49:35 +0000 (12:49 +0000)
committerNikodemus Siivola <nikodemus@random-state.net>
Fri, 17 Oct 2008 12:49:35 +0000 (12:49 +0000)
 * SAVE-LISP-AND-DIE argument :SAVE-RUNTIME-OPTIONS causes the values
   of --dynamic-space-size and --control-stack-size used in the
   current invocation of SBCL to be saved in the executable core, which
   will then reuse them instead of doing normal runtime option processing.

 * Thanks to Zach Beane.

CREDITS
NEWS
src/code/save.lisp
src/runtime/coreparse.c
src/runtime/gencgc.c
src/runtime/globals.h
src/runtime/runtime.c
src/runtime/runtime.h
src/runtime/save.c
src/runtime/save.h
version.lisp-expr

diff --git a/CREDITS b/CREDITS
index 8c015e4..3aafdf1 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -520,8 +520,9 @@ Daniel Barlow:
   contrib packages.
 
 Zach Beane:
-  He provided a number of additions to SB-POSIX and implemented the
-  original timer facility on which SBCL's timers are based.
+  He provided a number of additions to SB-POSIX, implemented the
+  original timer facility on which SBCL's timers are based. and also
+  contributed the :SAVE-RUNTIME-OPTIONS support for SAVE-LISP-AND-DIE.
 
 James Bielman:
   He assisted in work on the port to the Windows operating system, and
diff --git a/NEWS b/NEWS
index deaea56..32e66eb 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,10 @@ changes in sbcl-1.0.22 relative to 1.0.21:
   * new feature: new commandline argument: --script, which supports
     shebang lines. See documentation for details. (based on work by
     Kevin Reid)
+  * new feature: SAVE-LISP-AND-DIE can save current values of
+    --dynamic-space-size and --control-stack-size in the executable core,
+    causing it to skip normal runtime option processing. See documentation
+    for details. (thanks to Zach Beane)
   * enhancement: inoccous calls to EVAL or generic functions dispatching
     on subclasses of eg. STREAM no longer cause compiler notes to appear.
   * enhancement: the system no longer resignals errors from --load and
index 76b6e5b..c7b5b6d 100644 (file)
 (define-alien-routine "save" (boolean)
   (file c-string)
   (initial-fun (unsigned #.sb!vm:n-word-bits))
-  (prepend-runtime int))
+  (prepend-runtime int)
+  (save-runtime-options int))
 
 #!+gencgc
 (define-alien-routine "gc_and_save" void
   (file c-string)
-  (prepend-runtime int))
+  (prepend-runtime int)
+  (save-runtime-options int))
 
 #!+gencgc
 (defvar sb!vm::*restart-lisp-function*)
 
 (defun save-lisp-and-die (core-file-name &key
                                          (toplevel #'toplevel-init)
+                                         (executable nil)
+                                         (save-runtime-options nil)
                                          (purify t)
                                          (root-structures ())
-                                         (environment-name "auxiliary")
-                                         (executable nil))
+                                         (environment-name "auxiliary"))
   #!+sb-doc
   "Save a \"core image\", i.e. enough information to restart a Lisp
 process later in the same state, in the file of the specified name.
@@ -52,7 +55,16 @@ The following &KEY arguments are defined:
   :EXECUTABLE
      If true, arrange to combine the SBCL runtime and the core image
      to create a standalone executable.  If false (the default), the
-     core image will not be executable on its own.
+     core image will not be executable on its own. Executable images
+     always behave as if they were passed the --noinform runtime option.
+
+  :SAVE-RUNTIME-OPTIONS
+     If true, values of runtime options --dynamic-space-size and
+     --control-stack-size that were used to start SBCL are stored in
+     the standalone executable, and restored when the executable is
+     run. This also inhibits normal runtime option processing, causing
+     all command line arguments to be passed to the toplevel.
+     Meaningless if :EXECUTABLE is NIL.
 
   :PURIFY
      If true (the default on cheneygc), do a purifying GC which moves all
@@ -114,6 +126,8 @@ sufficiently motivated to do lengthy fixes."
              (handling-end-of-the-world
                (reinit)
                (funcall toplevel)))
+           (foreign-bool (value)
+             (if value 1 0))
            (save-core (gc)
              (when gc
                #!-gencgc (gc)
@@ -122,11 +136,13 @@ sufficiently motivated to do lengthy fixes."
                ;; (over 50% on x86). This needs to be done by a single function
                ;; since the GC will invalidate the stack.
                #!+gencgc (gc-and-save (unix-namestring core-file-name nil)
-                                      (if executable 1 0)))
+                                      (foreign-bool executable)
+                                      (foreign-bool save-runtime-options))
              (without-gcing
               (save (unix-namestring core-file-name nil)
                     (get-lisp-obj-address #'restart-lisp)
-                    (if executable 1 0)))))
+                    (foreign-bool executable)
+                    (foreign-bool save-runtime-options))))))
     ;; Save the restart function into a static symbol, to allow GC-AND-SAVE
     ;; access to it even after the GC has moved it.
     #!+gencgc
index 87891dc..5fd590e 100644 (file)
@@ -56,11 +56,47 @@ open_binary(char *filename, int mode)
     return open(filename, mode);
 }
 
+
+static struct runtime_options *
+read_runtime_options(int fd)
+{
+    size_t optarray[RUNTIME_OPTIONS_WORDS];
+    struct runtime_options *options = NULL;
+
+    if (read(fd, optarray, RUNTIME_OPTIONS_WORDS * sizeof(size_t)) !=
+        RUNTIME_OPTIONS_WORDS * sizeof(size_t)) {
+        return NULL;
+    }
+
+    if ((RUNTIME_OPTIONS_MAGIC != optarray[0]) || (0 == optarray[1])) {
+        return NULL;
+    }
+
+    options = successful_malloc(sizeof(struct runtime_options));
+
+    options->dynamic_space_size = optarray[2];
+    options->thread_control_stack_size = optarray[3];
+
+    return options;
+}
+
+void
+maybe_initialize_runtime_options(int fd)
+{
+    off_t end_offset = sizeof(lispobj) +
+        sizeof(os_vm_offset_t) +
+        (RUNTIME_OPTIONS_WORDS * sizeof(size_t));
+
+    lseek(fd, -end_offset, SEEK_END);
+    runtime_options = read_runtime_options(fd);
+}
+
 /* Search 'filename' for an embedded core.  An SBCL core has, at the
- * end of the file, a trailer containing the size of the core (an
- * os_vm_offset_t) and a final signature word (the lispobj
- * CORE_MAGIC).  If this trailer is found at the end of the file, the
- * start of the core can be determined from the core size.
+ * end of the file, a trailer containing optional saved runtime
+ * options, the start of the core (an os_vm_offset_t), and a final
+ * signature word (the lispobj CORE_MAGIC).  If this trailer is found
+ * at the end of the file, the start of the core can be determined
+ * from the core size.
  *
  * If an embedded core is present, this returns the offset into the
  * file to load the core from, or -1 if no core is present. */
@@ -70,7 +106,7 @@ search_for_embedded_core(char *filename)
     lispobj header;
     os_vm_offset_t lispobj_size = sizeof(lispobj);
     os_vm_offset_t trailer_size = lispobj_size + sizeof(os_vm_offset_t);
-    os_vm_offset_t core_size, pos;
+    os_vm_offset_t core_start, pos;
     int fd = -1;
 
     if ((fd = open_binary(filename, O_RDONLY)) < 0)
@@ -83,10 +119,10 @@ search_for_embedded_core(char *filename)
     if (header == CORE_MAGIC) {
         if (lseek(fd, -trailer_size, SEEK_END) < 0)
             goto lose;
-        if (read(fd, &core_size, sizeof(os_vm_offset_t)) < 0)
+        if (read(fd, &core_start, sizeof(os_vm_offset_t)) < 0)
             goto lose;
 
-        if (lseek(fd, -(core_size + trailer_size), SEEK_END) < 0)
+        if (lseek(fd, core_start, SEEK_SET) < 0)
             goto lose;
         pos = lseek(fd, 0, SEEK_CUR);
 
@@ -96,6 +132,8 @@ search_for_embedded_core(char *filename)
         if (header != CORE_MAGIC)
             goto lose;
 
+        maybe_initialize_runtime_options(fd);
+
         close(fd);
         return pos;
     }
index c9d529c..ced46d0 100644 (file)
@@ -4848,7 +4848,8 @@ prepare_for_final_gc ()
  * function being set to the value of the static symbol
  * SB!VM:RESTART-LISP-FUNCTION */
 void
-gc_and_save(char *filename, int prepend_runtime)
+gc_and_save(char *filename, boolean prepend_runtime,
+            boolean save_runtime_options)
 {
     FILE *file;
     void *runtime_bytes = NULL;
@@ -4883,7 +4884,7 @@ gc_and_save(char *filename, int prepend_runtime)
     /* The dumper doesn't know that pages need to be zeroed before use. */
     zero_all_free_pages();
     save_to_filehandle(file, filename, SymbolValue(RESTART_LISP_FUNCTION,0),
-                       prepend_runtime);
+                       prepend_runtime, save_runtime_options);
     /* Oops. Save still managed to fail. Since we've mangled the stack
      * beyond hope, there's not much we can do.
      * (beyond FUNCALLing RESTART_LISP_FUNCTION, but I suspect that's
index eacb440..e5a3a23 100644 (file)
@@ -35,6 +35,8 @@ extern int foreign_function_call_active;
 extern size_t dynamic_space_size;
 extern size_t thread_control_stack_size;
 
+extern struct runtime_options *runtime_options;
+
 #ifdef LISP_FEATURE_WIN32
 #define ENVIRON _environ
 #else
index 3e5cf1c..2d0e68d 100644 (file)
@@ -202,6 +202,8 @@ search_for_core ()
 char **posix_argv;
 char *core_string;
 
+struct runtime_options *runtime_options;
+
 \f
 int
 main(int argc, char *argv[], char *envp[])
@@ -216,6 +218,7 @@ main(int argc, char *argv[], char *envp[])
     char *core = 0;
     char **sbcl_argv = 0;
     os_vm_offset_t embedded_core_offset = 0;
+    char *runtime_path = 0;
 
     /* other command line options */
     boolean noinform = 0;
@@ -229,10 +232,34 @@ main(int argc, char *argv[], char *envp[])
 
     setlocale(LC_ALL, "");
 
+    runtime_options = NULL;
+
+    /* Check early to see if this executable has an embedded core,
+     * which also populates runtime_options if the core has runtime
+     * options */
+    runtime_path = os_get_runtime_executable_path();
+    if (runtime_path) {
+        os_vm_offset_t offset = search_for_embedded_core(runtime_path);
+        if (offset != -1) {
+            embedded_core_offset = offset;
+            core = runtime_path;
+        } else {
+            free(runtime_path);
+        }
+    }
+
+
     /* Parse our part of the command line (aka "runtime options"),
      * stripping out those options that we handle. */
-    {
+    if (runtime_options != NULL) {
+        dynamic_space_size = runtime_options->dynamic_space_size;
+        thread_control_stack_size = runtime_options->thread_control_stack_size;
+        sbcl_argv = argv;
+    } else {
         int argi = 1;
+
+        runtime_options = successful_malloc(sizeof(struct runtime_options));
+
         while (argi < argc) {
             char *arg = argv[argi];
             if (0 == strcmp(arg, "--script")) {
@@ -341,6 +368,10 @@ main(int argc, char *argv[], char *envp[])
     dynamic_space_size &= ~(PAGE_BYTES-1);
     thread_control_stack_size &= ~(CONTROL_STACK_ALIGNMENT_BYTES-1);
 
+    /* Preserve the runtime options for possible future core saving */
+    runtime_options->dynamic_space_size = dynamic_space_size;
+    runtime_options->thread_control_stack_size = thread_control_stack_size;
+
     /* KLUDGE: os_vm_page_size is set by os_init(), and on some
      * systems (e.g. Alpha) arch_init() needs need os_vm_page_size, so
      * it must follow os_init(). -- WHN 2000-01-26 */
@@ -351,21 +382,7 @@ main(int argc, char *argv[], char *envp[])
 
     /* If no core file was specified, look for one. */
     if (!core) {
-       char *runtime_path = os_get_runtime_executable_path();
-
-       if (runtime_path) {
-          os_vm_offset_t offset = search_for_embedded_core(runtime_path);
-
-          if (offset != -1) {
-             embedded_core_offset = offset;
-             core = runtime_path;
-          } else {
-             free(runtime_path);
-             core = search_for_core();
-          }
-       } else {
-          core = search_for_core();
-       }
+        core = search_for_core();
     }
 
     /* Make sure that SBCL_HOME is set and not the empty string,
index 9905632..30bccd7 100644 (file)
@@ -197,4 +197,13 @@ typedef int boolean;
 extern void *successful_malloc (size_t size);
 extern char *copied_string (char *string);
 
+#define RUNTIME_OPTIONS_MAGIC 0x31EBF355
+/* 1 for magic, 1 for boolean, 2 for struct runtime_options fields */
+#define RUNTIME_OPTIONS_WORDS (1 + 1 + 2)
+
+struct runtime_options {
+    size_t dynamic_space_size;
+    size_t thread_control_stack_size;
+};
+
 #endif /* _SBCL_RUNTIME_H_ */
index 51c01a1..20d72d7 100644 (file)
 #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;
+    }
+
+    fwrite(optarray, sizeof(size_t), RUNTIME_OPTIONS_WORDS, file);
+}
+
 static void
 write_lispobj(lispobj obj, FILE *file)
 {
@@ -192,10 +213,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
@@ -323,10 +345,16 @@ save_to_filehandle(FILE *file, char *filename, lispobj init_function,
      * 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);
+    /* 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));
+
+    fwrite(&core_start_pos, sizeof(os_vm_offset_t), 1, file);
     write_lispobj(CORE_MAGIC, file);
     fclose(file);
 
@@ -434,7 +462,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;
@@ -447,5 +476,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);
 }
index 7ad3ac6..5b1cdcd 100644 (file)
@@ -18,7 +18,7 @@ extern FILE* open_core_for_saving(char *filename);
 extern void *load_runtime(char *runtime_path, size_t *size_out);
 extern FILE *prepare_to_save(char *filename, boolean prepend_runtime, void **runtime_bytes, size_t *runtime_size);
 extern boolean save_runtime_to_filehandle(FILE *output, void *runtime_bytes, size_t runtime_size);
-extern boolean save_to_filehandle(FILE *file, char *filename, lispobj initfun, int make_executable);
-extern boolean save(char *filename, lispobj initfun, boolean prepend_runtime);
+extern boolean save_to_filehandle(FILE *file, char *filename, lispobj initfun, boolean make_executable, boolean keep_runtime_options);
+extern boolean save(char *filename, lispobj initfun, boolean prepend_runtime, boolean keep_runtime_options);
 
 #endif
index 0198417..eba6737 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.21.23"
+"1.0.21.24"