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

2230 lines
85 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/process_loader.h>
#include <ntlite/path.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 "> "
#define SHELL_GLYPH_WIDTH 8
#define SHELL_GLYPH_HEIGHT 16
#define SHELL_TEXT_FOREGROUND 0x00CCCCCCu
#define SHELL_TEXT_BACKGROUND 0x00012456u
#define SHELL_CURSOR_FOREGROUND 0x00000000u
#define SHELL_CURSOR_BACKGROUND 0x00E0E0E0u
// Output helpers.
static void print(const char* s) { SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromCstr(s) }; SysWriteConsole(&req); }
// Opens file.
static uint32_t open_file(const char* path, uint32_t* handleOut) {
return NtlOpenFile(SysSvFromCstr(path), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, handleOut);
}
// Creates file.
static uint32_t create_file(const char* path, uint32_t* handleOut) {
return NtlOpenFile(SysSvFromCstr(path), SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_CREATE_ALWAYS, SYS_FILE_FLAG_NONE, handleOut);
}
// Opens dir.
static uint32_t open_dir(const char* path, uint32_t* handleOut) {
return NtlOpenDir(SysSvFromCstr(path), SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN,
SYS_DIR_FLAG_NONE, handleOut);
}
// Updates cwd.
static uint32_t set_cwd(const char* path) {
return NtlSetCurrentDirectory(SysSvFromCstr(path));
}
// Releases file.
static uint32_t delete_file(const char* path) {
return NtlDeleteFile(SysSvFromCstr(path));
}
// Creates process.
static uint32_t create_process(const char* path, uint32_t* handleOut, const char* cmdline) {
uint32_t imageFile = SYS_HANDLE_INVALID;
uint32_t status = open_file(path, &imageFile);
if (status != NTLSTATUS_SUCCESS) return status;
NTL_CREATE_SYSTEM_PROCESS_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
req.ImageFile = imageFile;
req.Path = SysSvFromCstr(path);
req.CommandLine = SysSvFromCstr(cmdline);
req.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
req.ResolveModuleName = NtlResolveSystemModuleName;
req.ProcessHandleOut = handleOut;
status = NtlCreateSystemProcess(&req);
(void)SysCloseHandle(imageFile);
return status;
}
// 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 NTLSTATUS_PROCESS_KILLED: return "killed";
case NTLSTATUS_PROCESS_PAGE_FAULT: return "page-fault";
case NTLSTATUS_PROCESS_GENERAL_PROTECTION: return "general-protection";
case NTLSTATUS_PROCESS_ILLEGAL_OPCODE: return "illegal-opcode";
case NTLSTATUS_PROCESS_DIVIDE_BY_ZERO: return "divide-by-zero";
case NTLSTATUS_PROCESS_BREAKPOINT: return "breakpoint";
case NTLSTATUS_PROCESS_ALIGNMENT_FAULT: return "alignment-fault";
case NTLSTATUS_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;
typedef struct SHELL_EDITOR {
char* Buffer;
int Capacity;
int Length;
int Cursor;
} SHELL_EDITOR;
typedef struct SHELL_VISIBLE_CURSOR {
int Column;
int Row;
char Character;
bool Visible;
} SHELL_VISIBLE_CURSOR;
static const char* const gShellBuiltinCommands[] = {
"cd",
"clear",
"crash",
"date",
"del",
"dir",
"display",
"exit",
"fbbench",
"gpu",
"help",
"kill",
"mem",
"ps",
"stats",
"telemetry",
"threads",
"time",
"type",
"volume",
"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++;
}
// Restores the character underneath the visible edit cursor.
static void shellEditorHideVisibleCursor(SHELL_VISIBLE_CURSOR* cursor) {
if (!cursor || !cursor->Visible) return;
SYS_PUT_SCREEN_CHAR_REQUEST request = {
sizeof(SYS_PUT_SCREEN_CHAR_REQUEST), cursor->Character, { 0, 0, 0 },
cursor->Column * SHELL_GLYPH_WIDTH,
cursor->Row * SHELL_GLYPH_HEIGHT,
SHELL_TEXT_FOREGROUND, SHELL_TEXT_BACKGROUND
};
(void)SysPutScreenChar(&request);
cursor->Visible = false;
}
// Draws the same steady block cursor used by edit without changing console state.
static void shellEditorShowVisibleCursor(SHELL_VISIBLE_CURSOR* cursor, const SHELL_EDITOR* editor) {
if (!cursor || !editor) return;
shellEditorHideVisibleCursor(cursor);
SYS_CONSOLE_INFO info;
if (SysGetConsoleInfo(&info) != NTLSTATUS_SUCCESS) return;
char character = editor->Cursor < editor->Length
? editor->Buffer[editor->Cursor]
: ' ';
SYS_PUT_SCREEN_CHAR_REQUEST request = {
sizeof(SYS_PUT_SCREEN_CHAR_REQUEST), character, { 0, 0, 0 },
(int)info.CursorX * SHELL_GLYPH_WIDTH,
(int)info.CursorY * SHELL_GLYPH_HEIGHT,
SHELL_CURSOR_FOREGROUND, SHELL_CURSOR_BACKGROUND
};
if (SysPutScreenChar(&request) != NTLSTATUS_SUCCESS)
return;
cursor->Column = (int)info.CursorX;
cursor->Row = (int)info.CursorY;
cursor->Character = character;
cursor->Visible = true;
}
// Moves the visible cursor left by the requested number of cells.
static void shellEditorMoveLeftCells(int count) {
while (count-- > 0) print("\b");
}
// Moves the editor cursor without changing the buffer.
static void shellEditorMoveCursor(SHELL_EDITOR* editor, int target) {
if (!editor) return;
if (target < 0) target = 0;
if (target > editor->Length) target = editor->Length;
if (target < editor->Cursor) {
shellEditorMoveLeftCells(editor->Cursor - target);
} else {
while (editor->Cursor < target) {
char echo[2] = { editor->Buffer[editor->Cursor], '\0' };
print(echo);
editor->Cursor++;
}
}
editor->Cursor = target;
}
// Redraws the changed tail and restores the requested cursor position.
static void shellEditorRedrawTail(SHELL_EDITOR* editor, int start, int oldLength, int targetCursor) {
if (!editor) return;
print(editor->Buffer + start);
if (oldLength > editor->Length) printSpaces((size_t)(oldLength - editor->Length));
int displayedEnd = oldLength > editor->Length ? oldLength : editor->Length;
shellEditorMoveLeftCells(displayedEnd - targetCursor);
editor->Cursor = targetCursor;
}
// Replaces one buffer range and keeps the visible line synchronized.
static bool shellEditorReplaceRange(SHELL_EDITOR* editor, int start, int end, const char* text) {
if (!editor || !editor->Buffer || editor->Capacity <= 0 || !text) return false;
if (start < 0 || end < start || end > editor->Length) return false;
shellEditorMoveCursor(editor, start);
int oldLength = editor->Length;
int removed = end - start;
int available = editor->Capacity - 1 - (oldLength - removed);
int replacementLength = 0;
while (text[replacementLength] && replacementLength < available) replacementLength++;
memmove(editor->Buffer + start + replacementLength,
editor->Buffer + end,
(size_t)(oldLength - end + 1));
if (replacementLength > 0)
memcpy(editor->Buffer + start, text, (size_t)replacementLength);
editor->Length = oldLength - removed + replacementLength;
int targetCursor = start + replacementLength;
shellEditorRedrawTail(editor, start, oldLength, targetCursor);
return removed != replacementLength || replacementLength != 0;
}
// Inserts text at the current cursor position.
static bool shellEditorInsert(SHELL_EDITOR* editor, const char* text) {
if (!editor) return false;
return shellEditorReplaceRange(editor, editor->Cursor, editor->Cursor, text);
}
// Replaces the complete visible line and leaves the cursor at its end.
static void shellEditorReplaceLine(SHELL_EDITOR* editor, const char* text) {
if (!editor) return;
(void)shellEditorReplaceRange(editor, 0, editor->Length, text ? text : "");
}
// 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';
}
// 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(SHELL_EDITOR* editor) {
print(SHELL_PROMPT);
if (!editor) return;
print(editor->Buffer);
shellEditorMoveLeftCells(editor->Length - editor->Cursor);
}
// Completes the token around the current cursor position.
static void shellCompleteLine(SHELL_EDITOR* editor) {
if (!editor || !editor->Buffer || editor->Capacity <= 0 || editor->Cursor < 0) return;
char* buf = editor->Buffer;
size_t cursor = (size_t)editor->Cursor;
size_t tokenStart = cursor;
while (tokenStart > 0 && !shellIsTokenSeparator(buf[tokenStart - 1])) tokenStart--;
size_t tokenEnd = cursor;
while (tokenEnd < (size_t)editor->Length && !shellIsTokenSeparator(buf[tokenEnd])) tokenEnd++;
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) {
(void)shellEditorReplaceRange(editor, editor->Cursor, (int)tokenEnd,
state.Common + namePrefixLength);
}
if (state.MatchCount == 1) {
if (state.LastIsDirectory) {
if (editor->Cursor < editor->Capacity - 1 && editor->Cursor > 0 &&
!shellIsPathSeparator(buf[editor->Cursor - 1]))
(void)shellEditorInsert(editor, "/");
} else if (state.LastAddsSpace) {
if (editor->Cursor < editor->Capacity - 1 &&
(editor->Cursor == editor->Length ||
!shellIsTokenSeparator(buf[editor->Cursor])))
(void)shellEditorInsert(editor, " ");
}
return;
}
if (expanded) return;
print("\n");
if (commandPosition && !explicitPath) {
shellCollectBuiltinCompletions(token, tokenLength, NULL, true);
}
shellCollectPathCompletions(token, tokenLength, NULL, true);
shellRedrawPromptAndLine(editor);
}
// Reads one editable command line from the keyboard.
static bool readLine(char* buf, int len) {
SHELL_EDITOR editor;
SHELL_VISIBLE_CURSOR visibleCursor;
char historyDraft[MAX_CMD];
size_t historyCursor = gHistoryCount;
if (len <= 0) return false;
editor.Buffer = buf;
editor.Capacity = len;
editor.Length = 0;
editor.Cursor = 0;
memset(&visibleCursor, 0, sizeof(visibleCursor));
buf[0] = '\0';
historyDraft[0] = '\0';
while (true) {
shellEditorShowVisibleCursor(&visibleCursor, &editor);
INPUT_KEY_RECORD input;
SYS_INPUT_EVENT_REQUEST inputReq;
inputReq.Size = sizeof(inputReq);
inputReq.QueueHandle = 0;
inputReq.Flags = SYS_INPUT_EVENT_FLAG_NONE;
inputReq.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
inputReq.RecordOut = &input;
KSTATUS inputStatus = SysReadInputEvent(&inputReq);
shellEditorHideVisibleCursor(&visibleCursor);
if (inputStatus != NTLSTATUS_SUCCESS) { 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 (editor.Cursor > 0)
(void)shellEditorReplaceRange(&editor, editor.Cursor - 1, editor.Cursor, "");
continue;
}
if (key == INPUT_KEY_DELETE) {
if (editor.Cursor < editor.Length)
(void)shellEditorReplaceRange(&editor, editor.Cursor, editor.Cursor + 1, "");
continue;
}
if (key == INPUT_KEY_LEFT) {
shellEditorMoveCursor(&editor, editor.Cursor - 1);
continue;
}
if (key == INPUT_KEY_RIGHT) {
shellEditorMoveCursor(&editor, editor.Cursor + 1);
continue;
}
if (key == INPUT_KEY_HOME) {
shellEditorMoveCursor(&editor, 0);
continue;
}
if (key == INPUT_KEY_END) {
shellEditorMoveCursor(&editor, editor.Length);
continue;
}
if (key == INPUT_KEY_UP) {
if (historyCursor > 0) {
if (historyCursor == gHistoryCount) {
strncpy(historyDraft, editor.Buffer, sizeof(historyDraft) - 1);
historyDraft[sizeof(historyDraft) - 1] = '\0';
}
historyCursor--;
shellEditorReplaceLine(&editor, gHistory[historyCursor]);
}
continue;
}
if (key == INPUT_KEY_DOWN) {
if (historyCursor < gHistoryCount) {
historyCursor++;
shellEditorReplaceLine(&editor,
historyCursor < gHistoryCount ? gHistory[historyCursor] : historyDraft);
}
continue;
}
if (key == INPUT_KEY_TAB) {
shellCompleteLine(&editor);
continue;
}
char c = input.Char ? input.Char : shellKeyToChar(key, modifiers);
if (c < 0x20 || c == 127) continue;
char text[2] = { c, '\0' };
(void)shellEditorInsert(&editor, text);
}
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"
" fbbench - Benchmark Doom-size and full-screen presents\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"
" volume [0-100] - Query or set master audio volume\n"
" volume mute - Mute the default audio endpoint\n"
" volume unmute - Unmute the default audio endpoint\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_BOOT_PROCESSOR) print("boot ");
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 ");
}
// Selects the available default audio endpoint, or the first available endpoint.
static bool shellQueryDefaultAudioEndpoint(SYS_AUDIO_ENDPOINT_INFO* endpointOut) {
if (!endpointOut) return false;
SYS_AUDIO_ENDPOINT_INFO endpoints[SYS_AUDIO_MAX_ENDPOINTS];
uint32_t count = 0;
SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Endpoints = endpoints;
request.Capacity = SYS_AUDIO_MAX_ENDPOINTS;
request.CountOut = &count;
KSTATUS status = SysAudioEnumerateEndpoints(&request);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_BUFFER_TOO_SMALL) {
print("volume: endpoint query failed status=");
printUInt(status);
print("\n");
return false;
}
const SYS_AUDIO_ENDPOINT_INFO* fallback = NULL;
uint32_t availableCount = count < SYS_AUDIO_MAX_ENDPOINTS
? count
: SYS_AUDIO_MAX_ENDPOINTS;
for (uint32_t index = 0; index < availableCount; index++) {
const SYS_AUDIO_ENDPOINT_INFO* endpoint = &endpoints[index];
if (endpoint->Size != sizeof(*endpoint) ||
endpoint->Version != SYS_AUDIO_ABI_VERSION ||
endpoint->State != SysAudioEndpointReady ||
(endpoint->Flags & SYS_AUDIO_ENDPOINT_FLAG_AVAILABLE) == 0)
continue;
if (!fallback) fallback = endpoint;
if (endpoint->Flags & SYS_AUDIO_ENDPOINT_FLAG_DEFAULT) {
*endpointOut = *endpoint;
return true;
}
}
if (fallback) {
*endpointOut = *fallback;
return true;
}
print("volume: no available audio endpoint\n");
return false;
}
// Converts one public Q16 gain into a rounded percentage.
static uint32_t shellAudioVolumePercent(uint32_t volumeQ16) {
uint64_t scaled = (uint64_t)volumeQ16 * 100u + SYS_AUDIO_GAIN_Q16_UNITY / 2u;
return (uint32_t)(scaled / SYS_AUDIO_GAIN_Q16_UNITY);
}
// Converts one percentage into public Q16 gain.
static uint32_t shellAudioVolumeQ16(uint32_t percent) {
return (uint32_t)(((uint64_t)percent * SYS_AUDIO_GAIN_Q16_UNITY + 50u) / 100u);
}
// Queries or updates the default endpoint master volume.
static void cmd_volume(const char* args) {
SYS_AUDIO_ENDPOINT_INFO endpoint;
if (!shellQueryDefaultAudioEndpoint(&endpoint)) return;
while (args && (*args == ' ' || *args == '\t')) args++;
if (!args || *args == '\0') {
print("Volume: ");
printUInt(shellAudioVolumePercent(endpoint.MasterVolumeQ16));
print("%");
if (endpoint.MasterMuted) print(" (muted)");
print("\n");
return;
}
uint32_t volumeQ16 = endpoint.MasterVolumeQ16;
uint32_t muted = endpoint.MasterMuted ? 1u : 0u;
if (strcmp(args, "mute") == 0) {
muted = 1u;
} else if (strcmp(args, "unmute") == 0) {
muted = 0u;
} else {
uint32_t percent = 0;
if (!parseUInt(args, &percent) || percent > 100u) {
print("Usage: volume [0-100|mute|unmute]\n");
return;
}
volumeQ16 = shellAudioVolumeQ16(percent);
muted = 0u;
}
SYS_AUDIO_SET_MASTER_VOLUME_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.EndpointId = endpoint.EndpointId;
request.Muted = muted;
request.VolumeQ16 = volumeQ16;
KSTATUS status = SysAudioSetMasterVolume(&request);
if (status != NTLSTATUS_SUCCESS) {
print("volume: update failed status=");
printUInt(status);
print("\n");
return;
}
print("Volume: ");
printUInt(shellAudioVolumePercent(volumeQ16));
print("%");
if (muted) print(" (muted)");
print("\n");
}
// Implements the cmd stats operation.
static void cmd_stats(void) {
SYS_SCHEDULER_STATS sched;
SYS_PMM_STATS pmm;
SYS_CPU_STATS* cpu = 0;
void* cpuBuffer = 0;
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;
SYS_EXCEPTION_STATS exceptionStats;
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;
}
size_t cpuSize = 0;
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, 0, 0, &cpuSize);
if (status != 0 || cpuSize < sizeof(SYS_CPU_STATS)) {
print("stats: cpu size query failed: ");
printUInt(status);
print("\n");
return;
}
size_t cpuPages = (cpuSize + 4095u) / 4096u;
if (cpuPages == 0) cpuPages = 1;
SYS_ALLOC_VM_REQUEST allocReq;
memset(&allocReq, 0, sizeof(allocReq));
allocReq.Size = sizeof(allocReq);
allocReq.PageCount = cpuPages;
allocReq.Protect = SYS_VM_READWRITE;
allocReq.Flags = SYS_VM_FLAG_NONE;
allocReq.BaseOut = &cpuBuffer;
status = SysAllocVm(&allocReq);
if (status != 0 || !cpuBuffer) {
print("stats: cpu buffer allocation failed: ");
printUInt(status);
print("\n");
return;
}
cpu = (SYS_CPU_STATS*)cpuBuffer;
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, cpu, cpuPages * 4096u, NULL);
if (status != 0) {
print("stats: cpu query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_PFN, &pfn, sizeof(pfn), NULL);
if (status != 0) {
print("stats: pfn query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_VAD, &vad, sizeof(vad), NULL);
if (status != 0) {
print("stats: vad query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_SWAP, &swap, sizeof(swap), NULL);
if (status != 0) {
print("stats: swap query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
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");
SysFreeVm(cpuBuffer);
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_FILESYSTEM, &filesystem, sizeof(filesystem), NULL);
if (status != 0) {
print("stats: filesystem query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_DISK, &disk, sizeof(disk), NULL);
if (status != 0) {
print("stats: disk query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_PTE, &pte, sizeof(pte), NULL);
if (status != 0) {
print("stats: pte query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_SURFACE, &surface, sizeof(surface), NULL);
if (status != 0) {
print("stats: surface query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_EXCEPTION, &exceptionStats, sizeof(exceptionStats), NULL);
if (status != 0) {
print("stats: exception query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
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(" RT dispatches: "); printUInt64(sched.RealtimeDispatches); print("\n");
print(" RT preemptions: "); printUInt64(sched.RealtimePreemptions); print("\n");
print(" PI boosts: "); printUInt64(sched.PriorityInheritanceBoosts); print("\n");
print(" PI drops: "); printUInt64(sched.PriorityInheritanceDrops); print("\n");
print(" PI propagations: "); printUInt64(sched.PriorityInheritancePropagations); print("\n");
print(" PI max depth: "); printUInt64(sched.PriorityInheritanceMaxDepth); print("\n");
print("Exceptions:\n");
print(" delivered: "); printUInt64(exceptionStats.Delivered); print("\n");
print(" user-raised: "); printUInt64(exceptionStats.UserRaised); print("\n");
print(" continued: "); printUInt64(exceptionStats.Continued); print("\n");
print(" unhandled: "); printUInt64(exceptionStats.Unhandled); print("\n");
print(" invalid continue: "); printUInt64(exceptionStats.InvalidContinue); print("\n");
print(" max nesting: "); printUInt(exceptionStats.MaxNestedDispatch); print("\n");
print(" last code: "); printUInt(exceptionStats.LastCode); print("\n");
print("CPUs:\n");
const SYS_CPU_STAT* cpuRecords = SYS_CPU_STATS_CONST_RECORDS(cpu);
uint32_t n = cpu->RecordCount;
for (uint32_t i = 0; i < n; i++) {
const SYS_CPU_STAT* c = &cpuRecords[i];
print(" cpu"); printUInt(c->CpuIndex);
print(" hardware="); printUInt(c->HardwareId);
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("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(" blocks="); printUInt64(filesystem.DirectoryBlocksRead);
print(" entries="); printUInt64(filesystem.DirectoryEntriesScanned); 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("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[SYS_PTE_QUERY_GENERIC]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_GENERIC]); print("\n");
print(" user-probe: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_USER_PROBE]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_USER_PROBE]); print("\n");
print(" heap: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_HEAP]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_HEAP]); print("\n");
print(" vad: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_VAD]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_VAD]); print("\n");
print(" debug: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_DEBUG]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_DEBUG]); print("\n");
print(" self-test: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_SELF_TEST]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_SELF_TEST]); 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");
SysFreeVm(cpuBuffer);
}
// 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(" direct="); printUInt64(st->DirectPresentCount); print("\n");
print(" dirty: "); printUInt64(st->DirtyRectCount); print(" all="); printUInt64(st->DirtyAllCount); print("\n");
print(" bytes: logical="); printUInt64(st->BytesCopied);
print(" scanout="); printUInt64(st->ScanoutBytes); 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");
print(" scanout: ");
print((info.Flags & DISPLAY_INFO_FLAG_PAT_WC_AVAILABLE) ? "PAT WC available" : "PAT WC unavailable");
print("\n");
printDisplayStats("Manager counters", &info.ManagerStats);
printDisplayStats("Provider counters", &info.DeviceStats);
}
typedef struct SHELL_FB_BENCH_RESULT {
uint32_t Width;
uint32_t Height;
uint32_t Frames;
uint32_t Status;
uint64_t ElapsedTicks;
uint64_t Frequency;
uint64_t ExpectedBytes;
uint64_t ScanoutBytes;
uint64_t DirectPresents;
} SHELL_FB_BENCH_RESULT;
// Computes a decimal fixed-point ratio with one digit after the decimal point.
static uint64_t shellRatioTenths(uint64_t numerator, uint64_t denominator) {
if (!denominator)
return 0;
if (numerator > UINT64_MAX / 10u)
return (numerator / denominator) * 10u;
return (numerator * 10u) / denominator;
}
// Fills a benchmark surface with a deterministic high-contrast pattern.
static void shellFillBenchmarkSurface(uint32_t* pixels,
uint32_t pitchPixels,
uint32_t width,
uint32_t height) {
for (uint32_t y = 0; y < height; y++) {
uint32_t* row = pixels + (size_t)y * pitchPixels;
for (uint32_t x = 0; x < width; x++) {
uint32_t red = (x * 255u) / (width ? width : 1u);
uint32_t green = (y * 255u) / (height ? height : 1u);
uint32_t blue = ((x ^ y) & 0xFFu);
row[x] = (red << 16) | (green << 8) | blue;
}
}
}
// Runs one synchronous surface-present benchmark case.
static void shellRunFramebufferBenchmark(uint32_t width,
uint32_t height,
uint32_t frames,
uint64_t frequency,
SHELL_FB_BENCH_RESULT* result) {
memset(result, 0, sizeof(*result));
result->Width = width;
result->Height = height;
result->Frames = frames;
result->Frequency = frequency;
uint32_t surface = 0;
SYS_CREATE_SURFACE_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Width = width;
create.Height = height;
create.Format = SYS_SURFACE_FORMAT_XRGB8888;
create.Flags = SYS_SURFACE_FLAG_NONE;
create.HandleOut = &surface;
uint32_t status = SysCreateSurface(&create);
if (status != NTLSTATUS_SUCCESS || !surface) {
result->Status = status ? status : NTLSTATUS_FAILURE;
return;
}
void* base = 0;
uint32_t pitchBytes = 0;
status = SysMapSurface(surface, &base, &pitchBytes);
if (status != NTLSTATUS_SUCCESS || !base || pitchBytes < width * sizeof(uint32_t)) {
result->Status = status ? status : NTLSTATUS_FAILURE;
SysCloseHandle(surface);
return;
}
shellFillBenchmarkSurface((uint32_t*)base, pitchBytes / sizeof(uint32_t), width, height);
DISPLAY_RECT rect = { 0, 0, (int32_t)width, (int32_t)height };
for (uint32_t i = 0; i < 2; i++) {
status = SysPresentSurface(surface, &rect, 1);
if (status != NTLSTATUS_SUCCESS)
break;
}
SYS_DISPLAY_INFO before;
SYS_DISPLAY_INFO after;
memset(&before, 0, sizeof(before));
memset(&after, 0, sizeof(after));
if (status == NTLSTATUS_SUCCESS)
status = SysQueryDisplayInfo(&before);
uint64_t begin = 0;
uint64_t end = 0;
uint32_t completed = 0;
if (status == NTLSTATUS_SUCCESS) {
begin = SysGetTick();
for (; completed < frames; completed++) {
status = SysPresentSurface(surface, &rect, 1);
if (status != NTLSTATUS_SUCCESS)
break;
}
end = SysGetTick();
uint32_t queryStatus = SysQueryDisplayInfo(&after);
if (status == NTLSTATUS_SUCCESS && queryStatus != NTLSTATUS_SUCCESS)
status = queryStatus;
}
result->Frames = completed;
result->Status = status;
result->ElapsedTicks = end - begin;
result->ExpectedBytes = (uint64_t)width * (uint64_t)height * sizeof(uint32_t) * completed;
if (after.DeviceStats.ScanoutBytes >= before.DeviceStats.ScanoutBytes)
result->ScanoutBytes = after.DeviceStats.ScanoutBytes - before.DeviceStats.ScanoutBytes;
if (after.DeviceStats.DirectPresentCount >= before.DeviceStats.DirectPresentCount)
result->DirectPresents = after.DeviceStats.DirectPresentCount - before.DeviceStats.DirectPresentCount;
SysCloseHandle(surface);
}
// Prints one framebuffer benchmark result.
static void shellPrintFramebufferBenchmark(const char* name,
const SHELL_FB_BENCH_RESULT* result) {
print(" "); print(name); print(" ");
printUInt(result->Width); print("x"); printUInt(result->Height); print(": ");
if (result->Status != NTLSTATUS_SUCCESS || !result->ElapsedTicks || !result->Frequency) {
print("failed status=0x"); printHex32(result->Status); print("\n");
return;
}
uint64_t elapsedUs = SysTicksToMicroseconds(result->ElapsedTicks, result->Frequency);
uint64_t fpsTenths = shellRatioTenths((uint64_t)result->Frames * result->Frequency,
result->ElapsedTicks);
uint64_t bytes = result->ScanoutBytes ? result->ScanoutBytes : result->ExpectedBytes;
uint64_t mibTenths = shellRatioTenths(bytes * result->Frequency,
result->ElapsedTicks * 1024u * 1024u);
print("frames="); printUInt(result->Frames);
print(" time="); printUInt64(elapsedUs / 1000u); print(" ms");
print(" fps="); printUInt64(fpsTenths / 10u); print("."); printUInt64(fpsTenths % 10u);
print(" scanout="); printUInt64(mibTenths / 10u); print("."); printUInt64(mibTenths % 10u); print(" MiB/s");
print(" direct="); printUInt64(result->DirectPresents);
print(" bytes="); printUInt64(bytes);
print("\n");
}
// Benchmarks the synchronous surface-to-scanout path without console traffic.
static void cmd_fbbench(void) {
SYS_DISPLAY_INFO info;
uint32_t status = SysQueryDisplayInfo(&info);
if (status != NTLSTATUS_SUCCESS) {
print("fbbench: display query failed status=0x"); printHex32(status); print("\n");
return;
}
uint64_t frequency = 0;
status = SysGetTickFrequency(&frequency);
if (status != NTLSTATUS_SUCCESS || !frequency) {
print("fbbench: timer query failed status=0x"); printHex32(status); print("\n");
return;
}
uint32_t doomWidth = info.Mode.Width < 960u ? info.Mode.Width : 960u;
uint32_t doomHeight = info.Mode.Height < 600u ? info.Mode.Height : 600u;
uint32_t fullWidth = info.Mode.Width;
uint32_t fullHeight = info.Mode.Height;
uint64_t targetBytes = 128ull * 1024ull * 1024ull;
uint64_t doomFrameBytes = (uint64_t)doomWidth * doomHeight * sizeof(uint32_t);
uint64_t fullFrameBytes = (uint64_t)fullWidth * fullHeight * sizeof(uint32_t);
uint32_t doomFrames = doomFrameBytes ? (uint32_t)((targetBytes + doomFrameBytes - 1u) / doomFrameBytes) : 1u;
uint32_t fullFrames = fullFrameBytes ? (uint32_t)((targetBytes + fullFrameBytes - 1u) / fullFrameBytes) : 1u;
if (doomFrames < 8u) doomFrames = 8u;
if (doomFrames > 128u) doomFrames = 128u;
if (fullFrames < 8u) fullFrames = 8u;
if (fullFrames > 64u) fullFrames = 64u;
print("fbbench: running; the framebuffer will be overwritten temporarily\n");
SHELL_FB_BENCH_RESULT doom;
SHELL_FB_BENCH_RESULT full;
shellRunFramebufferBenchmark(doomWidth, doomHeight, doomFrames, frequency, &doom);
shellRunFramebufferBenchmark(fullWidth, fullHeight, fullFrames, frequency, &full);
SysClearScreen(0x00012456u);
print("Framebuffer benchmark:\n");
print(" provider: "); print(info.PrimaryName[0] ? info.PrimaryName : "?");
print(" mode="); printUInt(info.Mode.Width); print("x"); printUInt(info.Mode.Height);
print(" stride="); printUInt(info.Mode.Stride);
print(" cache="); print((info.Flags & DISPLAY_INFO_FLAG_PAT_WC_AVAILABLE) ? "PAT-WC" : "fallback");
print("\n");
shellPrintFramebufferBenchmark("doom", &doom);
shellPrintFramebufferBenchmark("full", &full);
}
// 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";
case SYS_PROCESS_STATE_CONSTRUCTING: return "build";
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";
case SYS_THREAD_STATE_SUSPENDED: return "suspend";
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 UserKiB ImageKiB Flags Name\n");
print(" Flags: S=system I=idle P=protected *=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[7];
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_TERMINATION_PROTECTED) flags[flagLen++] = 'P';
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(" ");
printUInt64Width((uint64_t)p->UserCommittedBytes / 1024u, 7);
print(" ");
printUInt64Width((uint64_t)p->ImageSize / 1024u, 8);
print(" ");
printStringWidth(flags, 5);
print(" ");
print(p->Name);
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 Base Eff 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(" ");
if (t->BaseSchedulingClass == SYS_SCHEDULING_REALTIME) print("R"); else print("N");
printUIntWidth(t->BasePriority, 2);
print(" ");
if (t->EffectiveSchedulingClass == SYS_SCHEDULING_REALTIME) print("R"); else print("N");
printUIntWidth(t->EffectivePriority, 2);
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)) { print("kill: usage: kill <pid>\n"); return; }
uint32_t hProc = 0;
uint32_t status = SysOpenProcess(pid, SYS_PROCESS_RIGHT_TERMINATE, &hProc);
if (status == 0) {
status = SysKillProcess(hProc, (int32_t)NTLSTATUS_PROCESS_KILLED);
SysCloseHandle(hProc);
}
if (status == NTLSTATUS_ACCESS_DENIED) {
print("kill: process ");
printUInt(pid);
print(" is termination-protected.\n");
} else 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");
}
#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 = "";
while (true) {
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(&current, &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) {
print("dir: failed to open directory, status=0x");
printHex32(st);
print("\n");
return;
}
SYS_DIR_ENTRY* entries = 0;
uint32_t count = 0;
uint32_t capacity = 0;
while (true) {
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.
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;
while (true) {
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;
}
// Returns whether one input key is currently held.
static bool shellIsKeyDown(INPUT_KEY key) {
bool down = false;
return SysIsKeyDown(key, &down) == NTLSTATUS_SUCCESS && down;
}
// Waits for a foreground process while polling Ctrl-C without consuming child input.
static uint32_t waitForegroundProcess(uint32_t hProc) {
bool sentBreak = false;
while (true) {
uint32_t status = SysWaitSingle(hProc,
SYS_WAIT_FLAG_NONE,
SHELL_FOREGROUND_POLL_MS);
if (status == NTLSTATUS_SUCCESS)
return status;
if (status != NTLSTATUS_TIMEOUT)
return status;
uint32_t modifiers = SysGetKeyModifiers();
if (!sentBreak && (modifiers & INPUT_MOD_CTRL) &&
shellIsKeyDown(INPUT_KEY_C)) {
print("^C\n");
uint32_t killStatus = SysKillProcess(
hProc,
(int32_t)NTLSTATUS_PROCESS_KILLED);
if (killStatus != NTLSTATUS_SUCCESS) {
print("shell: foreground termination failed status=");
printUInt(killStatus);
print("\n");
return killStatus;
}
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, "fbbench") == 0) { cmd_fbbench(); return 0; }
if (strcmp(cmd, "volume") == 0) { cmd_volume(""); return 0; }
if (strncmp(cmd, "volume ", 7) == 0) { cmd_volume(cmd + 7); 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 != NTLSTATUS_SUCCESS) {
print("shell: wait failed: ");
printUInt(waitStatus);
print("\n");
}
print("Process exited with code: ");
int32_t 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);
INPUT_KEY drainedKey;
while (SysPollKeyboard(&drainedKey) == NTLSTATUS_SUCCESS) (void)SysReadKey(&drainedKey);
}
} 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;
}