0.9.8.7:
[sbcl.git] / src / runtime / save.c
1 /*
2  * This software is part of the SBCL system. See the README file for
3  * more information.
4  *
5  * This software is derived from the CMU CL system, which was
6  * written at Carnegie Mellon University and released into the
7  * public domain. The software is in the public domain and is
8  * provided with absolutely no warranty. See the COPYING and CREDITS
9  * files for more information.
10  */
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <signal.h>
16 #include <sys/file.h>
17
18 #include "sbcl.h"
19 #include "runtime.h"
20 #include "os.h"
21 #include "core.h"
22 #include "globals.h"
23 #include "save.h"
24 #include "dynbind.h"
25 #include "lispregs.h"
26 #include "validate.h"
27 #include "gc-internal.h"
28 #include "thread.h"
29
30 #include "genesis/static-symbols.h"
31 #include "genesis/symbol.h"
32
33 static void
34 write_lispobj(lispobj obj, FILE *file)
35 {
36     fwrite(&obj, sizeof(lispobj), 1, file);
37 }
38
39 static long
40 write_bytes(FILE *file, char *addr, long bytes)
41 {
42     long count, here, data;
43
44     bytes = (bytes+os_vm_page_size-1)&~(os_vm_page_size-1);
45
46 #ifdef LISP_FEATURE_WIN32
47     /* touch every single page in the space to force it to be mapped. */
48     for (count = 0; count < bytes; count += 0x1000) {
49         volatile int temp = addr[count];
50     }
51 #endif
52
53     fflush(file);
54     here = ftell(file);
55     fseek(file, 0, 2);
56     data = (ftell(file)+os_vm_page_size-1)&~(os_vm_page_size-1);
57     fseek(file, data, 0);
58
59     while (bytes > 0) {
60         count = fwrite(addr, 1, bytes, file);
61         if (count > 0) {
62             bytes -= count;
63             addr += count;
64         }
65         else {
66             perror("error writing to save file");
67             bytes = 0;
68         }
69     }
70     fflush(file);
71     fseek(file, here, 0);
72     return data/os_vm_page_size - 1;
73 }
74
75 static void
76 output_space(FILE *file, int id, lispobj *addr, lispobj *end)
77 {
78     int words, bytes, data;
79     static char *names[] = {NULL, "dynamic", "static", "read-only"};
80
81     write_lispobj(id, file);
82     words = end - addr;
83     write_lispobj(words, file);
84
85     bytes = words * sizeof(lispobj);
86
87     printf("writing %d bytes from the %s space at 0x%08lx\n",
88            bytes, names[id], (unsigned long)addr);
89
90     data = write_bytes(file, (char *)addr, bytes);
91
92     write_lispobj(data, file);
93     write_lispobj((long)addr / os_vm_page_size, file);
94     write_lispobj((bytes + os_vm_page_size - 1) / os_vm_page_size, file);
95 }
96
97 FILE *
98 open_core_for_saving(char *filename)
99 {
100     /* Open the output file. We don't actually need the file yet, but
101      * the fopen() might fail for some reason, and we want to detect
102      * that and back out before we do anything irreversible. */
103     unlink(filename);
104     return fopen(filename, "wb");
105 }
106
107 boolean
108 save_to_filehandle(FILE *file, char *filename, lispobj init_function)
109 {
110     struct thread *th;
111
112     /* Smash the enclosing state. (Once we do this, there's no good
113      * way to go back, which is a sufficient reason that this ends up
114      * being SAVE-LISP-AND-DIE instead of SAVE-LISP-AND-GO-ON). */
115     printf("[undoing binding stack and other enclosing state... ");
116     fflush(stdout);
117     for_each_thread(th) {       /* XXX really? */
118         unbind_to_here((lispobj *)th->binding_stack_start,th);
119         SetSymbolValue(CURRENT_CATCH_BLOCK, 0,th);
120         SetSymbolValue(CURRENT_UNWIND_PROTECT_BLOCK, 0,th);
121     }
122     printf("done]\n");
123     fflush(stdout);
124
125     /* (Now we can actually start copying ourselves into the output file.) */
126
127     printf("[saving current Lisp image into %s:\n", filename);
128     fflush(stdout);
129
130     write_lispobj(CORE_MAGIC, file);
131
132     write_lispobj(VERSION_CORE_ENTRY_TYPE_CODE, file);
133     write_lispobj(3, file);
134     write_lispobj(SBCL_CORE_VERSION_INTEGER, file);
135
136     write_lispobj(BUILD_ID_CORE_ENTRY_TYPE_CODE, file);
137     write_lispobj(/* (We're writing the word count of the entry here, and the 2
138           * term is one word for the leading BUILD_ID_CORE_ENTRY_TYPE_CODE
139           * word and one word where we store the count itself.) */
140          2 + strlen((const char *)build_id),
141          file);
142     {
143         unsigned char *p;
144         for (p = build_id; *p; ++p)
145             write_lispobj(*p, file);
146     }
147
148     write_lispobj(NEW_DIRECTORY_CORE_ENTRY_TYPE_CODE, file);
149     write_lispobj(/* (word count = 3 spaces described by 5 words each, plus the
150           * entry type code, plus this count itself) */
151          (5*3)+2, file);
152     output_space(file,
153                  READ_ONLY_CORE_SPACE_ID,
154                  (lispobj *)READ_ONLY_SPACE_START,
155                  (lispobj *)SymbolValue(READ_ONLY_SPACE_FREE_POINTER,0));
156     output_space(file,
157                  STATIC_CORE_SPACE_ID,
158                  (lispobj *)STATIC_SPACE_START,
159                  (lispobj *)SymbolValue(STATIC_SPACE_FREE_POINTER,0));
160 #ifdef reg_ALLOC
161     output_space(file,
162                  DYNAMIC_CORE_SPACE_ID,
163                  (lispobj *)current_dynamic_space,
164                  dynamic_space_free_pointer);
165 #else
166 #ifdef LISP_FEATURE_GENCGC
167     /* Flush the current_region, updating the tables. */
168     gc_alloc_update_all_page_tables();
169     update_dynamic_space_free_pointer();
170 #endif
171     output_space(file,
172                  DYNAMIC_CORE_SPACE_ID,
173                  (lispobj *)DYNAMIC_SPACE_START,
174                  (lispobj *)SymbolValue(ALLOCATION_POINTER,0));
175 #endif
176
177     write_lispobj(INITIAL_FUN_CORE_ENTRY_TYPE_CODE, file);
178     write_lispobj(3, file);
179     write_lispobj(init_function, file);
180
181 #ifdef LISP_FEATURE_GENCGC
182     {
183         size_t size = (last_free_page*sizeof(long)+os_vm_page_size-1)
184             &~(os_vm_page_size-1);
185         long *data = calloc(size, 1);
186         if (data) {
187             long offset;
188             int i;
189             for (i = 0; i < last_free_page; i++) {
190                 data[i] = page_table[i].first_object_offset;
191             }
192             write_lispobj(PAGE_TABLE_CORE_ENTRY_TYPE_CODE, file);
193             write_lispobj(4, file);
194             write_lispobj(size, file);
195             offset = write_bytes(file, (char *) data, size);
196             write_lispobj(offset, file);
197         }
198     }
199 #endif
200
201     write_lispobj(END_CORE_ENTRY_TYPE_CODE, file);
202
203     fclose(file);
204     printf("done]\n");
205
206     exit(0);
207 }
208
209 boolean
210 save(char *filename, lispobj init_function)
211 {
212     FILE *file = open_core_for_saving(filename);
213
214     if (!file) {
215         perror(filename);
216         return 1;
217     }
218
219     return save_to_filehandle(file, filename, init_function);
220 }