/* (c) Copyright by Anders Chrigstroem 1993, All rights reserved */ /* Permission is granted to use this source code and any executables * created from this source code as part of the CD Gamedriver as long * as it is not used in any way whatsoever for monetary gain. */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include "config.h" #include "lint.h" #include "interpret.h" #include "object.h" #include "exec.h" #include "mudstat.h" #include "mapping.h" #include <alloca.h> #define SWAP_FILE "LP_SWAP.3" #define BUFFER_DATA_SIZE 256 #define BUFFER_PTR_SIZE (BUFFER_DATA_SIZE / sizeof(unsigned int)) #define NUM_FREE 8 static struct buffer { unsigned int bufnum; unsigned short offset; unsigned short flags; #define B_DIRTY 1 #define B_INUSE 2 union { char data[BUFFER_DATA_SIZE]; unsigned int addr[BUFFER_PTR_SIZE]; } u; } free_blocks[NUM_FREE], zero; static unsigned short cur_free; static unsigned int maxblock = 0; static int swap_fd; static char swap_file[80]; void init_swap() { char hn[80]; static int initialized = 0; int i; if (initialized) return; initialized = 1; gethostname(hn, sizeof(hn)); sprintf(swap_file, "%s.%s.%d", SWAP_FILE, hn, getpid()); swap_fd = open(swap_file, O_RDWR | O_CREAT | O_TRUNC, 0600); if (swap_fd < 0) fatal("Can't create swapfile.\n"); for (i = 0; i < NUM_FREE; i++) free_blocks[i].flags = 0; #ifdef HAS_PREAD pwrite(swap_fd, zero.u.data, BUFFER_DATA_SIZE, maxblock * BUFFER_DATA_SIZE); #else lseek(swap_fd, maxblock * BUFFER_DATA_SIZE, SEEK_SET); write(swap_fd, zero.u.data, BUFFER_DATA_SIZE); #endif cur_free = 0; free_blocks[0].flags |= B_INUSE; free_blocks[0].u.addr[0] = 0; free_blocks[0].offset = 1; } void unlink_swap_file() { unlink(swap_file); } unsigned int alloc_swap(void) { unsigned int ret; if (free_blocks[cur_free].offset > 1) /* Blocks exists in free list */ ret = free_blocks[cur_free].u.addr[--free_blocks[cur_free].offset]; else if (free_blocks[cur_free].u.addr[0]) /* End of current block */ { ret = free_blocks[cur_free].u.addr[0]; free_blocks[cur_free].flags &= ~B_INUSE; cur_free = (cur_free + (NUM_FREE - 1)) % NUM_FREE; if ((free_blocks[cur_free].flags & B_INUSE) == 0) { /* Read next block of free list */ #ifdef HAS_PREAD pread(swap_fd, free_blocks[cur_free].u.data, BUFFER_DATA_SIZE, ret * BUFFER_DATA_SIZE); #else lseek(swap_fd, ret * BUFFER_DATA_SIZE, SEEK_SET); read(swap_fd, free_blocks[cur_free].u.data, BUFFER_DATA_SIZE); #endif free_blocks[cur_free].flags |= B_INUSE; } free_blocks[cur_free].offset = BUFFER_PTR_SIZE; } else { /* End of blocks */ /* Extend the file */ maxblock++; #ifdef HAS_PREAD pwrite(swap_fd, zero.u.data, BUFFER_DATA_SIZE, maxblock * BUFFER_DATA_SIZE); #else lseek(swap_fd, maxblock * BUFFER_DATA_SIZE, SEEK_SET); write(swap_fd, zero.u.data, BUFFER_DATA_SIZE); #endif ret = maxblock; } return ret; } void free_swap(unsigned int addr) { if (addr == 0) return; if (free_blocks[cur_free].offset >= BUFFER_PTR_SIZE) { free_blocks[cur_free].bufnum = addr; cur_free = (cur_free + 1) % NUM_FREE; if (free_blocks[cur_free].flags & B_INUSE) { #ifdef HAS_PREAD pwrite(swap_fd, free_blocks[cur_free].u.data, BUFFER_DATA_SIZE, free_blocks[cur_free].bufnum * BUFFER_DATA_SIZE); #else lseek(swap_fd, free_blocks[cur_free].bufnum * BUFFER_DATA_SIZE, SEEK_SET); write(swap_fd, free_blocks[cur_free].u.data, BUFFER_DATA_SIZE); #endif } free_blocks[cur_free].flags |= B_INUSE; free_blocks[cur_free].offset = 0; } free_blocks[cur_free].u.addr[free_blocks[cur_free].offset++] = addr; } struct buffer * start_read(unsigned int addr) { struct buffer *ret; /* Allocate a buffer */ ret = (struct buffer *)xalloc(sizeof(struct buffer)); ret->flags = 0; ret->bufnum = addr; /* Read first block */ #ifdef HAS_PREAD pread(swap_fd, ret->u.data, BUFFER_DATA_SIZE, addr * BUFFER_DATA_SIZE); #else lseek(swap_fd, addr * BUFFER_DATA_SIZE, SEEK_SET); read(swap_fd, ret->u.data, BUFFER_DATA_SIZE); #endif ret->offset = sizeof(unsigned int); return ret; } int swap_read(struct buffer *buf, char *dest, unsigned int size) { unsigned int csize, len; char *cdest; csize = size; cdest = dest; while (csize > (len = BUFFER_DATA_SIZE - buf->offset)) { /* Empty buffer */ memcpy(cdest, buf->u.data + buf->offset, len); cdest += len; csize -= len; buf->offset += len; if (buf->u.addr[0] == 0) /* Return if end of blocks */ return size - csize; /* Read next block */ buf->bufnum = buf->u.addr[0]; #ifdef HAS_PREAD pread(swap_fd, buf->u.data, BUFFER_DATA_SIZE, buf->bufnum * BUFFER_DATA_SIZE); #else lseek(swap_fd, buf->bufnum * BUFFER_DATA_SIZE, SEEK_SET); read(swap_fd, buf->u.data, BUFFER_DATA_SIZE); #endif buf->offset = sizeof(unsigned int); } /* Get the last needed from the buffer */ memcpy(cdest, buf->u.data + buf->offset, csize); buf->offset += csize; return size; } void end_read(struct buffer *buf) { /* Free the buffer */ free((char *)buf); } struct buffer * start_write(unsigned int *addrp) { struct buffer *ret; /* Allocate a buffer */ ret = (struct buffer *)xalloc(sizeof(struct buffer)); if (*addrp == 0) { /* Allocate a new blocks */ *addrp = alloc_swap(); } ret->bufnum = *addrp; ret->flags = 0; /* Read next pointer from first block */ #ifdef HAS_PREAD pread(swap_fd, ret->u.data, sizeof(unsigned int), ret->bufnum * BUFFER_DATA_SIZE); #else lseek(swap_fd, ret->bufnum * BUFFER_DATA_SIZE, SEEK_SET); read(swap_fd, ret->u.data, sizeof(unsigned int)); #endif ret->offset = sizeof(unsigned int); return ret; } int swap_write(struct buffer *buf, char *dest, unsigned int size) { unsigned int csize, len; char *cdest; csize = size; cdest = dest; while (csize > (len = BUFFER_DATA_SIZE - buf->offset)) { /* Fill the buffer with data */ memcpy(buf->u.data + buf->offset, cdest, len); cdest += len; csize -= len; /* Allocate new blocks if we need to */ if (buf->u.addr[0] == 0) buf->u.addr[0] = alloc_swap(); /* Flush the block to disk */ #ifdef HAS_PREAD pwrite(swap_fd, buf->u.data, BUFFER_DATA_SIZE, buf->bufnum * BUFFER_DATA_SIZE); #else lseek(swap_fd, buf->bufnum * BUFFER_DATA_SIZE, SEEK_SET); write(swap_fd, buf->u.data, BUFFER_DATA_SIZE); #endif /* Read in next pointer from next block */ buf->bufnum = buf->u.addr[0]; #ifdef HAS_PREAD pread(swap_fd, buf->u.data, sizeof(unsigned int), buf->bufnum * BUFFER_DATA_SIZE); #else lseek(swap_fd, buf->bufnum * BUFFER_DATA_SIZE, SEEK_SET); read(swap_fd, buf->u.data, sizeof(unsigned int)); #endif buf->offset = sizeof(unsigned int); } /* Copy the remaining data to the buffer */ memcpy(buf->u.data + buf->offset, cdest, csize); buf->offset += csize; return size; } void end_write(struct buffer *buf) { /* Truncate the block list */ free_swap(buf->u.addr[0]); buf->u.addr[0] = 0; /* Flush current buffer to disk */ #ifdef HAS_PREAD pwrite(swap_fd, buf->u.data, buf->offset, buf->bufnum * BUFFER_DATA_SIZE); #else lseek(swap_fd, buf->bufnum * BUFFER_DATA_SIZE, SEEK_SET); write(swap_fd, buf->u.data, buf->offset); #endif /* Free the buffer */ free((char *)buf); return; } #define align(x) ( ((x) + (sizeof(void *)-1) ) & ~(sizeof(void *)-1) ) struct object *swap_ob = 0; struct program *swap_prog = 0; int max_swap_memory = 0x40000001; int min_swap_memory = 0x40000000; int min_swap_time = 0x40000000; int max_swap_time = 0x40000001; extern struct program *prog_list; extern struct object *obj_list; extern int current_time; int used_memory; extern int tot_alloc_object_size; int total_lineno_swapped = 0; int obj_swapped = 0; int obj_bytes_swapped = 0; int num_swapped; int total_bytes_swapped; int total_num_prog_blocks = 0, total_prog_block_size = 0; int prog_code_size = 0, prog_func_size = 0, prog_var_size = 0, prog_inherit_size = 0, prog_string_size = 0, prog_line_size = 0; int program_bytes_swapped = 0, total_program_size = 0, programs_swapped = 0; int last_address = 0, first_hole = 0, allocated_swap = 0, allocated_swap_blocks = 0, current_hole = 0; int tot_alloc_variable_size = 0; int swap_out_prog, swap_out_obj; int swap_in_prog, swap_in_obj; int swap_out = 0, swap_in = 0; extern int d_flag; int num_swapped_arrays, size_swapped_arrays; int num_swapped_mappings, size_swapped_mappings; int num_strings_swapped, size_strings_swapped; extern struct svalue const0; static void swap_svalue(struct buffer *buf, struct svalue *arg) { switch(arg->type) { case T_OBJECT: if (arg->u.ob->flags & O_DESTRUCTED) free_svalue(arg); case T_NUMBER: case T_FLOAT: arg->type |= T_LVALUE; swap_write(buf, (char *)arg, sizeof(struct svalue)); *arg = const0; break; case T_STRING: { char *str; int len; str = arg->u.string; len = arg->u.number = strlen(str) + 1; swap_write(buf, (char *)arg, sizeof(struct svalue)); swap_write(buf, str, len); num_strings_swapped++; size_strings_swapped += len; switch(arg->string_type) { case STRING_MALLOC: free(str); break; case STRING_SHARED: free_string(str); break; case STRING_CONSTANT: break; default: fatal("Invalid variable value.\n"); } total_bytes_swapped += len; *arg = const0; break; } case T_POINTER: { struct vector *v; v = arg->u.vec; if (v->ref != 1) { arg->type |= T_LVALUE; swap_write(buf, (char *)arg, sizeof(struct svalue)); *arg = const0; } else { int i; arg->u.number = v->size; swap_write(buf, (char *)arg, sizeof(struct svalue)); for (i = 0; i < v->size; i++) swap_svalue(buf, &v->item[i]); *arg = const0; num_swapped_arrays++; size_swapped_arrays += sizeof(struct vector) + sizeof(struct svalue) * v->size - 1; total_bytes_swapped += sizeof(struct svalue) * v->size; free_vector(v); } break; } case T_MAPPING: { struct mapping *m; struct apair *pair; int i, j; m = arg->u.map; if (m->ref != 1) { arg->type |= T_LVALUE; swap_write(buf, (char *)arg, sizeof(struct svalue)); *arg = const0; } else { int size; size = arg->u.number = m->card; arg->string_type = m->size; swap_write(buf, (char *)arg, sizeof(struct svalue)); for (i = j = 0; i < m->size; i++) for(pair = m->pairs[i]; pair; pair = pair->next) { swap_svalue(buf, &pair->arg); swap_svalue(buf, &pair->val); j++; } if (j != size) fatal("Wrong cardinality of mapping.\n"); num_swapped_mappings++; size_swapped_mappings += sizeof(struct mapping) + m->card * sizeof(struct apair) + m->size * sizeof(struct apair *); total_bytes_swapped += size * 2 * sizeof(struct svalue); free_mapping(m); *arg = const0; } break; } default: fatal("Invalid variable type.\n"); } } static void unswap_svalue(struct buffer *buf, struct svalue *arg) { swap_read(buf, (char *)arg, sizeof(struct svalue)); if (arg->type & T_LVALUE) { arg->type &= ~T_LVALUE; if (arg->type == T_OBJECT && arg->u.ob->flags & O_DESTRUCTED) free_svalue(arg); if (!(arg->type & (T_OBJECT | T_NUMBER | T_FLOAT | T_POINTER | T_MAPPING))) fatal("Invalid variable lvalue.\n"); return; } switch(arg->type) { case T_STRING: { int len; len = arg->u.number; #ifdef ALWAYS_SHARE arg->u.string = alloca(len); #else arg->u.string = xalloc(len); #endif swap_read(buf, (char *)arg->u.string, len); #ifdef ALWAYS_SHARE arg->string_type = STRING_SHARED; arg->u.string = make_shared_string(arg->u.string); #else arg->string_type = STRING_MALLOC; #endif num_strings_swapped--; size_strings_swapped -= len; total_bytes_swapped -= len; break; } case T_POINTER: { int size, i; struct vector *v; size = arg->u.number; v = allocate_array(size); for (i = 0; i < size; i++) unswap_svalue(buf, &v->item[i]); arg->u.vec = v; num_swapped_arrays--; size_swapped_arrays -= sizeof(struct vector) + sizeof(struct svalue) * size - 1; total_bytes_swapped -= sizeof(struct svalue) * v->size; break; } case T_MAPPING: { int size, i; struct svalue marg, *mval; size = arg->u.number; arg->u.map = allocate_map(size); for (i = 0; i < size; i++) { unswap_svalue(buf, &marg); mval = get_map_lvalue(arg->u.map, &marg, 1); unswap_svalue(buf, mval); free_svalue(&marg); } num_swapped_mappings--; size_swapped_mappings -= sizeof(struct mapping) + size * sizeof(struct apair) + arg->string_type * sizeof(struct apair *); total_bytes_swapped -= size * 2 * sizeof(struct svalue); break; } default: fatal("Invalid variable value.\n"); } } /* * Swap out an object. Only the program is swapped, not the struct object. * */ int swap_object(struct object *ob) { int size; int num_vars, i; struct buffer *buf; if (ob->flags & (O_DESTRUCTED | O_SWAPPED)) return 0; if (d_flag & DEBUG_SWAP) { fprintf(stderr, "Swap object %s (ref %d) from 0x%x\n", ob->name, ob->ref, ob->variables); } if (!ob->variables) { return 0; } ob->variables--; num_vars = ob->prog->inherit[ob->prog->num_inherited - 1]. variable_index_offset + ob->prog->num_variables + 1; size = num_vars * sizeof(struct svalue); buf = start_write(&ob->swap_num); if (ob->swap_num == -1) { ob->variables++; end_write(buf); return 0; } for (i = 0; i < num_vars; i++) swap_svalue(buf, &ob->variables[i]); end_write(buf); free((char *)ob->variables); ob->variables = (struct svalue *)-1; obj_bytes_swapped += size; obj_swapped++; tot_alloc_variable_size -= size; total_bytes_swapped += size; num_swapped++; swap_out_obj++; ob->flags |= O_SWAPPED; return 1; } void load_ob_from_swap(struct object *ob) { int i, size; int num_var; struct buffer *buf; if (!(ob->flags & O_SWAPPED)) fatal("Swapping in not swapped out object!\n"); if (d_flag & DEBUG_SWAP) { fprintf(stderr,"Unswap object %s (ref %d) from 0x%x\n", ob->name, ob->ref, ob->variables); } size = (num_var = ob->prog->inherit[ob->prog->num_inherited - 1].variable_index_offset + ob->prog->num_variables + 1) * sizeof(struct svalue); ob->variables = (struct svalue *)xalloc(size); buf = start_read(ob->swap_num); for (i = 0; i < num_var; i++) unswap_svalue(buf, &ob->variables[i]); end_read(buf); ob->flags &= ~O_SWAPPED; obj_bytes_swapped -= size; obj_swapped--; tot_alloc_variable_size += size; swap_in_obj++; total_bytes_swapped -= size; num_swapped--; ob->variables++; } static int swap_segment(char *hdr, struct segment_desc *seg) { struct section_desc *sect; char *block = *(char **)(hdr + seg->ptr_offset); int i; if (*(int *)(hdr + seg->swap_idx_offset) == 0) { struct buffer *buf; buf = start_write((int *)(hdr + seg->swap_idx_offset)); swap_write(buf, block, *(int *)(hdr + seg->size_offset)); end_write(buf); } if (*(int *)(hdr + seg->swap_idx_offset) == -1) fatal("error while swapping segment.\n"); for (sect = seg->sections, i = 0; sect->section != -1; sect++, i++) if (sect->ptr_offset != -1) *(long *)(hdr + sect->ptr_offset) = *(char **)(hdr + sect->ptr_offset) - block; free(block); return *(int *)(hdr + seg->size_offset); } static int unswap_segment(char *hdr, struct segment_desc *seg) { struct section_desc *sect; char *block; int i; struct buffer *buf; block = xalloc(*(int *)(hdr + seg->size_offset)); buf = start_read(*(int *)(hdr + seg->swap_idx_offset)); swap_read(buf, block, *(int *)(hdr + seg->size_offset)); end_read(buf); for (sect = seg->sections, i = 0; sect->section != -1; sect++, i++) if (sect->ptr_offset != -1) *(char **)(hdr + sect->ptr_offset) = block + *(long *)(hdr + sect->ptr_offset); *(char **)(hdr + seg->ptr_offset) = block; return *(int *)(hdr + seg->size_offset); } /* * Swap out lineno info for a program to the swap file * The linenoinfo is separated from the programblock thereby saving memory. * This is called directly after compilation from epilog() in postlang.y */ int swap_lineno(struct program *prog) { int size; if (d_flag & DEBUG_SWAP) /* marion */ debug_message("Swap lineno for %s (ref %d)\n", prog->name); if (!prog->line_numbers) return 1; size = swap_segment((char *)prog, segm_desc + S_DBG); total_bytes_swapped += size; total_lineno_swapped += size; num_swapped++; return 1; } /* Load the lineno info, this is only done when a runtimeerror occurs */ void load_lineno_from_swap(struct program *prog) { int size; if (prog->line_numbers) return; if (prog->swap_lineno_index == 0) fatal("Loading not swapped linenoinfo.\n"); size = unswap_segment((char *)prog, segm_desc + S_DBG); total_lineno_swapped -= size; total_bytes_swapped -= size; num_swapped--; } static int swap_program(struct program *prog) { int size; if (prog->line_numbers) swap_lineno(prog); if (prog->program == (char *)0) return 0; if (d_flag & DEBUG_SWAP) { /* marion */ debug_message("Swap program %s (ref %d)\n", prog->name, prog->ref); } size = swap_segment((char *)prog, segm_desc + S_EXEC); total_program_size -= size; program_bytes_swapped += size; programs_swapped++; swap_out_prog++; total_bytes_swapped += size; num_swapped++; return 1; } void load_prog_from_swap(struct program *prog) { int size; if (prog->program != (char *)0 || prog->swap_num == -1) return; if (d_flag & DEBUG_SWAP) { /* marion */ debug_message("Unswap program %s (ref %d)\n", prog->name, prog->ref); } size = unswap_segment((char *)prog, segm_desc + S_EXEC); swap_in_prog++; total_program_size += size; program_bytes_swapped -= size; programs_swapped--; total_bytes_swapped -= size; num_swapped--; } void remove_ob_from_swap(struct object *ob) { if (ob->flags & O_SWAPPED) load_ob_from_swap(ob); if (ob->swap_num > 0) { free_swap(ob->swap_num); ob->swap_num = 0; } return; } void remove_prog_from_swap(struct program *prog) { if (prog->swap_num > 0) { if (prog->program == (char *)0) load_prog_from_swap(prog); free_swap(prog->swap_num); prog->swap_num = 0; } if (prog->swap_lineno_index > 0) { free_swap(prog->swap_lineno_index); prog->swap_lineno_index = 0; } return; } void try_to_swap(volatile int *interupted) { int swap_time; if (swap_ob && swap_prog && used_memory >= min_swap_memory) while (swap_ob != obj_list && swap_prog != prog_list) { swap_time = (used_memory < max_swap_memory) ? max_swap_time : min_swap_time; if (swap_ob->time_of_ref > swap_prog->time_of_ref) { if (current_time - swap_prog->time_of_ref < swap_time) break; swap_program(swap_prog); swap_prog = swap_prog->prev_all; } else { if (current_time - swap_ob->time_of_ref < swap_time) break; swap_object(swap_ob); swap_ob = swap_ob->prev_all; } if (*interupted) return; } }