feat(web): surface QEMU logs in-page and enable guest-error diagnostics
Add a log panel that captures QEMU stdout/stderr, route serial to stdio, and pass -d guest_errors,unimp so device warnings (e.g. the m25p80 Read-ID gap that stalls CrossPoint boot) are visible in the browser. Read the framebuffer via HEAPU8 to match the validated node path.
This commit is contained in:
+28
-4
@@ -76,7 +76,7 @@ function drawFrame(module, canvas) {
|
||||
canvas.height = height;
|
||||
}
|
||||
|
||||
const source = new Uint8Array(module.wasmMemory.buffer);
|
||||
const source = module.HEAPU8;
|
||||
const image = new ImageData(width, height);
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
@@ -153,6 +153,24 @@ function enableControls(module) {
|
||||
}
|
||||
}
|
||||
|
||||
let logEl = null;
|
||||
const logBuffer = [];
|
||||
const LOG_MAX_LINES = 1000;
|
||||
|
||||
function logLine(message) {
|
||||
logBuffer.push(message);
|
||||
if (logBuffer.length > LOG_MAX_LINES) {
|
||||
logBuffer.splice(0, logBuffer.length - LOG_MAX_LINES);
|
||||
}
|
||||
if (logEl) {
|
||||
const atBottom = logEl.scrollTop + logEl.clientHeight >= logEl.scrollHeight - 4;
|
||||
logEl.textContent = logBuffer.join('\n');
|
||||
if (atBottom) {
|
||||
logEl.scrollTop = logEl.scrollHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function boot(file, variant, setStatus) {
|
||||
if (!crossOriginIsolated) {
|
||||
throw new Error('This emulator requires COOP/COEP headers. Start it with `make web`.');
|
||||
@@ -175,14 +193,15 @@ async function boot(file, variant, setStatus) {
|
||||
'-machine', `xteink,variant=${variant}`,
|
||||
'-L', '/bios',
|
||||
'-display', 'none',
|
||||
'-serial', 'null',
|
||||
'-serial', 'stdio',
|
||||
'-nic', 'none',
|
||||
'-d', 'guest_errors,unimp',
|
||||
'-drive', 'file=/flash.bin,if=mtd,format=raw',
|
||||
],
|
||||
locateFile: path => new URL(path, artifactRoot).href,
|
||||
mainScriptUrlOrBlob: qemuUrl.href,
|
||||
print: message => console.log(message),
|
||||
printErr: message => console.error(message),
|
||||
print: message => { console.log(message); logLine(message); },
|
||||
printErr: message => { console.error(message); logLine(message); },
|
||||
};
|
||||
options.preRun = [() => {
|
||||
options.FS.mkdir('/bios');
|
||||
@@ -201,6 +220,11 @@ function initialize() {
|
||||
const bootButton = document.querySelector('#boot');
|
||||
const status = document.querySelector('#status');
|
||||
const canvas = document.querySelector('#screen');
|
||||
logEl = document.querySelector('#log');
|
||||
document.querySelector('#log-clear').addEventListener('click', () => {
|
||||
logBuffer.length = 0;
|
||||
logEl.textContent = '';
|
||||
});
|
||||
|
||||
const setStatus = (message, error = false) => {
|
||||
status.textContent = message;
|
||||
|
||||
@@ -48,6 +48,14 @@
|
||||
<button type="button" class="power" data-button="power" disabled>Power</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="log-panel" aria-label="Emulator logs">
|
||||
<div class="log-header">
|
||||
<span>QEMU log</span>
|
||||
<button id="log-clear" type="button">Clear</button>
|
||||
</div>
|
||||
<pre id="log" class="log" role="log" aria-live="polite"></pre>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script type="module" src="app.js"></script>
|
||||
|
||||
@@ -69,3 +69,8 @@ canvas { display: block; width: 100%; height: auto; background: #fff; image-rend
|
||||
.device { grid-template-columns: 1fr; }
|
||||
.controls { max-width: 24rem; width: 100%; margin: 0 auto; }
|
||||
}
|
||||
|
||||
.log-panel { margin-top: 2rem; }
|
||||
.log-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: .5rem; font-size: .85rem; color: #b9b3a4; }
|
||||
.log-header button { min-height: auto; padding: .25rem .75rem; font-size: .8rem; }
|
||||
.log { margin: 0; max-height: 20rem; overflow: auto; padding: .75rem 1rem; border-radius: .6rem; background: #1c1d1a; color: #d6d0c2; font: .78rem/1.4 ui-monospace, monospace; white-space: pre-wrap; word-break: break-word; }
|
||||
|
||||
Reference in New Issue
Block a user