X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fruntime%2Fsave.c;h=9746524918536e3e6abcba38486ab23c15d9f255;hb=d6f9676ae94419cb5544c45821a8d31adbc1fbe8;hp=f6742c3d65a0110880eb7273b66625f201b513d5;hpb=3a0f3612dc2bbf3e4e8e7395bcbbf8cd1791b963;p=sbcl.git diff --git a/src/runtime/save.c b/src/runtime/save.c index f6742c3..9746524 100644 --- a/src/runtime/save.c +++ b/src/runtime/save.c @@ -34,20 +34,117 @@ #include "genesis/static-symbols.h" #include "genesis/symbol.h" +#ifdef LISP_FEATURE_SB_CORE_COMPRESSION +# include +#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 void +write_bytes_to_file(FILE * file, char *addr, long bytes, int compression) +{ + if (compression == COMPRESSION_LEVEL_NONE) { + while (bytes > 0) { + long count = fwrite(addr, 1, bytes, file); + if (count > 0) { + bytes -= count; + addr += count; + } + else { + perror("error writing to save file"); + bytes = 0; + } + } +#ifdef LISP_FEATURE_SB_CORE_COMPRESSION + } else if ((compression >= -1) && (compression <= 9)) { +# define ZLIB_BUFFER_SIZE (1u<<16) + z_stream stream; + unsigned char buf[ZLIB_BUFFER_SIZE]; + unsigned char * written, * end; + long total_written = 0; + int ret; + stream.zalloc = NULL; + stream.zfree = NULL; + stream.opaque = NULL; + stream.avail_in = bytes; + stream.next_in = (void*)addr; + ret = deflateInit(&stream, compression); + if (ret != Z_OK) + lose("deflateInit: %i\n", ret); + do { + stream.avail_out = sizeof(buf); + stream.next_out = buf; + ret = deflate(&stream, Z_FINISH); + if (ret < 0) lose("zlib deflate error: %i... exiting\n", ret); + written = buf; + end = buf+sizeof(buf)-stream.avail_out; + total_written += end - written; + while (written < end) { + long count = fwrite(written, 1, end-written, file); + if (count > 0) { + written += count; + } else { + lose("unable to write to core file\n"); + } + } + } while (stream.avail_out == 0); + deflateEnd(&stream); + printf("compressed %lu bytes into %lu at level %i\n", + bytes, total_written, compression); +# undef ZLIB_BUFFER_SIZE +#endif + } else { +#ifdef LISP_FEATURE_SB_CORE_COMPRESSION + lose("Unknown core compression level %i, exiting\n", compression); +#else + lose("zlib-compressed core support not built in this runtime\n"); +#endif + } + + fflush(file); +}; + + static long -write_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset) +write_and_compress_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset, + int compression) { - long count, here, data; + long here, data; bytes = (bytes+os_vm_page_size-1)&~(os_vm_page_size-1); #ifdef LISP_FEATURE_WIN32 + long count; /* touch every single page in the space to force it to be mapped. */ for (count = 0; count < bytes; count += 0x1000) { volatile int temp = addr[count]; @@ -59,39 +156,41 @@ write_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset) fseek(file, 0, SEEK_END); data = (ftell(file)+os_vm_page_size-1)&~(os_vm_page_size-1); fseek(file, data, SEEK_SET); - - while (bytes > 0) { - count = fwrite(addr, 1, bytes, file); - if (count > 0) { - bytes -= count; - addr += count; - } - else { - perror("error writing to save file"); - bytes = 0; - } - } - fflush(file); + write_bytes_to_file(file, addr, bytes, compression); fseek(file, here, SEEK_SET); return ((data - file_offset) / os_vm_page_size) - 1; } +static long +write_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset) +{ + return write_and_compress_bytes(file, addr, bytes, file_offset, + COMPRESSION_LEVEL_NONE); +} + static void -output_space(FILE *file, int id, lispobj *addr, lispobj *end, os_vm_offset_t file_offset) +output_space(FILE *file, int id, lispobj *addr, lispobj *end, + os_vm_offset_t file_offset, + int core_compression_level) { - int words, bytes, data; + size_t words, bytes, data, compressed_flag; static char *names[] = {NULL, "dynamic", "static", "read-only"}; - write_lispobj(id, file); + compressed_flag + = ((core_compression_level != COMPRESSION_LEVEL_NONE) + ? DEFLATED_CORE_SPACE_ID_FLAG : 0); + + write_lispobj(id | compressed_flag, file); words = end - addr; write_lispobj(words, file); bytes = words * sizeof(lispobj); - printf("writing %d bytes from the %s space at 0x%08lx\n", - bytes, names[id], (unsigned long)addr); + 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); + data = write_and_compress_bytes(file, (char *)addr, bytes, file_offset, + core_compression_level); write_lispobj(data, file); write_lispobj((long)addr / os_vm_page_size, file); @@ -110,10 +209,12 @@ 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, + int core_compression_level) { 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 +249,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); } @@ -160,12 +261,14 @@ save_to_filehandle(FILE *file, char *filename, lispobj init_function, READ_ONLY_CORE_SPACE_ID, (lispobj *)READ_ONLY_SPACE_START, (lispobj *)SymbolValue(READ_ONLY_SPACE_FREE_POINTER,0), - core_start_pos); + core_start_pos, + core_compression_level); output_space(file, STATIC_CORE_SPACE_ID, (lispobj *)STATIC_SPACE_START, (lispobj *)SymbolValue(STATIC_SPACE_FREE_POINTER,0), - core_start_pos); + core_start_pos, + core_compression_level); #ifdef LISP_FEATURE_GENCGC /* Flush the current_region, updating the tables. */ gc_alloc_update_all_page_tables(); @@ -177,20 +280,23 @@ save_to_filehandle(FILE *file, char *filename, lispobj init_function, DYNAMIC_CORE_SPACE_ID, (lispobj *)DYNAMIC_SPACE_START, dynamic_space_free_pointer, - core_start_pos); + core_start_pos, + core_compression_level); #else output_space(file, DYNAMIC_CORE_SPACE_ID, (lispobj *)current_dynamic_space, dynamic_space_free_pointer, - core_start_pos); + core_start_pos, + core_compression_level); #endif #else output_space(file, DYNAMIC_CORE_SPACE_ID, (lispobj *)DYNAMIC_SPACE_START, (lispobj *)SymbolValue(ALLOCATION_POINTER,0), - core_start_pos); + core_start_pos, + core_compression_level); #endif write_lispobj(INITIAL_FUN_CORE_ENTRY_TYPE_CODE, file); @@ -201,17 +307,24 @@ 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); } } @@ -223,12 +336,22 @@ 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); - 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 +362,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 +412,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 +436,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 +464,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 +493,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, boolean compressed, int compression_level) { FILE *file; void *runtime_bytes = NULL; @@ -347,5 +507,7 @@ 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, + compressed ? compressed : COMPRESSION_LEVEL_NONE); }