Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
<_ILLinkDescriptorsFilePaths Include="$(ILLinkDirectory)ILLink.Descriptors.Debug.xml"
Condition="'$(Configuration)' == 'Debug' or '$(Configuration)' == 'Checked'" />
<_ILLinkDescriptorsFilePaths Include="$(CoreLibSharedDir)ILLink\ILLink.Descriptors.Shared.xml" />
<_ILLinkDescriptorsFilePaths Include="$(CoreLibSharedDir)ILLink\ILLink.Descriptors.EventSource.xml" />
</ItemGroup>

<!--
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@
<Compile Include="$(BclSourcesRoot)\System\RuntimeType.ActivatorCache.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeType.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Security\DynamicSecurityMethodAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\StartupHookProvider.cs" />
<Compile Include="$(BclSourcesRoot)\System\StartupHookProvider.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\String.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\StubHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Text\StringBuilder.CoreCLR.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<linker>
<assembly fullname="System.Private.CoreLib">
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2026</argument>
<property name="Scope">member</property>
<property name="Target">M:System.StartupHookProvider.ProcessStartupHooks()</property>
<property name="Justification">This warning is left in the product so developers get an ILLink warning when trimming an app with System.StartupHookProvider.IsSupported=true.</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2026</argument>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;

namespace System
{
internal static partial class StartupHookProvider
{
private static void ManagedStartup()
{
#if FEATURE_PERFTRACING
if (EventSource.IsSupported)
RuntimeEventSource.Initialize();
#endif

if (IsSupported)
ProcessStartupHooks();
}
}
}
8 changes: 4 additions & 4 deletions src/coreclr/vm/assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1421,7 +1421,7 @@ static void RunMainPost()
}
}

static void RunStartupHooks()
static void RunManagedStartup()
{
CONTRACTL
{
Expand All @@ -1432,8 +1432,8 @@ static void RunStartupHooks()
}
CONTRACTL_END;

MethodDescCallSite processStartupHooks(METHOD__STARTUP_HOOK_PROVIDER__PROCESS_STARTUP_HOOKS);
processStartupHooks.Call(NULL);
MethodDescCallSite managedStartup(METHOD__STARTUP_HOOK_PROVIDER__MANAGED_STARTUP);
managedStartup.Call(NULL);
}

INT32 Assembly::ExecuteMainMethod(PTRARRAYREF *stringArgs, BOOL waitForOtherThreads)
Expand Down Expand Up @@ -1499,7 +1499,7 @@ INT32 Assembly::ExecuteMainMethod(PTRARRAYREF *stringArgs, BOOL waitForOtherThre
// Main thread wasn't started by the runtime.
Thread::InitializationForManagedThreadInNative(pThread);

RunStartupHooks();
RunManagedStartup();

hr = RunMain(pMeth, 1, &iRetVal, stringArgs);

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ DEFINE_FIELD_U(rgiLastFrameFromForeignExceptionStackTrace, StackFrame
DEFINE_FIELD_U(iFrameCount, StackFrameHelper, iFrameCount)

DEFINE_CLASS(STARTUP_HOOK_PROVIDER, System, StartupHookProvider)
DEFINE_METHOD(STARTUP_HOOK_PROVIDER, PROCESS_STARTUP_HOOKS, ProcessStartupHooks, SM_RetVoid)
DEFINE_METHOD(STARTUP_HOOK_PROVIDER, MANAGED_STARTUP, ManagedStartup, SM_RetVoid)

DEFINE_CLASS(STREAM, IO, Stream)
DEFINE_METHOD(STREAM, BEGIN_READ, BeginRead, IM_ArrByte_Int_Int_AsyncCallback_Object_RetIAsyncResult)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,12 @@
<property name="Target">M:Internal.Runtime.InteropServices.ComponentActivator.GetFunctionPointer(System.IntPtr,System.IntPtr,System.IntPtr,System.IntPtr,System.IntPtr,System.IntPtr)</property>
<property name="Justification">This warning is left in the product so developers get an ILLink warning when trimming an app with Internal.Runtime.InteropServices.ComponentActivator.IsSupported=true.</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2026</argument>
<property name="Scope">member</property>
<property name="Target">M:System.StartupHookProvider.ProcessStartupHooks()</property>
<property name="Justification">This warning is left in the product so developers get an ILLink warning when trimming an app with System.StartupHookProvider.IsSupported=true.</property>
</attribute>
</assembly>
</linker>
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.T.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SR.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\StackOverflowException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\StartupHookProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\String.Comparison.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\String.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\String.Manipulation.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ public static class Keywords

public static void Initialize()
{
s_RuntimeEventSource = new RuntimeEventSource();
// initializing more than once may lead to missing events
Debug.Assert(s_RuntimeEventSource == null);
if (EventSource.IsSupported)
s_RuntimeEventSource = new RuntimeEventSource();
}

// Parameterized constructor to block initialization and ensure the EventSourceGenerator is creating the default constructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace System
{
internal static class StartupHookProvider
internal static partial class StartupHookProvider
{
private const string StartupHookTypeName = "StartupHook";
private const string InitializeMethodName = "Initialize";
Expand All @@ -32,12 +32,6 @@ private static void ProcessStartupHooks()
if (!IsSupported)
return;

// Initialize tracing before any user code can be called if EventSource is enabled.
if (EventSource.IsSupported)
{
System.Diagnostics.Tracing.RuntimeEventSource.Initialize();
}

string? startupHooksVariable = AppContext.GetData("STARTUP_HOOKS") as string;
if (startupHooksVariable == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -644,4 +644,10 @@
<method name="GetInstanceFieldFieldStore" />
</type>
</assembly>
<assembly fullname="System.Private.CoreLib" feature="System.StartupHookProvider.IsSupported" featurevalue="true" featuredefault="true">
<type fullname="System.StartupHookProvider">
<!-- object.c: mono_runtime_run_startup_hooks -->
<method name="ProcessStartupHooks" />
</type>
</assembly>
</linker>
3 changes: 3 additions & 0 deletions src/mono/mono/metadata/object-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -2148,4 +2148,7 @@ mono_string_instance_is_interned (MonoString *str);
gpointer
mono_method_get_unmanaged_wrapper_ftnptr_internal (MonoMethod *method, gboolean only_unmanaged_callers_only, MonoError *error);

void
mono_runtime_run_startup_hooks (void);

#endif /* __MONO_OBJECT_INTERNALS_H__ */
19 changes: 19 additions & 0 deletions src/mono/mono/metadata/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -8124,6 +8124,25 @@ mono_runtime_get_managed_cmd_line (void)
return cmd_line ? g_string_free (cmd_line, FALSE) : NULL;
}

void
mono_runtime_run_startup_hooks (void)
{
if (mono_runtime_get_no_exec ())
return;

MonoClass *klass = mono_class_try_load_from_name (mono_defaults.corlib, "System", "StartupHookProvider");
Copy link
Member

@lateralusX lateralusX Jan 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why we won't call the same startup hook logic as CoreCLR and remove the call in ep_rt_mono_init_finish on Mono? If there might be a scenario in the future where the managed parts gets included without EventPipe on Mono (currenlty not the case), then we would need to change this since ep_finish_init would then just be a stub implementation., but the user still expect to get these events delivered over alternative provider. We could of course do that when needed, or we could just do it now, since we get the support that previously was missing and why init of runtime events ended up in ep_rt_mono_init_finish. Anything with the current startup hook implementation that would prevent us from getting the same behavior as we currently have, but done from startup hook instead of ep_rt_mono_init_finish?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why we won't call the same startup hook logic as CoreCLR and remove the call in ep_rt_mono_init_finish on Mono? If there might be a scenario in the future where the managed parts gets included without EventPipe on Mono (currenlty not the case), then we would need to change this since ep_finish_init would then just be a stub implementation., but the user still expect to get these events delivered over alternative provider. We could of course do that when needed, or we could just do it now, since we get the support that previously was missing and why init of runtime events ended up in ep_rt_mono_init_finish. Anything with the current startup hook implementation that would prevent us from getting the same behavior as we currently have, but done from startup hook instead of ep_rt_mono_init_finish?

There are two separate issues:

  1. Support EventSource on Mono without EventPipe.
  2. Use the same single ManagedStartup call as CoreCLR.

I think it's probably reasonable to do something about 1 - we can move the invoke of RuntimeEventSource.Initialize out of EP and into mini_init, for example.

For 2, the issue is that ManagedStartup cannot be trimmed - there is no way to tell the trimmer "remove a method if 2 feature switches are both disabled". So we would always be paying for a call to managed even if event source and startup hooks are both disabled (for example on single-threaded WASM).

So if you think it makes sense to start decoupling EventSource and EventPipe in Mono now, we can do that. But I don't think it makes sense to do 2. Of course if we had some other managed initialization that had to be done explicitly on all platforms, then we could roll all that logic into one function.

Copy link
Member

@lateralusX lateralusX Jan 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, understood, if we still won't end up on the same solution as CoreCLR, then I believe we can keep things as is for now, and move the call to init when/if we ever have a need to separate init of runtime event source from EventPipe availability.

if (!klass)
return; // Linked away
ERROR_DECL (error);
MonoMethod *method = mono_class_get_method_from_name_checked (klass, "ProcessStartupHooks", -1, 0, error);
mono_error_cleanup (error);
if (!method)
return;
mono_runtime_invoke_checked (method, NULL, NULL, error);
// runtime hooks design doc says not to catch exceptions from the hooks
mono_error_raise_exception_deprecated (error);
}

#if NEVER_DEFINED
/*
* The following section is purely to declare prototypes and
Expand Down
2 changes: 2 additions & 0 deletions src/mono/mono/mini/mini-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -4729,6 +4729,8 @@ mini_init (const char *filename)

MONO_PROFILER_RAISE (runtime_initialized, ());

mono_runtime_run_startup_hooks ();

MONO_VES_INIT_END ();

return domain;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TestRuntime>true</TestRuntime>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<MainLibraryFileName>Android.Device_Emulator.StartupHook.Test.dll</MainLibraryFileName>
<IncludesTestRunner>false</IncludesTestRunner>
<ExpectedExitCode>42</ExpectedExitCode>
<StartupHookSupport>true</StartupHookSupport>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\TestAssets\StartupHookForFunctionalTest\StartupHookForFunctionalTest.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

public static class Program
{
public static int Main()
{
string appContextKey = "Test.StartupHookForFunctionalTest.DidRun";
var data = (string) AppContext.GetData (appContextKey);

if (data != "Yes") {
string msg = $"Expected startup hook to set {appContextKey} to 'Yes', got '{data}'";
Console.Error.WriteLine(msg);
return 104;
}
return 42;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"configProperties": {
"STARTUP_HOOKS": "StartupHookForFunctionalTest"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

internal class StartupHook
{
public static void Initialize()
{
AppContext.SetData("Test.StartupHookForFunctionalTest.DidRun", "Yes");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<IsTestProject>false</IsTestProject>
<IsFunctionalTest>false</IsFunctionalTest>
</PropertyGroup>

<ItemGroup>
<Compile Include="StartupHookForFunctionalTest.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.JavaScript;

namespace Sample
{
public partial class Test
{
public static void Main()
{
}

[JSExport]
public static int TestMeaning()
{
string appContextKey = "Test.StartupHookForFunctionalTest.DidRun";
var data = (string) AppContext.GetData (appContextKey);

if (data != "Yes") {
string msg = $"Expected startup hook to set {appContextKey} to 'Yes', got '{data}'";
Console.Error.WriteLine(msg);
return 104;
}
return 42;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TestRuntime>true</TestRuntime>
<Scenario>WasmTestOnBrowser</Scenario>
<TestArchiveTestsRoot>$(TestArchiveRoot)browseronly/</TestArchiveTestsRoot>
<TestArchiveTestsDir>$(TestArchiveTestsRoot)$(OSPlatformConfig)/</TestArchiveTestsDir>
<DefineConstants>$(DefineConstants);TARGET_BROWSER</DefineConstants>
<ExpectedExitCode>42</ExpectedExitCode>
<WasmMainJSPath>main.js</WasmMainJSPath>
<StartupHookSupport>true</StartupHookSupport>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.cs" />
<WasmExtraFilesToDeploy Include="index.html" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\TestAssets\StartupHookForFunctionalTest\StartupHookForFunctionalTest.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<!-- Licensed to the .NET Foundation under one or more agreements. -->
<!-- The .NET Foundation licenses this file to you under the MIT license. -->
<html>

<head>
<title>Runtime config test</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type='module' src="./main.js"></script>
</head>

<body>
<h3 id="header">Runtime config test</h3>
Answer to the Ultimate Question of Life, the Universe, and Everything is : <span id="out"></span>
</body>

</html>
24 changes: 24 additions & 0 deletions src/tests/FunctionalTests/WebAssembly/Browser/StartupHook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { dotnet } from './dotnet.js'

function wasm_exit(exit_code) {
var tests_done_elem = document.createElement("label");
tests_done_elem.id = "tests_done";
tests_done_elem.innerHTML = exit_code.toString();
document.body.appendChild(tests_done_elem);

console.log(`WASM EXIT ${exit_code}`);
}

try {
const { getAssemblyExports } = await dotnet.create();
const exports = await getAssemblyExports("WebAssembly.Browser.StartupHook.Test.dll");
const testMeaning = exports.Sample.Test.TestMeaning;
const ret = testMeaning();
document.getElementById("out").innerHTML = `${ret}`;
console.debug(`ret: ${ret}`);

let exit_code = ret;
wasm_exit(exit_code);
} catch (err) {
console.log(`WASM ERROR ${err}`);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"configProperties": {
"STARTUP_HOOKS": "StartupHookForFunctionalTest"
}
}
Loading