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