Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions assembly/api-debugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,41 @@ export function getPageDump(index: u32): Uint8Array {
* Read a chunk of memory at `[address, address + length)`.
*
* Returns the requested memory chunk or `null` if reading triggered a page fault.
*
* @deprecated Getting memory like that is extremely inefficient (copying mulitple times)
Comment thread
tomusdrw marked this conversation as resolved.
* and error prone (we may not be able to allocate).
* Instead WASM should be able to return memory pointers for already allocated pages.
* So reading memory on the caller side should be something like this:
* ```ts
* let pagesRead = 0;
* for (let address = start; address < end; address += PAGE_SIZE) {
* const page = address >> PAGE_SIZE_SHIFT;
* const maybePointer = getPagePointer(page);
* // check page fault
* if (maybePointer === null) {
* throw new Error(`Page fault at ${page << PAGE_SIZE_SHIFT}`);
* }
* // otherwise copy to JS
* destination.set(
* pagesRead << PAGE_SIZE_SHIFT,
* new Uint8Array(wasm.instance.memory, maybePointer, Math.min(end, PAGE_SIZE))
* );
* pagesRead += 1;
* }
* ```
*
* goals:
* 1. No additional allocations on the WASM side
* 2. Copying directly from wasm memory on the JS side
*
*/
export function getMemory(address: u32, length: u32): Uint8Array | null {
if (interpreter === null) {
return null;
}
const int = <Interpreter>interpreter;
const result = new Uint8Array(length);
const faultRes = new MaybePageFault();
int.memory.bytesRead(faultRes, address, result, 0);
const result = int.memory.getMemory(faultRes, address, length);
if (faultRes.isFault) {
return null;
}
Expand Down
44 changes: 44 additions & 0 deletions assembly/memory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,48 @@ export const TESTS: Test[] = [
assert.isEqual(e.isFault, false, "e.fault");
return assert;
}),
test("should page fault when going beyond memory", (assert) => {
const address = 2343629385;
const length = 2145386496;

const mem = new MemoryBuilder().setData(Access.Read, address, new Uint8Array(0)).build();
const fault = new MaybePageFault();
const res = mem.getMemory(fault, address, length);

assert.isEqual(fault.isFault, true);
assert.isEqual(res, null);

return assert;
}),
test("should page fault when trying to allocate too much", (assert) => {
const address = 16 * PAGE_SIZE;
const length = 2145386496;

const mem = new MemoryBuilder().setData(Access.Read, address, new Uint8Array(0)).build();
const fault = new MaybePageFault();
const res = mem.getMemory(fault, address, length);

assert.isEqual(fault.isFault, true);
assert.isEqual(res, null);

return assert;
}),
test("should read memory succesfully", (assert) => {
const address = 20 * PAGE_SIZE;
const length = 1024;

const mem = new MemoryBuilder().setData(Access.Read, address, new Uint8Array(4096)).build();
const fault = new MaybePageFault();
const res = mem.getMemory(fault, address, length);

assert.isEqual(fault.fault, 0);
assert.isEqual(fault.isFault, false);
if (res !== null) {
assert.isEqual(res.length, length);
} else {
assert.fail("Expected to read the memory successfully.");
}

return assert;
}),
];
32 changes: 32 additions & 0 deletions assembly/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,36 @@ export class Memory {
this.setBytes(faultRes, address, value, 8);
}

/**
* DO NOT USE.
*
* @deprecated exposed temporarily for debugger/typeberry API.
*/
Comment thread
tomusdrw marked this conversation as resolved.
getMemory(fault: MaybePageFault, address: u32, length: u32): Uint8Array | null {
// first traverse memory and see if we don't page fault
let nextAddress = address;
let destinationIndex = i32(0);
const iLength = i32(length);
while (destinationIndex < iLength) {
const pageData = this.pageResult;
this.getPage(fault, pageData, Access.Read, nextAddress);
if (fault.isFault) {
return null;
}
nextAddress += PAGE_SIZE;
destinationIndex += PAGE_SIZE;
}

// only after, actually allocate and read the bytes.
const destination = new Uint8Array(length);
this.bytesRead(fault, address, destination, 0);
if (fault.isFault) {
return null;
}

return destination;
}
Comment thread
tomusdrw marked this conversation as resolved.

bytesRead(faultRes: MaybePageFault, address: u32, destination: Uint8Array, destinationOffset: u32): void {
let nextAddress = address;
let destinationIndex = i32(destinationOffset);
Expand Down Expand Up @@ -218,6 +248,7 @@ export class Memory {
return;
}

/** Write bytes from given `source` (with `sourceOffset`) at given `address`. */
bytesWrite(faultRes: MaybePageFault, address: u32, source: Uint8Array, sourceOffset: u32): void {
let nextAddress = address;
let sourceIndex = i32(sourceOffset);
Expand Down Expand Up @@ -326,6 +357,7 @@ export class Memory {
return;
}

/** Write some bytes to at most 2 pages. */
private setBytes(faultRes: MaybePageFault, address: u32, value: u64, bytes: u8): void {
const r = this.chunksResult;
this.getChunks(faultRes, r, Access.Write, address, bytes);
Expand Down
2 changes: 1 addition & 1 deletion biome.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@
}
},
"files": {
"includes": ["**/assembly/**/*"]
"includes": ["**/assembly/**/*", "!build", "!web/build"]
}
}