]> gitweb.factorcode.org Git - factor.git/blob - vm/image.cpp
VM: simplified save_image, goto works here!
[factor.git] / vm / image.cpp
1 #include "master.hpp"
2
3 namespace factor {
4
5 /* Certain special objects in the image are known to the runtime */
6 void factor_vm::init_objects(image_header* h) {
7   memcpy(special_objects, h->special_objects, sizeof(special_objects));
8
9   true_object = h->true_object;
10   bignum_zero = h->bignum_zero;
11   bignum_pos_one = h->bignum_pos_one;
12   bignum_neg_one = h->bignum_neg_one;
13 }
14
15 void factor_vm::load_data_heap(FILE* file, image_header* h, vm_parameters* p) {
16   p->tenured_size = std::max((h->data_size * 3) / 2, p->tenured_size);
17
18   init_data_heap(p->young_size, p->aging_size, p->tenured_size);
19
20   fixnum bytes_read =
21       raw_fread((void*)data->tenured->start, 1, h->data_size, file);
22
23   if ((cell)bytes_read != h->data_size) {
24     std::cout << "truncated image: " << bytes_read << " bytes read, ";
25     std::cout << h->data_size << " bytes expected\n";
26     fatal_error("load_data_heap failed", 0);
27   }
28
29   data->tenured->initial_free_list(h->data_size);
30 }
31
32 void factor_vm::load_code_heap(FILE* file, image_header* h, vm_parameters* p) {
33   if (h->code_size > p->code_size)
34     fatal_error("Code heap too small to fit image", h->code_size);
35
36   code = new code_heap(p->code_size);
37
38   if (h->code_size != 0) {
39     size_t bytes_read =
40         raw_fread((void*)code->allocator->start, 1, h->code_size, file);
41     if (bytes_read != h->code_size) {
42       std::cout << "truncated image: " << bytes_read << " bytes read, ";
43       std::cout << h->code_size << " bytes expected\n";
44       fatal_error("load_code_heap failed", 0);
45     }
46   }
47
48   code->allocator->initial_free_list(h->code_size);
49   code->initialize_all_blocks_set();
50 }
51
52 struct startup_fixup {
53   static const bool translated_code_block_map = true;
54
55   cell data_offset;
56   cell code_offset;
57
58   startup_fixup(cell data_offset, cell code_offset)
59       : data_offset(data_offset), code_offset(code_offset) {}
60
61   object* fixup_data(object* obj) {
62     return (object*)((cell)obj + data_offset);
63   }
64
65   code_block* fixup_code(code_block* obj) {
66     return (code_block*)((cell)obj + code_offset);
67   }
68
69   object* translate_data(const object* obj) { return fixup_data((object*)obj); }
70
71   code_block* translate_code(const code_block* compiled) {
72     return fixup_code((code_block*)compiled);
73   }
74
75   cell size(const object* obj) { return obj->size(*this); }
76
77   cell size(code_block* compiled) { return compiled->size(*this); }
78 };
79
80 void factor_vm::fixup_data(cell data_offset, cell code_offset) {
81   startup_fixup fixup(data_offset, code_offset);
82   slot_visitor<startup_fixup> visitor(this, fixup);
83   visitor.visit_all_roots();
84
85   auto start_object_updater = [&](object *obj, cell size) {
86     data->tenured->starts.record_object_start_offset(obj);
87     visitor.visit_slots(obj);
88     switch (obj->type()) {
89       case ALIEN_TYPE: {
90
91         alien* ptr = (alien*)obj;
92
93         if (to_boolean(ptr->base))
94           ptr->update_address();
95         else
96           ptr->expired = true_object;
97         break;
98       }
99       case DLL_TYPE: {
100         ffi_dlopen((dll*)obj);
101         break;
102       }
103       default: {
104         visitor.visit_object_code_block(obj);
105         break;
106       }
107     }
108   };
109   data->tenured->iterate(start_object_updater, fixup);
110 }
111
112 void factor_vm::fixup_code(cell data_offset, cell code_offset) {
113   startup_fixup fixup(data_offset, code_offset);
114   auto updater = [&](code_block* compiled, cell size) {
115     slot_visitor<startup_fixup> visitor(this, fixup);
116     visitor.visit_code_block_objects(compiled);
117     cell rel_base = compiled->entry_point() - fixup.code_offset;
118     visitor.visit_instruction_operands(compiled, rel_base);
119   };
120   code->allocator->iterate(updater, fixup);
121 }
122
123 bool factor_vm::read_embedded_image_footer(FILE* file,
124                                            embedded_image_footer* footer) {
125   safe_fseek(file, -(off_t)sizeof(embedded_image_footer), SEEK_END);
126   safe_fread(footer, (off_t)sizeof(embedded_image_footer), 1, file);
127   return footer->magic == image_magic;
128 }
129
130 char *threadsafe_strerror(int errnum) {
131   char *buf = (char *) malloc(STRERROR_BUFFER_SIZE);
132   if(!buf) {
133     fatal_error("Out of memory in threadsafe_strerror, errno", errnum);
134   }
135   THREADSAFE_STRERROR(errnum, buf, STRERROR_BUFFER_SIZE);
136   return buf;
137 }
138
139 FILE* factor_vm::open_image(vm_parameters* p) {
140   if (p->embedded_image) {
141     FILE* file = OPEN_READ(p->executable_path);
142     if (file == NULL) {
143       std::cout << "Cannot open embedded image" << std::endl;
144       char *msg = threadsafe_strerror(errno);
145       std::cout << "strerror:1: " << msg << std::endl;
146       free(msg);
147       exit(1);
148     }
149     embedded_image_footer footer;
150     if (!read_embedded_image_footer(file, &footer)) {
151       std::cout << "No embedded image" << std::endl;
152       exit(1);
153     }
154     safe_fseek(file, (off_t)footer.image_offset, SEEK_SET);
155     return file;
156   } else
157     return OPEN_READ(p->image_path);
158 }
159
160 /* Read an image file from disk, only done once during startup */
161 /* This function also initializes the data and code heaps */
162 void factor_vm::load_image(vm_parameters* p) {
163   FILE* file = open_image(p);
164   if (file == NULL) {
165     std::cout << "Cannot open image file: " << p->image_path << std::endl;
166     char *msg = threadsafe_strerror(errno);
167     std::cout << "strerror:2: " << msg << std::endl;
168     free(msg);
169     exit(1);
170   }
171   image_header h;
172   if (raw_fread(&h, sizeof(image_header), 1, file) != 1)
173     fatal_error("Cannot read image header", 0);
174
175   if (h.magic != image_magic)
176     fatal_error("Bad image: magic number check failed", h.magic);
177
178   if (h.version != image_version)
179     fatal_error("Bad image: version number check failed", h.version);
180
181   load_data_heap(file, &h, p);
182   load_code_heap(file, &h, p);
183
184   raw_fclose(file);
185
186   init_objects(&h);
187
188   cell data_offset = data->tenured->start - h.data_relocation_base;
189   cell code_offset = code->allocator->start - h.code_relocation_base;
190
191   fixup_data(data_offset, code_offset);
192   fixup_code(data_offset, code_offset);
193
194   /* Store image path name */
195   special_objects[OBJ_IMAGE] = allot_alien(false_object, (cell)p->image_path);
196 }
197
198 /* Save the current image to disk */
199 bool factor_vm::save_image(const vm_char* saving_filename,
200                            const vm_char* filename) {
201   image_header h;
202
203   h.magic = image_magic;
204   h.version = image_version;
205   h.data_relocation_base = data->tenured->start;
206   h.data_size = data->tenured->occupied_space();
207   h.code_relocation_base = code->allocator->start;
208   h.code_size = code->allocator->occupied_space();
209
210   h.true_object = true_object;
211   h.bignum_zero = bignum_zero;
212   h.bignum_pos_one = bignum_pos_one;
213   h.bignum_neg_one = bignum_neg_one;
214
215   for (cell i = 0; i < special_object_count; i++)
216     h.special_objects[i] =
217         (save_special_p(i) ? special_objects[i] : false_object);
218
219   FILE* file = OPEN_WRITE(saving_filename);
220   if (file == NULL)
221     goto error;
222   if (safe_fwrite(&h, sizeof(image_header), 1, file) != 1)
223     goto error;
224   if (safe_fwrite((void*)data->tenured->start, h.data_size, 1, file) != 1)
225     goto error;
226   if (safe_fwrite((void*)code->allocator->start, h.code_size, 1, file) != 1)
227     goto error;
228   safe_fclose(file);
229   move_file(saving_filename, filename);
230   return true;
231
232  error:
233   std::cout << "save_image failed." << std::endl;
234   char *msg = threadsafe_strerror(errno);
235   std::cout << "strerror:4: " << msg << std::endl;
236   free(msg);
237   return false;
238
239 }
240
241 /* Allocates memory */
242 void factor_vm::primitive_save_image() {
243   /* We unbox this before doing anything else. This is the only point
244      where we might throw an error, so we have to throw an error here since
245      later steps destroy the current image. */
246   bool then_die = to_boolean(ctx->pop());
247   byte_array* path2 = untag_check<byte_array>(ctx->pop());
248   byte_array* path1 = untag_check<byte_array>(ctx->pop());
249
250   /* Copy the paths to non-gc memory to avoid them hanging around in
251      the saved image. */
252   vm_char* path1_saved = safe_strdup(path1->data<vm_char>());
253   vm_char* path2_saved = safe_strdup(path2->data<vm_char>());
254
255   if (then_die) {
256     /* strip out special_objects data which is set on startup anyway */
257     for (cell i = 0; i < special_object_count; i++)
258       if (!save_special_p(i))
259         special_objects[i] = false_object;
260
261     /* dont trace objects only reachable from context stacks so we don't
262        get volatile data saved in the image. */
263     active_contexts.clear();
264     code->uninitialized_blocks.clear();
265   }
266
267   /* do a full GC to push everything remaining into tenured space */
268   primitive_compact_gc();
269
270   /* Save the image */
271   bool ret = save_image(path1_saved, path2_saved);
272   if (then_die) {
273     exit(ret ? 0 : 1);
274   }
275   free(path1_saved);
276   free(path2_saved);
277 }
278
279 bool factor_vm::embedded_image_p() {
280   const vm_char* vm_path = vm_executable_path();
281   if (!vm_path)
282     return false;
283   FILE* file = OPEN_READ(vm_path);
284   if (!file)
285     return false;
286   embedded_image_footer footer;
287   bool embedded_p = read_embedded_image_footer(file, &footer);
288   fclose(file);
289   return embedded_p;
290 }
291
292 }