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

[wasm] Enable the log profiler #107434

Merged
merged 24 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4d5088b
feat(wasm): Enable the log profiler
jeromelaban Sep 5, 2024
5665edf
chore: Disable for wasi
jeromelaban Sep 6, 2024
c10694a
chore: Add log profiler docs
jeromelaban Sep 6, 2024
2439386
chore: Adjust flush logs syntax
jeromelaban Sep 6, 2024
15f8151
chore: Adjust defines, add conditionals, remove Module dependency
jeromelaban Sep 6, 2024
51d038b
chore: Adjust buffer_unlock exclusion
jeromelaban Sep 6, 2024
0158a26
chore: Adjust doc
jeromelaban Sep 6, 2024
402ed92
chore: Add log profiler sample
jeromelaban Sep 6, 2024
a817dfb
chore: Add more sample logging
jeromelaban Sep 6, 2024
59bdd89
chore: Remove unused makefile target
jeromelaban Sep 6, 2024
d6cce7c
chore: Remove icall, use jit interception to take heap sot
jeromelaban Sep 9, 2024
61337f5
chore: Remove unused logs
jeromelaban Sep 9, 2024
dc0ad26
Merge branch 'main' into dev/jela/log-profiler
jeromelaban Sep 9, 2024
33221c4
chore: Remove unused dependency
jeromelaban Sep 10, 2024
d4887e5
Revert "chore: Remove unused dependency"
pavelsavara Sep 10, 2024
afee25a
remove ENABLE_BROWSER_PROFILER from default build
pavelsavara Sep 10, 2024
a03da8f
Draft of WBT.
ilonatommy Sep 10, 2024
760689b
Miss-commit, there's no profiler.js in the sample.
ilonatommy Sep 11, 2024
8e4bc94
Shift the responsibility of checking profile's size to the browser.
ilonatommy Sep 11, 2024
8c9da17
Test linking of all 3 types of loggers + running one of them.
ilonatommy Sep 11, 2024
32bfeaa
link all 3 at the same time
pavelsavara Sep 11, 2024
3b1273e
fix [Fact]
pavelsavara Sep 11, 2024
ab0b6e4
Treat a list of loggers as one argument.
ilonatommy Sep 11, 2024
5a21413
fix?
pavelsavara Sep 12, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@
<PlatformManifestFileEntry Include="libmono-ee-interp.a" IsNative="true" />
<PlatformManifestFileEntry Include="libmono-icall-table.a" IsNative="true" />
<PlatformManifestFileEntry Include="libmono-profiler-aot.a" IsNative="true" />
<PlatformManifestFileEntry Include="libmono-profiler-log.a" IsNative="true" />
<PlatformManifestFileEntry Include="libmono-profiler-browser.a" IsNative="true" />
<PlatformManifestFileEntry Include="libmono-wasm-eh-js.a" IsNative="true" />
<PlatformManifestFileEntry Include="libmono-wasm-eh-wasm.a" IsNative="true" />
Expand Down
9 changes: 7 additions & 2 deletions src/mono/browser/runtime/profiler.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import { ENVIRONMENT_IS_WEB, mono_assert, runtimeHelpers } from "./globals";
import { MonoMethod, AOTProfilerOptions, BrowserProfilerOptions } from "./types/internal";
import { ENVIRONMENT_IS_WEB, mono_assert, Module, runtimeHelpers } from "./globals";
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
import { MonoMethod, AOTProfilerOptions, BrowserProfilerOptions, LogProfilerOptions } from "./types/internal";
import { profiler_c_functions as cwraps } from "./cwraps";
import { utf8ToString } from "./strings";

Expand Down Expand Up @@ -99,3 +99,8 @@ export function mono_wasm_profiler_leave (method: MonoMethod): void {
globalThis.performance.measure(methodName, options);
}
}

// options = { configuration: "log profiler options string" }
export function mono_wasm_init_log_profiler (options: LogProfilerOptions): void {
Module.ccall("mono_wasm_load_profiler_log", null, ["string"], [options.configuration]);
}
6 changes: 5 additions & 1 deletion src/mono/browser/runtime/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers, cr
import cwraps, { init_c_exports, threads_c_functions as tcwraps } from "./cwraps";
import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
import { toBase64StringImpl } from "./base64";
import { mono_wasm_init_aot_profiler, mono_wasm_init_browser_profiler } from "./profiler";
import { mono_wasm_init_aot_profiler, mono_wasm_init_browser_profiler, mono_wasm_init_log_profiler } from "./profiler";
import { initialize_marshalers_to_cs } from "./marshal-to-cs";
import { initialize_marshalers_to_js } from "./marshal-to-js";
import { init_polyfills_async } from "./polyfills";
Expand Down Expand Up @@ -318,6 +318,10 @@ async function onRuntimeInitializedAsync (userOnRuntimeInitialized: () => void)

await wait_for_all_assets();

if (runtimeHelpers.config.logProfilerOptions) {
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
mono_wasm_init_log_profiler(runtimeHelpers.config.logProfilerOptions);
}

if (WasmEnableThreads) {
runtimeHelpers.deputyWorker.thread!.postMessageToWorker({
type:"deputyThread",
Expand Down
5 changes: 5 additions & 0 deletions src/mono/browser/runtime/types/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export type MonoConfigInternal = MonoConfig & {
assets?: AssetEntryInternal[],
runtimeOptions?: string[], // array of runtime options as strings
aotProfilerOptions?: AOTProfilerOptions, // dictionary-style Object. If omitted, aot profiler will not be initialized.
logProfilerOptions?: LogProfilerOptions, // dictionary-style Object. If omitted, log profiler will not be initialized.
browserProfilerOptions?: BrowserProfilerOptions, // dictionary-style Object. If omitted, browser profiler will not be initialized.
waitForDebugger?: number,
appendElementOnExit?: boolean
Expand Down Expand Up @@ -274,6 +275,10 @@ export type AOTProfilerOptions = {
export type BrowserProfilerOptions = {
}

export type LogProfilerOptions = {
configuration?: string // log profiler options string"
}

// how we extended emscripten Module
export type DotnetModule = EmscriptenModule & DotnetModuleConfig;
export type DotnetModuleInternal = EmscriptenModule & DotnetModuleConfig & EmscriptenModuleInternal;
Expand Down
3 changes: 3 additions & 0 deletions src/mono/mono.proj
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,9 @@ JS_ENGINES = [NODE_JS]
<_MonoRuntimeArtifacts Condition="'$(TargetsBrowser)' == 'true' and '$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(MonoObjDir)out\lib\libmono-profiler-aot.a">
<Destination>$(RuntimeBinDir)libmono-profiler-aot.a</Destination>
</_MonoRuntimeArtifacts>
<_MonoRuntimeArtifacts Condition="'$(TargetsBrowser)' == 'true' and '$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(MonoObjDir)out\lib\libmono-profiler-log.a">
<Destination>$(RuntimeBinDir)libmono-profiler-log.a</Destination>
</_MonoRuntimeArtifacts>
<_MonoRuntimeArtifacts Condition="'$(TargetsBrowser)' == 'true' and '$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(MonoObjDir)out\lib\libmono-profiler-browser.a">
<Destination>$(RuntimeBinDir)libmono-profiler-browser.a</Destination>
</_MonoRuntimeArtifacts>
Expand Down
1 change: 0 additions & 1 deletion src/mono/mono/mini/mini-wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,6 @@ mono_init_native_crash_info (void)
void
mono_runtime_setup_stat_profiler (void)
{
g_error ("mono_runtime_setup_stat_profiler");
}

gboolean
Expand Down
15 changes: 15 additions & 0 deletions src/mono/mono/profiler/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ include_directories(
${PROJECT_BINARY_DIR}/../../mono/eglib
${CMAKE_CURRENT_SOURCE_DIR}/../..
${PROJECT_SOURCE_DIR}/../
${PROJECT_SOURCE_DIR}/../../../native/public
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
${PROJECT_SOURCE_DIR}/../eglib
${PROJECT_SOURCE_DIR}/../sgen)

Expand Down Expand Up @@ -34,6 +35,20 @@ if(NOT DISABLE_LIBS)
endif()
endif()

if(HOST_WASM AND NOT HOST_WASI)
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
add_library(mono-profiler-log-static STATIC helper.c log.c log-args.c)
set_target_properties(mono-profiler-log-static PROPERTIES OUTPUT_NAME mono-profiler-log)
install(TARGETS mono-profiler-log-static LIBRARY)

if(NOT DISABLE_LOG_PROFILER_GZ)
if (CLR_CMAKE_USE_SYSTEM_ZLIB)
target_link_libraries(mono-profiler-log-static PRIVATE ${Z_LIBS})
else()
target_link_libraries(mono-profiler-log-static PRIVATE zlib)
endif()
endif()
endif()

if(NOT HOST_WASI)
add_library(mono-profiler-aot-static STATIC aot.c helper.c)
target_link_libraries(mono-profiler-aot-static PRIVATE monoapi)
Expand Down
6 changes: 6 additions & 0 deletions src/mono/mono/profiler/helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

#include <config.h>

#if !defined (HOST_WASM)
#include <mono/utils/mono-logger-internals.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
Expand Down Expand Up @@ -42,6 +44,7 @@ mono_profhelper_close_socket_fd (SOCKET fd)
void
mono_profhelper_setup_command_server (SOCKET *server_socket, int *command_port, const char* profiler_name)
{
#if !defined (HOST_WASM)
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
*server_socket = socket (PF_INET, SOCK_STREAM, 0);

if (*server_socket == INVALID_SOCKET) {
Expand Down Expand Up @@ -77,11 +80,13 @@ mono_profhelper_setup_command_server (SOCKET *server_socket, int *command_port,
}

*command_port = ntohs (server_address.sin_port);
#endif
}

void
mono_profhelper_add_to_fd_set (fd_set *set, SOCKET fd, int *max_fd)
{
#if !defined (HOST_WASM)
/*
* This should only trigger for the basic FDs (server socket, pipes) at
* startup if for some mysterious reason they're too large. In this case,
Expand All @@ -99,4 +104,5 @@ mono_profhelper_add_to_fd_set (fd_set *set, SOCKET fd, int *max_fd)

if (*max_fd < GUINT64_TO_INT(fd))
*max_fd = (int)fd;
#endif
}
23 changes: 22 additions & 1 deletion src/mono/mono/profiler/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@ buffer_lock_helper (void);
static void
buffer_lock (void)
{
#if !defined (HOST_WASM)
/*
* If the thread holding the exclusive lock tries to modify the
* reader count, just make it a no-op. This way, we also avoid
Expand Down Expand Up @@ -657,6 +658,8 @@ buffer_lock (void)
}

mono_memory_barrier ();

#endif //HOST_WASM
}

static void
Expand Down Expand Up @@ -3499,6 +3502,7 @@ runtime_initialized (MonoProfiler *profiler)

mono_os_sem_init (&log_profiler.attach_threads_sem, 0);

#if !defined (HOST_WASM)
/*
* We must start the helper thread before the writer thread. This is
* because start_helper_thread () sets up the command port which is written
Expand All @@ -3507,6 +3511,9 @@ runtime_initialized (MonoProfiler *profiler)
start_helper_thread ();
start_writer_thread ();
start_dumper_thread ();
#else
dump_header ();
#endif

/*
* Wait for all the internal threads to be started. If we don't do this, we
Expand Down Expand Up @@ -3588,7 +3595,7 @@ create_profiler (const char *args, const char *filename, GPtrArray *filters)
}
}
if (*nf == '|') {
#if HAVE_API_SUPPORT_WIN32_PIPE_OPEN_CLOSE && !defined (HOST_WIN32)
#if HAVE_API_SUPPORT_WIN32_PIPE_OPEN_CLOSE && !defined (HOST_WIN32) && !defined (HOST_WASM)
log_profiler.file = popen (nf + 1, "w");
log_profiler.pipe_output = 1;
#else
Expand Down Expand Up @@ -3770,3 +3777,17 @@ mono_profiler_init_log (const char *desc)
done:
;
}

#if defined (HOST_WASM)

MONO_API void
mono_profiler_flush_log (void);

void
mono_profiler_flush_log (void)
{
while (handle_writer_queue_entry ());
while (handle_dumper_queue_entry ());
}

#endif // HOST_WASM
42 changes: 42 additions & 0 deletions src/mono/wasm/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,48 @@ import { dotnet } from './dotnet.js'
await dotnet.withConfig({browserProfilerOptions: {}}).run();
```

### Log Profiling for Memory Troubleshooting

You can enable integration with browser profiler via following elements in your .csproj:

```xml
<PropertyGroup>
<WasmProfilers>log;</WasmProfilers>
</PropertyGroup>
```

In simple browser template, you can add following to your `main.js`

```javascript
import { dotnet } from './dotnet.js'
await dotnet.withConfig({ logProfilerOptions: { configuration: "log:alloc,output=output.mlpd" }}).run();
```

In order to trigger a heap shot, add the following:

```csharp
using System.Runtime.CompilerServices;

namespace Mono.Profiler.Log
{
/// <summary>
/// Internal calls to match with https://github.com/dotnet/runtime/blob/release/6.0/src/mono/mono/profiler/log.c#L4061-L4097
/// </summary>
internal class LogProfiler
{
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern static void TriggerHeapshot();

[DllImport("__Native")]
public extern static private void mono_profiler_flush_log();
}
}
```

Invoke `LogProfiler.TriggerHeapshot()` from your code in order to create a memory heap shot, then invoke `mono_profiler_flush_log` in order to flush the contents of the profile to the VFS.

You can download the mpld file to analyze it.

### Diagnostic tools

We have initial implementation of diagnostic server and [event pipe](https://learn.microsoft.com/dotnet/core/diagnostics/eventpipe)
Expand Down
Loading