]> gitweb.factorcode.org Git - factor.git/blob - vm/image.cpp
VM: init_data_heap() isn't needed
[factor.git] / vm / image.cpp
1 #include "master.hpp"
2
3 namespace factor {
4
5 bool factor_arg(const vm_char* str, const vm_char* arg, cell* value) {
6   int val;
7   if (SSCANF(str, arg, &val) > 0) {
8     *value = val;
9     return true;
10   }
11   return false;
12 }
13
14 vm_parameters::vm_parameters() {
15   embedded_image = false;
16   image_path = NULL;
17
18   datastack_size = 32 * sizeof(cell);
19   retainstack_size = 32 * sizeof(cell);
20
21 #if defined(FACTOR_PPC)
22   callstack_size = 256 * sizeof(cell);
23 #else
24   callstack_size = 128 * sizeof(cell);
25 #endif
26
27   code_size = 64;
28   young_size = sizeof(cell) / 4;
29   aging_size = sizeof(cell) / 2;
30   tenured_size = 24 * sizeof(cell);
31
32   max_pic_size = 3;
33
34   fep = false;
35   signals = true;
36
37 #ifdef WINDOWS
38   console = GetConsoleWindow() != NULL;
39 #else
40   console = true;
41 #endif
42
43   callback_size = 256;
44 }
45
46 vm_parameters::~vm_parameters() {
47   free((vm_char *)image_path);
48   free((vm_char *)executable_path);
49 }
50
51 void vm_parameters::init_from_args(int argc, vm_char** argv) {
52   int i = 0;
53
54   for (i = 1; i < argc; i++) {
55     vm_char* arg = argv[i];
56     if (STRCMP(arg, STRING_LITERAL("--")) == 0)
57       break;
58     else if (factor_arg(arg, STRING_LITERAL("-datastack=%d"),
59                         &datastack_size))
60       ;
61     else if (factor_arg(arg, STRING_LITERAL("-retainstack=%d"),
62                         &retainstack_size))
63       ;
64     else if (factor_arg(arg, STRING_LITERAL("-callstack=%d"),
65                         &callstack_size))
66       ;
67     else if (factor_arg(arg, STRING_LITERAL("-young=%d"), &young_size))
68       ;
69     else if (factor_arg(arg, STRING_LITERAL("-aging=%d"), &aging_size))
70       ;
71     else if (factor_arg(arg, STRING_LITERAL("-tenured=%d"), &tenured_size))
72       ;
73     else if (factor_arg(arg, STRING_LITERAL("-codeheap=%d"), &code_size))
74       ;
75     else if (factor_arg(arg, STRING_LITERAL("-pic=%d"), &max_pic_size))
76       ;
77     else if (factor_arg(arg, STRING_LITERAL("-callbacks=%d"), &callback_size))
78       ;
79     else if (STRNCMP(arg, STRING_LITERAL("-i="), 3) == 0) {
80       // In case you specify -i more than once.
81       if (image_path) {
82         free((vm_char *)image_path);
83       }
84       image_path = safe_strdup(arg + 3);
85     }
86     else if (STRCMP(arg, STRING_LITERAL("-fep")) == 0)
87       fep = true;
88     else if (STRCMP(arg, STRING_LITERAL("-nosignals")) == 0)
89       signals = false;
90     else if (STRCMP(arg, STRING_LITERAL("-console")) == 0)
91       console = true;
92   }
93 }
94
95 void factor_vm::load_data_heap(FILE* file, image_header* h, vm_parameters* p) {
96   p->tenured_size = std::max((h->data_size * 3) / 2, p->tenured_size);
97
98   data_heap *d = new data_heap(&nursery,
99                                p->young_size, p->aging_size, p->tenured_size);
100   set_data_heap(d);
101   fixnum bytes_read =
102       raw_fread((void*)data->tenured->start, 1, h->data_size, file);
103
104   if ((cell)bytes_read != h->data_size) {
105     std::cout << "truncated image: " << bytes_read << " bytes read, ";
106     std::cout << h->data_size << " bytes expected\n";
107     fatal_error("load_data_heap failed", 0);
108   }
109
110   data->tenured->initial_free_list(h->data_size);
111 }
112
113 void factor_vm::load_code_heap(FILE* file, image_header* h, vm_parameters* p) {
114   if (h->code_size > p->code_size)
115     fatal_error("Code heap too small to fit image", h->code_size);
116
117   code = new code_heap(p->code_size);
118
119   if (h->code_size != 0) {
120     size_t bytes_read =
121         raw_fread((void*)code->allocator->start, 1, h->code_size, file);
122     if (bytes_read != h->code_size) {
123       std::cout << "truncated image: " << bytes_read << " bytes read, ";
124       std::cout << h->code_size << " bytes expected\n";
125       fatal_error("load_code_heap failed", 0);
126     }
127   }
128
129   code->allocator->initial_free_list(h->code_size);
130   code->initialize_all_blocks_set();
131 }
132
133 struct startup_fixup {
134   static const bool translated_code_block_map = true;
135
136   cell data_offset;
137   cell code_offset;
138
139   startup_fixup(cell data_offset, cell code_offset)
140       : data_offset(data_offset), code_offset(code_offset) {}
141
142   object* fixup_data(object* obj) {
143     return (object*)((cell)obj + data_offset);
144   }
145
146   code_block* fixup_code(code_block* obj) {
147     return (code_block*)((cell)obj + code_offset);
148   }
149
150   object* translate_data(const object* obj) { return fixup_data((object*)obj); }
151
152   code_block* translate_code(const code_block* compiled) {
153     return fixup_code((code_block*)compiled);
154   }
155
156   cell size(const object* obj) { return obj->size(*this); }
157
158   cell size(code_block* compiled) { return compiled->size(*this); }
159 };
160
161 void factor_vm::fixup_heaps(cell data_offset, cell code_offset) {
162   startup_fixup fixup(data_offset, code_offset);
163   slot_visitor<startup_fixup> visitor(this, fixup);
164   visitor.visit_all_roots();
165
166   auto start_object_updater = [&](object *obj, cell size) {
167     data->tenured->starts.record_object_start_offset(obj);
168     visitor.visit_slots(obj);
169     switch (obj->type()) {
170       case ALIEN_TYPE: {
171         alien* ptr = (alien*)obj;
172         if (to_boolean(ptr->base))
173           ptr->update_address();
174         else
175           ptr->expired = special_objects[OBJ_CANONICAL_TRUE];
176         break;
177       }
178       case DLL_TYPE: {
179         ffi_dlopen((dll*)obj);
180         break;
181       }
182       default: {
183         visitor.visit_object_code_block(obj);
184         break;
185       }
186     }
187   };
188   data->tenured->iterate(start_object_updater, fixup);
189
190   auto updater = [&](code_block* compiled, cell size) {
191     visitor.visit_code_block_objects(compiled);
192     cell rel_base = compiled->entry_point() - fixup.code_offset;
193     visitor.visit_instruction_operands(compiled, rel_base);
194   };
195   code->allocator->iterate(updater, fixup);
196 }
197
198 bool factor_vm::read_embedded_image_footer(FILE* file,
199                                            embedded_image_footer* footer) {
200   safe_fseek(file, -(off_t)sizeof(embedded_image_footer), SEEK_END);
201   safe_fread(footer, (off_t)sizeof(embedded_image_footer), 1, file);
202   return footer->magic == image_magic;
203 }
204
205 char *threadsafe_strerror(int errnum) {
206   char *buf = (char *) malloc(STRERROR_BUFFER_SIZE);
207   if(!buf) {
208     fatal_error("Out of memory in threadsafe_strerror, errno", errnum);
209   }
210   THREADSAFE_STRERROR(errnum, buf, STRERROR_BUFFER_SIZE);
211   return buf;
212 }
213
214 // Read an image file from disk, only done once during startup
215 // This function also initializes the data and code heaps
216 void factor_vm::load_image(vm_parameters* p) {
217
218   FILE* file = OPEN_READ(p->image_path);
219   if (file == NULL) {
220     std::cout << "Cannot open image file: " << p->image_path << std::endl;
221     char *msg = threadsafe_strerror(errno);
222     std::cout << "strerror:2: " << msg << std::endl;
223     free(msg);
224     exit(1);
225   }
226   if (p->embedded_image) {
227     embedded_image_footer footer;
228     if (!read_embedded_image_footer(file, &footer)) {
229       std::cout << "No embedded image" << std::endl;
230       exit(1);
231     }
232     safe_fseek(file, (off_t)footer.image_offset, SEEK_SET);
233   }
234
235   image_header h;
236   if (raw_fread(&h, sizeof(image_header), 1, file) != 1)
237     fatal_error("Cannot read image header", 0);
238
239   if (h.magic != image_magic)
240     fatal_error("Bad image: magic number check failed", h.magic);
241
242   if (h.version != image_version)
243     fatal_error("Bad image: version number check failed", h.version);
244
245   load_data_heap(file, &h, p);
246   load_code_heap(file, &h, p);
247
248   raw_fclose(file);
249
250   // Certain special objects in the image are known to the runtime
251   memcpy(special_objects, h.special_objects, sizeof(special_objects));
252
253   cell data_offset = data->tenured->start - h.data_relocation_base;
254   cell code_offset = code->allocator->start - h.code_relocation_base;
255   fixup_heaps(data_offset, code_offset);
256 }
257
258 // Save the current image to disk. We don't throw any exceptions here
259 // because if the 'then-die' argument is t it is not safe to do
260 // so. Instead we signal failure by returning false.
261 bool factor_vm::save_image(const vm_char* saving_filename,
262                            const vm_char* filename) {
263   image_header h;
264
265   h.magic = image_magic;
266   h.version = image_version;
267   h.data_relocation_base = data->tenured->start;
268   h.data_size = data->tenured->occupied_space();
269   h.code_relocation_base = code->allocator->start;
270   h.code_size = code->allocator->occupied_space();
271
272   for (cell i = 0; i < special_object_count; i++)
273     h.special_objects[i] =
274         (save_special_p(i) ? special_objects[i] : false_object);
275
276   FILE* file = OPEN_WRITE(saving_filename);
277   if (file == NULL)
278     return false;
279   if (safe_fwrite(&h, sizeof(image_header), 1, file) != 1)
280     return false;
281   if (h.data_size > 0 &&
282       safe_fwrite((void*)data->tenured->start, h.data_size, 1, file) != 1)
283     return false;
284   if (h.code_size > 0 &&
285       safe_fwrite((void*)code->allocator->start, h.code_size, 1, file) != 1)
286     return false;
287   if (raw_fclose(file) == -1)
288     return false;
289   if (!move_file(saving_filename, filename))
290     return false;
291   return true;
292 }
293
294 // Allocates memory
295 void factor_vm::primitive_save_image() {
296   // We unbox this before doing anything else. This is the only point
297   // where we might throw an error, so we have to throw an error here since
298   // later steps destroy the current image.
299   bool then_die = to_boolean(ctx->pop());
300   byte_array* path2 = untag_check<byte_array>(ctx->pop());
301   byte_array* path1 = untag_check<byte_array>(ctx->pop());
302
303   // Copy the paths to non-gc memory to avoid them hanging around in
304   // the saved image.
305   vm_char* path1_saved = safe_strdup(path1->data<vm_char>());
306   vm_char* path2_saved = safe_strdup(path2->data<vm_char>());
307
308   if (then_die) {
309     // strip out special_objects data which is set on startup anyway
310     for (cell i = 0; i < special_object_count; i++)
311       if (!save_special_p(i))
312         special_objects[i] = false_object;
313
314     // dont trace objects only reachable from context stacks so we don't
315     // get volatile data saved in the image.
316     active_contexts.clear();
317     code->uninitialized_blocks.clear();
318
319     // I think clearing the callback heap should be fine too.
320     callbacks->allocator->initial_free_list(0);
321   }
322
323   // do a full GC to push everything remaining into tenured space
324   primitive_compact_gc();
325
326   // Save the image
327   bool ret = save_image(path1_saved, path2_saved);
328   if (then_die) {
329     exit(ret ? 0 : 1);
330   }
331   free(path1_saved);
332   free(path2_saved);
333
334   if (!ret) {
335     general_error(ERROR_IO, tag_fixnum(errno), false_object);
336   }
337 }
338
339 bool factor_vm::embedded_image_p() {
340   const vm_char* vm_path = vm_executable_path();
341   FILE* file = OPEN_READ(vm_path);
342   if (!file) {
343     free((vm_char *)vm_path);
344     return false;
345   }
346   embedded_image_footer footer;
347   bool embedded_p = read_embedded_image_footer(file, &footer);
348   fclose(file);
349   free((vm_char *)vm_path);
350   return embedded_p;
351 }
352
353 }