Files
nt-lite/code/user/apps/shell_native/shell_native.c
T

1614 lines
62 KiB
C

// User application entry points for shell_native.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <ntlite/syscall.h>
#include <ntlite/status.h>
#define MAX_CMD 256
#define SHELL_FOREGROUND_POLL_MS 30u
#define MAX_PROCS 64 // Maximum number of processes we will fetch at once
#define MAX_THREADS 128
#define SHELL_PROMPT "> "
// Output helpers.
static void print(const char* s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
// Opens file.
static uint32_t open_file(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenFile(&req);
}
// Creates file.
static uint32_t create_file(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysCreateFile(&req);
}
// Opens dir.
static uint32_t open_dir(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenDir(&req);
}
// Updates cwd.
static uint32_t set_cwd(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
return SysSetCwd(&req);
}
// Releases file.
static uint32_t delete_file(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
return SysDeleteFile(&req);
}
// Creates process.
static uint32_t create_process(const char* path, uint32_t* handleOut, const char* cmdline) {
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(path), SysSvFromCstr(cmdline), handleOut };
return SysCreateProcess(&req);
}
// Prints u Int.
static void printUInt(uint32_t n) {
char buf[12]; int i = 0;
if (n == 0) { print("0"); return; }
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
}
// Prints int.
static void printInt(int32_t n) {
if (n < 0) { print("-"); printUInt((uint32_t)-(n + 1) + 1); }
else { printUInt((uint32_t)n); }
}
// Parses an unsigned decimal integer.
static bool parseUInt(const char* text, uint32_t* out) {
if (!text || !out) return false;
while (*text == ' ' || *text == '\t') text++;
if (*text < '0' || *text > '9') return false;
uint32_t value = 0;
while (*text >= '0' && *text <= '9') {
uint32_t digit = (uint32_t)(*text - '0');
if (value > (0xffffffffu - digit) / 10u) return false;
value = value * 10u + digit;
text++;
}
while (*text == ' ' || *text == '\t') text++;
if (*text != '\0') return false;
*out = value;
return true;
}
// Prints u Int64.
static void printUInt64(uint64_t n) {
if (n == 0) { print("0"); return; }
char buf[20]; int i = 0;
while (n > 0) { buf[i++] = (char)('0' + (int)(n % 10)); n /= 10; }
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
}
// Implements the u64 Digits operation.
static uint32_t u64Digits(uint64_t n) {
uint32_t digits = 1;
while (n >= 10u) { n /= 10u; digits++; }
return digits;
}
// Prints spaces.
static void printSpaces(size_t count) {
while (count--) print(" ");
}
// Prints u Int Width.
static void printUIntWidth(uint32_t n, size_t width) {
uint32_t digits = u64Digits(n);
if (width > digits) printSpaces(width - digits);
printUInt(n);
}
// Prints u Int Zero Width.
static void printUIntZeroWidth(uint32_t n, size_t width) {
uint32_t digits = u64Digits(n);
while (width > digits) { print("0"); width--; }
printUInt(n);
}
// Prints u Int64 Width.
static void printUInt64Width(uint64_t n, size_t width) {
uint32_t digits = u64Digits(n);
if (width > digits) printSpaces(width - digits);
printUInt64(n);
}
// Names nt-lite process-abort exit statuses.
static const char* processExitStatusName(uint32_t code) {
switch (code) {
case STATUS_PROCESS_KILLED: return "killed";
case STATUS_PROCESS_PAGE_FAULT: return "page-fault";
case STATUS_PROCESS_GENERAL_PROTECTION: return "general-protection";
case STATUS_PROCESS_ILLEGAL_OPCODE: return "illegal-opcode";
case STATUS_PROCESS_DIVIDE_BY_ZERO: return "divide-by-zero";
case STATUS_PROCESS_BREAKPOINT: return "breakpoint";
case STATUS_PROCESS_ALIGNMENT_FAULT: return "alignment-fault";
case STATUS_PROCESS_UNKNOWN_FAULT: return "unknown-fault";
default: return NULL;
}
}
// Prints hex32.
static void printHex32(uint32_t v) {
static const char hex[] = "0123456789abcdef";
char buf[9];
for (int i = 7; i >= 0; i--) {
buf[i] = hex[v & 0xFu];
v >>= 4;
}
buf[8] = '\0';
print(buf);
}
// Prints dir Time.
static void printDirTime(const SYS_SYSTEM_TIME* t) {
if (!t || !(t->Flags & SYS_TIME_FLAG_VALID)) {
print("---- -- -- --:-- ");
return;
}
printUIntZeroWidth(t->Year, 4);
print("-");
printUIntZeroWidth(t->Month, 2);
print("-");
printUIntZeroWidth(t->Day, 2);
print(" ");
printUIntZeroWidth(t->Hour, 2);
print(":");
printUIntZeroWidth(t->Minute, 2);
print(" ");
}
// Prints string Width.
static void printStringWidth(const char* s, size_t width) {
size_t len = s ? strlen(s) : 0;
if (s) print(s);
if (width > len) printSpaces(width - len);
}
#define SHELL_HISTORY_DEPTH 16
static char gHistory[SHELL_HISTORY_DEPTH][MAX_CMD];
static size_t gHistoryCount;
static const char* const gShellBuiltinCommands[] = {
"cd",
"clear",
"crash",
"date",
"del",
"dir",
"display",
"exit",
"gpu",
"help",
"kill",
"mem",
"ps",
"stats",
"telemetry",
"threads",
"time",
"type",
"write",
};
// Converts a key event into the shell's printable character model.
static char shellKeyToChar(INPUT_KEY key, uint32_t modifiers) {
bool shifted = (modifiers & INPUT_MOD_SHIFT) != 0;
bool caps = (modifiers & INPUT_MOD_CAPS_LOCK) != 0;
if (key >= INPUT_KEY_A && key <= INPUT_KEY_Z) {
char c = (char)('a' + (key - INPUT_KEY_A));
if (shifted ^ caps) c = (char)(c - 'a' + 'A');
return c;
}
if (key >= INPUT_KEY_1 && key <= INPUT_KEY_9) {
static const char shiftedDigits[] = "!@#$%^&*(";
char normal = (char)('1' + (key - INPUT_KEY_1));
return shifted ? shiftedDigits[key - INPUT_KEY_1] : normal;
}
switch (key) {
case INPUT_KEY_0: return shifted ? ')' : '0';
case INPUT_KEY_MINUS: return shifted ? '_' : '-';
case INPUT_KEY_EQUAL: return shifted ? '+' : '=';
case INPUT_KEY_LEFT_BRACKET: return shifted ? '{' : '[';
case INPUT_KEY_RIGHT_BRACKET: return shifted ? '}' : ']';
case INPUT_KEY_BACKSLASH: return shifted ? '|' : '\\';
case INPUT_KEY_SEMICOLON: return shifted ? ':' : ';';
case INPUT_KEY_APOSTROPHE: return shifted ? '"' : '\'';
case INPUT_KEY_GRAVE: return shifted ? '~' : '`';
case INPUT_KEY_COMMA: return shifted ? '<' : ',';
case INPUT_KEY_PERIOD: return shifted ? '>' : '.';
case INPUT_KEY_SLASH: return shifted ? '?' : '/';
case INPUT_KEY_SPACE: return ' ';
case INPUT_KEY_TAB: return '\t';
case INPUT_KEY_KP_DIVIDE: return '/';
case INPUT_KEY_KP_MULTIPLY: return '*';
case INPUT_KEY_KP_MINUS: return '-';
case INPUT_KEY_KP_PLUS: return '+';
case INPUT_KEY_KP_0: return '0';
case INPUT_KEY_KP_1: return '1';
case INPUT_KEY_KP_2: return '2';
case INPUT_KEY_KP_3: return '3';
case INPUT_KEY_KP_4: return '4';
case INPUT_KEY_KP_5: return '5';
case INPUT_KEY_KP_6: return '6';
case INPUT_KEY_KP_7: return '7';
case INPUT_KEY_KP_8: return '8';
case INPUT_KEY_KP_9: return '9';
case INPUT_KEY_KP_PERIOD: return '.';
default: return 0;
}
}
// Adds a non-empty command to the in-memory history ring.
static void shellHistoryAdd(const char* cmd) {
if (!cmd || !*cmd) return;
if (gHistoryCount > 0 && strcmp(gHistory[gHistoryCount - 1], cmd) == 0) return;
if (gHistoryCount == SHELL_HISTORY_DEPTH) {
memmove(gHistory[0], gHistory[1], (SHELL_HISTORY_DEPTH - 1) * MAX_CMD);
gHistoryCount--;
}
strncpy(gHistory[gHistoryCount], cmd, MAX_CMD - 1);
gHistory[gHistoryCount][MAX_CMD - 1] = '\0';
gHistoryCount++;
}
// Replaces the visible line while keeping the cursor at the end.
static void shellEditorReplaceLine(char* buf, int len, int* pos, const char* text) {
int oldPos = *pos;
for (int i = 0; i < oldPos; i++) print("\b");
for (int i = 0; i < oldPos; i++) print(" ");
for (int i = 0; i < oldPos; i++) print("\b");
int newPos = 0;
if (text) {
while (text[newPos] && newPos < len - 1) {
buf[newPos] = text[newPos];
newPos++;
}
}
buf[newPos] = '\0';
*pos = newPos;
print(buf);
}
// Returns true when a character separates shell tokens.
static bool shellIsTokenSeparator(char c) {
return c == ' ' || c == '\t';
}
// Returns true when a character separates path components.
static bool shellIsPathSeparator(char c) {
return c == '/' || c == '\\';
}
// Returns true when the token already names an explicit path.
static bool shellTokenHasPathSeparator(const char* token, size_t length) {
for (size_t i = 0; i < length; i++) {
if (shellIsPathSeparator(token[i])) return true;
}
return false;
}
// Returns true when name starts with prefix.
static bool shellNameStartsWith(const char* name, const char* prefix, size_t prefixLength) {
if (!name || !prefix) return false;
for (size_t i = 0; i < prefixLength; i++) {
if (name[i] == '\0' || name[i] != prefix[i]) return false;
}
return true;
}
// Extends an in-progress common prefix with one completion candidate.
static void shellUpdateCommonPrefix(char* common, size_t commonSize, const char* name, uint32_t matchCount) {
if (!common || commonSize == 0 || !name) return;
if (matchCount == 0) {
strncpy(common, name, commonSize - 1);
common[commonSize - 1] = '\0';
return;
}
size_t i = 0;
while (common[i] && name[i] && common[i] == name[i]) i++;
common[i] = '\0';
}
// Appends text to the edited line and echoes it.
static bool shellEditorAppend(char* buf, int len, int* pos, const char* text) {
if (!buf || len <= 0 || !pos || !text) return false;
bool changed = false;
while (*text && *pos < len - 1) {
buf[*pos] = *text;
(*pos)++;
buf[*pos] = '\0';
char echo[2] = { *text, '\0' };
print(echo);
text++;
changed = true;
}
return changed;
}
// Prints one completion candidate.
static void shellPrintCompletionCandidate(const char* name, bool directory) {
print(" ");
print(name);
if (directory) print("/");
print("\n");
}
// Tracks completion candidates without storing the full candidate list.
typedef struct SHELL_COMPLETION_STATE {
uint32_t MatchCount;
bool LastIsDirectory;
bool LastAddsSpace;
char Common[SYS_DIR_NAME_MAX];
} SHELL_COMPLETION_STATE;
// Records one completion candidate.
static void shellRecordCompletion(SHELL_COMPLETION_STATE* state, const char* name, bool directory, bool addsSpace) {
if (!state || !name || !*name) return;
shellUpdateCommonPrefix(state->Common, sizeof(state->Common), name, state->MatchCount);
state->MatchCount++;
state->LastIsDirectory = directory;
state->LastAddsSpace = addsSpace;
}
// Collects builtin command matches for a command-position token.
static void shellCollectBuiltinCompletions(const char* prefix, size_t prefixLength,
SHELL_COMPLETION_STATE* state,
bool printMatches) {
size_t count = sizeof(gShellBuiltinCommands) / sizeof(gShellBuiltinCommands[0]);
for (size_t i = 0; i < count; i++) {
const char* name = gShellBuiltinCommands[i];
if (!shellNameStartsWith(name, prefix, prefixLength)) continue;
if (printMatches) shellPrintCompletionCandidate(name, false);
else shellRecordCompletion(state, name, false, true);
}
}
// Splits a token into a directory to scan and a filename prefix to match.
static bool shellSplitCompletionPath(const char* token, size_t tokenLength,
char* dir, size_t dirSize,
const char** prefixOut, size_t* prefixLengthOut) {
if (!token || !dir || dirSize == 0 || !prefixOut || !prefixLengthOut) return false;
size_t lastSeparator = (size_t)-1;
for (size_t i = 0; i < tokenLength; i++) {
if (shellIsPathSeparator(token[i])) lastSeparator = i;
}
if (lastSeparator == (size_t)-1) {
if (dirSize < 2) return false;
dir[0] = '.';
dir[1] = '\0';
*prefixOut = token;
*prefixLengthOut = tokenLength;
return true;
}
size_t dirLength = lastSeparator;
if (lastSeparator == 0) dirLength = 1;
if (lastSeparator >= 2 && token[1] == ':' && shellIsPathSeparator(token[2])) dirLength = lastSeparator + 1;
if (dirLength + 1 > dirSize) return false;
memcpy(dir, token, dirLength);
dir[dirLength] = '\0';
*prefixOut = token + lastSeparator + 1;
*prefixLengthOut = tokenLength - lastSeparator - 1;
return true;
}
// Collects filesystem path matches using the existing directory enumeration ABI.
static void shellCollectPathCompletions(const char* token, size_t tokenLength,
SHELL_COMPLETION_STATE* state,
bool printMatches) {
char dir[MAX_CMD];
const char* prefix = token;
size_t prefixLength = tokenLength;
if (!shellSplitCompletionPath(token, tokenLength, dir, sizeof(dir), &prefix, &prefixLength)) return;
uint32_t hDir = 0;
if (open_dir(dir, &hDir) != 0) return;
SYS_DIR_ENTRY entry;
while (SysReadDir(hDir, &entry) == 0) {
if (!entry.Name[0]) continue;
if (!shellNameStartsWith(entry.Name, prefix, prefixLength)) continue;
bool directory = (entry.Attr & 0x10u) != 0;
if (printMatches) shellPrintCompletionCandidate(entry.Name, directory);
else shellRecordCompletion(state, entry.Name, directory, true);
}
SysCloseHandle(hDir);
}
// Redraws the prompt and current edit buffer after a completion list.
static void shellRedrawPromptAndLine(const char* buf) {
print(SHELL_PROMPT);
print(buf ? buf : "");
}
// Completes the token at the end of the current line.
static void shellCompleteLine(char* buf, int len, int* pos) {
if (!buf || len <= 0 || !pos || *pos < 0) return;
size_t cursor = (size_t)*pos;
size_t tokenStart = cursor;
while (tokenStart > 0 && !shellIsTokenSeparator(buf[tokenStart - 1])) tokenStart--;
const char* token = buf + tokenStart;
size_t tokenLength = cursor - tokenStart;
bool commandPosition = tokenStart == 0;
bool explicitPath = shellTokenHasPathSeparator(token, tokenLength);
char completionDir[MAX_CMD];
const char* namePrefix = token;
size_t namePrefixLength = tokenLength;
(void)shellSplitCompletionPath(token, tokenLength, completionDir, sizeof(completionDir), &namePrefix, &namePrefixLength);
SHELL_COMPLETION_STATE state;
memset(&state, 0, sizeof(state));
if (commandPosition && !explicitPath) {
shellCollectBuiltinCompletions(token, tokenLength, &state, false);
}
shellCollectPathCompletions(token, tokenLength, &state, false);
if (state.MatchCount == 0) return;
size_t commonLength = strlen(state.Common);
bool expanded = commonLength > namePrefixLength;
if (expanded) {
shellEditorAppend(buf, len, pos, state.Common + namePrefixLength);
}
if (state.MatchCount == 1) {
if (state.LastIsDirectory) {
if (*pos < len - 1 && *pos > 0 && !shellIsPathSeparator(buf[*pos - 1])) shellEditorAppend(buf, len, pos, "/");
} else if (state.LastAddsSpace) {
if (*pos < len - 1 && (*pos == 0 || buf[*pos - 1] != ' ')) shellEditorAppend(buf, len, pos, " ");
}
return;
}
if (expanded) return;
print("\n");
if (commandPosition && !explicitPath) {
shellCollectBuiltinCompletions(token, tokenLength, NULL, true);
}
shellCollectPathCompletions(token, tokenLength, NULL, true);
shellRedrawPromptAndLine(buf);
}
// Reads one editable command line from the keyboard.
static bool readLine(char* buf, int len) {
int pos = 0;
size_t historyCursor = gHistoryCount;
if (len <= 0) return false;
buf[0] = '\0';
while (true) {
INPUT_KEY_RECORD input;
if (SysReadInputEvent(&input) != 0) { SysYield(); continue; }
INPUT_KEY key = input.Key;
if (key == INPUT_KEY_NONE) { SysYield(); continue; }
uint32_t modifiers = input.Modifiers;
if ((modifiers & INPUT_MOD_CTRL) && key == INPUT_KEY_C) {
buf[0] = '\0';
print("^C\n");
return true;
}
if (key == INPUT_KEY_ENTER || key == INPUT_KEY_KP_ENTER) {
print("\n");
break;
}
if (key == INPUT_KEY_BACKSPACE) {
if (pos > 0) {
pos--;
buf[pos] = '\0';
print("\b \b");
}
continue;
}
if (key == INPUT_KEY_UP) {
if (historyCursor > 0) {
historyCursor--;
shellEditorReplaceLine(buf, len, &pos, gHistory[historyCursor]);
}
continue;
}
if (key == INPUT_KEY_DOWN) {
if (historyCursor < gHistoryCount) {
historyCursor++;
shellEditorReplaceLine(buf, len, &pos,
historyCursor < gHistoryCount ? gHistory[historyCursor] : "");
}
continue;
}
if (key == INPUT_KEY_TAB) {
shellCompleteLine(buf, len, &pos);
continue;
}
char c = input.Char ? input.Char : shellKeyToChar(key, modifiers);
if (c < 0x20 || c == 127) continue;
if (pos < len - 1) {
buf[pos++] = c;
buf[pos] = '\0';
char echo[2] = { c, '\0' };
print(echo);
}
}
return false;
}
// Implements the trim operation.
static char* trim(char* s) {
while (*s == ' ') s++;
int len = (int)strlen(s);
while (len > 0 && s[len - 1] == ' ') len--;
s[len] = '\0';
return s;
}
// Built-in commands.
static void cmd_help() {
print("Available commands:\n"
" help - Show this list\n"
" mem - Show memory usage\n"
" time - Show system wall-clock time\n"
" display - Show display provider and counters\n"
" stats - Show scheduler/CPU/MM telemetry\n"
" ps - List processes\n"
" threads <pid> - Show one process's thread statuses\n"
" kill <pid> - Terminate a process\n"
" dir [path] - List directory contents\n"
" cd <path> - Change directory\n"
" type <file> - Display file contents\n"
" del <file> - Delete a file\n"
" write <file> <tx> - Create a file with text\n"
" clear - Clear the screen\n"
" exit - Close the shell\n");
}
// Prints size Ki B.
static void printSizeKiB(uint64_t bytes) {
printUInt64(bytes / 1024u);
print(" KiB");
}
// Prints pages Ki B.
static void printPagesKiB(uint64_t pages) {
printUInt64(pages * 4u);
print(" KiB");
}
// Prints cpu Flags.
static void printCpuFlags(uint32_t flags) {
if (flags & SYS_CPU_FLAG_BSP) print("bsp ");
if (flags & SYS_CPU_FLAG_ONLINE) print("online ");
if (flags & SYS_CPU_FLAG_PARKED) print("parked ");
if (flags & SYS_CPU_FLAG_PRIMARY) print("primary ");
if (flags & SYS_CPU_FLAG_STARTED) print("started ");
if (flags & SYS_CPU_FLAG_PRESENT) print("present ");
}
// Implements the cmd stats operation.
static void cmd_stats(void) {
SYS_SCHEDULER_STATS sched;
SYS_PMM_STATS pmm;
SYS_CPU_STATS cpu;
SYS_PFN_STATS pfn;
SYS_VAD_STATS vad;
SYS_SWAP_STATS swap;
SYS_FILE_CACHE_STATS fileCache;
SYS_FILESYSTEM_STATS filesystem;
SYS_DISK_STATS disk;
SYS_PTE_STATS pte;
SYS_SURFACE_STATS surface;
uint32_t status;
status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, &sched, sizeof(sched), NULL);
if (status != 0) {
print("stats: scheduler query failed: ");
printUInt(status);
print("\n");
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_PMM, &pmm, sizeof(pmm), NULL);
if (status != 0) {
print("stats: pmm query failed: ");
printUInt(status);
print("\n");
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, &cpu, sizeof(cpu), NULL);
if (status != 0) {
print("stats: cpu query failed: ");
printUInt(status);
print("\n");
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_PFN, &pfn, sizeof(pfn), NULL);
if (status != 0) {
print("stats: pfn query failed: ");
printUInt(status);
print("\n");
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_VAD, &vad, sizeof(vad), NULL);
if (status != 0) {
print("stats: vad query failed: ");
printUInt(status);
print("\n");
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_SWAP, &swap, sizeof(swap), NULL);
if (status != 0) {
print("stats: swap query failed: ");
printUInt(status);
print("\n");
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, &fileCache, sizeof(fileCache), NULL);
if (status != 0) {
print("stats: file cache query failed: ");
printUInt(status);
print("\n");
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_FILESYSTEM, &filesystem, sizeof(filesystem), NULL);
if (status != 0) {
print("stats: filesystem query failed: ");
printUInt(status);
print("\n");
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_DISK, &disk, sizeof(disk), NULL);
if (status != 0) {
print("stats: disk query failed: ");
printUInt(status);
print("\n");
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_PTE, &pte, sizeof(pte), NULL);
if (status != 0) {
print("stats: pte query failed: ");
printUInt(status);
print("\n");
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_SURFACE, &surface, sizeof(surface), NULL);
if (status != 0) {
print("stats: surface query failed: ");
printUInt(status);
print("\n");
return;
}
print("Scheduler:\n");
print(" ticks: "); printUInt64(sched.Ticks); print("\n");
print(" context switches: "); printUInt64(sched.ContextSwitches); print("\n");
print(" quantum expires: "); printUInt64(sched.QuantumExpirations); print("\n");
print(" priority preempts: "); printUInt64(sched.PriorityPreemptions); print("\n");
print(" idle selections: "); printUInt64(sched.IdleSelections); print("\n");
print(" keep-current: "); printUInt64(sched.KeepCurrentDecisions); print("\n");
print(" demotions: "); printUInt64(sched.Demotions); print("\n");
print(" aging promotions: "); printUInt64(sched.AgingPromotions); print("\n");
print(" voluntary blocks: "); printUInt64(sched.VoluntaryBlocks); print("\n");
print(" SMT deferrals: "); printUInt64(sched.SmtSiblingDeferrals); print("\n");
print(" SMT primary pokes: "); printUInt64(sched.SmtPrimaryPokes); print("\n");
print("CPUs:\n");
uint32_t n = cpu.CpuCount;
if (n > SYS_KERNEL_STATS_MAX_CPUS)
n = SYS_KERNEL_STATS_MAX_CPUS;
for (uint32_t i = 0; i < n; i++) {
SYS_CPU_STAT* c = &cpu.Cpu[i];
print(" cpu"); printUInt(c->CpuIndex);
print(" lapic="); printUInt(c->LapicId);
print(" current=");
if (c->CurrentThreadFlags & SYS_CPU_THREAD_FLAG_IDLE) {
print("idle/"); printUInt(c->CpuIndex);
} else {
print("tid/"); printUInt(c->CurrentTid);
}
print(" ticks="); printUInt64(c->SchedulerTicks);
print(" switches="); printUInt64(c->ContextSwitches);
print(" flags="); printCpuFlags(c->Flags);
print("\n");
}
print("Physical memory:\n");
print(" total: "); printPagesKiB(pmm.TotalFrames); print(" (frames="); printUInt64(pmm.TotalFrames); print(")\n");
print(" used: "); printPagesKiB(pmm.UsedFrames); print(" (frames="); printUInt64(pmm.UsedFrames); print(")\n");
print(" free: "); printPagesKiB(pmm.FreeFrames); print(" (frames="); printUInt64(pmm.FreeFrames); print(")\n");
print(" largest free run: "); printPagesKiB(pmm.LargestFreeRun); print(" (frames="); printUInt64(pmm.LargestFreeRun); print(")\n");
print(" free runs: "); printUInt64(pmm.FreeRunCount); print("\n");
print(" available+reclaim: "); printSizeKiB(pfn.AvailableFrames * 4096ull);
print(" (reclaimable="); printSizeKiB(pfn.ReclaimableFrames * 4096ull); print(")\n");
print(" run histogram:\n");
print(" 1: "); printUInt64(pmm.FreeRunHistogram[0]); print("\n");
print(" 2-3: "); printUInt64(pmm.FreeRunHistogram[1]); print("\n");
print(" 4-7: "); printUInt64(pmm.FreeRunHistogram[2]); print("\n");
print(" 8-15: "); printUInt64(pmm.FreeRunHistogram[3]); print("\n");
print(" 16-63: "); printUInt64(pmm.FreeRunHistogram[4]); print("\n");
print(" 64-255: "); printUInt64(pmm.FreeRunHistogram[5]); print("\n");
print(" 256-1023: "); printUInt64(pmm.FreeRunHistogram[6]); print("\n");
print(" 1024+: "); printUInt64(pmm.FreeRunHistogram[7]); print("\n");
print("PFN database:\n");
print(" states: active="); printUInt64(pfn.ActiveFrames);
print(" free="); printUInt64(pfn.FreeFrames);
print(" zeroed="); printUInt64(pfn.ZeroedFrames);
print(" standby="); printUInt64(pfn.StandbyFrames);
print(" modified="); printUInt64(pfn.ModifiedFrames);
print(" reserved="); printUInt64(pfn.ReservedFrames); print("\n");
print(" eviction: evictable="); printUInt64(pfn.EvictableFrames);
print(" reclaimable="); printUInt64(pfn.ReclaimableFrames);
print(" available="); printUInt64(pfn.AvailableFrames);
print(" mapped="); printUInt64(pfn.MappedFrames);
print(" non-evictable="); printUInt64(pfn.NonEvictableFrames);
print(" pinned="); printUInt64(pfn.PinnedFrames); print("\n");
print(" usage frames: kernel="); printUInt64(pfn.KernelFrames);
print(" pt="); printUInt64(pfn.PageTableFrames);
print(" user="); printUInt64(pfn.UserFrames);
print(" driver="); printUInt64(pfn.DriverFrames);
print(" dma="); printUInt64(pfn.DmaFrames);
print(" boot="); printUInt64(pfn.BootloaderFrames);
print(" pfn-db="); printUInt64(pfn.PfnDatabaseFrames);
print(" mmio="); printUInt64(pfn.MmioFrames); print("\n");
print(" buckets: clean-file="); printUInt64(pfn.CleanFileCacheFrames);
print(" (active="); printUInt64(pfn.ActiveCleanFileCacheFrames);
print(" standby="); printUInt64(pfn.StandbyCleanFileCacheFrames); print(")");
print(" image="); printUInt64(pfn.ImageFrames);
print(" (active="); printUInt64(pfn.ActiveImageFrames);
print(" standby="); printUInt64(pfn.StandbyImageFrames); print(")");
print(" mapped-file="); printUInt64(pfn.MappedFileFrames);
print(" anon="); printUInt64(pfn.AnonymousFrames);
print(" cow="); printUInt64(pfn.CowFrames);
print(" surface="); printUInt64(pfn.SurfaceFrames); print("\n");
print(" operations: alloc="); printUInt64(pfn.AllocationCount);
print(" free="); printUInt64(pfn.FreeCount);
print(" retain="); printUInt64(pfn.RetainCount);
print(" release="); printUInt64(pfn.ReleaseCount);
print(" pin="); printUInt64(pfn.PinCount);
print(" unpin="); printUInt64(pfn.UnpinCount); print("\n");
print(" failures: alloc="); printUInt64(pfn.FailedAllocations);
print(" contiguous="); printUInt64(pfn.FailedContiguousAllocations); print("\n");
print(" alloc reasons: invalid-range="); printUInt64(pfn.FailedInvalidRange);
print(" empty-range="); printUInt64(pfn.FailedEmptyRange);
print(" reserve-denied="); printUInt64(pfn.FailedReserveDenied);
print(" no-frame="); printUInt64(pfn.FailedNoFrame); print("\n");
print(" contig reasons: invalid="); printUInt64(pfn.FailedContiguousInvalidRequest);
print(" reserve-denied="); printUInt64(pfn.FailedContiguousReserveDenied);
print(" no-run="); printUInt64(pfn.FailedContiguousNoRun); print("\n");
print(" reclaim: attempts="); printUInt64(pfn.ReclaimAttempts);
print(" requested="); printUInt64(pfn.ReclaimPagesRequested);
print(" succeeded="); printUInt64(pfn.ReclaimPagesSucceeded);
print(" failed="); printUInt64(pfn.ReclaimPagesFailed);
print(" reentrant-skips="); printUInt64(pfn.ReclaimReentrantSkips); print("\n");
print("VAD faults:\n");
print(" resolve: calls="); printUInt64(vad.ResolveFaultCalls);
print(" resolved="); printUInt64(vad.ResolvedFaults);
print(" failed="); printUInt64(vad.FailedFaults); print("\n");
print(" failure reasons: protection="); printUInt64(vad.ProtectionFaults);
print(" no-vad="); printUInt64(vad.NoVadFaults);
print(" uncommitted="); printUInt64(vad.UncommittedFaults);
print(" noaccess="); printUInt64(vad.NoAccessFaults); print("\n");
print(" materialization: demand-zero="); printUInt64(vad.DemandZeroPages);
print(" zero-maps="); printUInt64(vad.ZeroPageMaps);
print(" zero-splits="); printUInt64(vad.ZeroPageCowSplits);
print(" ensure-calls="); printUInt64(vad.EnsureResidentCalls);
print(" ensure-pages="); printUInt64(vad.EnsureResidentPages);
print(" already-resident="); printUInt64(vad.AlreadyResidentPages); print("\n");
print(" cow: faults="); printUInt64(vad.CowFaults);
print(" copied="); printUInt64(vad.CowPagesCopied);
print(" views="); printUInt64(vad.CowViewsCreated);
print(" shared-pages="); printUInt64(vad.CowPagesShared); print("\n");
print(" file-backed: faults="); printUInt64(vad.FileBackedFaults);
print(" reads="); printUInt64(vad.FileBackedReads);
print(" read-fail="); printUInt64(vad.FileBackedReadFailures);
print(" discards="); printUInt64(vad.FileBackedDiscards); print("\n");
print(" mapped-file: shared-faults="); printUInt64(vad.FileBackedSharedWritableFaults);
print(" dirty-faults="); printUInt64(vad.FileBackedDirtyFaults);
print(" dirty-syncs="); printUInt64(vad.FileBackedDirtySyncs); print("\n");
print(" image-backed: faults="); printUInt64(vad.ImageBackedFaults);
print(" zero-faults="); printUInt64(vad.ImageBackedZeroFaults);
print(" private-faults="); printUInt64(vad.ImageBackedPrivateFaults); print("\n");
print(" image fixups: calls="); printUInt64(vad.ImageFixupPrivateCalls);
print(" private-pages="); printUInt64(vad.ImageFixupPrivatePages); print("\n");
print(" swap-backed: page-outs="); printUInt64(vad.SwapPageOuts);
print(" page-ins="); printUInt64(vad.SwapPageIns); print("\n");
print("Swap:\n");
print(" enabled: "); printUInt64(swap.Enabled); print("\n");
print(" slots: total="); printUInt64(swap.TotalSlots);
print(" used="); printUInt64(swap.UsedSlots);
print(" free="); printUInt64(swap.FreeSlots);
print(" high-water="); printUInt64(swap.HighWaterSlots); print("\n");
print(" io: out="); printUInt64(swap.PageOuts);
print(" in="); printUInt64(swap.PageIns);
print(" out-fail="); printUInt64(swap.PageOutFailures);
print(" in-fail="); printUInt64(swap.PageInFailures); print("\n");
print(" slots ops: alloc="); printUInt64(swap.SlotAllocations);
print(" free="); printUInt64(swap.SlotFrees);
print(" alloc-fail="); printUInt64(swap.SlotAllocationFailures); print("\n");
print("File cache:\n");
print(" lookups: total="); printUInt64(fileCache.Lookups);
print(" hits="); printUInt64(fileCache.Hits);
print(" misses="); printUInt64(fileCache.Misses); print("\n");
print(" loads: ok="); printUInt64(fileCache.Loads);
print(" fail="); printUInt64(fileCache.LoadFailures);
print(" races="); printUInt64(fileCache.RaceDiscards);
print(" waits="); printUInt64(fileCache.InflightWaits); print("\n");
print(" lifetime: resident="); printUInt64(fileCache.ResidentPages);
print(" active="); printUInt64(fileCache.ActivePages);
print(" standby="); printUInt64(fileCache.StandbyPages);
print(" max="); printUInt64(fileCache.MaxResidentPages);
print(" invalidations="); printUInt64(fileCache.Invalidations); print("\n");
print(" dirty/writeback: dirty="); printUInt64(fileCache.DirtyPages);
print(" max-dirty="); printUInt64(fileCache.MaxDirtyPages);
print(" marks="); printUInt64(fileCache.DirtyMarks);
print(" wb-req="); printUInt64(fileCache.WritebackRequests);
print(" wb-pages="); printUInt64(fileCache.WritebackPages);
print(" wb-bytes="); printUInt64(fileCache.WritebackBytes);
print(" wb-fail="); printUInt64(fileCache.WritebackFailures); print("\n");
print(" mapped dirty: syncs="); printUInt64(fileCache.MappedDirtySyncs);
print(" pages="); printUInt64(fileCache.MappedDirtyPages); print("\n");
print(" flushes: calls="); printUInt64(fileCache.FlushFileCalls);
print(" fail="); printUInt64(fileCache.FlushFileFailures); print("\n");
print(" reclaim: drops="); printUInt64(fileCache.Drops);
print(" standby="); printUInt64(fileCache.StandbyTransitions);
print(" standby-hits="); printUInt64(fileCache.StandbyHits);
print(" drop-fail="); printUInt64(fileCache.DropFailures); print("\n");
print(" read-ahead: requests="); printUInt64(fileCache.ReadAheadRequests);
print(" requested="); printUInt64(fileCache.ReadAheadPagesRequested);
print(" queued="); printUInt64(fileCache.ReadAheadPagesQueued);
print(" loaded="); printUInt64(fileCache.ReadAheadPagesLoaded);
print(" skipped="); printUInt64(fileCache.ReadAheadPagesSkipped);
print(" fail="); printUInt64(fileCache.ReadAheadFailures);
print(" throttled="); printUInt64(fileCache.ReadAheadPagesThrottled); print("\n");
print(" read-ahead max: pages/request="); printUInt64(fileCache.MaxReadAheadPagesPerRequest); print("\n");
print(" batch fills: reads="); printUInt64(fileCache.BatchReads);
print(" requested="); printUInt64(fileCache.BatchPagesRequested);
print(" loaded="); printUInt64(fileCache.BatchPagesLoaded);
print(" max-request="); printUInt64(fileCache.MaxBatchPagesRequested);
print(" max-loaded="); printUInt64(fileCache.MaxBatchPagesLoaded); print("\n");
print("Filesystem:\n");
print(" path cache: lookups="); printUInt64(filesystem.PathLookups);
print(" hits="); printUInt64(filesystem.PathCacheHits);
print(" misses="); printUInt64(filesystem.PathCacheMisses);
print(" stores="); printUInt64(filesystem.PathCacheStores); print("\n");
print(" invalidation: count="); printUInt64(filesystem.PathCacheInvalidations);
print(" generation="); printUInt64(filesystem.PathCacheGeneration); print("\n");
print(" directory search: searches="); printUInt64(filesystem.DirectorySearches);
print(" clusters="); printUInt64(filesystem.DirectoryClustersRead);
print(" entries="); printUInt64(filesystem.DirectoryEntriesScanned); print("\n");
print(" FAT chain cache: lookups="); printUInt64(filesystem.FatChainCacheLookups);
print(" hits="); printUInt64(filesystem.FatChainCacheHits);
print(" misses="); printUInt64(filesystem.FatChainCacheMisses);
print(" stores="); printUInt64(filesystem.FatChainCacheStores); print("\n");
print(" FAT chain work: invalidations="); printUInt64(filesystem.FatChainCacheInvalidations);
print(" fat-steps="); printUInt64(filesystem.FatChainWalkSteps); print("\n");
print(" data read spans: runs="); printUInt64(filesystem.DataReadRuns);
print(" sectors="); printUInt64(filesystem.DataReadSectors);
print(" merges="); printUInt64(filesystem.DataReadRunMerges);
print(" max-sectors="); printUInt64(filesystem.MaxDataReadSpanSectors); print("\n");
print("Disk I/O:\n");
print(" devices: registered="); printUInt64(disk.RegisteredDisks); print("\n");
print(" reads: requests="); printUInt64(disk.ReadRequests);
print(" sectors="); printUInt64(disk.ReadSectors);
print(" max-sectors="); printUInt64(disk.MaxReadSectors); print("\n");
print(" writes: requests="); printUInt64(disk.WriteRequests);
print(" sectors="); printUInt64(disk.WriteSectors);
print(" max-sectors="); printUInt64(disk.MaxWriteSectors); print("\n");
print(" read-ahead: hits="); printUInt64(disk.ReadaheadHits);
print(" misses="); printUInt64(disk.ReadaheadMisses);
print(" stores="); printUInt64(disk.ReadaheadStores);
print(" sectors="); printUInt64(disk.ReadaheadSectorsLoaded);
print(" max-sectors="); printUInt64(disk.MaxReadaheadSectors); print("\n");
print(" read-ahead misc: served-bytes="); printUInt64(disk.ReadaheadBytesServed);
print(" seq="); printUInt64(disk.ReadaheadSequentialDetections);
print(" invalidations="); printUInt64(disk.ReadaheadInvalidations);
print(" suppressed="); printUInt64(disk.ReadaheadSuppressed);
print(" alloc-fail="); printUInt64(disk.ReadaheadAllocFailures);
print(" read-fail="); printUInt64(disk.ReadaheadReadFailures); print("\n");
print("Surfaces:\n");
print(" lifetime: created="); printUInt64(surface.CreatedSurfaces);
print(" destroyed="); printUInt64(surface.DestroyedSurfaces);
print(" live="); printUInt64(surface.LiveSurfaces); print("\n");
print(" live memory: pages="); printUInt64(surface.LivePages);
print(" bytes="); printUInt64(surface.LiveBytes); print("\n");
print(" maps/presents: maps="); printUInt64(surface.MapCalls);
print(" presents="); printUInt64(surface.PresentCalls);
print(" input-rects="); printUInt64(surface.PresentRectInputs); print("\n");
print(" present work: clipped="); printUInt64(surface.PresentClippedRects);
print(" empty="); printUInt64(surface.PresentEmptyClips);
print(" rows="); printUInt64(surface.PresentRowsCopied);
print(" bytes="); printUInt64(surface.PresentBytesCopied); print("\n");
print("Page tables:\n");
print(" leaf mappings: "); printUInt64(pte.CurrentLeafMappings); print("\n");
print(" page-table pages: "); printUInt64(pte.PageTablePagesAllocated); print("\n");
print(" map/unmap: map="); printUInt64(pte.MapCalls);
print(" failed="); printUInt64(pte.MapFailures);
print(" unmap="); printUInt64(pte.UnmapCalls);
print(" failed="); printUInt64(pte.UnmapFailures); print("\n");
print(" protect: calls="); printUInt64(pte.ProtectCalls);
print(" failed="); printUInt64(pte.ProtectFailures); print("\n");
print(" query: total="); printUInt64(pte.QueryCalls);
print(" failed="); printUInt64(pte.QueryFailures);
print(" translate="); printUInt64(pte.TranslateCalls); print("\n");
print(" generic: "); printUInt64(pte.QueryByPurpose[0]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[0]); print("\n");
print(" user-probe: "); printUInt64(pte.QueryByPurpose[1]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[1]); print("\n");
print(" heap: "); printUInt64(pte.QueryByPurpose[2]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[2]); print("\n");
print(" vad: "); printUInt64(pte.QueryByPurpose[3]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[3]); print("\n");
print(" debug: "); printUInt64(pte.QueryByPurpose[4]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[4]); print("\n");
print(" self-test: "); printUInt64(pte.QueryByPurpose[5]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[5]); print("\n");
print(" tlb: local="); printUInt64(pte.LocalFlushes);
print(" global="); printUInt64(pte.GlobalFlushes);
print(" new-map-skip="); printUInt64(pte.NewMappingNoFlushes);
print(" replace-flush="); printUInt64(pte.ReplacedMappingFlushes); print("\n");
print(" shootdowns: requests="); printUInt64(pte.ShootdownRequests);
print(" targets="); printUInt64(pte.ShootdownTargetCpus);
print(" skipped="); printUInt64(pte.ShootdownSkippedNoTargets);
print(" acks="); printUInt64(pte.ShootdownAcks);
print(" timeouts="); printUInt64(pte.ShootdownTimeouts); print("\n");
}
// Implements the cmd time operation.
static void cmd_time(void) {
SYS_SYSTEM_TIME t;
uint32_t status = SysGetSystemTime(&t);
if (status != 0) {
print("time: failed: ");
printUInt(status);
print("\n");
return;
}
printUIntZeroWidth(t.Year, 4); print("-");
printUIntZeroWidth(t.Month, 2); print("-");
printUIntZeroWidth(t.Day, 2); print(" ");
printUIntZeroWidth(t.Hour, 2); print(":");
printUIntZeroWidth(t.Minute, 2); print(":");
printUIntZeroWidth(t.Second, 2); print(".");
printUIntZeroWidth(t.Millisecond, 3);
print(" epoch="); printUInt64(t.EpochSeconds);
print(" flags=0x"); printUInt(t.Flags);
if (t.Flags & SYS_TIME_FLAG_TZ_KNOWN) {
print(" tz="); printInt(t.TimeZoneMinutes);
} else {
print(" tz=unspecified");
}
print("\n");
}
// Prints display Caps.
static void printDisplayCaps(uint32_t caps) {
bool any = false;
if (caps & DISPLAY_CAP_FRAMEBUFFER) { print("framebuffer"); any = true; }
if (caps & DISPLAY_CAP_SOFTWARE_CURSOR) { if (any) print(","); print("software-cursor"); any = true; }
if (caps & DISPLAY_CAP_HARDWARE_CURSOR) { if (any) print(","); print("hardware-cursor"); any = true; }
if (caps & DISPLAY_CAP_ACCEL_FILL) { if (any) print(","); print("accel-fill"); any = true; }
if (caps & DISPLAY_CAP_ACCEL_COPY) { if (any) print(","); print("accel-copy"); any = true; }
if (caps & DISPLAY_CAP_PRESENT_SYNC) { if (any) print(","); print("present-sync"); any = true; }
if (caps & DISPLAY_CAP_VBLANK) { if (any) print(","); print("vblank"); any = true; }
if (caps & DISPLAY_CAP_3D) { if (any) print(","); print("3d"); any = true; }
if (!any) print("none");
}
// Prints display Stats.
static void printDisplayStats(const char* title, const DISPLAY_STATS* st) {
print(title); print(":\n");
print(" blit: "); printUInt64(st->BlitCount); print("\n");
print(" fill: "); printUInt64(st->FillRectCount); print("\n");
print(" copy: "); printUInt64(st->CopyRectCount); print("\n");
print(" read: "); printUInt64(st->ReadRectCount); print("\n");
print(" present: "); printUInt64(st->PresentCount); print(" rects="); printUInt64(st->PresentRectCount); print("\n");
print(" dirty: "); printUInt64(st->DirtyRectCount); print(" all="); printUInt64(st->DirtyAllCount); print("\n");
print(" bytes: "); printUInt64(st->BytesCopied); print("\n");
print(" sync: "); printUInt64(st->SyncCount); print("\n");
print(" cursor: move="); printUInt64(st->CursorMoveCount);
print(" show="); printUInt64(st->CursorShowCount);
print(" hide="); printUInt64(st->CursorHideCount); print("\n");
}
// Implements the cmd display operation.
static void cmd_display(void) {
SYS_DISPLAY_INFO info;
uint32_t status = SysQueryDisplayInfo(&info);
if (status != 0) {
print("display: query failed: ");
printUInt(status);
print("\n");
return;
}
print("Display:\n");
print(" primary: "); print(info.PrimaryName[0] ? info.PrimaryName : "?"); print("\n");
print(" devices: "); printUInt(info.DeviceCount); print("\n");
print(" mode: "); printUInt(info.Mode.Width); print("x"); printUInt(info.Mode.Height);
print(" stride="); printUInt(info.Mode.Stride); print(" format="); printUInt((uint32_t)info.Mode.Format); print("\n");
print(" caps: 0x"); printHex32(info.Caps); print(" ["); printDisplayCaps(info.Caps); print("]\n");
printDisplayStats("Manager counters", &info.ManagerStats);
printDisplayStats("Provider counters", &info.DeviceStats);
}
// Implements the cmd mem operation.
static void cmd_mem(void) {
SYS_MEMORY_STATUS st;
if (SysGetMemoryStatus(&st) != 0) {
print("mem: failed\n");
return;
}
print("Memory Status:\n");
print(" Free physical: ");
printSizeKiB(st.FreePhysicalBytes);
print("\n Reclaimable: ");
printSizeKiB(st.ReclaimablePhysicalBytes);
print("\n Available physical: ");
printSizeKiB(st.AvailablePhysicalBytes);
print("\n Pinned physical: ");
printSizeKiB(st.PinnedPhysicalBytes);
print("\n Non-evictable: ");
printSizeKiB(st.NonEvictablePhysicalBytes);
print("\n User frames: ");
printSizeKiB(st.UserPhysicalBytes);
print("\n Page tables: ");
printSizeKiB(st.PageTablePhysicalBytes);
print("\n Kernel pool: ");
printSizeKiB(st.KernelPoolBytes);
print("\n User heap: ");
printSizeKiB(st.UserHeapBytes);
print("\n");
}
// Processes state Name.
static const char* processStateName(uint32_t state) {
switch (state) {
case SYS_PROCESS_STATE_RUNNING: return "run";
case SYS_PROCESS_STATE_TERMINATING: return "term";
case SYS_PROCESS_STATE_EXITED: return "exit";
default: return "?";
}
}
// Names public thread states.
static const char* threadStateName(uint32_t state) {
switch (state) {
case SYS_THREAD_STATE_READY: return "ready";
case SYS_THREAD_STATE_RUNNING: return "run";
case SYS_THREAD_STATE_WAITING: return "wait";
case SYS_THREAD_STATE_DEAD: return "dead";
default: return "?";
}
}
// Names public wait object types.
static const char* threadWaitName(uint32_t waitType) {
switch (waitType) {
case SYS_THREAD_WAIT_NONE: return "-";
case SYS_THREAD_WAIT_PROCESS: return "process";
case SYS_THREAD_WAIT_THREAD: return "thread";
case SYS_THREAD_WAIT_MUTEX: return "mutex";
case SYS_THREAD_WAIT_SEMAPHORE: return "sem";
case SYS_THREAD_WAIT_EVENT: return "event";
case SYS_THREAD_WAIT_QUEUE: return "queue";
case SYS_THREAD_WAIT_FILE: return "file";
case SYS_THREAD_WAIT_DIR: return "dir";
case SYS_THREAD_WAIT_ASYNC_IO: return "async";
case SYS_THREAD_WAIT_SOCKET: return "socket";
case SYS_THREAD_WAIT_DEVICE: return "device";
case SYS_THREAD_WAIT_DRIVER: return "driver";
case SYS_THREAD_WAIT_USB_DEVICE: return "usbdev";
case SYS_THREAD_WAIT_USB_PIPE: return "usbpipe";
case SYS_THREAD_WAIT_FILE_MAPPING: return "mapping";
case SYS_THREAD_WAIT_SURFACE: return "surface";
default: return "unknown";
}
}
// Prints thread status flags.
static void printThreadFlags(uint32_t flags) {
char buf[8];
size_t n = 0;
if (flags & SYS_THREAD_FLAG_CURRENT) buf[n++] = '*';
if (flags & SYS_THREAD_FLAG_IDLE) buf[n++] = 'I';
if (flags & SYS_THREAD_FLAG_KERNEL) buf[n++] = 'K';
if (flags & SYS_THREAD_FLAG_QUEUED) buf[n++] = 'Q';
if (flags & SYS_THREAD_FLAG_ALERTABLE_WAIT) buf[n++] = 'A';
if (flags & SYS_THREAD_FLAG_KERNEL_APC_PENDING) buf[n++] = 'k';
if (flags & SYS_THREAD_FLAG_USER_APC_PENDING) buf[n++] = 'u';
if (n == 0) buf[n++] = '-';
buf[n] = '\0';
printStringWidth(buf, 7);
}
// Implements the cmd ps operation.
static void cmd_ps(void) {
SYS_PROCESS_INFO procs[MAX_PROCS];
size_t count = 0;
uint32_t status = SysGetProcessList(procs, MAX_PROCS, &count);
if (status != 0) {
print("ps: failed to retrieve process list\n");
return;
}
print("Processes (Count: ");
printUInt((uint32_t)count);
print("):\n");
print(" PID State Thr Hnd Mod UserKiB ImageKiB Flags Name\n");
print(" Flags: S=system I=idle *=current T=terminating X=exited.\n");
size_t displayCount = count < MAX_PROCS ? count : MAX_PROCS;
for (size_t i = 0; i < displayCount; i++) {
SYS_PROCESS_INFO* p = &procs[i];
char flags[6];
size_t flagLen = 0;
if (p->Flags & SYS_PROCESS_FLAG_SYSTEM) flags[flagLen++] = 'S';
if (p->Flags & SYS_PROCESS_FLAG_IDLE) flags[flagLen++] = 'I';
if (p->Flags & SYS_PROCESS_FLAG_CURRENT) flags[flagLen++] = '*';
if (p->Flags & SYS_PROCESS_FLAG_TERMINATING) flags[flagLen++] = 'T';
if (p->Flags & SYS_PROCESS_FLAG_EXITED) flags[flagLen++] = 'X';
if (flagLen == 0) flags[flagLen++] = '-';
flags[flagLen] = '\0';
print(" ");
printUIntWidth(p->ProcessId, 3);
print(" ");
printStringWidth(processStateName(p->State), 5);
print(" ");
printUIntWidth(p->ThreadCount, 3);
print(" ");
printUIntWidth(p->HandleCount, 3);
print(" ");
printUIntWidth(p->ModuleCount, 3);
print(" ");
printUInt64Width((uint64_t)p->UserCommittedBytes / 1024u, 7);
print(" ");
printUInt64Width((uint64_t)p->ImageSize / 1024u, 8);
print(" ");
printStringWidth(flags, 5);
print(" ");
print(p->Name);
if (p->CurrentDirectory[0]) {
print(" cwd=");
print(p->CurrentDirectory);
}
print("\n");
}
if (count > MAX_PROCS) {
print(" ... and ");
printUInt((uint32_t)(count - MAX_PROCS));
print(" more processes.\n");
}
}
// Implements the cmd threads operation.
static void cmd_threads(const char* args) {
uint32_t pid = 0;
if (!parseUInt(args, &pid)) {
print("threads: usage: threads <pid>\n");
return;
}
SYS_THREAD_INFO threads[MAX_THREADS];
size_t count = 0;
uint32_t status = SysGetThreadList(pid, threads, MAX_THREADS, &count);
if (status != 0) {
print("threads: query failed status=");
printUInt(status);
print("\n");
return;
}
print("Threads for PID ");
printUInt(pid);
print(" (Count: ");
printUInt((uint32_t)count);
print("):\n");
print(" TID State Pri CPU LastCPU QRem QTick Burst Wait WStat Flags\n");
print(" Flags: *=current I=idle K=kernel Q=queued A=alertable k=kernel-apc u=user-apc.\n");
size_t displayCount = count < MAX_THREADS ? count : MAX_THREADS;
for (size_t i = 0; i < displayCount; i++) {
SYS_THREAD_INFO* t = &threads[i];
print(" ");
printUIntWidth(t->ThreadId, 3);
print(" ");
printStringWidth(threadStateName(t->State), 5);
print(" ");
printUIntWidth(t->Priority, 3);
print(" ");
if (t->RunningCpu == 0xffffffffu) printStringWidth("-", 3);
else printUIntWidth(t->RunningCpu, 3);
print(" ");
if (t->LastCpu == 0xffffffffu) printStringWidth("-", 7);
else printUIntWidth(t->LastCpu, 7);
print(" ");
printUIntWidth(t->QuantumRemaining, 4);
print(" ");
printUIntWidth(t->QuantumTicks, 5);
print(" ");
printUIntWidth(t->CpuBurstTicks, 5);
print(" ");
printStringWidth(threadWaitName(t->WaitObjectType), 8);
print(" ");
printUIntWidth(t->WaitStatus, 5);
print(" ");
printThreadFlags(t->Flags);
print("\n");
}
if (count > MAX_THREADS) {
print(" ... and ");
printUInt((uint32_t)(count - MAX_THREADS));
print(" more threads.\n");
}
}
static void cmd_kill(const char* args) {
uint32_t pid = 0;
if (!parseUInt(args, &pid) || pid == 0) { print("kill: usage: kill <pid>\n"); return; }
uint32_t status = SysKillProcess(pid, (int)STATUS_PROCESS_KILLED);
if (status != 0) {
print("shell: failed to kill process ");
printUInt(pid);
print(", error code: ");
printUInt(status);
print("\n");
} else {
print("Process ");
printUInt(pid);
print(" terminated successfully.\n");
}
}
// Implements the cmd write operation.
static void cmd_write(const char* args) {
char filename[MAX_CMD]; int i = 0;
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
filename[i] = args[i]; i++;
}
filename[i] = '\0';
if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
uint32_t hFile;
if (create_file(filename, &hFile) != 0) { print("write: could not create file\n"); return; }
size_t written;
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(hFile, args + i + 1, strlen(args + i + 1), &written);
SysWriteFile(&writeReq);
SysCloseHandle(hFile);
print("File written successfully.\n");
}
// Implements the cmd dir operation.
static void cmd_dir(const char* path) {
uint32_t hDir;
const char* target = (path && *path != '\0') ? path : ".";
if (open_dir(target, &hDir) != 0) {
print("dir: failed to open directory\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");
}
SysCloseHandle(hDir);
}
// Implements the cmd type operation.
static void cmd_type(const char* filename) {
uint32_t hFile;
if (open_file(filename, &hFile) != 0) { print("type: cannot open file\n"); return; }
char buffer[512]; size_t bytesRead;
for (;;) {
SYS_FILE_IO_REQUEST readReq = SysMakeFileIoRequest(hFile, buffer, sizeof(buffer) - 1, &bytesRead);
if (SysReadFile(&readReq) != 0 || bytesRead == 0) break;
buffer[bytesRead] = '\0'; print(buffer);
}
SysCloseHandle(hFile); print("\n");
}
// Implements the extract Program Path operation.
static bool extractProgramPath(const char* cmdline, char* out, size_t outSize) {
if (!cmdline || !out || outSize == 0) return false;
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
if (*cmdline == '\0') return false;
size_t pos = 0;
if (*cmdline == '"') {
cmdline++;
while (*cmdline && *cmdline != '"' && pos + 1 < outSize)
out[pos++] = *cmdline++;
} else {
while (*cmdline && *cmdline != ' ' && *cmdline != '\t' && pos + 1 < outSize)
out[pos++] = *cmdline++;
}
out[pos] = '\0';
return pos != 0;
}
// Consumes queued Ctrl-C input without taking ordinary foreground stdin.
static bool shellConsumeQueuedCtrlC(void) {
for (int i = 0; i < 4; i++) {
INPUT_KEY_RECORD input;
if (SysPeekInputEvent(&input) != 0) return false;
if ((input.Modifiers & INPUT_MOD_CTRL) && input.Key == INPUT_KEY_C) {
(void)SysReadInputEvent(&input);
return true;
}
if (input.Key == INPUT_KEY_LEFT_CTRL || input.Key == INPUT_KEY_RIGHT_CTRL) {
(void)SysReadInputEvent(&input);
continue;
}
return false;
}
return false;
}
// Waits for a foreground process while watching Ctrl-C without stealing ordinary stdin.
static uint32_t waitForegroundProcess(uint32_t hProc) {
uint32_t pid = 0;
bool havePid = SysGetProcessPid(hProc, &pid) == 0 && pid != 0;
bool sentBreak = false;
uint32_t inputQueue = 0;
if (SysOpenInputQueue(0, &inputQueue) != 0)
return SysWait(hProc, SYS_WAIT_FLAG_NONE);
uint32_t handles[2] = { hProc, inputQueue };
uint32_t index = 0;
SYS_WAIT_MANY_REQUEST waitReq;
waitReq.Size = sizeof(waitReq);
waitReq.Flags = SYS_WAIT_FLAG_NONE;
waitReq.Handles = handles;
waitReq.Count = 2;
waitReq.SignaledIndexOut = &index;
waitReq.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
for (;;) {
index = 0;
uint32_t status = SysWaitMany(&waitReq);
if (status != SYS_STATUS_SUCCESS) {
SysCloseHandle(inputQueue);
return status;
}
if (index == 0) {
SysCloseHandle(inputQueue);
return status;
}
uint32_t modifiers = SysGetKeyModifiers();
if (((modifiers & INPUT_MOD_CTRL) && SysIsKeyDown(INPUT_KEY_C)) || shellConsumeQueuedCtrlC()) {
if (!sentBreak) {
print("^C\n");
if (havePid) (void)SysKillProcess(pid, (int)STATUS_PROCESS_KILLED);
sentBreak = true;
}
}
}
}
// Run one command.
static int runCommand(const char* cmd) {
if (!cmd || *cmd == '\0') return 0;
if (strcmp(cmd, "exit") == 0) return 1;
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
if (strcmp(cmd, "clear") == 0) { SysClearScreen(0x012456); return 0; }
if (strncmp(cmd, "crash", 5) == 0) {
// Check the sub-option trailing "crash " (offset 6).
if (strcmp(cmd + 6, "void") == 0) {
// Jumping into the void.
void (*invalid_func)(void) = NULL;
invalid_func();
return 0;
}
else if (strcmp(cmd + 6, "noncanonical") == 0) {
// Non-canonical pointer dereference.
volatile int *ptr = (volatile int*)0x4242424242424242ULL;
*ptr = 1337;
return 0;
}
else if (strcmp(cmd + 6, "ud2") == 0) {
__ud2();
return 0;
}
else if (strcmp(cmd + 6, "nx") == 0) {
// NX / DEP Violation.
unsigned char code[] = { 0xC3 };
void (*execute_stack)(void) = (void (*)(void))code;
execute_stack();
return 0;
}
else if (strcmp(cmd + 6, "divzero") == 0) {
// Division by zero.
volatile int zero = 0;
volatile int result = 1 / zero;
(void)result;
return 0;
}
else {
// Default fallback if just "crash" or an unknown sub-option is passed.
*(volatile int*)0 = 42;
return 0;
}
}
if (strcmp(cmd, "mem") == 0) { cmd_mem(); return 0; }
if (strcmp(cmd, "time") == 0 || strcmp(cmd, "date") == 0) { cmd_time(); return 0; }
if (strcmp(cmd, "display") == 0 || strcmp(cmd, "gpu") == 0) { cmd_display(); return 0; }
if (strcmp(cmd, "stats") == 0 || strcmp(cmd, "telemetry") == 0) { cmd_stats(); return 0; }
if (strcmp(cmd, "ps") == 0) { cmd_ps(); return 0; }
if (strncmp(cmd, "threads ", 8) == 0) { cmd_threads(cmd + 8); return 0; }
if (strncmp(cmd, "kill ", 5) == 0) { cmd_kill(cmd + 5); return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { if (set_cwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { delete_file(cmd + 4); return 0; }
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
// Make a local copy of the command line so we can mutate it safely if needed
char modifiedCmd[MAX_CMD];
strncpy(modifiedCmd, cmd, sizeof(modifiedCmd) - 1);
modifiedCmd[sizeof(modifiedCmd) - 1] = '\0';
// Parse background indicator execution flag (&)
bool background = false;
size_t cmdLen = strlen(modifiedCmd);
if (cmdLen > 0 && modifiedCmd[cmdLen - 1] == '&') {
background = true;
modifiedCmd[cmdLen - 1] = '\0'; // Strip the ampersand
// Clean up any trailing whitespace left before the ampersand
cmdLen = strlen(modifiedCmd);
while (cmdLen > 0 && (modifiedCmd[cmdLen - 1] == ' ' || modifiedCmd[cmdLen - 1] == '\t')) {
modifiedCmd[cmdLen - 1] = '\0';
cmdLen--;
}
}
char program[MAX_CMD];
if (!extractProgramPath(modifiedCmd, program, sizeof(program))) return 0;
uint32_t hProc;
if (create_process(program, &hProc, modifiedCmd) == 0) {
if (background) {
// Background mode: Clean up the handle immediately and skip waiting.
print("[Launched in background, handle: ");
printUInt(hProc);
print("]\n");
SysCloseHandle(hProc);
} else {
// Foreground mode: Wait for completion and flush standard keyboard stream.
uint32_t waitStatus = waitForegroundProcess(hProc);
if (waitStatus != SYS_STATUS_SUCCESS) {
print("shell: wait failed: ");
printUInt(waitStatus);
print("\n");
}
print("Process exited with code: ");
int exitCode;
SysGetProcessExitCode(hProc, &exitCode);
printInt(exitCode);
const char* exitName = processExitStatusName((uint32_t)exitCode);
if (exitName) {
print(" (0x");
printHex32((uint32_t)exitCode);
print(" ");
print(exitName);
print(")");
}
print("\n");
SysCloseHandle(hProc);
while (SysPollKeyboard()) (void)SysReadKey();
}
} else {
print("shell: launch failed\n");
}
return 0;
}
// Runs the requested operation.
int main(void) {
const char* cmdline = SysGetPeb()->CommandLine.Data;
char lineBuf[MAX_CMD];
print("shell: command line: ");
print(cmdline);
print("\n");
print("shell: ready\n");
while (true) {
print(SHELL_PROMPT);
if (readLine(lineBuf, MAX_CMD)) continue;
char* cmd = trim(lineBuf);
shellHistoryAdd(cmd);
if (runCommand(cmd)) break;
}
print("shell: goodbye\n");
SysExitProcess(0);
return 0;
}