[DYNACACHE] Created dynacache.c (#3865)

* [DYNACACHE] Created dynacache.c

This is pure code moving

* Update

* Fixed a typo
This commit is contained in:
Yang Liu authored and GitHub committed 2026-05-15 11:48:51 +02:00
1 parent 5e472e3963
commit 7d3b1dfbfb
12 files changed
+884 -833

No files matched your search

+1
View File
@@ -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
View File
@@ -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
+8
View File
@@ -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": {
+1
View File
@@ -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"
+3 -2
View File
@@ -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);
+34
View File
@@ -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 -1
View File
@@ -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__
-20
View File
@@ -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
+808
View File
@@ -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
View File
@@ -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
+18
View File
@@ -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__
+1
View File
@@ -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"
)