Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[browser] remove memory snapshot feature #98093

Merged
merged 3 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 0 additions & 2 deletions eng/testing/tests.browser.targets
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@
<_AppArgs Condition="'$(WasmTestAppArgs)' != ''">$(_AppArgs) $(WasmTestAppArgs)</_AppArgs>

<WasmXHarnessMonoArgs Condition="'$(XunitShowProgress)' == 'true'">$(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=1</WasmXHarnessMonoArgs>
<!-- snapshots are not yet supported with threads -->
<WasmXHarnessMonoArgs Condition="'$(WasmEnableThreads)' == 'true'">$(WasmXHarnessMonoArgs) --no-memory-snapshot</WasmXHarnessMonoArgs>
<!-- help unit test with PlatformDetection.IsThreadingSupported via IsBrowserThreadingSupported env variable -->
<WasmXHarnessMonoArgs Condition="'$(WasmEnableThreads)' == 'true'">$(WasmXHarnessMonoArgs) --setenv=IsBrowserThreadingSupported=true</WasmXHarnessMonoArgs>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<PropertyGroup Condition="'$(TargetOS)' == 'browser'">
<WasmXHarnessMonoArgs>--setenv=XHARNESS_LOG_TEST_START=true --no-memory-snapshot</WasmXHarnessMonoArgs>
<WasmXHarnessMonoArgs>--setenv=XHARNESS_LOG_TEST_START=true</WasmXHarnessMonoArgs>
<!-- This WASM test is problematic and slow right now. This sets the xharness timeout but there is also override in sendtohelix-browser.targets -->
<WasmXHarnessTestsTimeout>01:15:00</WasmXHarnessTestsTimeout>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<TestArchiveTestsRoot>$(TestArchiveRoot)browserornodejs/</TestArchiveTestsRoot>
<TestArchiveTestsDir>$(TestArchiveTestsRoot)$(OSPlatformConfig)/</TestArchiveTestsDir>
<DefineConstants>$(DefineConstants);TARGET_BROWSER</DefineConstants>
<WasmXHarnessMonoArgs>--setenv=XHARNESS_LOG_TEST_START=true --no-memory-snapshot</WasmXHarnessMonoArgs>
<WasmXHarnessMonoArgs>--setenv=XHARNESS_LOG_TEST_START=true</WasmXHarnessMonoArgs>
<!-- This WASM test is problematic and slow right now. This sets the xharness timeout but there is also override in sendtohelix-browser.targets -->
<WasmXHarnessTestsTimeout>01:15:00</WasmXHarnessTestsTimeout>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<TestArchiveTestsRoot>$(TestArchiveRoot)browserornodejs/</TestArchiveTestsRoot>
<TestArchiveTestsDir>$(TestArchiveTestsRoot)$(OSPlatformConfig)/</TestArchiveTestsDir>
<DefineConstants>$(DefineConstants);TARGET_BROWSER</DefineConstants>
<WasmXHarnessMonoArgs>--setenv=XHARNESS_LOG_TEST_START=true --no-memory-snapshot</WasmXHarnessMonoArgs>
<WasmXHarnessMonoArgs>--setenv=XHARNESS_LOG_TEST_START=true</WasmXHarnessMonoArgs>

<!-- This WASM test is problematic and slow right now. This sets the xharness timeout but there is also override in sendtohelix-browser.targets -->
<WasmXHarnessTestsTimeout>01:15:00</WasmXHarnessTestsTimeout>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<TestArchiveTestsRoot>$(TestArchiveRoot)browserornodejs/</TestArchiveTestsRoot>
<TestArchiveTestsDir>$(TestArchiveTestsRoot)$(OSPlatformConfig)/</TestArchiveTestsDir>
<DefineConstants>$(DefineConstants);TARGET_BROWSER</DefineConstants>
<WasmXHarnessMonoArgs>--setenv=XHARNESS_LOG_TEST_START=true --no-memory-snapshot</WasmXHarnessMonoArgs>
<WasmXHarnessMonoArgs>--setenv=XHARNESS_LOG_TEST_START=true</WasmXHarnessMonoArgs>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<!-- Use following lines to write the generated files to disk. -->
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<!-- to see timing and which test aborted the runtime -->
<WasmXHarnessMonoArgs>$(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=true --no-memory-snapshot</WasmXHarnessMonoArgs>
<WasmXHarnessMonoArgs>$(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=true</WasmXHarnessMonoArgs>
</PropertyGroup>
<!-- Make debugging easier -->
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetOS)' == 'browser'">
<WasmXHarnessMonoArgs>--setenv=XHARNESS_LOG_TEST_START=true --no-memory-snapshot</WasmXHarnessMonoArgs>
<WasmXHarnessMonoArgs>--setenv=XHARNESS_LOG_TEST_START=true</WasmXHarnessMonoArgs>
<!-- This WASM test is problematic and slow right now. This sets the xharness timeout but there is also override in sendtohelix-browser.targets -->
<WasmXHarnessTestsTimeout>01:15:00</WasmXHarnessTestsTimeout>
</PropertyGroup>
Expand Down
4 changes: 0 additions & 4 deletions src/mono/browser/runtime/dotnet.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,6 @@ type MonoConfig = {
* initial number of workers to add to the emscripten pthread pool
*/
pthreadPoolSize?: number;
/**
* If true, the snapshot of runtime's memory will be stored in the browser and used for faster startup next time. Default is false.
*/
startupMemoryCache?: boolean;
/**
* If true, a list of the methods optimized by the interpreter will be saved and used for faster startup
* on future runs of the application
Expand Down
148 changes: 145 additions & 3 deletions src/mono/browser/runtime/interp-pgo.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import ProductVersion from "consts:productVersion";
import WasmEnableThreads from "consts:wasmEnableThreads";

import { Module, loaderHelpers } from "./globals";
import { getCacheKey, cleanupCache, getCacheEntry, storeCacheEntry } from "./snapshot";
import { mono_log_info, mono_log_error } from "./logging";
import { ENVIRONMENT_IS_WEB, Module, loaderHelpers, runtimeHelpers } from "./globals";
import { mono_log_info, mono_log_error, mono_log_warn } from "./logging";
import { localHeapViewU8 } from "./memory";
import cwraps from "./cwraps";
import { MonoConfigInternal } from "./types/internal";

export const tablePrefix = "https://dotnet.generated.invalid/interp_pgo";

Expand Down Expand Up @@ -73,3 +75,143 @@ export async function interp_pgo_load_data() {

Module._free(pData);
}

async function openCache(): Promise<Cache | null> {
// cache integrity is compromised if the first request has been served over http (except localhost)
// in this case, we want to disable caching and integrity validation
if (ENVIRONMENT_IS_WEB && globalThis.window.isSecureContext === false) {
mono_log_warn("Failed to open the cache, running on an insecure origin");
return null;
}

// caches will be undefined if we're running on an insecure origin (secure means https or localhost)
if (typeof globalThis.caches === "undefined") {
mono_log_warn("Failed to open the cache, probably running on an insecure origin");
return null;
}

// Define a separate cache for each base href, so we're isolated from any other
// Blazor application running on the same origin. We need this so that we're free
// to purge from the cache anything we're not using and don't let it keep growing,
// since we don't want to be worst offenders for space usage.
const relativeBaseHref = document.baseURI.substring(document.location.origin.length);
const cacheName = `dotnet-resources${relativeBaseHref}`;

try {
// There's a Chromium bug we need to be aware of here: the CacheStorage APIs say that when
// caches.open(name) returns a promise that succeeds, the value is meant to be a Cache instance.
// However, if the browser was launched with a --user-data-dir param that's "too long" in some sense,
// then even through the promise resolves as success, the value given is `undefined`.
// See https://stackoverflow.com/a/46626574 and https://bugs.chromium.org/p/chromium/issues/detail?id=1054541
// If we see this happening, return "null" to mean "proceed without caching".
return (await globalThis.caches.open(cacheName)) || null;
} catch {
// There's no known scenario where we should get an exception here, but considering the
// Chromium bug above, let's tolerate it and treat as "proceed without caching".
mono_log_warn("Failed to open cache");
return null;
}
}

export async function getCacheEntry(cacheKey: string): Promise<ArrayBuffer | undefined> {
try {
const cache = await openCache();
if (!cache) {
return undefined;
}
const res = await cache.match(cacheKey);
if (!res) {
return undefined;
}
return res.arrayBuffer();
} catch (ex) {
mono_log_warn("Failed to load entry from the cache: " + cacheKey, ex);
return undefined;
}
}

export async function storeCacheEntry(cacheKey: string, memory: ArrayBuffer, mimeType: string): Promise<boolean> {
try {
const cache = await openCache();
if (!cache) {
return false;
}
const copy = WasmEnableThreads
// storing SHaredArrayBuffer in the cache is not working
? (new Uint8Array(memory)).slice(0)
: memory;

const responseToCache = new Response(copy, {
headers: {
"content-type": mimeType,
"content-length": memory.byteLength.toString(),
},
});

await cache.put(cacheKey, responseToCache);

return true;
} catch (ex) {
mono_log_warn("Failed to store entry to the cache: " + cacheKey, ex);
return false;
}
}

export async function cleanupCache(prefix: string, protectKey: string) {
try {
const cache = await openCache();
if (!cache) {
return;
}
const items = await cache.keys();
for (const item of items) {
if (item.url && item.url !== protectKey && item.url.startsWith(prefix)) {
await cache.delete(item);
}
}
} catch (ex) {
return;
}
}

// calculate hash of things which affect config hash
export async function getCacheKey(prefix: string): Promise<string | null> {
if (!runtimeHelpers.subtle) {
return null;
}
const inputs = Object.assign({}, runtimeHelpers.config) as MonoConfigInternal;

// Now we remove assets collection from the hash.
inputs.resourcesHash = inputs.resources!.hash;
delete inputs.assets;
delete inputs.resources;
// some things are calculated at runtime, so we need to add them to the hash
inputs.preferredIcuAsset = loaderHelpers.preferredIcuAsset;
// timezone is part of env variables, so it is already in the hash

// some things are not relevant for config hash
delete inputs.forwardConsoleLogsToWS;
delete inputs.diagnosticTracing;
delete inputs.appendElementOnExit;
delete inputs.assertAfterExit;
delete inputs.interopCleanupOnExit;
delete inputs.dumpThreadsOnNonZeroExit;
delete inputs.logExitCode;
delete inputs.pthreadPoolSize;
delete inputs.asyncFlushOnExit;
delete inputs.remoteSources;
delete inputs.ignorePdbLoadErrors;
delete inputs.maxParallelDownloads;
delete inputs.enableDownloadRetry;
delete inputs.extensions;
delete inputs.runtimeId;

inputs.GitHash = loaderHelpers.gitHash;
inputs.ProductVersion = ProductVersion;

const inputsJson = JSON.stringify(inputs);
const sha256Buffer = await runtimeHelpers.subtle.digest("SHA-256", new TextEncoder().encode(inputsJson));
const uint8ViewOfHash = new Uint8Array(sha256Buffer);
const hashAsString = Array.from(uint8ViewOfHash).map((b) => b.toString(16).padStart(2, "0")).join("");
return `${prefix}-${hashAsString}`;
}
1 change: 0 additions & 1 deletion src/mono/browser/runtime/jiterpreter-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1557,7 +1557,6 @@ export function getWasmFunctionTable() {

export function addWasmFunctionPointer(table: JiterpreterTable, f: Function) {
mono_assert(f, "Attempting to set null function into table");
mono_assert(!runtimeHelpers.storeMemorySnapshotPending, "Attempting to set function into table during creation of memory snapshot");

const index = cwraps.mono_jiterp_allocate_table_entry(table);
if (index > 0) {
Expand Down
3 changes: 1 addition & 2 deletions src/mono/browser/runtime/jiterpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -884,9 +884,8 @@ function generate_wasm(
// independently jitting traces will not stomp on each other and all threads
// have a globally consistent view of which function pointer maps to each trace.
rejected = false;
mono_assert(!runtimeHelpers.storeMemorySnapshotPending, "Attempting to set function into table during creation of memory snapshot");

let idx : number;
let idx: number;
if (presetFunctionPointer) {
const fnTable = getWasmFunctionTable();
fnTable.set(presetFunctionPointer, fn);
Expand Down
Loading
Loading