replace the VHD/diskpart disk image pipeline with a directory-based vvfat root, we'll see when it breaks, fix /O2, add an all_tests, generalize the fat32 driver into a fat 16+32 driver, change policy on volume drivers, collapse KCPU's fields into a single atomic KE_CPU_STATE state machine, move scheduler ticking to per-CPU LAPIC timers, gate verbose SMP topology logging behind selftest diagnostics and add SMP pool-checkpoint validation around AP admission, add checked intrusive list helpers behind a new KE_DEBUG_LIST_CHECKS flag, add ObInitializeObjectHeader/ObTryReferenceObject, add waitable display clock objects and a SYS_OPEN_DISPLAY_CLOCK syscall for vsync-style waits, refactor kernel input into a per-queue KE_INPUT_CONTEXT with its own key/mouse ring buffers and DPC-driven key repeat, add KeInputQueuePeekEvent/ReadEvent/WaitKey and a SYS_QUERY_GLOBAL_INPUT_STATE syscall, split the kernel pool allocator out of mm_virt, new video files!
This commit is contained in:
1 parent
485a6bbfe7
commit
8b254f84f4
74 files changed
+4488
-2503
No files matched your search
@@ -274,17 +274,25 @@ static const KeyMap g_keymap[] = {
|
||||
|
||||
static bool g_prev_key[KEYMAP_COUNT];
|
||||
|
||||
// Returns whether one NTLite input key is currently held.
|
||||
static bool ntlite_key_down(INPUT_KEY key)
|
||||
// Returns whether one key bit is set in a global input snapshot.
|
||||
static bool ntlite_snapshot_key_down(const SYS_GLOBAL_INPUT_STATE* state, INPUT_KEY key)
|
||||
{
|
||||
bool down = false;
|
||||
return SysIsKeyDown(key, &down) == NTLSTATUS_SUCCESS && down;
|
||||
if (!state || key >= 256u) return false;
|
||||
return (state->KeyDown[key >> 3] & (uint8_t)(1u << (key & 7u))) != 0;
|
||||
}
|
||||
|
||||
static void poll_input(void)
|
||||
{
|
||||
SYS_GLOBAL_INPUT_STATE input;
|
||||
memset(&input, 0, sizeof(input));
|
||||
input.Size = sizeof(input);
|
||||
input.Flags = SYS_GLOBAL_INPUT_STATE_FLAG_NONE;
|
||||
|
||||
if (SysQueryGlobalInputState(&input) != NTLSTATUS_SUCCESS)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < (int)KEYMAP_COUNT; i++) {
|
||||
bool cur_down = ntlite_key_down(g_keymap[i].inputKey);
|
||||
bool cur_down = ntlite_snapshot_key_down(&input, g_keymap[i].inputKey);
|
||||
bool prev_down = g_prev_key[i];
|
||||
|
||||
if (cur_down && !prev_down) doom_key_down(g_keymap[i].key);
|
||||
@@ -304,6 +312,7 @@ static void poll_input(void)
|
||||
#define DISP_H (DOOM_H * SCALE)
|
||||
|
||||
static uint32_t g_surface_handle;
|
||||
static uint32_t g_display_clock_handle;
|
||||
static uint32_t* g_surface_pixels;
|
||||
static uint32_t g_surface_pitch_pixels;
|
||||
|
||||
@@ -335,6 +344,22 @@ static void init_surface(void)
|
||||
g_surface_pitch_pixels = pitchBytes / sizeof(uint32_t);
|
||||
}
|
||||
|
||||
static void init_display_clock(void)
|
||||
{
|
||||
SYS_OPEN_DISPLAY_CLOCK_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Size = sizeof(req);
|
||||
req.Flags = SYS_DISPLAY_CLOCK_FLAG_NONE;
|
||||
req.PeriodMilliseconds = 1;
|
||||
req.HandleOut = &g_display_clock_handle;
|
||||
|
||||
uint32_t s = SysOpenDisplayClock(&req);
|
||||
if (s != NTLSTATUS_SUCCESS) {
|
||||
g_display_clock_handle = 0;
|
||||
doom_print_wrapper("doom: display clock unavailable, using sleep pacing\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void render_frame(const uint8_t* rgba)
|
||||
{
|
||||
const uint32_t* src = (const uint32_t*)rgba;
|
||||
@@ -409,6 +434,30 @@ static int build_argv_from_command_line(const char* cmdline, char* storage, size
|
||||
return argc;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timing
|
||||
// ---------------------------------------------------------------------------
|
||||
static int doom_now_tic(void)
|
||||
{
|
||||
return I_GetTime();
|
||||
}
|
||||
|
||||
// Waits until Doom has a new simulation tic to render.
|
||||
static int wait_for_next_doom_tic(int lastTic)
|
||||
{
|
||||
for (;;) {
|
||||
int now = doom_now_tic();
|
||||
if (now != lastTic)
|
||||
return now;
|
||||
|
||||
if (g_display_clock_handle) {
|
||||
(void)SysWaitSingle(g_display_clock_handle, SYS_WAIT_FLAG_NONE, SYS_WAIT_TIMEOUT_INFINITE);
|
||||
} else {
|
||||
SysSleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// main
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -447,12 +496,18 @@ int main(void)
|
||||
const char* argv[32];
|
||||
int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32);
|
||||
init_surface();
|
||||
init_display_clock();
|
||||
doom_init(argc, argv, 0);
|
||||
|
||||
do { SYS_WRITE_CONSOLE_REQUEST req = { SYS_SV_LITERAL("doom: entering game loop\n") }; SysWriteConsole(&req); } while (0);
|
||||
|
||||
// Core execution loop
|
||||
// Core execution loop. Doom advances at TICRATE, so rendering duplicate
|
||||
// frames as fast as possible only amplifies scheduler and input jitter.
|
||||
int lastFrameTic = doom_now_tic();
|
||||
while (1) {
|
||||
int frameTic = wait_for_next_doom_tic(lastFrameTic);
|
||||
lastFrameTic = frameTic;
|
||||
|
||||
poll_input();
|
||||
doom_update();
|
||||
render_frame((const uint8_t*)doom_get_framebuffer(4 /* RGBA */));
|
||||
|
||||
Binary file not shown.
@@ -1416,36 +1416,158 @@ static void cmd_write(const char* args) {
|
||||
print("File written successfully.\n");
|
||||
}
|
||||
|
||||
#define SHELL_DIR_INITIAL_CAPACITY 64u
|
||||
|
||||
// Returns an ASCII lower-case value for case-insensitive directory sorting.
|
||||
static char shellAsciiLower(char c) {
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return (char)(c - 'A' + 'a');
|
||||
return c;
|
||||
}
|
||||
|
||||
// Compares two names using case-insensitive order with a stable case tie-breaker.
|
||||
static int shellCompareNames(const char* a, const char* b) {
|
||||
size_t i = 0;
|
||||
if (!a) a = "";
|
||||
if (!b) b = "";
|
||||
|
||||
for (;;) {
|
||||
char ca = shellAsciiLower(a[i]);
|
||||
char cb = shellAsciiLower(b[i]);
|
||||
if (ca != cb)
|
||||
return (ca < cb) ? -1 : 1;
|
||||
if (ca == '\0')
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
// Compares two directory entries by display name.
|
||||
static int shellCompareDirEntries(const SYS_DIR_ENTRY* a, const SYS_DIR_ENTRY* b) {
|
||||
return shellCompareNames(a->Name, b->Name);
|
||||
}
|
||||
|
||||
// Allocates a directory-entry array from VM so large listings do not consume stack.
|
||||
static uint32_t shellAllocDirEntries(uint32_t capacity, SYS_DIR_ENTRY** entriesOut) {
|
||||
void* base = 0;
|
||||
size_t bytes = (size_t)capacity * sizeof(SYS_DIR_ENTRY);
|
||||
size_t pages = (bytes + 4095u) / 4096u;
|
||||
if (pages == 0) pages = 1;
|
||||
|
||||
SYS_ALLOC_VM_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Size = sizeof(req);
|
||||
req.Protect = SYS_VM_READWRITE;
|
||||
req.Flags = SYS_VM_FLAG_NONE;
|
||||
req.PageCount = pages;
|
||||
req.BaseOut = &base;
|
||||
|
||||
uint32_t status = SysAllocVm(&req);
|
||||
if (status != 0 || !base) {
|
||||
*entriesOut = 0;
|
||||
return status ? status : 1;
|
||||
}
|
||||
|
||||
*entriesOut = (SYS_DIR_ENTRY*)base;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Grows a directory-entry array while preserving already-read entries.
|
||||
static uint32_t shellGrowDirEntries(SYS_DIR_ENTRY** entries, uint32_t* capacity, uint32_t count) {
|
||||
uint32_t oldCapacity = *capacity;
|
||||
uint32_t newCapacity = oldCapacity ? oldCapacity * 2u : SHELL_DIR_INITIAL_CAPACITY;
|
||||
SYS_DIR_ENTRY* next = 0;
|
||||
uint32_t status = shellAllocDirEntries(newCapacity, &next);
|
||||
if (status != 0)
|
||||
return status;
|
||||
|
||||
if (*entries && count)
|
||||
memcpy(next, *entries, (size_t)count * sizeof(SYS_DIR_ENTRY));
|
||||
if (*entries)
|
||||
SysFreeVm(*entries);
|
||||
|
||||
*entries = next;
|
||||
*capacity = newCapacity;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sorts directory entries alphabetically.
|
||||
static void shellSortDirEntries(SYS_DIR_ENTRY* entries, uint32_t count) {
|
||||
for (uint32_t i = 1; i < count; i++) {
|
||||
SYS_DIR_ENTRY current = entries[i];
|
||||
uint32_t j = i;
|
||||
while (j > 0 && shellCompareDirEntries(¤t, &entries[j - 1]) < 0) {
|
||||
entries[j] = entries[j - 1];
|
||||
j--;
|
||||
}
|
||||
entries[j] = current;
|
||||
}
|
||||
}
|
||||
|
||||
// Prints one directory entry in cmd dir format.
|
||||
static void shellPrintDirEntry(const SYS_DIR_ENTRY* ke) {
|
||||
printDirTime(&ke->LastWriteTime);
|
||||
|
||||
if (ke->Attr & 0x10) {
|
||||
print("<DIR> ");
|
||||
print(ke->Name);
|
||||
} else {
|
||||
print(" ");
|
||||
print(ke->Name);
|
||||
print(" (");
|
||||
printUInt64(ke->FileSize);
|
||||
print(" bytes)");
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
|
||||
// Implements the cmd dir operation.
|
||||
static void cmd_dir(const char* path) {
|
||||
uint32_t hDir;
|
||||
const char* target = (path && *path != '\0') ? path : ".";
|
||||
|
||||
|
||||
uint32_t st = open_dir(target, &hDir);
|
||||
if (st != 0) {
|
||||
if (st != 0) {
|
||||
print("dir: failed to open directory, status=0x");
|
||||
printHex32(st);
|
||||
print("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SYS_DIR_ENTRY ke;
|
||||
while (SysReadDir(hDir, &ke) == 0) {
|
||||
printDirTime(&ke.LastWriteTime);
|
||||
|
||||
if (ke.Attr & 0x10) {
|
||||
print("<DIR> ");
|
||||
print(ke.Name);
|
||||
} else {
|
||||
print(" ");
|
||||
print(ke.Name);
|
||||
print(" (");
|
||||
printUInt64(ke.FileSize);
|
||||
print(" bytes)");
|
||||
}
|
||||
print("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SYS_DIR_ENTRY* entries = 0;
|
||||
uint32_t count = 0;
|
||||
uint32_t capacity = 0;
|
||||
|
||||
for (;;) {
|
||||
SYS_DIR_ENTRY ke;
|
||||
st = SysReadDir(hDir, &ke);
|
||||
if (st != 0)
|
||||
break;
|
||||
|
||||
if (count == capacity) {
|
||||
st = shellGrowDirEntries(&entries, &capacity, count);
|
||||
if (st != 0) {
|
||||
print("dir: out of memory, status=0x");
|
||||
printHex32(st);
|
||||
print("\n");
|
||||
SysCloseHandle(hDir);
|
||||
if (entries) SysFreeVm(entries);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
entries[count++] = ke;
|
||||
}
|
||||
|
||||
SysCloseHandle(hDir);
|
||||
shellSortDirEntries(entries, count);
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
shellPrintDirEntry(&entries[i]);
|
||||
|
||||
if (entries)
|
||||
SysFreeVm(entries);
|
||||
}
|
||||
|
||||
// Implements the cmd type operation.
|
||||
|
||||
@@ -23,6 +23,9 @@ extern int closedir(DIR* d);
|
||||
extern int unlink(const char *pathname);
|
||||
extern int posix_spawn(int *pid, const char *path, const void *act, const void *attr, char *const argv[], char *const envp[]);
|
||||
extern int waitpid(int pid, int *status, int options);
|
||||
extern void* malloc(size_t size);
|
||||
extern void* realloc(void* ptr, size_t size);
|
||||
extern void free(void* ptr);
|
||||
extern void exit(int status);
|
||||
extern void clearscreen(void);
|
||||
extern void clearkeyboard(void);
|
||||
@@ -121,17 +124,89 @@ static void cmd_write(const char* args) {
|
||||
print("File written successfully.\n");
|
||||
}
|
||||
|
||||
typedef struct SHELL_POSIX_DIR_ENTRY {
|
||||
char Name[NAME_MAX + 1];
|
||||
unsigned char Type;
|
||||
} SHELL_POSIX_DIR_ENTRY;
|
||||
|
||||
// Returns an ASCII lower-case value for case-insensitive directory sorting.
|
||||
static char shellAsciiLower(char c) {
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return (char)(c - 'A' + 'a');
|
||||
return c;
|
||||
}
|
||||
|
||||
// Compares two names using case-insensitive order with a stable case tie-breaker.
|
||||
static int shellCompareNames(const char* a, const char* b) {
|
||||
size_t i = 0;
|
||||
if (!a) a = "";
|
||||
if (!b) b = "";
|
||||
|
||||
for (;;) {
|
||||
char ca = shellAsciiLower(a[i]);
|
||||
char cb = shellAsciiLower(b[i]);
|
||||
if (ca != cb)
|
||||
return (ca < cb) ? -1 : 1;
|
||||
if (ca == '\0')
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
// Sorts directory entries alphabetically.
|
||||
static void shellSortDirEntries(SHELL_POSIX_DIR_ENTRY* entries, size_t count) {
|
||||
for (size_t i = 1; i < count; i++) {
|
||||
SHELL_POSIX_DIR_ENTRY current = entries[i];
|
||||
size_t j = i;
|
||||
while (j > 0 && shellCompareNames(current.Name, entries[j - 1].Name) < 0) {
|
||||
entries[j] = entries[j - 1];
|
||||
j--;
|
||||
}
|
||||
entries[j] = current;
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the cmd dir operation.
|
||||
static void cmd_dir(const char* path) {
|
||||
const char* target = (path && *path != '\0') ? path : ".";
|
||||
DIR* d = opendir(target);
|
||||
if (!d) { print("dir: cannot access directory\n"); return; }
|
||||
|
||||
SHELL_POSIX_DIR_ENTRY* entries = NULL;
|
||||
size_t count = 0;
|
||||
size_t capacity = 0;
|
||||
|
||||
dirent* entry;
|
||||
while ((entry = readdir(d)) != NULL) {
|
||||
print(entry->d_type == 1 ? "<DIR> " : " ");
|
||||
print(entry->d_name); print("\n");
|
||||
if (count == capacity) {
|
||||
size_t nextCapacity = capacity ? capacity * 2u : 64u;
|
||||
SHELL_POSIX_DIR_ENTRY* next = (SHELL_POSIX_DIR_ENTRY*)realloc(entries, nextCapacity * sizeof(SHELL_POSIX_DIR_ENTRY));
|
||||
if (!next) {
|
||||
print("dir: out of memory\n");
|
||||
free(entries);
|
||||
closedir(d);
|
||||
return;
|
||||
}
|
||||
entries = next;
|
||||
capacity = nextCapacity;
|
||||
}
|
||||
|
||||
strncpy(entries[count].Name, entry->d_name, NAME_MAX);
|
||||
entries[count].Name[NAME_MAX] = '\0';
|
||||
entries[count].Type = entry->d_type;
|
||||
count++;
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
shellSortDirEntries(entries, count);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
print(entries[i].Type == 1 ? "<DIR> " : " ");
|
||||
print(entries[i].Name);
|
||||
print("\n");
|
||||
}
|
||||
free(entries);
|
||||
}
|
||||
|
||||
// Implements the cmd type operation.
|
||||
|
||||
@@ -94,12 +94,56 @@ static void cmd_write(const char* args) {
|
||||
print("File written successfully.\n");
|
||||
}
|
||||
|
||||
typedef struct SHELL_WIN32_DIR_ENTRY {
|
||||
char Name[MAX_PATH];
|
||||
DWORD Attributes;
|
||||
} SHELL_WIN32_DIR_ENTRY;
|
||||
|
||||
// Returns an ASCII lower-case value for case-insensitive directory sorting.
|
||||
static char shellAsciiLower(char c) {
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return (char)(c - 'A' + 'a');
|
||||
return c;
|
||||
}
|
||||
|
||||
// Compares two names using case-insensitive order with a stable case tie-breaker.
|
||||
static int shellCompareNames(const char* a, const char* b) {
|
||||
size_t i = 0;
|
||||
if (!a) a = "";
|
||||
if (!b) b = "";
|
||||
|
||||
for (;;) {
|
||||
char ca = shellAsciiLower(a[i]);
|
||||
char cb = shellAsciiLower(b[i]);
|
||||
if (ca != cb)
|
||||
return (ca < cb) ? -1 : 1;
|
||||
if (ca == '\0')
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
// Sorts directory entries alphabetically.
|
||||
static void shellSortDirEntries(SHELL_WIN32_DIR_ENTRY* entries, size_t count) {
|
||||
for (size_t i = 1; i < count; i++) {
|
||||
SHELL_WIN32_DIR_ENTRY current = entries[i];
|
||||
size_t j = i;
|
||||
while (j > 0 && shellCompareNames(current.Name, entries[j - 1].Name) < 0) {
|
||||
entries[j] = entries[j - 1];
|
||||
j--;
|
||||
}
|
||||
entries[j] = current;
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the cmd dir operation.
|
||||
static void cmd_dir(const char* path) {
|
||||
WIN32_FIND_DATAA fd;
|
||||
char search_path[MAX_PATH];
|
||||
const char* target = (path && *path != '\0') ? path : ".";
|
||||
|
||||
|
||||
size_t len = strlen(target);
|
||||
if (len + 3 > MAX_PATH) { print("dir: path too long\n"); return; }
|
||||
strcpy(search_path, target);
|
||||
@@ -107,11 +151,46 @@ static void cmd_dir(const char* path) {
|
||||
|
||||
HANDLE hFind = FindFirstFileA(search_path, &fd);
|
||||
if (hFind == INVALID_HANDLE_VALUE) { print("dir: failed to read directory\n"); return; }
|
||||
|
||||
void* heap = GetProcessHeap();
|
||||
SHELL_WIN32_DIR_ENTRY* entries = NULL;
|
||||
size_t count = 0;
|
||||
size_t capacity = 0;
|
||||
|
||||
do {
|
||||
print(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? "<DIR> " : " ");
|
||||
print(fd.cFileName); print("\n");
|
||||
if (count == capacity) {
|
||||
size_t nextCapacity = capacity ? capacity * 2u : 64u;
|
||||
size_t bytes = nextCapacity * sizeof(SHELL_WIN32_DIR_ENTRY);
|
||||
SHELL_WIN32_DIR_ENTRY* next;
|
||||
if (entries)
|
||||
next = (SHELL_WIN32_DIR_ENTRY*)HeapReAlloc(heap, 0, entries, bytes);
|
||||
else
|
||||
next = (SHELL_WIN32_DIR_ENTRY*)HeapAlloc(heap, 0, bytes);
|
||||
if (!next) {
|
||||
print("dir: out of memory\n");
|
||||
if (entries) HeapFree(heap, 0, entries);
|
||||
FindClose(hFind);
|
||||
return;
|
||||
}
|
||||
entries = next;
|
||||
capacity = nextCapacity;
|
||||
}
|
||||
|
||||
strncpy(entries[count].Name, fd.cFileName, MAX_PATH - 1);
|
||||
entries[count].Name[MAX_PATH - 1] = '\0';
|
||||
entries[count].Attributes = fd.dwFileAttributes;
|
||||
count++;
|
||||
} while (FindNextFileA(hFind, &fd));
|
||||
FindClose(hFind);
|
||||
|
||||
shellSortDirEntries(entries, count);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
print(entries[i].Attributes & FILE_ATTRIBUTE_DIRECTORY ? "<DIR> " : " ");
|
||||
print(entries[i].Name);
|
||||
print("\n");
|
||||
}
|
||||
if (entries)
|
||||
HeapFree(heap, 0, entries);
|
||||
}
|
||||
|
||||
// Implements the cmd type operation.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -11,7 +11,9 @@
|
||||
#include <ntlite/syscall.h>
|
||||
#include "bink.h"
|
||||
|
||||
#define VIDEO_DEFAULT_PATH "video.bk2"
|
||||
#define VIDEO_USAGE "usage: video <file.bk2>\n" \
|
||||
" video --help\n" \
|
||||
"Plays a Bink video file from disk.\n"
|
||||
|
||||
static void print_span(const char* s, size_t len) {
|
||||
if (!s || !len) return;
|
||||
@@ -88,10 +90,10 @@ static SYS_STRING_VIEW skip_one_token(SYS_STRING_VIEW s) {
|
||||
return SysSvFromParts(s.Data + i, s.Length - i);
|
||||
}
|
||||
|
||||
static SYS_STRING_VIEW command_line_second_token(SYS_STRING_VIEW cmdline) {
|
||||
static SYS_STRING_VIEW command_line_first_argument(SYS_STRING_VIEW cmdline) {
|
||||
SYS_STRING_VIEW rest = skip_one_token(cmdline);
|
||||
if (!rest.Data || rest.Length == 0)
|
||||
return SYS_SV_LITERAL(VIDEO_DEFAULT_PATH);
|
||||
return SYS_SV_NULL;
|
||||
|
||||
size_t begin = 0;
|
||||
size_t end = 0;
|
||||
@@ -108,17 +110,43 @@ static SYS_STRING_VIEW command_line_second_token(SYS_STRING_VIEW cmdline) {
|
||||
}
|
||||
|
||||
if (end <= begin)
|
||||
return SYS_SV_LITERAL(VIDEO_DEFAULT_PATH);
|
||||
return SYS_SV_NULL;
|
||||
|
||||
return SysSvFromParts(rest.Data + begin, end - begin);
|
||||
}
|
||||
|
||||
static int sv_equals_cstr(SYS_STRING_VIEW s, const char* cstr) {
|
||||
size_t len = 0;
|
||||
if (!s.Data || !cstr)
|
||||
return 0;
|
||||
|
||||
while (cstr[len])
|
||||
len++;
|
||||
|
||||
if (s.Length != len)
|
||||
return 0;
|
||||
|
||||
return memcmp(s.Data, cstr, len) == 0;
|
||||
}
|
||||
|
||||
static int video_argument_is_help(SYS_STRING_VIEW arg) {
|
||||
return sv_equals_cstr(arg, "-h") ||
|
||||
sv_equals_cstr(arg, "--help") ||
|
||||
sv_equals_cstr(arg, "/?");
|
||||
}
|
||||
|
||||
static void print_usage(void) {
|
||||
print(VIDEO_USAGE);
|
||||
}
|
||||
|
||||
static const char* path_to_cstr(SYS_STRING_VIEW path, char* out, size_t outSize) {
|
||||
if (!out || outSize == 0)
|
||||
return VIDEO_DEFAULT_PATH;
|
||||
return "";
|
||||
|
||||
if (!path.Data || path.Length == 0)
|
||||
path = SYS_SV_LITERAL(VIDEO_DEFAULT_PATH);
|
||||
if (!path.Data || path.Length == 0) {
|
||||
out[0] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t n = path.Length;
|
||||
if (n + 1 > outSize)
|
||||
@@ -140,7 +168,12 @@ static uint32_t open_file_view(SYS_STRING_VIEW path, uint32_t* handleOut) {
|
||||
|
||||
int main(void) {
|
||||
SYS_STRING_VIEW cmdline = command_line_view();
|
||||
SYS_STRING_VIEW pathView = command_line_second_token(cmdline);
|
||||
SYS_STRING_VIEW pathView = command_line_first_argument(cmdline);
|
||||
|
||||
if (!pathView.Data || pathView.Length == 0 || video_argument_is_help(pathView)) {
|
||||
print_usage();
|
||||
exit_now(0);
|
||||
}
|
||||
|
||||
print("video: starting\n");
|
||||
if (cmdline.Data && cmdline.Length) {
|
||||
|
||||
@@ -85,61 +85,63 @@ SYS_SET_CWD EQU 72
|
||||
SYS_REAL_PATH EQU 73
|
||||
|
||||
; Console, display, and surfaces.
|
||||
SYS_WRITE_CONSOLE EQU 74
|
||||
SYS_GET_CONSOLE_INFO EQU 75
|
||||
SYS_SET_CONSOLE_CURSOR EQU 76
|
||||
SYS_FILL_CONSOLE_CHARACTER EQU 77
|
||||
SYS_FILL_CONSOLE_ATTRIBUTE EQU 78
|
||||
SYS_PUT_SCREEN_CHAR EQU 79
|
||||
SYS_CLEAR_SCREEN EQU 80
|
||||
SYS_GET_SCREEN_SIZE EQU 81
|
||||
SYS_QUERY_DISPLAY_INFO EQU 82
|
||||
SYS_CREATE_SURFACE EQU 83
|
||||
SYS_MAP_SURFACE EQU 84
|
||||
SYS_QUERY_SURFACE EQU 85
|
||||
SYS_PRESENT_SURFACE EQU 86
|
||||
SYS_WRITE_CONSOLE EQU 74
|
||||
SYS_GET_CONSOLE_INFO EQU 75
|
||||
SYS_SET_CONSOLE_CURSOR EQU 76
|
||||
SYS_FILL_CONSOLE_CHARACTER EQU 77
|
||||
SYS_FILL_CONSOLE_ATTRIBUTE EQU 78
|
||||
SYS_PUT_SCREEN_CHAR EQU 79
|
||||
SYS_CLEAR_SCREEN EQU 80
|
||||
SYS_GET_SCREEN_SIZE EQU 81
|
||||
SYS_QUERY_DISPLAY_INFO EQU 82
|
||||
SYS_OPEN_DISPLAY_CLOCK EQU 83
|
||||
SYS_CREATE_SURFACE EQU 84
|
||||
SYS_MAP_SURFACE EQU 85
|
||||
SYS_QUERY_SURFACE EQU 86
|
||||
SYS_PRESENT_SURFACE EQU 87
|
||||
|
||||
; Input.
|
||||
SYS_POLL_KEYBOARD EQU 87
|
||||
SYS_IS_KEY_DOWN EQU 88
|
||||
SYS_READ_KEYBOARD_CHAR EQU 89
|
||||
SYS_READ_KEY EQU 90
|
||||
SYS_GET_KEY_MODIFIERS EQU 91
|
||||
SYS_OPEN_INPUT_QUEUE EQU 92
|
||||
SYS_READ_INPUT_EVENT EQU 93
|
||||
SYS_PEEK_INPUT_EVENT EQU 94
|
||||
SYS_PEEK_MOUSE EQU 95
|
||||
SYS_READ_MOUSE EQU 96
|
||||
SYS_GET_MOUSE_POS EQU 97
|
||||
SYS_POLL_KEYBOARD EQU 88
|
||||
SYS_IS_KEY_DOWN EQU 89
|
||||
SYS_READ_KEYBOARD_CHAR EQU 90
|
||||
SYS_READ_KEY EQU 91
|
||||
SYS_GET_KEY_MODIFIERS EQU 92
|
||||
SYS_QUERY_GLOBAL_INPUT_STATE EQU 93
|
||||
SYS_OPEN_INPUT_QUEUE EQU 94
|
||||
SYS_READ_INPUT_EVENT EQU 95
|
||||
SYS_PEEK_INPUT_EVENT EQU 96
|
||||
SYS_PEEK_MOUSE EQU 97
|
||||
SYS_READ_MOUSE EQU 98
|
||||
SYS_GET_MOUSE_POS EQU 99
|
||||
|
||||
; Time, debug, and system information.
|
||||
SYS_CREATE_TIMER EQU 98
|
||||
SYS_SET_TIMER EQU 99
|
||||
SYS_CANCEL_TIMER EQU 100
|
||||
SYS_GET_TICK EQU 101
|
||||
SYS_GET_TICK_FREQUENCY EQU 102
|
||||
SYS_GET_SYSTEM_TIME EQU 103
|
||||
SYS_PRINT_DEBUG EQU 104
|
||||
SYS_QUERY_TRAP_FRAME EQU 105
|
||||
SYS_GET_MEMORY_STATUS EQU 106
|
||||
SYS_QUERY_KERNEL_STATS EQU 107
|
||||
SYS_CREATE_TIMER EQU 100
|
||||
SYS_SET_TIMER EQU 101
|
||||
SYS_CANCEL_TIMER EQU 102
|
||||
SYS_GET_TICK EQU 103
|
||||
SYS_GET_TICK_FREQUENCY EQU 104
|
||||
SYS_GET_SYSTEM_TIME EQU 105
|
||||
SYS_PRINT_DEBUG EQU 106
|
||||
SYS_QUERY_TRAP_FRAME EQU 107
|
||||
SYS_GET_MEMORY_STATUS EQU 108
|
||||
SYS_QUERY_KERNEL_STATS EQU 109
|
||||
|
||||
; Native sockets and polling.
|
||||
SYS_CREATE_SOCKET EQU 108
|
||||
SYS_SOCKET_IO_ASYNC EQU 109
|
||||
SYS_BIND_SOCKET EQU 110
|
||||
SYS_LISTEN_SOCKET EQU 111
|
||||
SYS_ACCEPT_SOCKET EQU 112
|
||||
SYS_CONNECT_SOCKET EQU 113
|
||||
SYS_SEND_SOCKET EQU 114
|
||||
SYS_RECV_SOCKET EQU 115
|
||||
SYS_SHUTDOWN_SOCKET EQU 116
|
||||
SYS_SEND_TO_SOCKET EQU 117
|
||||
SYS_RECV_FROM_SOCKET EQU 118
|
||||
SYS_RESOLVE_HOST EQU 119
|
||||
SYS_SET_SOCKET_OPTION EQU 120
|
||||
SYS_GET_SOCKET_OPTION EQU 121
|
||||
SYS_POLL EQU 122
|
||||
SYS_UNPUBLISH_QUEUE EQU 123
|
||||
SYS_CREATE_SOCKET EQU 110
|
||||
SYS_SOCKET_IO_ASYNC EQU 111
|
||||
SYS_BIND_SOCKET EQU 112
|
||||
SYS_LISTEN_SOCKET EQU 113
|
||||
SYS_ACCEPT_SOCKET EQU 114
|
||||
SYS_CONNECT_SOCKET EQU 115
|
||||
SYS_SEND_SOCKET EQU 116
|
||||
SYS_RECV_SOCKET EQU 117
|
||||
SYS_SHUTDOWN_SOCKET EQU 118
|
||||
SYS_SEND_TO_SOCKET EQU 119
|
||||
SYS_RECV_FROM_SOCKET EQU 120
|
||||
SYS_RESOLVE_HOST EQU 121
|
||||
SYS_SET_SOCKET_OPTION EQU 122
|
||||
SYS_GET_SOCKET_OPTION EQU 123
|
||||
SYS_POLL EQU 124
|
||||
SYS_UNPUBLISH_QUEUE EQU 125
|
||||
|
||||
SYS_MAX EQU 124
|
||||
SYS_MAX EQU 126
|
||||
@@ -218,6 +218,7 @@ SYSCALL_STUB2 PresentSurface, SYS_PRESENT_SURFACE
|
||||
SYSCALL_STUB2 ClearScreen, SYS_CLEAR_SCREEN
|
||||
SYSCALL_STUB2 GetScreenSize, SYS_GET_SCREEN_SIZE
|
||||
SYSCALL_STUB2 QueryDisplayInfo, SYS_QUERY_DISPLAY_INFO
|
||||
SYSCALL_STUB2 OpenDisplayClock, SYS_OPEN_DISPLAY_CLOCK
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Input
|
||||
@@ -227,6 +228,7 @@ SYSCALL_STUB2 IsKeyDown, SYS_IS_KEY_DOWN
|
||||
SYSCALL_STUB2 ReadKeyboardChar, SYS_READ_KEYBOARD_CHAR
|
||||
SYSCALL_STUB2 ReadKey, SYS_READ_KEY
|
||||
SYSCALL_STUB2 GetKeyModifiers, SYS_GET_KEY_MODIFIERS
|
||||
SYSCALL_STUB2 QueryGlobalInputState, SYS_QUERY_GLOBAL_INPUT_STATE
|
||||
SYSCALL_STUB2 ReadInputEvent, SYS_READ_INPUT_EVENT
|
||||
SYSCALL_STUB2 PeekInputEvent, SYS_PEEK_INPUT_EVENT
|
||||
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
// Dynamic test runner for every executable in the runner directory.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define ALL_TESTS_INITIAL_CAPACITY 32u
|
||||
|
||||
typedef struct TEST_ENTRY {
|
||||
char Name[SYS_DIR_NAME_MAX];
|
||||
} TEST_ENTRY;
|
||||
|
||||
// Writes one NUL-terminated string to the console.
|
||||
static void print(const char* s) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
// Writes one unsigned integer to the console.
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int i = 0;
|
||||
if (v == 0) {
|
||||
print("0");
|
||||
return;
|
||||
}
|
||||
while (v && i < (int)sizeof(buf)) {
|
||||
buf[i++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (i--) {
|
||||
char c[2] = { buf[i], 0 };
|
||||
print(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Writes one signed integer to the console.
|
||||
static void print_i32(int32_t v) {
|
||||
if (v < 0) {
|
||||
print("-");
|
||||
print_u64((uint32_t)(-(v + 1)) + 1u);
|
||||
return;
|
||||
}
|
||||
print_u64((uint32_t)v);
|
||||
}
|
||||
|
||||
// Writes one 32-bit value as hexadecimal.
|
||||
static void print_hex32(uint32_t v) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
char c[2] = { hex[(v >> (i * 4)) & 0xfu], 0 };
|
||||
print(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Exits the process without returning to the runtime.
|
||||
static void exit_now(int code) {
|
||||
for (;;) SysExitProcess(code);
|
||||
}
|
||||
|
||||
// Converts ASCII to lowercase for stable case-insensitive comparisons.
|
||||
static char ascii_lower(char c) {
|
||||
if (c >= 'A' && c <= 'Z') return (char)(c - 'A' + 'a');
|
||||
return c;
|
||||
}
|
||||
|
||||
// Compares two names case-insensitively, with a case-sensitive tie-breaker.
|
||||
static int compare_names(const char* a, const char* b) {
|
||||
size_t i = 0;
|
||||
for (;;) {
|
||||
char ca = ascii_lower(a[i]);
|
||||
char cb = ascii_lower(b[i]);
|
||||
if (ca != cb) return (unsigned char)ca < (unsigned char)cb ? -1 : 1;
|
||||
if (ca == '\0') break;
|
||||
i++;
|
||||
}
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
// Returns whether one character is a path separator.
|
||||
static bool is_separator(char c) {
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
|
||||
// Copies a SYS_STRING_VIEW into a NUL-terminated fixed buffer.
|
||||
static bool copy_view(char* out, size_t outSize, SYS_STRING_VIEW view) {
|
||||
if (!out || outSize == 0 || !view.Data || view.Length + 1 > outSize) return false;
|
||||
memcpy(out, view.Data, view.Length);
|
||||
out[view.Length] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
// Splits an executable path into its directory and basename components.
|
||||
static bool split_self_path(const char* path, char* dir, size_t dirSize, char* name, size_t nameSize) {
|
||||
if (!path || !dir || !name || dirSize == 0 || nameSize == 0) return false;
|
||||
|
||||
size_t len = strlen(path);
|
||||
size_t last = (size_t)-1;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (is_separator(path[i])) last = i;
|
||||
}
|
||||
|
||||
const char* base = path;
|
||||
size_t dirLen = 1;
|
||||
if (last == (size_t)-1) {
|
||||
if (dirSize < 2) return false;
|
||||
dir[0] = '.';
|
||||
dir[1] = '\0';
|
||||
} else {
|
||||
dirLen = last;
|
||||
if (last == 0) dirLen = 1;
|
||||
if (last >= 2 && path[1] == ':' && is_separator(path[2])) dirLen = last + 1;
|
||||
if (dirLen + 1 > dirSize) return false;
|
||||
memcpy(dir, path, dirLen);
|
||||
dir[dirLen] = '\0';
|
||||
base = path + last + 1;
|
||||
}
|
||||
|
||||
size_t baseLen = strlen(base);
|
||||
if (baseLen + 1 > nameSize) return false;
|
||||
memcpy(name, base, baseLen + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns whether one filename ends in .exe, case-insensitively.
|
||||
static bool has_exe_suffix(const char* name) {
|
||||
size_t len = strlen(name);
|
||||
if (len < 4) return false;
|
||||
const char* ext = name + len - 4;
|
||||
return ascii_lower(ext[0]) == '.' &&
|
||||
ascii_lower(ext[1]) == 'e' &&
|
||||
ascii_lower(ext[2]) == 'x' &&
|
||||
ascii_lower(ext[3]) == 'e';
|
||||
}
|
||||
|
||||
// Allocates a test-entry array from user VM.
|
||||
static uint32_t alloc_entries(uint32_t capacity, TEST_ENTRY** entriesOut) {
|
||||
void* base = 0;
|
||||
size_t bytes = (size_t)capacity * sizeof(TEST_ENTRY);
|
||||
size_t pages = (bytes + 4095u) / 4096u;
|
||||
if (pages == 0) pages = 1;
|
||||
|
||||
SYS_ALLOC_VM_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Size = sizeof(req);
|
||||
req.Protect = SYS_VM_READWRITE;
|
||||
req.Flags = SYS_VM_FLAG_NONE;
|
||||
req.PageCount = pages;
|
||||
req.BaseOut = &base;
|
||||
|
||||
uint32_t status = SysAllocVm(&req);
|
||||
if (status != NTLSTATUS_SUCCESS || !base) {
|
||||
*entriesOut = 0;
|
||||
return status ? status : NTLSTATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
*entriesOut = (TEST_ENTRY*)base;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Grows the test-entry array.
|
||||
static uint32_t grow_entries(TEST_ENTRY** entries, uint32_t* capacity, uint32_t count) {
|
||||
uint32_t oldCapacity = *capacity;
|
||||
uint32_t newCapacity = oldCapacity ? oldCapacity * 2u : ALL_TESTS_INITIAL_CAPACITY;
|
||||
TEST_ENTRY* next = 0;
|
||||
uint32_t status = alloc_entries(newCapacity, &next);
|
||||
if (status != NTLSTATUS_SUCCESS) return status;
|
||||
|
||||
if (*entries && count) memcpy(next, *entries, (size_t)count * sizeof(TEST_ENTRY));
|
||||
if (*entries) SysFreeVm(*entries);
|
||||
*entries = next;
|
||||
*capacity = newCapacity;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Sorts test entries in deterministic execution order.
|
||||
static void sort_entries(TEST_ENTRY* entries, uint32_t count) {
|
||||
for (uint32_t i = 1; i < count; i++) {
|
||||
TEST_ENTRY current = entries[i];
|
||||
uint32_t j = i;
|
||||
while (j > 0 && compare_names(current.Name, entries[j - 1].Name) < 0) {
|
||||
entries[j] = entries[j - 1];
|
||||
j--;
|
||||
}
|
||||
entries[j] = current;
|
||||
}
|
||||
}
|
||||
|
||||
// Builds one child path from the runner directory and test filename.
|
||||
static bool build_child_path(const char* dir, const char* name, char* out, size_t outSize) {
|
||||
if (!dir || !name || !out || outSize == 0) return false;
|
||||
size_t dirLen = strlen(dir);
|
||||
size_t nameLen = strlen(name);
|
||||
bool needSep = dirLen > 0 && !is_separator(dir[dirLen - 1]);
|
||||
size_t total = dirLen + (needSep ? 1u : 0u) + nameLen;
|
||||
if (total + 1 > outSize) return false;
|
||||
|
||||
memcpy(out, dir, dirLen);
|
||||
size_t pos = dirLen;
|
||||
if (needSep) out[pos++] = '\\';
|
||||
memcpy(out + pos, name, nameLen);
|
||||
out[total] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
// Collects executable tests from the runner directory.
|
||||
static uint32_t collect_tests(const char* dir, const char* selfName, TEST_ENTRY** entriesOut, uint32_t* countOut) {
|
||||
*entriesOut = 0;
|
||||
*countOut = 0;
|
||||
|
||||
uint32_t hDir = 0;
|
||||
SYS_PATH_HANDLE_REQUEST openReq = { SysSvFromCstr(dir), &hDir };
|
||||
uint32_t status = SysOpenDir(&openReq);
|
||||
if (status != NTLSTATUS_SUCCESS) return status;
|
||||
|
||||
TEST_ENTRY* entries = 0;
|
||||
uint32_t count = 0;
|
||||
uint32_t capacity = 0;
|
||||
|
||||
for (;;) {
|
||||
SYS_DIR_ENTRY entry;
|
||||
status = SysReadDir(hDir, &entry);
|
||||
if (status != NTLSTATUS_SUCCESS) break;
|
||||
if (!entry.Name[0]) continue;
|
||||
if ((entry.Attr & 0x10u) != 0) continue;
|
||||
if (!has_exe_suffix(entry.Name)) continue;
|
||||
if (compare_names(entry.Name, selfName) == 0) continue;
|
||||
|
||||
if (count == capacity) {
|
||||
status = grow_entries(&entries, &capacity, count);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
SysCloseHandle(hDir);
|
||||
if (entries) SysFreeVm(entries);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(entries[count].Name, entry.Name, sizeof(entries[count].Name) - 1);
|
||||
entries[count].Name[sizeof(entries[count].Name) - 1] = '\0';
|
||||
count++;
|
||||
}
|
||||
|
||||
SysCloseHandle(hDir);
|
||||
sort_entries(entries, count);
|
||||
*entriesOut = entries;
|
||||
*countOut = count;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Runs one test process and returns the child's exit code through exitCodeOut.
|
||||
static uint32_t run_test(const char* path, int32_t* exitCodeOut) {
|
||||
uint32_t process = 0;
|
||||
SYS_CREATE_PROCESS_REQUEST req;
|
||||
req.Path = SysSvFromCstr(path);
|
||||
req.CommandLine = SysSvFromCstr(path);
|
||||
req.HandleOut = &process;
|
||||
|
||||
uint32_t status = SysCreateProcess(&req);
|
||||
if (status != NTLSTATUS_SUCCESS) return status;
|
||||
|
||||
status = SysWait(process, SYS_WAIT_FLAG_NONE);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
SysCloseHandle(process);
|
||||
return status;
|
||||
}
|
||||
|
||||
int32_t exitCode = 1;
|
||||
status = SysGetProcessExitCode(process, &exitCode);
|
||||
SysCloseHandle(process);
|
||||
if (status != NTLSTATUS_SUCCESS) return status;
|
||||
|
||||
*exitCodeOut = exitCode;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Program entry point.
|
||||
int main(void) {
|
||||
print("all_tests: start\n");
|
||||
|
||||
char selfPath[SYS_PATH_MAX];
|
||||
char selfDir[SYS_PATH_MAX];
|
||||
char selfName[SYS_DIR_NAME_MAX];
|
||||
SYS_PEB* peb = SysGetPeb();
|
||||
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION ||
|
||||
!copy_view(selfPath, sizeof(selfPath), peb->ImagePath) ||
|
||||
!split_self_path(selfPath, selfDir, sizeof(selfDir), selfName, sizeof(selfName))) {
|
||||
print("all_tests: failed to resolve runner path\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
TEST_ENTRY* entries = 0;
|
||||
uint32_t count = 0;
|
||||
uint32_t status = collect_tests(selfDir, selfName, &entries, &count);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
print("all_tests: failed to enumerate tests status=0x");
|
||||
print_hex32(status);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
print("all_tests: no test executables found\n");
|
||||
if (entries) SysFreeVm(entries);
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
char childPath[SYS_PATH_MAX];
|
||||
if (!build_child_path(selfDir, entries[i].Name, childPath, sizeof(childPath))) {
|
||||
print("all_tests: child path too long: ");
|
||||
print(entries[i].Name);
|
||||
print("\n");
|
||||
SysFreeVm(entries);
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
print("all_tests: RUN ");
|
||||
print(childPath);
|
||||
print("\n");
|
||||
|
||||
int32_t exitCode = 1;
|
||||
status = run_test(childPath, &exitCode);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
print("all_tests: FAIL ");
|
||||
print(childPath);
|
||||
print(" launch/wait status=0x");
|
||||
print_hex32(status);
|
||||
print("\n");
|
||||
SysFreeVm(entries);
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
if (exitCode != 0) {
|
||||
print("all_tests: FAIL ");
|
||||
print(childPath);
|
||||
print(" exit=");
|
||||
print_i32(exitCode);
|
||||
print("\n");
|
||||
SysFreeVm(entries);
|
||||
exit_now(exitCode ? exitCode : 1);
|
||||
}
|
||||
|
||||
print("all_tests: PASS ");
|
||||
print(childPath);
|
||||
print("\n");
|
||||
}
|
||||
|
||||
print("all_tests: PASS count=");
|
||||
print_u64(count);
|
||||
print("\n");
|
||||
SysFreeVm(entries);
|
||||
exit_now(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -54,6 +54,51 @@ static void fail(const char* what) {
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
// Copies a string view into a NUL-terminated buffer.
|
||||
static int copy_view(char* dst, size_t cap, SYS_STRING_VIEW view) {
|
||||
if (!dst || cap == 0 || !view.Data || view.Length + 1 > cap)
|
||||
return 0;
|
||||
memcpy(dst, view.Data, view.Length);
|
||||
dst[view.Length] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Computes the parent of one canonical native current-directory string.
|
||||
static int parent_of_cwd(const char* cwd, char* out, size_t cap) {
|
||||
if (!cwd || !out || cap == 0)
|
||||
return 0;
|
||||
|
||||
size_t len = strlen(cwd);
|
||||
if (len < 3 || cwd[1] != ':' || (cwd[2] != '/' && cwd[2] != '\\'))
|
||||
return 0;
|
||||
if (len + 1 > cap)
|
||||
return 0;
|
||||
|
||||
memcpy(out, cwd, len + 1);
|
||||
while (len > 3 && (out[len - 1] == '/' || out[len - 1] == '\\'))
|
||||
out[--len] = '\0';
|
||||
if (len <= 3) {
|
||||
out[0] = cwd[0];
|
||||
out[1] = ':';
|
||||
out[2] = '/';
|
||||
out[3] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t pos = len;
|
||||
while (pos > 3 && out[pos - 1] != '/' && out[pos - 1] != '\\')
|
||||
pos--;
|
||||
if (pos <= 3) {
|
||||
out[0] = cwd[0];
|
||||
out[1] = ':';
|
||||
out[2] = '/';
|
||||
out[3] = '\0';
|
||||
} else {
|
||||
out[pos - 1] = '\0';
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Sets the current directory to one native path.
|
||||
static void set_cwd_or_fail(const char* path, const char* what) {
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||
@@ -72,8 +117,8 @@ static void expect_cwd(const char* expected) {
|
||||
|
||||
// Program entry point.
|
||||
int main(void) {
|
||||
static const char rootPath[] = "0:/";
|
||||
static const char tempDir[] = "0:/cwd_peb_smoke_dir";
|
||||
char original[SYS_CWD_MAX];
|
||||
char parent[SYS_CWD_MAX];
|
||||
|
||||
print("cwd_peb_smoke: start\n");
|
||||
|
||||
@@ -82,25 +127,19 @@ int main(void) {
|
||||
if (peb->Magic != SYS_PEB_MAGIC) fail("peb magic mismatch");
|
||||
if (peb->Version != SYS_ENV_VERSION) fail("peb version mismatch");
|
||||
|
||||
set_cwd_or_fail(rootPath, "set root cwd");
|
||||
expect_cwd(rootPath);
|
||||
if (!copy_view(original, sizeof(original), SysGetCurrentDirectoryView()))
|
||||
fail("initial cwd missing");
|
||||
if (!parent_of_cwd(original, parent, sizeof(parent)))
|
||||
fail("initial cwd malformed");
|
||||
|
||||
SYS_PATH_REQUEST dirReq = { SysSvFromCstr(tempDir) };
|
||||
uint32_t status = SysRemoveDir(&dirReq);
|
||||
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_NOT_FOUND)
|
||||
fail_status("remove stale temp dir", status);
|
||||
set_cwd_or_fail(".", "set dot cwd");
|
||||
expect_cwd(original);
|
||||
|
||||
status = SysCreateDir(&dirReq);
|
||||
if (status != NTLSTATUS_SUCCESS) fail_status("create temp dir", status);
|
||||
set_cwd_or_fail("..", "set parent cwd");
|
||||
expect_cwd(parent);
|
||||
|
||||
set_cwd_or_fail(tempDir, "set temp cwd");
|
||||
expect_cwd(tempDir);
|
||||
|
||||
set_cwd_or_fail(rootPath, "restore root cwd");
|
||||
expect_cwd(rootPath);
|
||||
|
||||
status = SysRemoveDir(&dirReq);
|
||||
if (status != NTLSTATUS_SUCCESS) fail_status("remove temp dir", status);
|
||||
set_cwd_or_fail(original, "restore original cwd");
|
||||
expect_cwd(original);
|
||||
|
||||
print("cwd_peb_smoke: PASS\n");
|
||||
exit_now(0);
|
||||
|
||||
@@ -68,11 +68,14 @@ static void delete_path(const char* path) {
|
||||
}
|
||||
|
||||
static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
|
||||
// Writable QEMU vvfat maps long-name creation through a host rename and
|
||||
// can abort before the guest receives an I/O status. Keep this file in
|
||||
// 8.3 form so the test measures writeback rather than the host bridge.
|
||||
static const char* paths[] = {
|
||||
"dirty_writeback.tmp",
|
||||
"/dirty_writeback.tmp",
|
||||
"1:/dirty_writeback.tmp",
|
||||
"0:/dirty_writeback.tmp",
|
||||
"dwrite.tmp",
|
||||
"/dwrite.tmp",
|
||||
"1:/dwrite.tmp",
|
||||
"0:/dwrite.tmp",
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// Native file metadata smoke test.
|
||||
// Verifies path stat, handle stat, directory metadata, missing paths, and invalid handles.
|
||||
// Verifies path stat, handle stat, directory metadata, missing paths, and invalid handles without mutating the filesystem.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define TEST_FILE_SIZE 1537u
|
||||
|
||||
static uint8_t gPattern[TEST_FILE_SIZE];
|
||||
static char gSelfPath[SYS_PATH_MAX];
|
||||
static char gSelfDir[SYS_PATH_MAX];
|
||||
|
||||
// Prints one NUL-terminated string to the kernel console.
|
||||
static void print(const char* s) {
|
||||
@@ -58,84 +57,77 @@ static void fail_status(const char* what, uint32_t status) {
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
// Removes a file path if it already exists.
|
||||
static void delete_file_path(const char* path) {
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||
(void)SysDeleteFile(&req);
|
||||
// Copies one string view into a fixed NUL-terminated buffer.
|
||||
static int copy_view(char* out, size_t outSize, SYS_STRING_VIEW view) {
|
||||
if (!out || outSize == 0 || !view.Data || view.Length + 1 > outSize)
|
||||
return 0;
|
||||
memcpy(out, view.Data, view.Length);
|
||||
out[view.Length] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Removes a directory path if it already exists.
|
||||
static void remove_dir_path(const char* path) {
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||
(void)SysRemoveDir(&req);
|
||||
}
|
||||
|
||||
// Opens a new temporary file on the first writable path that works.
|
||||
static uint32_t create_temp_file(uint32_t* fileOut, const char** pathOut) {
|
||||
static const char* paths[] = {
|
||||
"file_stat_smoke.tmp",
|
||||
"/file_stat_smoke.tmp",
|
||||
"1:/file_stat_smoke.tmp",
|
||||
"0:/file_stat_smoke.tmp",
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
|
||||
uint32_t h = 0;
|
||||
delete_file_path(paths[i]);
|
||||
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
|
||||
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
|
||||
SYS_FILE_CREATE_NEW,
|
||||
SYS_FILE_FLAG_NONE,
|
||||
&h);
|
||||
uint32_t s = SysOpenFile(&req);
|
||||
if (s == NTLSTATUS_SUCCESS) {
|
||||
*fileOut = h;
|
||||
*pathOut = paths[i];
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
// Builds the containing directory path for one process image path.
|
||||
static void build_parent_dir(char* out, size_t outSize, const char* path) {
|
||||
size_t len = strlen(path);
|
||||
size_t slash = (size_t)-1;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (path[i] == '/' || path[i] == '\\')
|
||||
slash = i;
|
||||
}
|
||||
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Creates a temporary directory on the first writable path that works.
|
||||
static uint32_t create_temp_dir(const char** pathOut) {
|
||||
static const char* paths[] = {
|
||||
"file_stat_smoke.dir",
|
||||
"/file_stat_smoke.dir",
|
||||
"1:/file_stat_smoke.dir",
|
||||
"0:/file_stat_smoke.dir",
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
|
||||
remove_dir_path(paths[i]);
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(paths[i]) };
|
||||
uint32_t s = SysCreateDir(&req);
|
||||
if (s == NTLSTATUS_SUCCESS) {
|
||||
*pathOut = paths[i];
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
if (slash == (size_t)-1) {
|
||||
if (outSize < 2) fail("parent directory buffer too small");
|
||||
out[0] = '.';
|
||||
out[1] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
if (slash == 0) {
|
||||
if (outSize < 2) fail("parent directory buffer too small");
|
||||
out[0] = path[0];
|
||||
out[1] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
if (slash + 1 >= outSize)
|
||||
fail("parent directory path too long");
|
||||
memcpy(out, path, slash);
|
||||
out[slash] = '\0';
|
||||
}
|
||||
|
||||
// Fills the write pattern used by the test file.
|
||||
static void fill_pattern(void) {
|
||||
for (size_t i = 0; i < sizeof(gPattern); ++i)
|
||||
gPattern[i] = (uint8_t)(0x31u + (uint8_t)(i * 17u + (i >> 3)));
|
||||
}
|
||||
// Opens this process image for read/query metadata checks.
|
||||
static uint32_t open_self_image(uint32_t* fileOut, uint64_t* sizeOut) {
|
||||
SYS_PEB* peb = SysGetPeb();
|
||||
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!copy_view(gSelfPath, sizeof(gSelfPath), peb->ImagePath))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
build_parent_dir(gSelfDir, sizeof(gSelfDir), gSelfPath);
|
||||
|
||||
// Writes the test file contents and flushes them to the filesystem.
|
||||
static void write_test_file(uint32_t file) {
|
||||
size_t done = 0;
|
||||
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(file, gPattern, sizeof(gPattern), &done);
|
||||
uint32_t s = SysWriteFile(&writeReq);
|
||||
if (s != NTLSTATUS_SUCCESS) fail_status("write test file", s);
|
||||
if (done != sizeof(gPattern)) fail("short write");
|
||||
uint32_t h = 0;
|
||||
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(gSelfPath),
|
||||
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
|
||||
SYS_FILE_OPEN_EXISTING,
|
||||
SYS_FILE_FLAG_NONE,
|
||||
&h);
|
||||
uint32_t s = SysOpenFile(&req);
|
||||
if (s != NTLSTATUS_SUCCESS)
|
||||
return s;
|
||||
|
||||
s = SysFlushFile(file);
|
||||
if (s != NTLSTATUS_SUCCESS) fail_status("flush test file", s);
|
||||
uint64_t size = 0;
|
||||
s = SysGetFileSize(h, &size);
|
||||
if (s != NTLSTATUS_SUCCESS) {
|
||||
SysCloseHandle(h);
|
||||
return s;
|
||||
}
|
||||
if (size == 0) {
|
||||
SysCloseHandle(h);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
*fileOut = h;
|
||||
*sizeOut = size;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Checks one regular-file metadata result.
|
||||
@@ -171,13 +163,13 @@ static void check_directory_stat(const SYS_FILE_STAT* stat) {
|
||||
if (stat->LinkCount == 0) fail("directory has zero links");
|
||||
}
|
||||
|
||||
// Exercises handle and path stat for one regular file.
|
||||
static void test_file_stat(uint32_t file, const char* path) {
|
||||
// Exercises handle and path stat for this process image.
|
||||
static void test_file_stat(uint32_t file, uint64_t expectedSize) {
|
||||
SYS_FILE_STAT byHandle;
|
||||
memset(&byHandle, 0xA5, sizeof(byHandle));
|
||||
uint32_t s = SysGetFileStat(file, &byHandle);
|
||||
if (s != NTLSTATUS_SUCCESS) fail_status("get file stat", s);
|
||||
check_regular_stat(&byHandle, sizeof(gPattern), "handle stat");
|
||||
check_regular_stat(&byHandle, expectedSize, "handle stat");
|
||||
|
||||
uint64_t size = 0;
|
||||
s = SysGetFileSize(file, &size);
|
||||
@@ -186,10 +178,10 @@ static void test_file_stat(uint32_t file, const char* path) {
|
||||
|
||||
SYS_FILE_STAT byPath;
|
||||
memset(&byPath, 0x5A, sizeof(byPath));
|
||||
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(path), &byPath };
|
||||
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(gSelfPath), &byPath };
|
||||
s = SysStatFile(&statReq);
|
||||
if (s != NTLSTATUS_SUCCESS) fail_status("path stat", s);
|
||||
check_regular_stat(&byPath, sizeof(gPattern), "path stat");
|
||||
check_regular_stat(&byPath, expectedSize, "path stat");
|
||||
|
||||
if (byHandle.VolumeId != 0 && byPath.VolumeId != 0 && byHandle.VolumeId != byPath.VolumeId)
|
||||
fail("handle and path volume ids differ");
|
||||
@@ -197,11 +189,11 @@ static void test_file_stat(uint32_t file, const char* path) {
|
||||
fail("handle and path file ids differ");
|
||||
}
|
||||
|
||||
// Exercises path stat for one directory.
|
||||
static void test_directory_stat(const char* path) {
|
||||
// Exercises path stat for the executable's containing directory.
|
||||
static void test_directory_stat(void) {
|
||||
SYS_FILE_STAT stat;
|
||||
memset(&stat, 0xCC, sizeof(stat));
|
||||
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(path), &stat };
|
||||
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(gSelfDir), &stat };
|
||||
uint32_t s = SysStatFile(&statReq);
|
||||
if (s != NTLSTATUS_SUCCESS) fail_status("directory stat", s);
|
||||
check_directory_stat(&stat);
|
||||
@@ -226,30 +218,15 @@ static void test_failure_paths(void) {
|
||||
int main(void) {
|
||||
print("file_stat_smoke: start\n");
|
||||
|
||||
fill_pattern();
|
||||
|
||||
uint32_t file = 0;
|
||||
const char* filePath = 0;
|
||||
uint32_t s = create_temp_file(&file, &filePath);
|
||||
if (s != NTLSTATUS_SUCCESS) fail_status("create temp file", s);
|
||||
uint64_t size = 0;
|
||||
uint32_t s = open_self_image(&file, &size);
|
||||
if (s != NTLSTATUS_SUCCESS) fail_status("open self image", s);
|
||||
|
||||
write_test_file(file);
|
||||
test_file_stat(file, filePath);
|
||||
test_file_stat(file, size);
|
||||
SysCloseHandle(file);
|
||||
delete_file_path(filePath);
|
||||
|
||||
const char* dirPath = 0;
|
||||
s = create_temp_dir(&dirPath);
|
||||
if (s != NTLSTATUS_SUCCESS) fail_status("create temp dir", s);
|
||||
test_directory_stat(dirPath);
|
||||
remove_dir_path(dirPath);
|
||||
|
||||
SYS_PATH_REQUEST dirReq = { SysSvFromCstr(dirPath) };
|
||||
s = SysCreateDir(&dirReq);
|
||||
if (s != NTLSTATUS_SUCCESS) fail_status("recreate temp dir", s);
|
||||
test_directory_stat(dirPath);
|
||||
remove_dir_path(dirPath);
|
||||
|
||||
test_directory_stat();
|
||||
test_failure_paths();
|
||||
|
||||
print("file_stat_smoke: PASS\n");
|
||||
|
||||
@@ -89,6 +89,27 @@ int main(void) {
|
||||
badReq.QueueHandle = 0xdeadbeefu;
|
||||
expect_status("read input bad handle", SysReadInputEvent(&badReq), NTLSTATUS_INVALID_HANDLE);
|
||||
|
||||
SYS_GLOBAL_INPUT_STATE state;
|
||||
state.Size = sizeof(state);
|
||||
state.Flags = SYS_GLOBAL_INPUT_STATE_FLAG_NONE;
|
||||
expect_status("query global input null", SysQueryGlobalInputState((SYS_GLOBAL_INPUT_STATE*)0), NTLSTATUS_INVALID_PARAMETER);
|
||||
|
||||
SYS_GLOBAL_INPUT_STATE badState = state;
|
||||
badState.Size = 0;
|
||||
expect_status("query global input bad size", SysQueryGlobalInputState(&badState), NTLSTATUS_INVALID_PARAMETER);
|
||||
|
||||
badState = state;
|
||||
badState.Flags = 1;
|
||||
expect_status("query global input bad flags", SysQueryGlobalInputState(&badState), NTLSTATUS_INVALID_PARAMETER);
|
||||
|
||||
status = SysQueryGlobalInputState(&state);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("query global input", status);
|
||||
if (state.Size != sizeof(state))
|
||||
fail_status("query global input size", state.Size);
|
||||
if (state.Flags != SYS_GLOBAL_INPUT_STATE_FLAG_NONE)
|
||||
fail_status("query global input flags", state.Flags);
|
||||
|
||||
status = SysPeekInputEvent(&req);
|
||||
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK)
|
||||
fail_status("peek input immediate", status);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Sequential file-cache pressure test.
|
||||
// Reads a large read-only asset through SysReadFile and checks that the
|
||||
// read-only page cache stays bounded by dropping clean pages.
|
||||
// Reads this test executable through SysReadFile and checks cache behavior when
|
||||
// the image is large enough to apply pressure.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
@@ -56,47 +56,50 @@ static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) {
|
||||
return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0);
|
||||
}
|
||||
|
||||
static uint32_t open_first_large(uint32_t* fileOut, const char** pathOut, uint64_t* sizeOut) {
|
||||
static const char* candidates[] = {
|
||||
"1:/apps/video.bk2",
|
||||
"0:/apps/video.bk2",
|
||||
"/apps/video.bk2",
|
||||
"video.bk2",
|
||||
static char gSelfPath[SYS_PATH_MAX];
|
||||
|
||||
"1:/apps/doom1.wad",
|
||||
"0:/apps/doom1.wad",
|
||||
"/apps/doom1.wad",
|
||||
"doom1.wad",
|
||||
};
|
||||
// Copies a string view into a fixed NUL-terminated buffer.
|
||||
static int copy_view(char* out, size_t outSize, SYS_STRING_VIEW view) {
|
||||
if (!out || outSize == 0 || !view.Data || view.Length + 1 > outSize)
|
||||
return 0;
|
||||
memcpy(out, view.Data, view.Length);
|
||||
out[view.Length] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) {
|
||||
uint32_t h = 0;
|
||||
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(candidates[i]),
|
||||
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
|
||||
SYS_FILE_OPEN_EXISTING,
|
||||
SYS_FILE_FLAG_NONE,
|
||||
&h);
|
||||
uint32_t s = SysOpenFile(&req);
|
||||
if (s != NTLSTATUS_SUCCESS)
|
||||
continue;
|
||||
// Opens this process image as the cache-pressure input file.
|
||||
static uint32_t open_self_image(uint32_t* fileOut, const char** pathOut, uint64_t* sizeOut) {
|
||||
SYS_PEB* peb = SysGetPeb();
|
||||
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!copy_view(gSelfPath, sizeof(gSelfPath), peb->ImagePath))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint64_t size = 0;
|
||||
s = SysGetFileSize(h, &size);
|
||||
if (s != NTLSTATUS_SUCCESS) {
|
||||
SysCloseHandle(h);
|
||||
continue;
|
||||
}
|
||||
if (size >= MIN_PRESSURE_BYTES) {
|
||||
*fileOut = h;
|
||||
*pathOut = candidates[i];
|
||||
*sizeOut = size;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
uint32_t h = 0;
|
||||
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(gSelfPath),
|
||||
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
|
||||
SYS_FILE_OPEN_EXISTING,
|
||||
SYS_FILE_FLAG_NONE,
|
||||
&h);
|
||||
uint32_t s = SysOpenFile(&req);
|
||||
if (s != NTLSTATUS_SUCCESS)
|
||||
return s;
|
||||
|
||||
uint64_t size = 0;
|
||||
s = SysGetFileSize(h, &size);
|
||||
if (s != NTLSTATUS_SUCCESS) {
|
||||
SysCloseHandle(h);
|
||||
return s;
|
||||
}
|
||||
if (size == 0) {
|
||||
SysCloseHandle(h);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
*fileOut = h;
|
||||
*pathOut = gSelfPath;
|
||||
*sizeOut = size;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
@@ -110,9 +113,9 @@ int main(void) {
|
||||
uint32_t file = 0;
|
||||
const char* path = 0;
|
||||
uint64_t size = 0;
|
||||
uint32_t s = open_first_large(&file, &path, &size);
|
||||
uint32_t s = open_self_image(&file, &path, &size);
|
||||
if (s != NTLSTATUS_SUCCESS)
|
||||
fail_status("open large asset", s);
|
||||
fail_status("open self image", s);
|
||||
|
||||
print("pcache_pressure: using ");
|
||||
print(path ? path : "<unknown>");
|
||||
@@ -135,8 +138,8 @@ int main(void) {
|
||||
|
||||
SysCloseHandle(file);
|
||||
|
||||
if (total < MIN_PRESSURE_BYTES)
|
||||
fail("asset read was too short to pressure the cache");
|
||||
if (total == 0)
|
||||
fail("self image read returned no bytes");
|
||||
|
||||
if (query_cache(&after) != NTLSTATUS_SUCCESS)
|
||||
fail("final cache stats query failed");
|
||||
@@ -165,12 +168,16 @@ int main(void) {
|
||||
print(" batch-pages+="); print_u64(batchPages);
|
||||
print("\n");
|
||||
|
||||
if (loads < 512)
|
||||
fail("expected at least 512 cache page loads");
|
||||
if (after.ResidentPages > RESIDENT_LIMIT_PAGES)
|
||||
fail("resident page cache exceeded pressure bound");
|
||||
if (loads > RESIDENT_LIMIT_PAGES && drops == 0)
|
||||
fail("cache did not drop clean pages under pressure");
|
||||
if (size >= MIN_PRESSURE_BYTES) {
|
||||
if (loads < 512)
|
||||
fail("expected at least 512 cache page loads");
|
||||
if (after.ResidentPages > RESIDENT_LIMIT_PAGES)
|
||||
fail("resident page cache exceeded pressure bound");
|
||||
if (loads > RESIDENT_LIMIT_PAGES && drops == 0)
|
||||
fail("cache did not drop clean pages under pressure");
|
||||
} else {
|
||||
print("pcache_pressure: self image below pressure size; pressure bounds skipped\n");
|
||||
}
|
||||
|
||||
print("pcache_pressure: PASS\n");
|
||||
exit_now(0);
|
||||
|
||||
Reference in new issue
Block a user