Skip to content

Commit 39bbff8

Browse files
authored
ApplyStartupHook diagnostic IPC command (#86813)
1 parent eb49594 commit 39bbff8

File tree

24 files changed

+551
-26
lines changed

24 files changed

+551
-26
lines changed

src/coreclr/System.Private.CoreLib/src/System/StartupHookProvider.CoreCLR.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ namespace System
1212
{
1313
internal static partial class StartupHookProvider
1414
{
15-
private static void ManagedStartup()
15+
private static unsafe void ManagedStartup(char* pDiagnosticStartupHooks)
1616
{
1717
#if FEATURE_PERFTRACING
1818
if (EventSource.IsSupported)
1919
RuntimeEventSource.Initialize();
2020
#endif
2121

2222
if (IsSupported)
23-
ProcessStartupHooks();
23+
ProcessStartupHooks(new string(pDiagnosticStartupHooks));
2424
}
2525
}
2626
}

src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,13 @@ ds_rt_disable_perfmap (void)
285285
return DS_IPC_E_NOTSUPPORTED;
286286
}
287287

288+
static
289+
uint32_t
290+
ds_rt_apply_startup_hook (const ep_char16_t *startup_hook_path)
291+
{
292+
return DS_IPC_E_NOTSUPPORTED;
293+
}
294+
288295
/*
289296
* DiagnosticServer.
290297
*/

src/coreclr/vm/assembly.cpp

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
//#define STRICT_JITLOCK_ENTRY_LEAK_DETECTION
5353
//#define STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
5454

55+
LPCWSTR s_wszDiagnosticStartupHookPaths = nullptr;
5556

5657
#ifndef DACCESS_COMPILE
5758

@@ -1122,7 +1123,47 @@ bool Assembly::IgnoresAccessChecksTo(Assembly *pAccessedAssembly)
11221123
return GetFriendAssemblyInfo()->IgnoresAccessChecksTo(pAccessedAssembly);
11231124
}
11241125

1126+
void Assembly::AddDiagnosticStartupHookPath(LPCWSTR wszPath)
1127+
{
1128+
LPCWSTR wszDiagnosticStartupHookPathsLocal = s_wszDiagnosticStartupHookPaths;
1129+
1130+
size_t cchPath = u16_strlen(wszPath);
1131+
size_t cchDiagnosticStartupHookPathsNew = cchPath;
1132+
size_t cchDiagnosticStartupHookPathsLocal = 0;
1133+
if (nullptr != wszDiagnosticStartupHookPathsLocal)
1134+
{
1135+
cchDiagnosticStartupHookPathsLocal = u16_strlen(wszDiagnosticStartupHookPathsLocal);
1136+
// Add 1 for the path separator
1137+
cchDiagnosticStartupHookPathsNew += cchDiagnosticStartupHookPathsLocal + 1;
1138+
}
11251139

1140+
size_t currentSize = cchDiagnosticStartupHookPathsNew + 1;
1141+
LPWSTR wszDiagnosticStartupHookPathsNew = new WCHAR[currentSize];
1142+
LPWSTR wszCurrent = wszDiagnosticStartupHookPathsNew;
1143+
1144+
u16_strcpy_s(wszCurrent, currentSize, wszPath);
1145+
wszCurrent += cchPath;
1146+
currentSize -= cchPath;
1147+
1148+
if (cchDiagnosticStartupHookPathsLocal > 0)
1149+
{
1150+
u16_strcpy_s(wszCurrent, currentSize, PATH_SEPARATOR_STR_W);
1151+
wszCurrent += 1;
1152+
currentSize -= 1;
1153+
1154+
u16_strcpy_s(wszCurrent, currentSize, wszDiagnosticStartupHookPathsLocal);
1155+
wszCurrent += cchDiagnosticStartupHookPathsLocal;
1156+
currentSize -= cchDiagnosticStartupHookPathsLocal;
1157+
}
1158+
1159+
// Expect null terminating character
1160+
_ASSERTE(currentSize == 1);
1161+
_ASSERTE(wszCurrent[0] == W('\0'));
1162+
1163+
s_wszDiagnosticStartupHookPaths = wszDiagnosticStartupHookPathsNew;
1164+
1165+
delete [] wszDiagnosticStartupHookPathsLocal;
1166+
}
11261167

11271168
enum CorEntryPointType
11281169
{
@@ -1376,7 +1417,7 @@ static void RunMainPost()
13761417
}
13771418
}
13781419

1379-
static void RunManagedStartup()
1420+
void RunManagedStartup()
13801421
{
13811422
CONTRACTL
13821423
{
@@ -1388,7 +1429,11 @@ static void RunManagedStartup()
13881429
CONTRACTL_END;
13891430

13901431
MethodDescCallSite managedStartup(METHOD__STARTUP_HOOK_PROVIDER__MANAGED_STARTUP);
1391-
managedStartup.Call(NULL);
1432+
1433+
ARG_SLOT args[1];
1434+
args[0] = PtrToArgSlot(s_wszDiagnosticStartupHookPaths);
1435+
1436+
managedStartup.Call(args);
13921437
}
13931438

13941439
INT32 Assembly::ExecuteMainMethod(PTRARRAYREF *stringArgs, BOOL waitForOtherThreads)

src/coreclr/vm/assembly.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,8 @@ class Assembly
380380
}
381381
#endif
382382

383+
static void AddDiagnosticStartupHookPath(LPCWSTR wszPath);
384+
383385

384386
protected:
385387
#ifdef FEATURE_COMINTEROP

src/coreclr/vm/corelib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ DEFINE_FIELD_U(rgiLastFrameFromForeignExceptionStackTrace, StackFrame
812812
DEFINE_FIELD_U(iFrameCount, StackFrameHelper, iFrameCount)
813813

814814
DEFINE_CLASS(STARTUP_HOOK_PROVIDER, System, StartupHookProvider)
815-
DEFINE_METHOD(STARTUP_HOOK_PROVIDER, MANAGED_STARTUP, ManagedStartup, SM_RetVoid)
815+
DEFINE_METHOD(STARTUP_HOOK_PROVIDER, MANAGED_STARTUP, ManagedStartup, SM_PtrChar_RetVoid)
816816

817817
DEFINE_CLASS(STREAM, IO, Stream)
818818
DEFINE_METHOD(STREAM, BEGIN_READ, BeginRead, IM_ArrByte_Int_Int_AsyncCallback_Object_RetIAsyncResult)

src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,28 @@ ds_rt_disable_perfmap (void)
329329
#endif // FEATURE_PERFMAP
330330
}
331331

332+
static ep_char16_t * _ds_rt_coreclr_diagnostic_startup_hook_paths = NULL;
333+
334+
static
335+
uint32_t
336+
ds_rt_apply_startup_hook (const ep_char16_t *startup_hook_path)
337+
{
338+
HRESULT hr = S_OK;
339+
// This is set to true when the EE has initialized, which occurs after
340+
// the diagnostic suspension point has completed.
341+
if (g_fEEStarted)
342+
{
343+
// TODO: Support loading and executing startup hook after EE has completely initialized.
344+
return DS_IPC_E_INVALIDARG;
345+
}
346+
else
347+
{
348+
Assembly::AddDiagnosticStartupHookPath(reinterpret_cast<LPCWSTR>(startup_hook_path));
349+
}
350+
351+
return DS_IPC_S_OK;
352+
}
353+
332354
/*
333355
* DiagnosticServer.
334356
*/

src/coreclr/vm/metasig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ DEFINE_METASIG(SM(IntPtr_Bool_RetVoid, I F, v))
338338
DEFINE_METASIG(SM(IntPtr_UInt_IntPtr_RetVoid, I K I, v))
339339
DEFINE_METASIG(SM(IntPtr_RetUInt, I, K))
340340
DEFINE_METASIG(SM(PtrChar_RetInt, P(u), i))
341+
DEFINE_METASIG(SM(PtrChar_RetVoid, P(u), v))
341342
DEFINE_METASIG(SM(IntPtr_IntPtr_RetIntPtr, I I, I))
342343
DEFINE_METASIG(SM(IntPtr_IntPtr_Int_RetIntPtr, I I i, I))
343344
DEFINE_METASIG(SM(PtrVoid_PtrVoid_RetVoid, P(v) P(v), v))

src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Suppressions.LibraryBuild.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<argument>ILLink</argument>
2020
<argument>IL2026</argument>
2121
<property name="Scope">member</property>
22-
<property name="Target">M:System.StartupHookProvider.ProcessStartupHooks()</property>
22+
<property name="Target">M:System.StartupHookProvider.ProcessStartupHooks(System.String)</property>
2323
<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>
2424
</attribute>
2525
</assembly>

src/libraries/System.Private.CoreLib/src/System/StartupHookProvider.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.Diagnostics;
67
using System.Diagnostics.Tracing;
78
using System.Diagnostics.CodeAnalysis;
@@ -27,15 +28,25 @@ private struct StartupHookNameOrPath
2728

2829
// Parse a string specifying a list of assemblies and types
2930
// containing a startup hook, and call each hook in turn.
30-
private static void ProcessStartupHooks()
31+
private static void ProcessStartupHooks(string diagnosticStartupHooks)
3132
{
3233
if (!IsSupported)
3334
return;
3435

3536
string? startupHooksVariable = AppContext.GetData("STARTUP_HOOKS") as string;
36-
if (startupHooksVariable == null)
37-
{
37+
if (null == startupHooksVariable && string.IsNullOrEmpty(diagnosticStartupHooks))
3838
return;
39+
40+
List<string> startupHookParts = new();
41+
42+
if (!string.IsNullOrEmpty(diagnosticStartupHooks))
43+
{
44+
startupHookParts.AddRange(diagnosticStartupHooks.Split(Path.PathSeparator));
45+
}
46+
47+
if (null != startupHooksVariable)
48+
{
49+
startupHookParts.AddRange(startupHooksVariable.Split(Path.PathSeparator));
3950
}
4051

4152
ReadOnlySpan<char> disallowedSimpleAssemblyNameChars = stackalloc char[4]
@@ -47,9 +58,8 @@ private static void ProcessStartupHooks()
4758
};
4859

4960
// Parse startup hooks variable
50-
string[] startupHookParts = startupHooksVariable.Split(Path.PathSeparator);
51-
StartupHookNameOrPath[] startupHooks = new StartupHookNameOrPath[startupHookParts.Length];
52-
for (int i = 0; i < startupHookParts.Length; i++)
61+
StartupHookNameOrPath[] startupHooks = new StartupHookNameOrPath[startupHookParts.Count];
62+
for (int i = 0; i < startupHookParts.Count; i++)
5363
{
5464
string startupHookPart = startupHookParts[i];
5565
if (string.IsNullOrEmpty(startupHookPart))

src/mono/mono/eventpipe/ds-rt-mono.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,14 @@ ds_rt_disable_perfmap (void)
239239
return DS_IPC_E_NOTSUPPORTED;
240240
}
241241

242+
static
243+
uint32_t
244+
ds_rt_apply_startup_hook (const ep_char16_t *startup_hook_path)
245+
{
246+
// TODO: Implement.
247+
return DS_IPC_E_NOTSUPPORTED;
248+
}
249+
242250
/*
243251
* DiagnosticServer.
244252
*/

src/mono/mono/metadata/object.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8135,7 +8135,11 @@ mono_runtime_run_startup_hooks (void)
81358135
mono_error_cleanup (error);
81368136
if (!method)
81378137
return;
8138-
mono_runtime_invoke_checked (method, NULL, NULL, error);
8138+
8139+
gpointer args [1];
8140+
args[0] = mono_string_empty_internal (mono_domain_get ());
8141+
8142+
mono_runtime_invoke_checked (method, NULL, args, error);
81398143
// runtime hooks design doc says not to catch exceptions from the hooks
81408144
mono_error_raise_exception_deprecated (error);
81418145
}

src/native/eventpipe/ds-process-protocol.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ process_protocol_helper_disable_perfmap (
8989
DiagnosticsIpcMessage *message,
9090
DiagnosticsIpcStream *stream);
9191

92+
static
93+
bool
94+
process_protocol_helper_apply_startup_hook (
95+
DiagnosticsIpcMessage *message,
96+
DiagnosticsIpcStream *stream);
97+
9298
static
9399
bool
94100
process_protocol_helper_unknown_command (
@@ -872,6 +878,87 @@ process_protocol_helper_disable_perfmap (
872878
ep_exit_error_handler ();
873879
}
874880

881+
DiagnosticsApplyStartupHookPayload *
882+
ds_apply_startup_hook_payload_alloc (void)
883+
{
884+
return ep_rt_object_alloc (DiagnosticsApplyStartupHookPayload);
885+
}
886+
887+
void
888+
ds_apply_startup_hook_payload_free (DiagnosticsApplyStartupHookPayload *payload)
889+
{
890+
ep_return_void_if_nok (payload != NULL);
891+
ep_rt_byte_array_free (payload->incoming_buffer);
892+
ep_rt_object_free (payload);
893+
}
894+
895+
static
896+
uint8_t *
897+
apply_startup_hook_command_try_parse_payload (
898+
uint8_t *buffer,
899+
uint16_t buffer_len)
900+
{
901+
EP_ASSERT (buffer != NULL);
902+
903+
uint8_t * buffer_cursor = buffer;
904+
uint32_t buffer_cursor_len = buffer_len;
905+
906+
DiagnosticsApplyStartupHookPayload *instance = ds_apply_startup_hook_payload_alloc ();
907+
ep_raise_error_if_nok (instance != NULL);
908+
909+
instance->incoming_buffer = buffer;
910+
911+
if (!ds_ipc_message_try_parse_string_utf16_t (&buffer_cursor, &buffer_cursor_len, &instance->startup_hook_path))
912+
ep_raise_error ();
913+
914+
ep_on_exit:
915+
return (uint8_t *)instance;
916+
917+
ep_on_error:
918+
ds_apply_startup_hook_payload_free (instance);
919+
instance = NULL;
920+
ep_exit_error_handler ();
921+
}
922+
923+
static
924+
bool
925+
process_protocol_helper_apply_startup_hook (
926+
DiagnosticsIpcMessage *message,
927+
DiagnosticsIpcStream *stream)
928+
{
929+
EP_ASSERT (message != NULL);
930+
EP_ASSERT (stream != NULL);
931+
932+
if (!stream)
933+
return false;
934+
935+
bool result = false;
936+
DiagnosticsApplyStartupHookPayload *payload = (DiagnosticsApplyStartupHookPayload *)ds_ipc_message_try_parse_payload (message, apply_startup_hook_command_try_parse_payload);
937+
if (!payload) {
938+
ds_ipc_message_send_error (stream, DS_IPC_E_BAD_ENCODING);
939+
ep_raise_error ();
940+
}
941+
942+
ds_ipc_result_t ipc_result;
943+
ipc_result = ds_rt_apply_startup_hook (payload->startup_hook_path);
944+
if (ipc_result != DS_IPC_S_OK) {
945+
ds_ipc_message_send_error (stream, ipc_result);
946+
ep_raise_error ();
947+
} else {
948+
ds_ipc_message_send_success (stream, ipc_result);
949+
}
950+
951+
result = true;
952+
953+
ep_on_exit:
954+
ds_ipc_stream_free (stream);
955+
return result;
956+
957+
ep_on_error:
958+
EP_ASSERT (!result);
959+
ep_exit_error_handler ();
960+
}
961+
875962
static
876963
bool
877964
process_protocol_helper_unknown_command (
@@ -916,6 +1003,9 @@ ds_process_protocol_helper_handle_ipc_message (
9161003
case DS_PROCESS_COMMANDID_DISABLE_PERFMAP:
9171004
result = process_protocol_helper_disable_perfmap (message, stream);
9181005
break;
1006+
case DS_PROCESS_COMMANDID_APPLY_STARTUP_HOOK:
1007+
result = process_protocol_helper_apply_startup_hook (message, stream);
1008+
break;
9191009
default:
9201010
result = process_protocol_helper_unknown_command (message, stream);
9211011
break;

src/native/eventpipe/ds-process-protocol.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,32 @@ ds_enable_perfmap_payload_alloc (void);
190190
void
191191
ds_enable_perfmap_payload_free (DiagnosticsEnablePerfmapPayload *payload);
192192

193+
/*
194+
* DiagnosticsApplyStartupHookPayload
195+
*/
196+
197+
#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_PROCESS_PROTOCOL_GETTER_SETTER)
198+
struct _DiagnosticsApplyStartupHookPayload {
199+
#else
200+
struct _DiagnosticsApplyStartupHookPayload_Internal {
201+
#endif
202+
uint8_t * incoming_buffer;
203+
204+
const ep_char16_t *startup_hook_path;
205+
};
206+
207+
#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_PROCESS_PROTOCOL_GETTER_SETTER)
208+
struct _DiagnosticsApplyStartupHookPayload {
209+
uint8_t _internal [sizeof (struct _DiagnosticsApplyStartupHookPayload_Internal)];
210+
};
211+
#endif
212+
213+
DiagnosticsApplyStartupHookPayload *
214+
ds_apply_startup_hook_payload_alloc (void);
215+
216+
void
217+
ds_apply_startup_hook_payload_free (DiagnosticsApplyStartupHookPayload *payload);
218+
193219
/*
194220
* DiagnosticsProcessProtocolHelper.
195221
*/

0 commit comments

Comments
 (0)