mirror of
https://github.com/ptitSeb/box64.git
synced 2026-11-07 09:26:11 -10:00
[DYNACACHE] Created dynacache.c (#3865)
* [DYNACACHE] Created dynacache.c This is pure code moving * Update * Fixed a typo
This commit is contained in:
1 parent
5e472e3963
commit
7d3b1dfbfb
12 files changed
+884
-833
No files matched your search
@@ -471,6 +471,7 @@ set(ELFLOADER_SRC
|
||||
"${BOX64_ROOT}/src/tools/pathcoll.c"
|
||||
"${BOX64_ROOT}/src/tools/rbtree.c"
|
||||
"${BOX64_ROOT}/src/tools/env.c"
|
||||
"${BOX64_ROOT}/src/tools/dynacache.c"
|
||||
"${BOX64_ROOT}/src/tools/wine_tools.c"
|
||||
"${BOX64_ROOT}/src/tools/pe_tools.c"
|
||||
"${BOX64_ROOT}/src/wrapped/generated/wrapper.c"
|
||||
|
||||
+4
-4
@@ -252,11 +252,11 @@ DynaCache 文件夹的最大大小(MiB)。超过限制时,会先删除无
|
||||
|
||||
### BOX64_DYNACACHE_COMPRESS
|
||||
|
||||
Compress or not DynaCache files on disk
|
||||
设置是否压缩磁盘上的 DynaCache 文件。
|
||||
|
||||
* 0: Do not compress dynacache files
|
||||
* 1: Compress dynacache files using Zlib with fastest compression. [默认值]
|
||||
* 2: Compress dynacache files using ZLib with default compression.
|
||||
* 0: 不压缩 DynaCache 文件。
|
||||
* 1: 使用 Zlib 最快压缩级别压缩 DynaCache 文件。 [默认值]
|
||||
* 2: 使用 Zlib 默认压缩级别压缩 DynaCache 文件。
|
||||
|
||||
### BOX64_DYNACACHE_MIN
|
||||
|
||||
|
||||
@@ -375,6 +375,14 @@
|
||||
"2048": "默认值为 2048 MiB。"
|
||||
}
|
||||
},
|
||||
"BOX64_DYNACACHE_COMPRESS": {
|
||||
"description": "设置是否压缩磁盘上的 DynaCache 文件。",
|
||||
"options": {
|
||||
"0": "不压缩 DynaCache 文件。",
|
||||
"1": "使用 Zlib 最快压缩级别压缩 DynaCache 文件。",
|
||||
"2": "使用 Zlib 默认压缩级别压缩 DynaCache 文件。"
|
||||
}
|
||||
},
|
||||
"BOX64_DYNACACHE_MIN": {
|
||||
"description": "DynaCache 写入磁盘的最小大小(KB)。默认大小为 350KB。",
|
||||
"options": {
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "library.h"
|
||||
#include "core.h"
|
||||
#include "env.h"
|
||||
#include "dynacache.h"
|
||||
#include "cleanup.h"
|
||||
#include "freq.h"
|
||||
#include "hostext.h"
|
||||
|
||||
@@ -30,17 +30,18 @@ size_t customGetUsableSize(void* p);
|
||||
#endif
|
||||
|
||||
#ifdef DYNAREC
|
||||
#include "dynacache.h"
|
||||
typedef struct dynablock_s dynablock_t;
|
||||
typedef struct mmaplist_s mmaplist_t;
|
||||
typedef struct DynaCacheBlock_s DynaCacheBlock_t;
|
||||
typedef struct CompressedDynaCacheBlock_s CompressedDynaCacheBlock_t;
|
||||
// custom protection flag to mark Page that are Write protected for Dynarec purpose
|
||||
uintptr_t AllocDynarecMap(uintptr_t x64_addr, size_t size, int is_new);
|
||||
void FreeDynarecMap(uintptr_t addr);
|
||||
mmaplist_t* NewMmaplist();
|
||||
void DelMmaplist(mmaplist_t* list);
|
||||
int MmaplistHasNew(mmaplist_t* list, int clear);
|
||||
int MmaplistIsDirty(mmaplist_t* list);
|
||||
int MmaplistNBlocks(mmaplist_t* list);
|
||||
size_t MmaplistTotalAlloc(mmaplist_t* list);
|
||||
void MmaplistFillBlocks(mmaplist_t* list, CompressedDynaCacheBlock_t* blocks);
|
||||
void MmaplistAddNBlocks(mmaplist_t* list, int nblocks);
|
||||
int MmaplistAddBlock(mmaplist_t* list, int fd, off_t offset, void* orig, size_t size, intptr_t delta_map, uintptr_t mapping_start);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef __DYNACACHE_H__
|
||||
#define __DYNACACHE_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct mapping_s mapping_t;
|
||||
|
||||
#ifdef DYNAREC
|
||||
typedef struct blocklist_s blocklist_t;
|
||||
|
||||
typedef struct DynaCacheBlock_s {
|
||||
blocklist_t* block;
|
||||
size_t size;
|
||||
size_t free_size;
|
||||
} DynaCacheBlock_t;
|
||||
|
||||
#define COMP_NONE 0
|
||||
#define COMP_Z 1
|
||||
#define COMP_Z_MAX 2
|
||||
typedef struct CompressedDynaCacheBlock_s {
|
||||
DynaCacheBlock_t block;
|
||||
size_t compsize;
|
||||
uint8_t type;
|
||||
} CompressedDynaCacheBlock_t;
|
||||
|
||||
void SerializeMmaplist(mapping_t* mapping);
|
||||
void MmapDynaCache(mapping_t* mapping);
|
||||
#endif
|
||||
|
||||
void DynaCacheList(const char* filter);
|
||||
void DynaCacheClean();
|
||||
|
||||
#endif // __DYNACACHE_H__
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "dynacache.h"
|
||||
|
||||
void* dc_compress(void* src, size_t src_size, int type, size_t* dest_size);
|
||||
int dc_uncompress(void* src, size_t src_size, int type, void* dest, size_t dest_size); // return 0 is OK
|
||||
|
||||
#endif //__DYNACACHE_COMPRESS__H__
|
||||
#endif //__DYNACACHE_COMPRESS__H__
|
||||
@@ -225,24 +225,6 @@ typedef struct box64env_s {
|
||||
} box64env_t;
|
||||
|
||||
typedef struct mmaplist_s mmaplist_t;
|
||||
#ifdef DYNAREC
|
||||
typedef struct blocklist_s blocklist_t;
|
||||
|
||||
typedef struct DynaCacheBlock_s {
|
||||
blocklist_t* block;
|
||||
size_t size;
|
||||
size_t free_size;
|
||||
} DynaCacheBlock_t;
|
||||
|
||||
#define COMP_NONE 0
|
||||
#define COMP_Z 1
|
||||
#define COMP_Z_MAX 2
|
||||
typedef struct CompressedDynaCacheBlock_s {
|
||||
DynaCacheBlock_t block;
|
||||
size_t compsize;
|
||||
uint8_t type;
|
||||
} CompressedDynaCacheBlock_t;
|
||||
#endif
|
||||
|
||||
void InitializeEnvFiles();
|
||||
int ApplyEnvFileEntry(const char* name);
|
||||
@@ -260,8 +242,6 @@ size_t SizeFileMapped(uintptr_t addr);
|
||||
mmaplist_t* GetMmaplistByAddr(uintptr_t addr);
|
||||
int IsAddrNeedReloc(uintptr_t addr);
|
||||
void SerializeAllMapping();
|
||||
void DynaCacheList(const char* name);
|
||||
void DynaCacheClean();
|
||||
int IsAddrMappingLoadAndClean(uintptr_t addr);
|
||||
|
||||
#endif // __ENV_H
|
||||
@@ -0,0 +1,808 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#if defined(DYNAREC) && !defined(WIN32)
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#include "os.h"
|
||||
#include "env.h"
|
||||
#include "custommem.h"
|
||||
#include "khash.h"
|
||||
#include "debug.h"
|
||||
#include "fileutils.h"
|
||||
#include "dynacache.h"
|
||||
#include "dynacache_compress.h"
|
||||
#include "env_private.h"
|
||||
#ifdef DYNAREC
|
||||
#include "dynacache_hashes.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PATHSEP "\\"
|
||||
#define HOME "USERPROFILE"
|
||||
#else
|
||||
#define PATHSEP "/"
|
||||
#define HOME "HOME"
|
||||
#endif
|
||||
|
||||
#ifdef DYNAREC
|
||||
int nUnalignedRange(uintptr_t start, size_t size);
|
||||
void getUnalignedRange(uintptr_t start, size_t size, uintptr_t addrs[]);
|
||||
void add_unaligned_address(uintptr_t addr);
|
||||
|
||||
const char* GetDynacacheFolder(mapping_t* mapping)
|
||||
{
|
||||
static char folder[4096] = { 0 };
|
||||
if(mapping && mapping->env && mapping->env->is_dynacache_folder_overridden && mapping->env->dynacache_folder) {
|
||||
if (FileExist(mapping->env->dynacache_folder, 0) || MakeDir(mapping->env->dynacache_folder)) {
|
||||
strcpy(folder, mapping->env->dynacache_folder);
|
||||
goto done;
|
||||
}
|
||||
} else if (box64env.dynacache_folder) {
|
||||
if (FileExist(box64env.dynacache_folder, 0) || MakeDir(box64env.dynacache_folder)) {
|
||||
strcpy(folder, box64env.dynacache_folder);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if(GetEnv("XDG_CACHE_HOME"))
|
||||
strcpy(folder, GetEnv("XDG_CACHE_HOME"));
|
||||
else if(GetEnv(HOME)) {
|
||||
strcpy(folder, GetEnv(HOME));
|
||||
strcat(folder, PATHSEP);
|
||||
strcat(folder, ".cache");
|
||||
if (!FileExist(folder, 0) && !MakeDir(folder))
|
||||
return NULL;
|
||||
|
||||
} else
|
||||
return NULL;
|
||||
strcat(folder, PATHSEP);
|
||||
strcat(folder, "box64");
|
||||
if (!FileExist(folder, 0) && !MakeDir(folder))
|
||||
return NULL;
|
||||
|
||||
done:
|
||||
if (folder[strlen(folder) - 1] != PATHSEP[0]) {
|
||||
strcat(folder, PATHSEP);
|
||||
}
|
||||
return folder;
|
||||
}
|
||||
|
||||
/*
|
||||
There are 3 compatibility values for DynaCache:
|
||||
1. FILE_VERSION for the DynaCache infrastructure and file format
|
||||
2. DYNAREC_VERSION as the generated hash of common dynarec sources
|
||||
3. ARCH_VERSION as the generated hash of the active backend sources
|
||||
|
||||
Dynacache will ignore any DynaCache file not exactly matching those 3 version.
|
||||
`box64 --dynacache-clean` can be used from command line to purge obsolete DyaCache files
|
||||
*/
|
||||
|
||||
#define FILE_VERSION 3
|
||||
#define HEADER_SIGN "DynaCache"
|
||||
|
||||
typedef struct DynaCacheHeader_s {
|
||||
char sign[10]; //"DynaCache\0"
|
||||
uint64_t file_version:16;
|
||||
uint64_t dynarec_version:24;
|
||||
uint64_t arch_version:24;
|
||||
uint64_t cpuext;
|
||||
uint64_t dynarec_settings;
|
||||
size_t pagesize;
|
||||
size_t codesize;
|
||||
uintptr_t map_addr;
|
||||
size_t map_len;
|
||||
size_t file_length;
|
||||
uint32_t filename_length;
|
||||
uint32_t nblocks;
|
||||
uint32_t nLockAddresses;
|
||||
uint32_t nUnalignedAddresses;
|
||||
char filename[];
|
||||
} DynaCacheHeader_t;
|
||||
|
||||
#define DYNAREC_SETTINGS() \
|
||||
DS_GO(BOX64_DYNAREC_ALIGNED_ATOMICS, dynarec_aligned_atomics, 1) \
|
||||
DS_GO(BOX64_DYNAREC_BIGBLOCK, dynarec_bigblock, 2) \
|
||||
DS_GO(BOX64_DYNAREC_CALLRET, dynarec_callret, 2) \
|
||||
DS_GO(BOX64_DYNAREC_DF, dynarec_df, 1) \
|
||||
DS_GO(BOX64_DYNAREC_DIRTY, dynarec_dirty, 2) \
|
||||
DS_GO(BOX64_DYNAREC_DIV0, dynarec_div0, 1) \
|
||||
DS_GO(BOX64_DYNAREC_FASTNAN, dynarec_fastnan, 1) \
|
||||
DS_GO(BOX64_DYNAREC_FASTROUND, dynarec_fastround, 2) \
|
||||
DS_GO(BOX64_DYNAREC_FORWARD, dynarec_forward, 10) \
|
||||
DS_GO(BOX64_DYNAREC_NATIVEFLAGS, dynarec_nativeflags, 1) \
|
||||
DS_GO(BOX64_DYNAREC_SAFEFLAGS, dynarec_safeflags, 2) \
|
||||
DS_GO(BOX64_DYNAREC_STRONGMEM, dynarec_strongmem, 2) \
|
||||
DS_GO(BOX64_DYNAREC_VOLATILE_METADATA, dynarec_volatile_metadata, 1)\
|
||||
DS_GO(BOX64_DYNAREC_WEAKBARRIER, dynarec_weakbarrier, 2) \
|
||||
DS_GO(BOX64_DYNAREC_X87DOUBLE, dynarec_x87double, 2) \
|
||||
DS_GO(BOX64_DYNAREC_NOARCH, dynarec_noarch, 2) \
|
||||
DS_GO(BOX64_AES, aes, 1) \
|
||||
DS_GO(BOX64_PCLMULQDQ, pclmulqdq, 1) \
|
||||
DS_GO(BOX64_SHAEXT, shaext, 1) \
|
||||
DS_GO(BOX64_SSE42, sse42, 1) \
|
||||
DS_GO(BOX64_AVX, avx, 2) \
|
||||
DS_GO(BOX64_X87_NO80BITS, x87_no80bits, 1) \
|
||||
DS_GO(BOX64_RDTSC_1GHZ, rdtsc_1ghz, 1) \
|
||||
DS_GO(BOX64_SSE_FLUSHTO0, sse_flushto0, 1) \
|
||||
|
||||
#define DS_GO(A, B, C) uint64_t B:C;
|
||||
typedef union dynarec_settings_s {
|
||||
struct {
|
||||
DYNAREC_SETTINGS()
|
||||
};
|
||||
uint64_t x;
|
||||
} dynarec_settings_t;
|
||||
#undef DS_GO
|
||||
uint64_t GetDynSetting(mapping_t* mapping)
|
||||
{
|
||||
dynarec_settings_t settings = {0};
|
||||
#define DS_GO(A, B, C) settings.B = (mapping->env && mapping->env->is_##B##_overridden)?mapping->env->B:box64env.B;
|
||||
DYNAREC_SETTINGS()
|
||||
#undef DS_GO
|
||||
return settings.x;
|
||||
}
|
||||
void PrintDynfSettings(int level, uint64_t s)
|
||||
{
|
||||
dynarec_settings_t settings = {0};
|
||||
settings.x = s;
|
||||
#define DS_GO(A, B, C) if(settings.B) printf_log_prefix(0, level, "\t\t" #A "=%d\n", settings.B);
|
||||
DYNAREC_SETTINGS()
|
||||
#undef DS_GO
|
||||
}
|
||||
#undef DYNAREC_SETTINGS
|
||||
|
||||
char* MmaplistName(const char* filename, uint64_t dynarec_settings, const char* fullname)
|
||||
{
|
||||
// names are FOLDER/filename-YYYYY-XXXXX.box64
|
||||
// Where XXXXX is the hash of the full name
|
||||
// and YYYY is the Dynarec optim (in hex)
|
||||
static char mapname[4096];
|
||||
snprintf(mapname, 4095-6, "%s-%llx-%u", filename, dynarec_settings, __ac_X31_hash_string(fullname));
|
||||
strcat(mapname, ".box64");
|
||||
return mapname;
|
||||
}
|
||||
|
||||
char* GetMmaplistName(mapping_t* mapping)
|
||||
{
|
||||
return MmaplistName(mapping->filename+1, GetDynSetting(mapping), mapping->fullname);
|
||||
}
|
||||
|
||||
const char* NicePrintSize(size_t sz)
|
||||
{
|
||||
static char buf[256];
|
||||
const char* units[] = {"", "kb", "Mb", "Gb"};
|
||||
int idx = 0;
|
||||
size_t ratio = 0;
|
||||
while(idx<sizeof(units)/sizeof(units[0]) && (1<<(ratio+10))<sz) {
|
||||
ratio+=10;
|
||||
++idx;
|
||||
}
|
||||
if(ratio && (sz>>ratio)<50) {
|
||||
snprintf(buf, 255, "%zd.%zd%s", sz>>ratio, (sz>>(ratio-1))%10, units[idx]);
|
||||
} else {
|
||||
snprintf(buf, 255, "%zd%s", sz>>ratio, units[idx]);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
typedef struct DynaCacheFileEntry_s {
|
||||
char* filename;
|
||||
uint64_t size;
|
||||
time_t mtime;
|
||||
int valid;
|
||||
} DynaCacheFileEntry_t;
|
||||
|
||||
int ReadDynaCache(const char* folder, const char* name, mapping_t* mapping, int verbose);
|
||||
|
||||
#ifndef O_CLOEXEC
|
||||
#define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
#define DYNACACHE_TMP_STALE_SECONDS 60
|
||||
|
||||
static void SyncDynaCacheFolder(const char* folder)
|
||||
{
|
||||
#ifdef O_DIRECTORY
|
||||
int fd = open(folder, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
#else
|
||||
int fd = open(folder, O_RDONLY|O_CLOEXEC);
|
||||
#endif
|
||||
if(fd>=0) {
|
||||
fsync(fd);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static int DynaCacheOpenTempFile(const char* mapname, char* tmpname, size_t tmpname_size)
|
||||
{
|
||||
for(int i=0; i<100; ++i) {
|
||||
snprintf(tmpname, tmpname_size, "%s.tmp.%ld.%d", mapname, (long)getpid(), i);
|
||||
int fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0644);
|
||||
if(fd>=0)
|
||||
return fd;
|
||||
if(errno!=EEXIST)
|
||||
return -1;
|
||||
}
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int RemoveStaleDynaCacheTempFile(const char* folder, const char* name)
|
||||
{
|
||||
if(!strstr(name, ".box64.tmp."))
|
||||
return 0;
|
||||
|
||||
char filename[strlen(folder)+strlen(name)+1];
|
||||
strcpy(filename, folder);
|
||||
strcat(filename, name);
|
||||
|
||||
struct stat st = {0};
|
||||
if(lstat(filename, &st) || !S_ISREG(st.st_mode) || st.st_size<0)
|
||||
return 0;
|
||||
|
||||
time_t now = time(NULL);
|
||||
if(now==(time_t)-1 || st.st_mtime > now-DYNACACHE_TMP_STALE_SECONDS)
|
||||
return 0;
|
||||
|
||||
size_t size = st.st_size;
|
||||
if(unlink(filename))
|
||||
return 0;
|
||||
|
||||
dynarec_log(LOG_INFO, "DynaCache removed stale temporary cache file %s (%s)\n", filename, NicePrintSize(size));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int DynaCacheFileEntryCmp(const void* a, const void* b)
|
||||
{
|
||||
const DynaCacheFileEntry_t* fa = a;
|
||||
const DynaCacheFileEntry_t* fb = b;
|
||||
if(fa->mtime < fb->mtime)
|
||||
return -1;
|
||||
if(fa->mtime > fb->mtime)
|
||||
return 1;
|
||||
return strcmp(fa->filename, fb->filename);
|
||||
}
|
||||
|
||||
static void PruneDynaCacheFolder(const char* folder, uint64_t max_size)
|
||||
{
|
||||
if(!max_size)
|
||||
return;
|
||||
DIR* dir = opendir(folder);
|
||||
if(!dir) return;
|
||||
|
||||
DynaCacheFileEntry_t* files = NULL;
|
||||
size_t count = 0;
|
||||
size_t capacity = 0;
|
||||
uint64_t total = 0;
|
||||
|
||||
// collect all dynacache files in the folder
|
||||
struct dirent* d = NULL;
|
||||
while((d = readdir(dir))) {
|
||||
size_t l = strlen(d->d_name);
|
||||
if(RemoveStaleDynaCacheTempFile(folder, d->d_name))
|
||||
continue;
|
||||
if(l<=6 || strcmp(d->d_name+l-6, ".box64")) continue;
|
||||
|
||||
char* filename = box_malloc(strlen(folder)+l+1);
|
||||
strcpy(filename, folder);
|
||||
strcat(filename, d->d_name);
|
||||
|
||||
struct stat st = {0};
|
||||
if(lstat(filename, &st) || !S_ISREG(st.st_mode) || st.st_size<0) {
|
||||
box_free(filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(count == capacity) {
|
||||
size_t new_capacity = capacity ? capacity*2 : 16;
|
||||
DynaCacheFileEntry_t* new_files = box_realloc(files, new_capacity*sizeof(*files));
|
||||
files = new_files;
|
||||
capacity = new_capacity;
|
||||
}
|
||||
|
||||
files[count].filename = filename;
|
||||
files[count].size = st.st_size;
|
||||
files[count].mtime = st.st_mtime;
|
||||
files[count].valid = -1;
|
||||
total += files[count].size;
|
||||
++count;
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
// start pruning, invalid files first, then older ones until we are under 80% of set max size
|
||||
if(total > max_size && count) {
|
||||
uint64_t target_size = (max_size/5) * 4;
|
||||
size_t folder_len = strlen(folder);
|
||||
for(size_t i=0; i<count && total>target_size; ++i) {
|
||||
files[i].valid = (ReadDynaCache(folder, files[i].filename+folder_len, NULL, 0)==0);
|
||||
if(files[i].valid) continue;
|
||||
uint64_t size = files[i].size;
|
||||
if(!unlink(files[i].filename)) {
|
||||
dynarec_log(LOG_INFO, "DynaCache removed invalid cache file %s (%s)\n", files[i].filename, NicePrintSize(size));
|
||||
files[i].size = 0;
|
||||
total = (size>total)?0:total-size;
|
||||
}
|
||||
}
|
||||
qsort(files, count, sizeof(*files), DynaCacheFileEntryCmp);
|
||||
for(size_t i=0; i<count && total>target_size; ++i) {
|
||||
if(files[i].valid!=1 || !files[i].size)
|
||||
continue;
|
||||
uint64_t size = files[i].size;
|
||||
if(!unlink(files[i].filename)) {
|
||||
dynarec_log(LOG_INFO, "DynaCache removed old cache file %s (%s)\n", files[i].filename, NicePrintSize(size));
|
||||
total = (size>total)?0:total-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i=0; i<count; ++i) box_free(files[i].filename);
|
||||
box_free(files);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SerializeMmaplist(mapping_t* mapping)
|
||||
{
|
||||
if(!DYNAREC_VERSION)
|
||||
return;
|
||||
if(mapping->env && mapping->env->is_dynacache_overridden && (mapping->env->dynacache!=1))
|
||||
return;
|
||||
if((!mapping->env || !mapping->env->is_dynacache_overridden) && box64env.dynacache!=1)
|
||||
return;
|
||||
// don't do serialize for program that needs purge=1
|
||||
if(mapping->env && mapping->env->is_dynarec_purge_overridden && mapping->env->dynarec_purge)
|
||||
return;
|
||||
if((!mapping->env || !mapping->env->is_dynarec_purge_overridden) && box64env.dynarec_purge)
|
||||
return;
|
||||
// don't do serialize for program that needs dirty=1
|
||||
if(mapping->env && mapping->env->is_dynarec_dirty_overridden && mapping->env->dynarec_dirty)
|
||||
return;
|
||||
if((!mapping->env || !mapping->env->is_dynarec_dirty_overridden) && box64env.dynarec_dirty)
|
||||
return;
|
||||
const char* folder = GetDynacacheFolder(mapping);
|
||||
if(!folder) return; // no folder, no serialize...
|
||||
const char* name = GetMmaplistName(mapping);
|
||||
if(!name) return;
|
||||
char mapname[strlen(folder)+strlen(name)+1];
|
||||
strcpy(mapname, folder);
|
||||
strcat(mapname, name);
|
||||
size_t filesize = FileSize(mapping->fullname);
|
||||
if(!filesize) {
|
||||
dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because filesize is 0\n", mapping->fullname);
|
||||
return; // mmaped file as a 0 size...
|
||||
}
|
||||
// prepare header
|
||||
int nblocks = MmaplistNBlocks(mapping->mmaplist);
|
||||
if(!nblocks) {
|
||||
dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because nblocks is 0\n", mapping->fullname);
|
||||
return; //How???
|
||||
}
|
||||
size_t map_len = SizeFileMapped(mapping->start);
|
||||
size_t nLockAddresses = nLockAddressRange(mapping->start, map_len);
|
||||
size_t nUnaligned = nUnalignedRange(mapping->start, map_len);
|
||||
size_t total = sizeof(DynaCacheHeader_t) + strlen(mapping->fullname) + 1 + nblocks*sizeof(CompressedDynaCacheBlock_t) + nLockAddresses*sizeof(uintptr_t) + nUnaligned*sizeof(uintptr_t);;
|
||||
total = ALIGN(total); // align on pagesize
|
||||
uint8_t all_header[total];
|
||||
memset(all_header, 0, total);
|
||||
void* p = all_header;
|
||||
DynaCacheHeader_t* header = p;
|
||||
strcpy(header->sign, HEADER_SIGN);
|
||||
header->file_version = FILE_VERSION;
|
||||
header->dynarec_version = DYNAREC_VERSION;
|
||||
header->arch_version = ARCH_VERSION;
|
||||
header->dynarec_settings = GetDynSetting(mapping);
|
||||
header->cpuext = cpuext.x;
|
||||
header->pagesize = box64_pagesize;
|
||||
header->codesize = MmaplistTotalAlloc(mapping->mmaplist);
|
||||
header->map_addr = mapping->start;
|
||||
header->file_length = filesize;
|
||||
header->filename_length = strlen(mapping->fullname);
|
||||
header->nblocks = MmaplistNBlocks(mapping->mmaplist);
|
||||
header->map_len = map_len;
|
||||
header->nLockAddresses = nLockAddresses;
|
||||
header->nUnalignedAddresses = nUnaligned;
|
||||
size_t dynacache_min = box64env.dynacache_min;
|
||||
if(mapping->env && mapping->env->is_dynacache_min_overridden)
|
||||
dynacache_min = mapping->env->dynacache_min;
|
||||
if(dynacache_min*1024>header->codesize) {
|
||||
dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because there is not enough usefull code (%s)\n", mapping->fullname, NicePrintSize(header->codesize));
|
||||
return; // not enugh code, do no write
|
||||
}
|
||||
p += sizeof(DynaCacheHeader_t); // fullname
|
||||
strcpy(p, mapping->fullname);
|
||||
p += strlen(p) + 1; // blocks
|
||||
CompressedDynaCacheBlock_t* blocks = p;
|
||||
MmaplistFillBlocks(mapping->mmaplist, blocks);
|
||||
p += nblocks*sizeof(CompressedDynaCacheBlock_t);
|
||||
uintptr_t* lockAddresses = p;
|
||||
p += nLockAddresses*sizeof(uintptr_t);
|
||||
uintptr_t* unalignedAddresses = p;
|
||||
if(nLockAddresses)
|
||||
getLockAddressRange(mapping->start, map_len, lockAddresses);
|
||||
if(nUnaligned)
|
||||
getUnalignedRange(mapping->start, map_len, unalignedAddresses);
|
||||
// all done, now just create the file and write all this down...
|
||||
#ifndef WIN32
|
||||
char tmpname[strlen(mapname)+64];
|
||||
int tmpfd = DynaCacheOpenTempFile(mapname, tmpname, sizeof(tmpname));
|
||||
if(tmpfd<0) {
|
||||
dynarec_log(LOG_INFO, "Cannot create cache file %s\n", mapname);
|
||||
return;
|
||||
}
|
||||
FILE* f = fdopen(tmpfd, "wb");
|
||||
if(!f) {
|
||||
close(tmpfd);
|
||||
unlink(tmpname);
|
||||
dynarec_log(LOG_INFO, "Cannot create cache file %s\n", mapname);
|
||||
return;
|
||||
}
|
||||
int write_error = 0;
|
||||
if(fwrite(all_header, total, 1, f)!=1)
|
||||
write_error = 1;
|
||||
int rewrite_header = 0;
|
||||
int type = BOX64ENV(dynacache_compress);
|
||||
if(type!=COMP_NONE) {
|
||||
for(int i=0; i<nblocks && !write_error; ++i) {
|
||||
size_t sz = 0;
|
||||
void* dest = dc_compress(blocks[i].block.block, blocks[i].block.size, type, &sz);
|
||||
if(dest) {
|
||||
if(fwrite(dest, sz, 1, f)!=1)
|
||||
write_error = 1;
|
||||
else {
|
||||
blocks[i].type = type;
|
||||
blocks[i].compsize = sz;
|
||||
rewrite_header = 1;
|
||||
// align to pagesize...
|
||||
if(sz&(box64_pagesize-1)) {
|
||||
size_t align = box64_pagesize - (sz&(box64_pagesize-1));
|
||||
char tmp[align];
|
||||
memset(tmp, 0, align);
|
||||
if(fwrite(tmp, align, 1, f)!=1)
|
||||
write_error = 1;
|
||||
}
|
||||
}
|
||||
box_free(dest);
|
||||
} else {
|
||||
if(fwrite(blocks[i].block.block, blocks[i].block.size, 1, f)!=1)
|
||||
write_error = 1;
|
||||
}
|
||||
}
|
||||
} else
|
||||
for(int i=0; i<nblocks && !write_error; ++i) {
|
||||
if(fwrite(blocks[i].block.block, blocks[i].block.size, 1, f)!=1) {
|
||||
write_error = 1;
|
||||
}
|
||||
}
|
||||
if(!write_error && rewrite_header) {
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if(fwrite(all_header, total, 1, f)!=1)
|
||||
write_error = 1;
|
||||
}
|
||||
if(!write_error && fflush(f))
|
||||
write_error = 1;
|
||||
if(!write_error && fsync(tmpfd))
|
||||
write_error = 1;
|
||||
if(fclose(f))
|
||||
write_error = 1;
|
||||
if(write_error) {
|
||||
dynarec_log(LOG_INFO, "Error writing Cache file (disk full?)\n");
|
||||
unlink(tmpname);
|
||||
return;
|
||||
}
|
||||
int dynacache_limit = box64env.dynacache_limit;
|
||||
if(mapping->env && mapping->env->is_dynacache_limit_overridden)
|
||||
dynacache_limit = mapping->env->dynacache_limit;
|
||||
|
||||
if(rename(tmpname, mapname)) {
|
||||
dynarec_log(LOG_INFO, "Cannot publish cache file %s\n", mapname);
|
||||
unlink(tmpname);
|
||||
return;
|
||||
}
|
||||
SyncDynaCacheFolder(folder);
|
||||
if(dynacache_limit>0) {
|
||||
PruneDynaCacheFolder(folder, (uint64_t)dynacache_limit*1024*1024);
|
||||
SyncDynaCacheFolder(folder);
|
||||
}
|
||||
#else
|
||||
// TODO?
|
||||
#endif
|
||||
}
|
||||
|
||||
#define DCERR_OK 0
|
||||
#define DCERR_NEXIST 1
|
||||
#define DCERR_TOOSMALL 2
|
||||
#define DCERR_FERROR 3
|
||||
#define DCERR_BADHEADER 4
|
||||
#define DCERR_FILEVER 5
|
||||
#define DCERR_DYNVER 6
|
||||
#define DCERR_DYNARCHVER 7
|
||||
#define DCERR_PAGESIZE 8
|
||||
#define DCERR_MAPNEXIST 9
|
||||
#define DCERR_MAPCHG 10
|
||||
#define DCERR_RELOC 11
|
||||
#define DCERR_BADNAME 12
|
||||
#define DCERR_CPUEXT 13
|
||||
|
||||
#ifndef WIN32
|
||||
int ReadDynaCache(const char* folder, const char* name, mapping_t* mapping, int verbose)
|
||||
{
|
||||
char filename[strlen(folder)+strlen(name)+1];
|
||||
strcpy(filename, folder);
|
||||
strcat(filename, name);
|
||||
if(verbose) printf_log(LOG_NONE, "File %s:\t", name);
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if(!f) {
|
||||
int exists = FileExist(filename, IS_FILE);
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "%s\n", exists?"Cannot open file":"Invalid file");
|
||||
return exists?DCERR_FERROR:DCERR_NEXIST;
|
||||
}
|
||||
struct stat st = {0};
|
||||
if(fstat(fileno(f), &st) || !S_ISREG(st.st_mode) || st.st_size<0) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot stat file\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
size_t filesize = st.st_size;
|
||||
if(filesize<sizeof(DynaCacheHeader_t)) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Invalid side: %zd\n", filesize);
|
||||
fclose(f);
|
||||
return DCERR_TOOSMALL;
|
||||
}
|
||||
DynaCacheHeader_t header = {0};
|
||||
if(fread(&header, sizeof(header), 1, f)!=1) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot read header\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
if(strcmp(header.sign, HEADER_SIGN)) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Bad header\n");
|
||||
fclose(f);
|
||||
return DCERR_BADHEADER;
|
||||
}
|
||||
if (header.file_version != FILE_VERSION) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Incompatible File Version\n");
|
||||
fclose(f);
|
||||
return DCERR_FILEVER;
|
||||
}
|
||||
if(header.dynarec_version!=DYNAREC_VERSION) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Incompatible Dynarec Version\n");
|
||||
fclose(f);
|
||||
return DCERR_DYNVER;
|
||||
}
|
||||
if(header.arch_version!=ARCH_VERSION) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Incompatible Dynarec Arch Version\n");
|
||||
fclose(f);
|
||||
return DCERR_DYNARCHVER;
|
||||
}
|
||||
if(header.pagesize!=box64_pagesize) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Bad pagesize\n");
|
||||
fclose(f);
|
||||
return DCERR_PAGESIZE;
|
||||
}
|
||||
if(header.cpuext!=cpuext.x) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Incompatible CPU extensions\n");
|
||||
fclose(f);
|
||||
return DCERR_CPUEXT;
|
||||
}
|
||||
char map_filename[header.filename_length+1];
|
||||
if(fread(map_filename, header.filename_length+1, 1, f)!=1) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot read filename\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
if(!FileExist(map_filename, IS_FILE)) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Mapfiled does not exists\n");
|
||||
fclose(f);
|
||||
return DCERR_MAPNEXIST;
|
||||
}
|
||||
if(FileSize(map_filename)!=header.file_length) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "File changed\n");
|
||||
fclose(f);
|
||||
return DCERR_MAPCHG;
|
||||
}
|
||||
CompressedDynaCacheBlock_t blocks[header.nblocks];
|
||||
if(fread(blocks, sizeof(CompressedDynaCacheBlock_t), header.nblocks, f)!=header.nblocks) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot read blocks\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
uintptr_t lockAddresses[header.nLockAddresses];
|
||||
if(fread(lockAddresses, sizeof(uintptr_t), header.nLockAddresses, f)!=header.nLockAddresses) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot read lockAddresses\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
uintptr_t unalignedAddresses[header.nUnalignedAddresses];
|
||||
if(fread(unalignedAddresses, sizeof(uintptr_t), header.nUnalignedAddresses, f)!=header.nUnalignedAddresses) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot read unalignedAddresses\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
off_t p = ftell(f);
|
||||
p = ALIGN(p);;
|
||||
if(fseek(f, p, SEEK_SET)<0) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Error reading a block\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
if(!mapping) {
|
||||
// check the blocks can be read without reading...
|
||||
for(int i=0; i<header.nblocks; ++i) {
|
||||
p+=(blocks[i].type==COMP_NONE)?blocks[i].block.size:blocks[i].compsize;
|
||||
if(fseek(f, (blocks[i].type==COMP_NONE)?blocks[i].block.size:blocks[i].compsize, SEEK_CUR)<0 || ftell(f)!=p) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Error reading a block\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
}
|
||||
char* short_name = strrchr(map_filename, '/');
|
||||
if(short_name)
|
||||
++short_name;
|
||||
else
|
||||
short_name = map_filename;
|
||||
short_name = LowerCase(short_name);
|
||||
const char* file_name = MmaplistName(short_name, header.dynarec_settings, map_filename);
|
||||
box_free(short_name);
|
||||
if(strcmp(file_name, name)) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Invalid cache name\n");
|
||||
fclose(f);
|
||||
return DCERR_BADNAME;
|
||||
}
|
||||
if(verbose) {
|
||||
// check if name is coherent
|
||||
// file is valid, gives informations:
|
||||
printf_log_prefix(0, LOG_NONE, "%s (%s)\n", map_filename, NicePrintSize(filesize));
|
||||
printf_log_prefix(0, LOG_NONE, "\tDynarec Settings:\n");
|
||||
PrintDynfSettings(LOG_NONE, header.dynarec_settings);
|
||||
size_t total_blocks = 0, total_free = 0, total_compressed = 0, total_uncompressed = 0;
|
||||
size_t total_code = header.codesize;
|
||||
for(int i=0; i<header.nblocks; ++i) {
|
||||
total_blocks += blocks[i].block.size;
|
||||
total_free += blocks[i].block.free_size;
|
||||
if(blocks[i].compsize)
|
||||
total_compressed += blocks[i].compsize;
|
||||
else
|
||||
total_uncompressed += blocks[i].block.size;
|
||||
}
|
||||
printf_log_prefix(0, LOG_NONE, "\tHas %d blocks for a total of %s", header.nblocks, NicePrintSize(total_blocks));
|
||||
printf_log_prefix(0, LOG_NONE, " with %s still free", NicePrintSize(total_free));
|
||||
printf_log_prefix(0, LOG_NONE, " and %s non-canceled blocks (mapped at %p-%p, with %zu lock and %zu unaligned addresses)", NicePrintSize(total_code), (void*)header.map_addr, (void*)header.map_addr+header.map_len, header.nLockAddresses, header.nUnalignedAddresses);
|
||||
if(total_compressed) {
|
||||
printf_log_prefix(0, LOG_NONE, " with %s compressed blocks", NicePrintSize(total_compressed));
|
||||
if(total_uncompressed)
|
||||
printf_log_prefix(0, LOG_NONE, " and %s uncompressed block", NicePrintSize(total_uncompressed));
|
||||
}
|
||||
printf_log_prefix(0, LOG_NONE, "\n");
|
||||
}
|
||||
} else {
|
||||
// actually reading!
|
||||
int fd = fileno(f);
|
||||
intptr_t delta_map = mapping->start - header.map_addr;
|
||||
dynarec_log(LOG_INFO, "Trying to load DynaCache for %s, with a delta_map=%zx\n", mapping->fullname, delta_map);
|
||||
if(!mapping->mmaplist)
|
||||
mapping->mmaplist = NewMmaplist();
|
||||
MmaplistAddNBlocks(mapping->mmaplist, header.nblocks);
|
||||
for(int i=0; i<header.nblocks; ++i) {
|
||||
int ret = 0;
|
||||
if(blocks[i].type==COMP_NONE)
|
||||
ret = MmaplistAddBlock(mapping->mmaplist, fd, p, blocks[i].block.block, blocks[i].block.size, delta_map, mapping->start);
|
||||
else {
|
||||
void* src = box_malloc(blocks[i].compsize);
|
||||
fseek(f, p, SEEK_SET);
|
||||
if(!src || (fread(src, blocks[i].compsize, 1, f)!=1)) {
|
||||
ret = -10;
|
||||
} else
|
||||
ret = MmaplistAddCompressedBlock(mapping->mmaplist, blocks[i].type, src, blocks[i].compsize, blocks[i].block.block, blocks[i].block.size, delta_map, mapping->start);
|
||||
box_free(src);
|
||||
}
|
||||
if(ret) {
|
||||
printf_log(LOG_NONE, "Error while doing relocation on a DynaCache (block %d)\n", i);
|
||||
fclose(f);
|
||||
return DCERR_RELOC;
|
||||
}
|
||||
p+=(blocks[i].type==COMP_NONE)?blocks[i].block.size:ALIGN(blocks[i].compsize);
|
||||
}
|
||||
for(size_t i=0; i<header.nLockAddresses; ++i)
|
||||
addLockAddress(lockAddresses[i]+delta_map);
|
||||
for(size_t i=0; i<header.nUnalignedAddresses; ++i)
|
||||
add_unaligned_address(unalignedAddresses[i]+delta_map);
|
||||
dynarec_log(LOG_INFO, "Loaded DynaCache for %s, with %d blocks\n", mapping->fullname, header.nblocks);
|
||||
}
|
||||
fclose(f);
|
||||
return DCERR_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
void DynaCacheList(const char* filter)
|
||||
{
|
||||
#ifndef WIN32
|
||||
const char* folder = GetDynacacheFolder(NULL);
|
||||
if(!folder) {
|
||||
printf_log(LOG_NONE, "DynaCache folder not found\n");
|
||||
return;
|
||||
}
|
||||
DIR* dir = opendir(folder);
|
||||
if(!dir) {
|
||||
printf_log(LOG_NONE, "Cannot open DynaCache folder\n");
|
||||
return;
|
||||
}
|
||||
struct dirent* d = NULL;
|
||||
int need_filter = (filter && strlen(filter));
|
||||
while((d = readdir(dir))) {
|
||||
size_t l = strlen(d->d_name);
|
||||
if(l>6 && !strcmp(d->d_name+l-6, ".box64")) {
|
||||
if(need_filter && !strstr(d->d_name, filter))
|
||||
continue;
|
||||
ReadDynaCache(folder, d->d_name, NULL, 1);
|
||||
printf_log_prefix(0, LOG_NONE, "\n");
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
#endif
|
||||
}
|
||||
void DynaCacheClean()
|
||||
{
|
||||
#ifndef WIN32
|
||||
const char* folder = GetDynacacheFolder(NULL);
|
||||
if(!folder) {
|
||||
printf_log(LOG_NONE, "DynaCache folder not found\n");
|
||||
return;
|
||||
}
|
||||
DIR* dir = opendir(folder);
|
||||
if(!dir) {
|
||||
printf_log(LOG_NONE, "Cannot open DynaCache folder\n");
|
||||
return;
|
||||
}
|
||||
struct dirent* d = NULL;
|
||||
while((d = readdir(dir))) {
|
||||
size_t l = strlen(d->d_name);
|
||||
if(RemoveStaleDynaCacheTempFile(folder, d->d_name))
|
||||
continue;
|
||||
if(l>6 && !strcmp(d->d_name+l-6, ".box64")) {
|
||||
int ret = ReadDynaCache(folder, d->d_name, NULL, 0);
|
||||
if(ret) {
|
||||
char filename[strlen(folder)+strlen(d->d_name)+1];
|
||||
strcpy(filename, folder);
|
||||
strcat(filename, d->d_name);
|
||||
size_t filesize = FileSize(filename);
|
||||
if(!unlink(filename)) {
|
||||
printf_log(LOG_NONE, "Removed %s for %s\n", d->d_name, NicePrintSize(filesize));
|
||||
} else {
|
||||
printf_log(LOG_NONE, "Could not remove %s\n", d->d_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
SyncDynaCacheFolder(folder);
|
||||
#endif
|
||||
}
|
||||
#ifndef WIN32
|
||||
void MmapDynaCache(mapping_t* mapping)
|
||||
{
|
||||
if(!DYNAREC_VERSION)
|
||||
return;
|
||||
// no need to test dynacache enabled or not, it has already been done before this call
|
||||
const char* folder = GetDynacacheFolder(mapping);
|
||||
if(!folder) return;
|
||||
const char* name = GetMmaplistName(mapping);
|
||||
if(!name) return;
|
||||
dynarec_log(LOG_DEBUG, "Looking for DynaCache %s in %s\n", name, folder);
|
||||
ReadDynaCache(folder, name, mapping, 0);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
void SerializeMmaplist(mapping_t* mapping) {}
|
||||
void DynaCacheList(const char* filter) { printf_log(LOG_NONE, "Dynarec not enable\n"); }
|
||||
void DynaCacheClean() {}
|
||||
#endif
|
||||
+3
-806
@@ -6,14 +6,9 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <strings.h>
|
||||
#if defined(DYNAREC) && !defined(WIN32)
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#include "os.h"
|
||||
#include "env.h"
|
||||
@@ -25,10 +20,8 @@
|
||||
#include "rbtree.h"
|
||||
#include "wine_tools.h"
|
||||
#include "pe_tools.h"
|
||||
#ifdef DYNAREC
|
||||
#include "dynacache_hashes.h"
|
||||
#include "dynacache_compress.h"
|
||||
#endif
|
||||
#include "dynacache.h"
|
||||
#include "env_private.h"
|
||||
|
||||
box64env_t box64env = { 0 };
|
||||
|
||||
@@ -36,23 +29,10 @@ KHASH_MAP_INIT_STR(box64env_entry, box64env_t)
|
||||
static kh_box64env_entry_t* box64env_entries = NULL;
|
||||
static kh_box64env_entry_t* box64env_entries_wildcard = NULL;
|
||||
|
||||
mmaplist_t* NewMmaplist();
|
||||
void DelMmaplist(mmaplist_t* list);
|
||||
#ifdef DYNAREC
|
||||
void DelMmaplist(mmaplist_t* list);
|
||||
int MmaplistHasNew(mmaplist_t* list, int clear);
|
||||
int MmaplistIsDirty(mmaplist_t* list);
|
||||
int MmaplistNBlocks(mmaplist_t* list);
|
||||
size_t MmaplistTotalAlloc(mmaplist_t* list);
|
||||
void MmaplistFillBlocks(mmaplist_t* list, CompressedDynaCacheBlock_t* blocks);
|
||||
void MmaplistAddNBlocks(mmaplist_t* list, int nblocks);
|
||||
int MmaplistAddBlock(mmaplist_t* list, int fd, off_t offset, void* orig, size_t size, intptr_t delta_map, uintptr_t mapping_start);
|
||||
int MmaplistAddCompressedBlock(mmaplist_t* list, int type, void* src, size_t src_size, void* orig, size_t size, intptr_t delta_map, uintptr_t mapping_start);
|
||||
int nLockAddressRange(uintptr_t start, size_t size);
|
||||
void getLockAddressRange(uintptr_t start, size_t size, uintptr_t addrs[]);
|
||||
void addLockAddress(uintptr_t addr);
|
||||
int nUnalignedRange(uintptr_t start, size_t size);
|
||||
void getUnalignedRange(uintptr_t start, size_t size, uintptr_t addrs[]);
|
||||
void add_unaligned_address(uintptr_t addr);
|
||||
#endif
|
||||
|
||||
static rbtree_t* envmap = NULL;
|
||||
@@ -782,22 +762,9 @@ void PrintEnvVariables(box64env_t* env, int level)
|
||||
|
||||
}
|
||||
|
||||
typedef struct mapping_s {
|
||||
char* filename;
|
||||
char* fullname;
|
||||
box64env_t* env;
|
||||
uintptr_t start; //lower address of the map for this file
|
||||
mmaplist_t* mmaplist;
|
||||
} mapping_t;
|
||||
|
||||
KHASH_MAP_INIT_STR(mapping_entry, mapping_t*);
|
||||
static kh_mapping_entry_t* mapping_entries = NULL;
|
||||
|
||||
#if defined(DYNAREC) && !defined(WIN32)
|
||||
void MmapDynaCache(mapping_t* mapping);
|
||||
#endif
|
||||
|
||||
|
||||
void RecordEnvMappings(uintptr_t addr, size_t length, int fd)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
@@ -885,776 +852,6 @@ void RecordEnvMappings(uintptr_t addr, size_t length, int fd)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DYNAREC
|
||||
const char* GetDynacacheFolder(mapping_t* mapping)
|
||||
{
|
||||
static char folder[4096] = { 0 };
|
||||
if(mapping && mapping->env && mapping->env->is_dynacache_folder_overridden && mapping->env->dynacache_folder) {
|
||||
if (FileExist(mapping->env->dynacache_folder, 0) || MakeDir(mapping->env->dynacache_folder)) {
|
||||
strcpy(folder, mapping->env->dynacache_folder);
|
||||
goto done;
|
||||
}
|
||||
} else if (box64env.dynacache_folder) {
|
||||
if (FileExist(box64env.dynacache_folder, 0) || MakeDir(box64env.dynacache_folder)) {
|
||||
strcpy(folder, box64env.dynacache_folder);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if(GetEnv("XDG_CACHE_HOME"))
|
||||
strcpy(folder, GetEnv("XDG_CACHE_HOME"));
|
||||
else if(GetEnv(HOME)) {
|
||||
strcpy(folder, GetEnv(HOME));
|
||||
strcat(folder, PATHSEP);
|
||||
strcat(folder, ".cache");
|
||||
if (!FileExist(folder, 0) && !MakeDir(folder))
|
||||
return NULL;
|
||||
|
||||
} else
|
||||
return NULL;
|
||||
strcat(folder, PATHSEP);
|
||||
strcat(folder, "box64");
|
||||
if (!FileExist(folder, 0) && !MakeDir(folder))
|
||||
return NULL;
|
||||
|
||||
done:
|
||||
if (folder[strlen(folder) - 1] != PATHSEP[0]) {
|
||||
strcat(folder, PATHSEP);
|
||||
}
|
||||
return folder;
|
||||
}
|
||||
|
||||
/*
|
||||
There are 3 compatibility values for DynaCache:
|
||||
1. FILE_VERSION for the DynaCache infrastructure and file format
|
||||
2. DYNAREC_VERSION as the generated hash of common dynarec sources
|
||||
3. ARCH_VERSION as the generated hash of the active backend sources
|
||||
|
||||
Dynacache will ignore any DynaCache file not exactly matching those 3 version.
|
||||
`box64 --dynacache-clean` can be used from command line to purge obsolete DyaCache files
|
||||
*/
|
||||
|
||||
#define FILE_VERSION 3
|
||||
#define HEADER_SIGN "DynaCache"
|
||||
|
||||
typedef struct DynaCacheHeader_s {
|
||||
char sign[10]; //"DynaCache\0"
|
||||
uint64_t file_version:16;
|
||||
uint64_t dynarec_version:24;
|
||||
uint64_t arch_version:24;
|
||||
uint64_t cpuext;
|
||||
uint64_t dynarec_settings;
|
||||
size_t pagesize;
|
||||
size_t codesize;
|
||||
uintptr_t map_addr;
|
||||
size_t map_len;
|
||||
size_t file_length;
|
||||
uint32_t filename_length;
|
||||
uint32_t nblocks;
|
||||
uint32_t nLockAddresses;
|
||||
uint32_t nUnalignedAddresses;
|
||||
char filename[];
|
||||
} DynaCacheHeader_t;
|
||||
|
||||
#define DYNAREC_SETTINGS() \
|
||||
DS_GO(BOX64_DYNAREC_ALIGNED_ATOMICS, dynarec_aligned_atomics, 1) \
|
||||
DS_GO(BOX64_DYNAREC_BIGBLOCK, dynarec_bigblock, 2) \
|
||||
DS_GO(BOX64_DYNAREC_CALLRET, dynarec_callret, 2) \
|
||||
DS_GO(BOX64_DYNAREC_DF, dynarec_df, 1) \
|
||||
DS_GO(BOX64_DYNAREC_DIRTY, dynarec_dirty, 2) \
|
||||
DS_GO(BOX64_DYNAREC_DIV0, dynarec_div0, 1) \
|
||||
DS_GO(BOX64_DYNAREC_FASTNAN, dynarec_fastnan, 1) \
|
||||
DS_GO(BOX64_DYNAREC_FASTROUND, dynarec_fastround, 2) \
|
||||
DS_GO(BOX64_DYNAREC_FORWARD, dynarec_forward, 10) \
|
||||
DS_GO(BOX64_DYNAREC_NATIVEFLAGS, dynarec_nativeflags, 1) \
|
||||
DS_GO(BOX64_DYNAREC_SAFEFLAGS, dynarec_safeflags, 2) \
|
||||
DS_GO(BOX64_DYNAREC_STRONGMEM, dynarec_strongmem, 2) \
|
||||
DS_GO(BOX64_DYNAREC_VOLATILE_METADATA, dynarec_volatile_metadata, 1)\
|
||||
DS_GO(BOX64_DYNAREC_WEAKBARRIER, dynarec_weakbarrier, 2) \
|
||||
DS_GO(BOX64_DYNAREC_X87DOUBLE, dynarec_x87double, 2) \
|
||||
DS_GO(BOX64_DYNAREC_NOARCH, dynarec_noarch, 2) \
|
||||
DS_GO(BOX64_aes, aes, 1) \
|
||||
DS_GO(BOX64_PCLMULQDQ, pclmulqdq, 1) \
|
||||
DS_GO(BOX64_SHAEXT, shaext, 1) \
|
||||
DS_GO(BOX64_SSE42, sse42, 1) \
|
||||
DS_GO(BOX64_AVX, avx, 2) \
|
||||
DS_GO(BOX64_X87_NO80BITS, x87_no80bits, 1) \
|
||||
DS_GO(BOX64_RDTSC_1GHZ, rdtsc_1ghz, 1) \
|
||||
DS_GO(BOX64_SSE_FLUSHTO0, sse_flushto0, 1) \
|
||||
|
||||
#define DS_GO(A, B, C) uint64_t B:C;
|
||||
typedef union dynarec_settings_s {
|
||||
struct {
|
||||
DYNAREC_SETTINGS()
|
||||
};
|
||||
uint64_t x;
|
||||
} dynarec_settings_t;
|
||||
#undef DS_GO
|
||||
uint64_t GetDynSetting(mapping_t* mapping)
|
||||
{
|
||||
dynarec_settings_t settings = {0};
|
||||
#define DS_GO(A, B, C) settings.B = (mapping->env && mapping->env->is_##B##_overridden)?mapping->env->B:box64env.B;
|
||||
DYNAREC_SETTINGS()
|
||||
#undef DS_GO
|
||||
return settings.x;
|
||||
}
|
||||
void PrintDynfSettings(int level, uint64_t s)
|
||||
{
|
||||
dynarec_settings_t settings = {0};
|
||||
settings.x = s;
|
||||
#define DS_GO(A, B, C) if(settings.B) printf_log_prefix(0, level, "\t\t" #A "=%d\n", settings.B);
|
||||
DYNAREC_SETTINGS()
|
||||
#undef DS_GO
|
||||
}
|
||||
#undef DYNAREC_SETTINGS
|
||||
|
||||
char* MmaplistName(const char* filename, uint64_t dynarec_settings, const char* fullname)
|
||||
{
|
||||
// names are FOLDER/filename-YYYYY-XXXXX.box64
|
||||
// Where XXXXX is the hash of the full name
|
||||
// and YYYY is the Dynarec optim (in hex)
|
||||
static char mapname[4096];
|
||||
snprintf(mapname, 4095-6, "%s-%llx-%u", filename, dynarec_settings, __ac_X31_hash_string(fullname));
|
||||
strcat(mapname, ".box64");
|
||||
return mapname;
|
||||
}
|
||||
|
||||
char* GetMmaplistName(mapping_t* mapping)
|
||||
{
|
||||
return MmaplistName(mapping->filename+1, GetDynSetting(mapping), mapping->fullname);
|
||||
}
|
||||
|
||||
const char* NicePrintSize(size_t sz)
|
||||
{
|
||||
static char buf[256];
|
||||
const char* units[] = {"", "kb", "Mb", "Gb"};
|
||||
size_t u, d;
|
||||
int idx = 0;
|
||||
size_t ratio = 0;
|
||||
while(idx<sizeof(units)/sizeof(units[0]) && (1<<(ratio+10))<sz) {
|
||||
ratio+=10;
|
||||
++idx;
|
||||
}
|
||||
if(ratio && (sz>>ratio)<50) {
|
||||
snprintf(buf, 255, "%zd.%zd%s", sz>>ratio, (sz>>(ratio-1))%10, units[idx]);
|
||||
} else {
|
||||
snprintf(buf, 255, "%zd%s", sz>>ratio, units[idx]);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
typedef struct DynaCacheFileEntry_s {
|
||||
char* filename;
|
||||
uint64_t size;
|
||||
time_t mtime;
|
||||
int valid;
|
||||
} DynaCacheFileEntry_t;
|
||||
|
||||
int ReadDynaCache(const char* folder, const char* name, mapping_t* mapping, int verbose);
|
||||
|
||||
#ifndef O_CLOEXEC
|
||||
#define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
#define DYNACACHE_TMP_STALE_SECONDS 60
|
||||
|
||||
static void SyncDynaCacheFolder(const char* folder)
|
||||
{
|
||||
#ifdef O_DIRECTORY
|
||||
int fd = open(folder, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
#else
|
||||
int fd = open(folder, O_RDONLY|O_CLOEXEC);
|
||||
#endif
|
||||
if(fd>=0) {
|
||||
fsync(fd);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static int DynaCacheOpenTempFile(const char* mapname, char* tmpname, size_t tmpname_size)
|
||||
{
|
||||
for(int i=0; i<100; ++i) {
|
||||
snprintf(tmpname, tmpname_size, "%s.tmp.%ld.%d", mapname, (long)getpid(), i);
|
||||
int fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0644);
|
||||
if(fd>=0)
|
||||
return fd;
|
||||
if(errno!=EEXIST)
|
||||
return -1;
|
||||
}
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int RemoveStaleDynaCacheTempFile(const char* folder, const char* name)
|
||||
{
|
||||
if(!strstr(name, ".box64.tmp."))
|
||||
return 0;
|
||||
|
||||
char filename[strlen(folder)+strlen(name)+1];
|
||||
strcpy(filename, folder);
|
||||
strcat(filename, name);
|
||||
|
||||
struct stat st = {0};
|
||||
if(lstat(filename, &st) || !S_ISREG(st.st_mode) || st.st_size<0)
|
||||
return 0;
|
||||
|
||||
time_t now = time(NULL);
|
||||
if(now==(time_t)-1 || st.st_mtime > now-DYNACACHE_TMP_STALE_SECONDS)
|
||||
return 0;
|
||||
|
||||
size_t size = st.st_size;
|
||||
if(unlink(filename))
|
||||
return 0;
|
||||
|
||||
dynarec_log(LOG_INFO, "DynaCache removed stale temporary cache file %s (%s)\n", filename, NicePrintSize(size));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int DynaCacheFileEntryCmp(const void* a, const void* b)
|
||||
{
|
||||
const DynaCacheFileEntry_t* fa = a;
|
||||
const DynaCacheFileEntry_t* fb = b;
|
||||
if(fa->mtime < fb->mtime)
|
||||
return -1;
|
||||
if(fa->mtime > fb->mtime)
|
||||
return 1;
|
||||
return strcmp(fa->filename, fb->filename);
|
||||
}
|
||||
|
||||
static void PruneDynaCacheFolder(const char* folder, uint64_t max_size)
|
||||
{
|
||||
if(!max_size)
|
||||
return;
|
||||
DIR* dir = opendir(folder);
|
||||
if(!dir) return;
|
||||
|
||||
DynaCacheFileEntry_t* files = NULL;
|
||||
size_t count = 0;
|
||||
size_t capacity = 0;
|
||||
uint64_t total = 0;
|
||||
|
||||
// collect all dynacache files in the folder
|
||||
struct dirent* d = NULL;
|
||||
while((d = readdir(dir))) {
|
||||
size_t l = strlen(d->d_name);
|
||||
if(RemoveStaleDynaCacheTempFile(folder, d->d_name))
|
||||
continue;
|
||||
if(l<=6 || strcmp(d->d_name+l-6, ".box64")) continue;
|
||||
|
||||
char* filename = box_malloc(strlen(folder)+l+1);
|
||||
strcpy(filename, folder);
|
||||
strcat(filename, d->d_name);
|
||||
|
||||
struct stat st = {0};
|
||||
if(lstat(filename, &st) || !S_ISREG(st.st_mode) || st.st_size<0) {
|
||||
box_free(filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(count == capacity) {
|
||||
size_t new_capacity = capacity ? capacity*2 : 16;
|
||||
DynaCacheFileEntry_t* new_files = box_realloc(files, new_capacity*sizeof(*files));
|
||||
files = new_files;
|
||||
capacity = new_capacity;
|
||||
}
|
||||
|
||||
files[count].filename = filename;
|
||||
files[count].size = st.st_size;
|
||||
files[count].mtime = st.st_mtime;
|
||||
files[count].valid = -1;
|
||||
total += files[count].size;
|
||||
++count;
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
// start pruning, invalid files first, then older ones until we are under 80% of set max size
|
||||
if(total > max_size && count) {
|
||||
uint64_t target_size = (max_size/5) * 4;
|
||||
size_t folder_len = strlen(folder);
|
||||
for(size_t i=0; i<count && total>target_size; ++i) {
|
||||
files[i].valid = (ReadDynaCache(folder, files[i].filename+folder_len, NULL, 0)==0);
|
||||
if(files[i].valid) continue;
|
||||
uint64_t size = files[i].size;
|
||||
if(!unlink(files[i].filename)) {
|
||||
dynarec_log(LOG_INFO, "DynaCache removed invalid cache file %s (%s)\n", files[i].filename, NicePrintSize(size));
|
||||
files[i].size = 0;
|
||||
total = (size>total)?0:total-size;
|
||||
}
|
||||
}
|
||||
qsort(files, count, sizeof(*files), DynaCacheFileEntryCmp);
|
||||
for(size_t i=0; i<count && total>target_size; ++i) {
|
||||
if(files[i].valid!=1 || !files[i].size)
|
||||
continue;
|
||||
uint64_t size = files[i].size;
|
||||
if(!unlink(files[i].filename)) {
|
||||
dynarec_log(LOG_INFO, "DynaCache removed old cache file %s (%s)\n", files[i].filename, NicePrintSize(size));
|
||||
total = (size>total)?0:total-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i=0; i<count; ++i) box_free(files[i].filename);
|
||||
box_free(files);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SerializeMmaplist(mapping_t* mapping)
|
||||
{
|
||||
if(!DYNAREC_VERSION)
|
||||
return;
|
||||
if(mapping->env && mapping->env->is_dynacache_overridden && (mapping->env->dynacache!=1))
|
||||
return;
|
||||
if((!mapping->env || !mapping->env->is_dynacache_overridden) && box64env.dynacache!=1)
|
||||
return;
|
||||
// don't do serialize for program that needs purge=1
|
||||
if(mapping->env && mapping->env->is_dynarec_purge_overridden && mapping->env->dynarec_purge)
|
||||
return;
|
||||
if((!mapping->env || !mapping->env->is_dynarec_purge_overridden) && box64env.dynarec_purge)
|
||||
return;
|
||||
// don't do serialize for program that needs dirty=1
|
||||
if(mapping->env && mapping->env->is_dynarec_dirty_overridden && mapping->env->dynarec_dirty)
|
||||
return;
|
||||
if((!mapping->env || !mapping->env->is_dynarec_dirty_overridden) && box64env.dynarec_dirty)
|
||||
return;
|
||||
const char* folder = GetDynacacheFolder(mapping);
|
||||
if(!folder) return; // no folder, no serialize...
|
||||
const char* name = GetMmaplistName(mapping);
|
||||
if(!name) return;
|
||||
char mapname[strlen(folder)+strlen(name)+1];
|
||||
strcpy(mapname, folder);
|
||||
strcat(mapname, name);
|
||||
size_t filesize = FileSize(mapping->fullname);
|
||||
if(!filesize) {
|
||||
dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because filesize is 0\n", mapping->fullname);
|
||||
return; // mmaped file as a 0 size...
|
||||
}
|
||||
// prepare header
|
||||
int nblocks = MmaplistNBlocks(mapping->mmaplist);
|
||||
if(!nblocks) {
|
||||
dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because nblocks is 0\n", mapping->fullname);
|
||||
return; //How???
|
||||
}
|
||||
size_t map_len = SizeFileMapped(mapping->start);
|
||||
size_t nLockAddresses = nLockAddressRange(mapping->start, map_len);
|
||||
size_t nUnaligned = nUnalignedRange(mapping->start, map_len);
|
||||
size_t total = sizeof(DynaCacheHeader_t) + strlen(mapping->fullname) + 1 + nblocks*sizeof(CompressedDynaCacheBlock_t) + nLockAddresses*sizeof(uintptr_t) + nUnaligned*sizeof(uintptr_t);;
|
||||
total = ALIGN(total); // align on pagesize
|
||||
uint8_t all_header[total];
|
||||
memset(all_header, 0, total);
|
||||
void* p = all_header;
|
||||
DynaCacheHeader_t* header = p;
|
||||
strcpy(header->sign, HEADER_SIGN);
|
||||
header->file_version = FILE_VERSION;
|
||||
header->dynarec_version = DYNAREC_VERSION;
|
||||
header->arch_version = ARCH_VERSION;
|
||||
header->dynarec_settings = GetDynSetting(mapping);
|
||||
header->cpuext = cpuext.x;
|
||||
header->pagesize = box64_pagesize;
|
||||
header->codesize = MmaplistTotalAlloc(mapping->mmaplist);
|
||||
header->map_addr = mapping->start;
|
||||
header->file_length = filesize;
|
||||
header->filename_length = strlen(mapping->fullname);
|
||||
header->nblocks = MmaplistNBlocks(mapping->mmaplist);
|
||||
header->map_len = map_len;
|
||||
header->nLockAddresses = nLockAddresses;
|
||||
header->nUnalignedAddresses = nUnaligned;
|
||||
size_t dynacache_min = box64env.dynacache_min;
|
||||
if(mapping->env && mapping->env->is_dynacache_min_overridden)
|
||||
dynacache_min = mapping->env->dynacache_min;
|
||||
if(dynacache_min*1024>header->codesize) {
|
||||
dynarec_log(LOG_INFO, "DynaCache will not serialize cache for %s because there is not enough usefull code (%s)\n", mapping->fullname, NicePrintSize(header->codesize));
|
||||
return; // not enugh code, do no write
|
||||
}
|
||||
p += sizeof(DynaCacheHeader_t); // fullname
|
||||
strcpy(p, mapping->fullname);
|
||||
p += strlen(p) + 1; // blocks
|
||||
CompressedDynaCacheBlock_t* blocks = p;
|
||||
MmaplistFillBlocks(mapping->mmaplist, blocks);
|
||||
p += nblocks*sizeof(CompressedDynaCacheBlock_t);
|
||||
uintptr_t* lockAddresses = p;
|
||||
p += nLockAddresses*sizeof(uintptr_t);
|
||||
uintptr_t* unalignedAddresses = p;
|
||||
if(nLockAddresses)
|
||||
getLockAddressRange(mapping->start, map_len, lockAddresses);
|
||||
if(nUnaligned)
|
||||
getUnalignedRange(mapping->start, map_len, unalignedAddresses);
|
||||
// all done, now just create the file and write all this down...
|
||||
#ifndef WIN32
|
||||
char tmpname[strlen(mapname)+64];
|
||||
int tmpfd = DynaCacheOpenTempFile(mapname, tmpname, sizeof(tmpname));
|
||||
if(tmpfd<0) {
|
||||
dynarec_log(LOG_INFO, "Cannot create cache file %s\n", mapname);
|
||||
return;
|
||||
}
|
||||
FILE* f = fdopen(tmpfd, "wb");
|
||||
if(!f) {
|
||||
close(tmpfd);
|
||||
unlink(tmpname);
|
||||
dynarec_log(LOG_INFO, "Cannot create cache file %s\n", mapname);
|
||||
return;
|
||||
}
|
||||
int write_error = 0;
|
||||
if(fwrite(all_header, total, 1, f)!=1)
|
||||
write_error = 1;
|
||||
int rewrite_header = 0;
|
||||
int type = BOX64ENV(dynacache_compress);
|
||||
if(type!=COMP_NONE) {
|
||||
for(int i=0; i<nblocks && !write_error; ++i) {
|
||||
size_t sz = 0;
|
||||
void* dest = dc_compress(blocks[i].block.block, blocks[i].block.size, type, &sz);
|
||||
if(dest) {
|
||||
if(fwrite(dest, sz, 1, f)!=1)
|
||||
write_error = 1;
|
||||
else {
|
||||
blocks[i].type = type;
|
||||
blocks[i].compsize = sz;
|
||||
rewrite_header = 1;
|
||||
// align to pagesize...
|
||||
if(sz&(box64_pagesize-1)) {
|
||||
size_t align = box64_pagesize - (sz&(box64_pagesize-1));
|
||||
char tmp[align];
|
||||
memset(tmp, 0, align);
|
||||
if(fwrite(tmp, align, 1, f)!=1)
|
||||
write_error = 1;
|
||||
}
|
||||
}
|
||||
box_free(dest);
|
||||
} else {
|
||||
if(fwrite(blocks[i].block.block, blocks[i].block.size, 1, f)!=1)
|
||||
write_error = 1;
|
||||
}
|
||||
}
|
||||
} else
|
||||
for(int i=0; i<nblocks && !write_error; ++i) {
|
||||
if(fwrite(blocks[i].block.block, blocks[i].block.size, 1, f)!=1) {
|
||||
write_error = 1;
|
||||
}
|
||||
}
|
||||
if(!write_error && rewrite_header) {
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if(fwrite(all_header, total, 1, f)!=1)
|
||||
write_error = 1;
|
||||
}
|
||||
if(!write_error && fflush(f))
|
||||
write_error = 1;
|
||||
if(!write_error && fsync(tmpfd))
|
||||
write_error = 1;
|
||||
if(fclose(f))
|
||||
write_error = 1;
|
||||
if(write_error) {
|
||||
dynarec_log(LOG_INFO, "Error writing Cache file (disk full?)\n");
|
||||
unlink(tmpname);
|
||||
return;
|
||||
}
|
||||
int dynacache_limit = box64env.dynacache_limit;
|
||||
if(mapping->env && mapping->env->is_dynacache_limit_overridden)
|
||||
dynacache_limit = mapping->env->dynacache_limit;
|
||||
|
||||
if(rename(tmpname, mapname)) {
|
||||
dynarec_log(LOG_INFO, "Cannot publish cache file %s\n", mapname);
|
||||
unlink(tmpname);
|
||||
return;
|
||||
}
|
||||
SyncDynaCacheFolder(folder);
|
||||
if(dynacache_limit>0) {
|
||||
PruneDynaCacheFolder(folder, (uint64_t)dynacache_limit*1024*1024);
|
||||
SyncDynaCacheFolder(folder);
|
||||
}
|
||||
#else
|
||||
// TODO?
|
||||
#endif
|
||||
}
|
||||
|
||||
#define DCERR_OK 0
|
||||
#define DCERR_NEXIST 1
|
||||
#define DCERR_TOOSMALL 2
|
||||
#define DCERR_FERROR 3
|
||||
#define DCERR_BADHEADER 4
|
||||
#define DCERR_FILEVER 5
|
||||
#define DCERR_DYNVER 6
|
||||
#define DCERR_DYNARCHVER 7
|
||||
#define DCERR_PAGESIZE 8
|
||||
#define DCERR_MAPNEXIST 9
|
||||
#define DCERR_MAPCHG 10
|
||||
#define DCERR_RELOC 11
|
||||
#define DCERR_BADNAME 12
|
||||
#define DCERR_CPUEXT 13
|
||||
|
||||
#ifndef WIN32
|
||||
int ReadDynaCache(const char* folder, const char* name, mapping_t* mapping, int verbose)
|
||||
{
|
||||
char filename[strlen(folder)+strlen(name)+1];
|
||||
strcpy(filename, folder);
|
||||
strcat(filename, name);
|
||||
if(verbose) printf_log(LOG_NONE, "File %s:\t", name);
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if(!f) {
|
||||
int exists = FileExist(filename, IS_FILE);
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "%s\n", exists?"Cannot open file":"Invalid file");
|
||||
return exists?DCERR_FERROR:DCERR_NEXIST;
|
||||
}
|
||||
struct stat st = {0};
|
||||
if(fstat(fileno(f), &st) || !S_ISREG(st.st_mode) || st.st_size<0) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot stat file\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
size_t filesize = st.st_size;
|
||||
if(filesize<sizeof(DynaCacheHeader_t)) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Invalid side: %zd\n", filesize);
|
||||
fclose(f);
|
||||
return DCERR_TOOSMALL;
|
||||
}
|
||||
DynaCacheHeader_t header = {0};
|
||||
if(fread(&header, sizeof(header), 1, f)!=1) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot read header\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
if(strcmp(header.sign, HEADER_SIGN)) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Bad header\n");
|
||||
fclose(f);
|
||||
return DCERR_BADHEADER;
|
||||
}
|
||||
if (header.file_version != FILE_VERSION) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Incompatible File Version\n");
|
||||
fclose(f);
|
||||
return DCERR_FILEVER;
|
||||
}
|
||||
if(header.dynarec_version!=DYNAREC_VERSION) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Incompatible Dynarec Version\n");
|
||||
fclose(f);
|
||||
return DCERR_DYNVER;
|
||||
}
|
||||
if(header.arch_version!=ARCH_VERSION) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Incompatible Dynarec Arch Version\n");
|
||||
fclose(f);
|
||||
return DCERR_DYNARCHVER;
|
||||
}
|
||||
if(header.pagesize!=box64_pagesize) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Bad pagesize\n");
|
||||
fclose(f);
|
||||
return DCERR_PAGESIZE;
|
||||
}
|
||||
if(header.cpuext!=cpuext.x) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Incompatible CPU extensions\n");
|
||||
fclose(f);
|
||||
return DCERR_CPUEXT;
|
||||
}
|
||||
char map_filename[header.filename_length+1];
|
||||
if(fread(map_filename, header.filename_length+1, 1, f)!=1) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot read filename\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
if(!FileExist(map_filename, IS_FILE)) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Mapfiled does not exists\n");
|
||||
fclose(f);
|
||||
return DCERR_MAPNEXIST;
|
||||
}
|
||||
if(FileSize(map_filename)!=header.file_length) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "File changed\n");
|
||||
fclose(f);
|
||||
return DCERR_MAPCHG;
|
||||
}
|
||||
CompressedDynaCacheBlock_t blocks[header.nblocks];
|
||||
if(fread(blocks, sizeof(CompressedDynaCacheBlock_t), header.nblocks, f)!=header.nblocks) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot read blocks\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
uintptr_t lockAddresses[header.nLockAddresses];
|
||||
if(fread(lockAddresses, sizeof(uintptr_t), header.nLockAddresses, f)!=header.nLockAddresses) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot read lockAddresses\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
uintptr_t unalignedAddresses[header.nUnalignedAddresses];
|
||||
if(fread(unalignedAddresses, sizeof(uintptr_t), header.nUnalignedAddresses, f)!=header.nUnalignedAddresses) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Cannot read unalignedAddresses\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
off_t p = ftell(f);
|
||||
p = ALIGN(p);;
|
||||
if(fseek(f, p, SEEK_SET)<0) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Error reading a block\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
if(!mapping) {
|
||||
// check the blocks can be read without reading...
|
||||
for(int i=0; i<header.nblocks; ++i) {
|
||||
p+=(blocks[i].type==COMP_NONE)?blocks[i].block.size:blocks[i].compsize;
|
||||
if(fseek(f, (blocks[i].type==COMP_NONE)?blocks[i].block.size:blocks[i].compsize, SEEK_CUR)<0 || ftell(f)!=p) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Error reading a block\n");
|
||||
fclose(f);
|
||||
return DCERR_FERROR;
|
||||
}
|
||||
}
|
||||
char* short_name = strrchr(map_filename, '/');
|
||||
if(short_name)
|
||||
++short_name;
|
||||
else
|
||||
short_name = map_filename;
|
||||
short_name = LowerCase(short_name);
|
||||
const char* file_name = MmaplistName(short_name, header.dynarec_settings, map_filename);
|
||||
box_free(short_name);
|
||||
if(strcmp(file_name, name)) {
|
||||
if(verbose) printf_log_prefix(0, LOG_NONE, "Invalid cache name\n");
|
||||
fclose(f);
|
||||
return DCERR_BADNAME;
|
||||
}
|
||||
if(verbose) {
|
||||
// check if name is coherent
|
||||
// file is valid, gives informations:
|
||||
printf_log_prefix(0, LOG_NONE, "%s (%s)\n", map_filename, NicePrintSize(filesize));
|
||||
printf_log_prefix(0, LOG_NONE, "\tDynarec Settings:\n");
|
||||
PrintDynfSettings(LOG_NONE, header.dynarec_settings);
|
||||
size_t total_blocks = 0, total_free = 0, total_compressed = 0, total_uncompressed = 0;
|
||||
size_t total_code = header.codesize;
|
||||
for(int i=0; i<header.nblocks; ++i) {
|
||||
total_blocks += blocks[i].block.size;
|
||||
total_free += blocks[i].block.free_size;
|
||||
if(blocks[i].compsize)
|
||||
total_compressed += blocks[i].compsize;
|
||||
else
|
||||
total_uncompressed += blocks[i].block.size;
|
||||
}
|
||||
printf_log_prefix(0, LOG_NONE, "\tHas %d blocks for a total of %s", header.nblocks, NicePrintSize(total_blocks));
|
||||
printf_log_prefix(0, LOG_NONE, " with %s still free", NicePrintSize(total_free));
|
||||
printf_log_prefix(0, LOG_NONE, " and %s non-canceled blocks (mapped at %p-%p, with %zu lock and %zu unaligned addresses)", NicePrintSize(total_code), (void*)header.map_addr, (void*)header.map_addr+header.map_len, header.nLockAddresses, header.nUnalignedAddresses);
|
||||
if(total_compressed) {
|
||||
printf_log_prefix(0, LOG_NONE, " with %s compressed blocks", NicePrintSize(total_compressed));
|
||||
if(total_uncompressed)
|
||||
printf_log_prefix(0, LOG_NONE, " and %s uncompressed block", NicePrintSize(total_uncompressed));
|
||||
}
|
||||
printf_log_prefix(0, LOG_NONE, "\n");
|
||||
}
|
||||
} else {
|
||||
// actually reading!
|
||||
int fd = fileno(f);
|
||||
intptr_t delta_map = mapping->start - header.map_addr;
|
||||
dynarec_log(LOG_INFO, "Trying to load DynaCache for %s, with a delta_map=%zx\n", mapping->fullname, delta_map);
|
||||
if(!mapping->mmaplist)
|
||||
mapping->mmaplist = NewMmaplist();
|
||||
MmaplistAddNBlocks(mapping->mmaplist, header.nblocks);
|
||||
for(int i=0; i<header.nblocks; ++i) {
|
||||
int ret = 0;
|
||||
if(blocks[i].type==COMP_NONE)
|
||||
ret = MmaplistAddBlock(mapping->mmaplist, fd, p, blocks[i].block.block, blocks[i].block.size, delta_map, mapping->start);
|
||||
else {
|
||||
void* src = box_malloc(blocks[i].compsize);
|
||||
fseek(f, p, SEEK_SET);
|
||||
if(!src || (fread(src, blocks[i].compsize, 1, f)!=1)) {
|
||||
ret = -10;
|
||||
} else
|
||||
ret = MmaplistAddCompressedBlock(mapping->mmaplist, blocks[i].type, src, blocks[i].compsize, blocks[i].block.block, blocks[i].block.size, delta_map, mapping->start);
|
||||
box_free(src);
|
||||
}
|
||||
if(ret) {
|
||||
printf_log(LOG_NONE, "Error while doing relocation on a DynaCache (block %d)\n", i);
|
||||
fclose(f);
|
||||
return DCERR_RELOC;
|
||||
}
|
||||
p+=(blocks[i].type==COMP_NONE)?blocks[i].block.size:ALIGN(blocks[i].compsize);
|
||||
}
|
||||
for(size_t i=0; i<header.nLockAddresses; ++i)
|
||||
addLockAddress(lockAddresses[i]+delta_map);
|
||||
for(size_t i=0; i<header.nUnalignedAddresses; ++i)
|
||||
add_unaligned_address(unalignedAddresses[i]+delta_map);
|
||||
dynarec_log(LOG_INFO, "Loaded DynaCache for %s, with %d blocks\n", mapping->fullname, header.nblocks);
|
||||
}
|
||||
fclose(f);
|
||||
return DCERR_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
void DynaCacheList(const char* filter)
|
||||
{
|
||||
#ifndef WIN32
|
||||
const char* folder = GetDynacacheFolder(NULL);
|
||||
if(!folder) {
|
||||
printf_log(LOG_NONE, "DynaCache folder not found\n");
|
||||
return;
|
||||
}
|
||||
DIR* dir = opendir(folder);
|
||||
if(!dir) {
|
||||
printf_log(LOG_NONE, "Cannot open DynaCache folder\n");
|
||||
return;
|
||||
}
|
||||
struct dirent* d = NULL;
|
||||
int need_filter = (filter && strlen(filter));
|
||||
while((d = readdir(dir))) {
|
||||
size_t l = strlen(d->d_name);
|
||||
if(l>6 && !strcmp(d->d_name+l-6, ".box64")) {
|
||||
if(need_filter && !strstr(d->d_name, filter))
|
||||
continue;
|
||||
ReadDynaCache(folder, d->d_name, NULL, 1);
|
||||
printf_log_prefix(0, LOG_NONE, "\n");
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
#endif
|
||||
}
|
||||
void DynaCacheClean()
|
||||
{
|
||||
#ifndef WIN32
|
||||
const char* folder = GetDynacacheFolder(NULL);
|
||||
if(!folder) {
|
||||
printf_log(LOG_NONE, "DynaCache folder not found\n");
|
||||
return;
|
||||
}
|
||||
DIR* dir = opendir(folder);
|
||||
if(!dir) {
|
||||
printf_log(LOG_NONE, "Cannot open DynaCache folder\n");
|
||||
return;
|
||||
}
|
||||
struct dirent* d = NULL;
|
||||
while((d = readdir(dir))) {
|
||||
size_t l = strlen(d->d_name);
|
||||
if(RemoveStaleDynaCacheTempFile(folder, d->d_name))
|
||||
continue;
|
||||
if(l>6 && !strcmp(d->d_name+l-6, ".box64")) {
|
||||
int ret = ReadDynaCache(folder, d->d_name, NULL, 0);
|
||||
if(ret) {
|
||||
char filename[strlen(folder)+strlen(d->d_name)+1];
|
||||
strcpy(filename, folder);
|
||||
strcat(filename, d->d_name);
|
||||
size_t filesize = FileSize(filename);
|
||||
if(!unlink(filename)) {
|
||||
printf_log(LOG_NONE, "Removed %s for %s\n", d->d_name, NicePrintSize(filesize));
|
||||
} else {
|
||||
printf_log(LOG_NONE, "Could not remove %s\n", d->d_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
SyncDynaCacheFolder(folder);
|
||||
#endif
|
||||
}
|
||||
#ifndef WIN32
|
||||
void MmapDynaCache(mapping_t* mapping)
|
||||
{
|
||||
if(!DYNAREC_VERSION)
|
||||
return;
|
||||
// no need to test dynacache enabled or not, it has already been done before this call
|
||||
const char* folder = GetDynacacheFolder(mapping);
|
||||
if(!folder) return;
|
||||
const char* name = GetMmaplistName(mapping);
|
||||
if(!name) return;
|
||||
dynarec_log(LOG_DEBUG, "Looking for DynaCache %s in %s\n", name, folder);
|
||||
ReadDynaCache(folder, name, mapping, 0);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
void SerializeMmaplist(mapping_t* mapping) {}
|
||||
void DynaCacheList(const char* filter) { printf_log(LOG_NONE, "Dynarec not enable\n"); }
|
||||
void DynaCacheClean() {}
|
||||
#endif
|
||||
|
||||
void WillRemoveMapping(uintptr_t addr, size_t length)
|
||||
{
|
||||
#ifdef DYNAREC
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef __ENV_PRIVATE_H__
|
||||
#define __ENV_PRIVATE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "env.h"
|
||||
|
||||
typedef struct mapping_s {
|
||||
char* filename;
|
||||
char* fullname;
|
||||
box64env_t* env;
|
||||
uintptr_t start; //lower address of the map for this file
|
||||
mmaplist_t* mmaplist;
|
||||
} mapping_t;
|
||||
|
||||
char* LowerCase(const char* s);
|
||||
|
||||
#endif // __ENV_PRIVATE_H__
|
||||
@@ -100,6 +100,7 @@ set(WOW64_BOX64CPU_SRC
|
||||
"${BOX64_ROOT}/src/tools/alternate.c"
|
||||
"${BOX64_ROOT}/src/tools/bitutils.c"
|
||||
"${BOX64_ROOT}/src/tools/env.c"
|
||||
"${BOX64_ROOT}/src/tools/dynacache.c"
|
||||
"${BOX64_ROOT}/src/tools/rbtree.c"
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user