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