From f82850855bab2cdaaf51c4e92d506b365866e65f Mon Sep 17 00:00:00 2001 From: Nikodemus Siivola Date: Fri, 17 Oct 2008 12:49:35 +0000 Subject: [PATCH] 1.0.21.24: saving runtime options in executables * 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 | 5 +++-- NEWS | 4 ++++ src/code/save.lisp | 30 ++++++++++++++++++++------- src/runtime/coreparse.c | 52 ++++++++++++++++++++++++++++++++++++++++------- src/runtime/gencgc.c | 5 +++-- src/runtime/globals.h | 2 ++ src/runtime/runtime.c | 49 +++++++++++++++++++++++++++++--------------- src/runtime/runtime.h | 9 ++++++++ src/runtime/save.c | 44 ++++++++++++++++++++++++++++++++------- src/runtime/save.h | 4 ++-- version.lisp-expr | 2 +- 11 files changed, 162 insertions(+), 44 deletions(-) diff --git a/CREDITS b/CREDITS index 8c015e4..3aafdf1 100644 --- 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 --- 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 diff --git a/src/code/save.lisp b/src/code/save.lisp index 76b6e5b..c7b5b6d 100644 --- a/src/code/save.lisp +++ b/src/code/save.lisp @@ -20,22 +20,25 @@ (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 diff --git a/src/runtime/coreparse.c b/src/runtime/coreparse.c index 87891dc..5fd590e 100644 --- a/src/runtime/coreparse.c +++ b/src/runtime/coreparse.c @@ -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; } diff --git a/src/runtime/gencgc.c b/src/runtime/gencgc.c index c9d529c..ced46d0 100644 --- a/src/runtime/gencgc.c +++ b/src/runtime/gencgc.c @@ -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 diff --git a/src/runtime/globals.h b/src/runtime/globals.h index eacb440..e5a3a23 100644 --- a/src/runtime/globals.h +++ b/src/runtime/globals.h @@ -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 diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index 3e5cf1c..2d0e68d 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -202,6 +202,8 @@ search_for_core () char **posix_argv; char *core_string; +struct runtime_options *runtime_options; + 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, diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 9905632..30bccd7 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -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_ */ diff --git a/src/runtime/save.c b/src/runtime/save.c index 51c01a1..20d72d7 100644 --- a/src/runtime/save.c +++ b/src/runtime/save.c @@ -38,6 +38,27 @@ #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); } diff --git a/src/runtime/save.h b/src/runtime/save.h index 7ad3ac6..5b1cdcd 100644 --- a/src/runtime/save.h +++ b/src/runtime/save.h @@ -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 diff --git a/version.lisp-expr b/version.lisp-expr index 0198417..eba6737 100644 --- a/version.lisp-expr +++ b/version.lisp-expr @@ -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" -- 1.7.10.4