diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp
index c9eed61a132f6..604aa8218b683 100644
--- a/src/coreclr/vm/loaderallocator.cpp
+++ b/src/coreclr/vm/loaderallocator.cpp
@@ -535,6 +535,8 @@ void LoaderAllocator::GCLoaderAllocators(LoaderAllocator* pOriginalLoaderAllocat
DomainAssemblyIterator domainAssemblyIt(pDomainLoaderAllocatorDestroyIterator->m_pFirstDomainAssemblyFromSameALCToDelete);
while (!domainAssemblyIt.end())
{
+ // Call AssemblyUnloadStarted event
+ domainAssemblyIt->GetAssembly()->StartUnload();
// Notify the debugger
domainAssemblyIt->NotifyDebuggerUnload();
domainAssemblyIt++;
diff --git a/src/tests/profiler/assembly/ALCTest.cs b/src/tests/profiler/assembly/ALCTest.cs
new file mode 100644
index 0000000000000..edc3c18421bec
--- /dev/null
+++ b/src/tests/profiler/assembly/ALCTest.cs
@@ -0,0 +1,35 @@
+// 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.IO;
+using System.Reflection;
+
+namespace Profiler.Tests
+{
+ class ALCTest
+ {
+ static readonly Guid AssemblyProfilerGuid = new Guid("19A49007-9E58-4E31-B655-83EC3B924E7B");
+
+ public static int RunTest(String[] args)
+ {
+ string currentAssemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ string testAssemblyFullPath = Path.Combine(currentAssemblyDirectory, "..", "TestFile", "TestFile.dll");
+
+ int exitCode = TestLibrary.Utilities.ExecuteAndUnload(testAssemblyFullPath, args);
+ return exitCode;
+ }
+
+ public static int Main(string[] args)
+ {
+ if (args.Length > 0 && args[0].Equals("RunTest", StringComparison.OrdinalIgnoreCase))
+ {
+ return RunTest(args);
+ }
+
+ return ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location,
+ testName: "ALCTest",
+ profilerClsid: AssemblyProfilerGuid);
+ }
+ }
+}
diff --git a/src/tests/profiler/assembly/ALCTest.csproj b/src/tests/profiler/assembly/ALCTest.csproj
new file mode 100644
index 0000000000000..f20fc4207d6ea
--- /dev/null
+++ b/src/tests/profiler/assembly/ALCTest.csproj
@@ -0,0 +1,19 @@
+
+
+ .NETCoreApp
+ exe
+ true
+ true
+
+ true
+
+
+
+
+
+
+
+
diff --git a/src/tests/profiler/assembly/TestFile.cs b/src/tests/profiler/assembly/TestFile.cs
new file mode 100644
index 0000000000000..074b25e1547bc
--- /dev/null
+++ b/src/tests/profiler/assembly/TestFile.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Profiler.Tests
+{
+ class TestFile
+ {
+
+ public static int Main(string[] args)
+ {
+ return 100;
+ }
+ }
+}
diff --git a/src/tests/profiler/assembly/TestFile.csproj b/src/tests/profiler/assembly/TestFile.csproj
new file mode 100644
index 0000000000000..f47300c2a5258
--- /dev/null
+++ b/src/tests/profiler/assembly/TestFile.csproj
@@ -0,0 +1,14 @@
+
+
+ .NETCoreApp
+ exe
+ true
+ true
+
+
+
+
+
+
+
+
diff --git a/src/tests/profiler/native/CMakeLists.txt b/src/tests/profiler/native/CMakeLists.txt
index 7ff5152f95539..59a753c56c91c 100644
--- a/src/tests/profiler/native/CMakeLists.txt
+++ b/src/tests/profiler/native/CMakeLists.txt
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.20)
project(Profiler)
set(SOURCES
+ assemblyprofiler/assemblyprofiler.cpp
eltprofiler/slowpatheltprofiler.cpp
eventpipeprofiler/eventpipereadingprofiler.cpp
eventpipeprofiler/eventpipewritingprofiler.cpp
diff --git a/src/tests/profiler/native/assemblyprofiler/assemblyprofiler.cpp b/src/tests/profiler/native/assemblyprofiler/assemblyprofiler.cpp
new file mode 100644
index 0000000000000..fd105cba81af7
--- /dev/null
+++ b/src/tests/profiler/native/assemblyprofiler/assemblyprofiler.cpp
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "assemblyprofiler.h"
+
+GUID AssemblyProfiler::GetClsid()
+{
+ GUID clsid = { 0x19A49007, 0x9E58, 0x4E31,{ 0xB6, 0x55, 0x83, 0xEC, 0x3B, 0x92, 0x4E, 0x7B } };
+ return clsid;
+}
+
+HRESULT AssemblyProfiler::Initialize(IUnknown* pICorProfilerInfoUnk)
+{
+ Profiler::Initialize(pICorProfilerInfoUnk);
+
+ return S_OK;
+}
+
+HRESULT AssemblyProfiler::Shutdown()
+{
+ Profiler::Shutdown();
+
+ if (_assemblyUnloadStartedCount != _assemblyUnloadFinishedCount)
+ {
+ printf("AssemblyProfiler::Shutdown: FAIL: Expected AssemblyUnloadStarted and AssemblyUnloadFinished to be called the same number of times\n");
+ }
+ else
+ {
+ printf("PROFILER TEST PASSES\n");
+ }
+
+ fflush(stdout);
+ return S_OK;
+}
+
+HRESULT AssemblyProfiler::AssemblyUnloadStarted(AssemblyID assemblyId)
+{
+ SHUTDOWNGUARD();
+
+ _assemblyUnloadStartedCount++;
+ return S_OK;
+}
+
+HRESULT AssemblyProfiler::AssemblyUnloadFinished(AssemblyID assemblyId, HRESULT hrStatus)
+{
+ SHUTDOWNGUARD();
+
+ _assemblyUnloadFinishedCount++;
+ return S_OK;
+}
diff --git a/src/tests/profiler/native/assemblyprofiler/assemblyprofiler.h b/src/tests/profiler/native/assemblyprofiler/assemblyprofiler.h
new file mode 100644
index 0000000000000..0d77e7c67c986
--- /dev/null
+++ b/src/tests/profiler/native/assemblyprofiler/assemblyprofiler.h
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include "../profiler.h"
+
+class AssemblyProfiler : public Profiler
+{
+public:
+ AssemblyProfiler() : Profiler(),
+ _assemblyUnloadStartedCount(0),
+ _assemblyUnloadFinishedCount(0)
+ {}
+
+ static GUID GetClsid();
+ virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
+ virtual HRESULT STDMETHODCALLTYPE Shutdown();
+ virtual HRESULT STDMETHODCALLTYPE AssemblyUnloadStarted(AssemblyID assemblyId);
+ virtual HRESULT STDMETHODCALLTYPE AssemblyUnloadFinished(AssemblyID assemblyId, HRESULT hrStatus);
+
+private:
+ std::atomic _assemblyUnloadStartedCount;
+ std::atomic _assemblyUnloadFinishedCount;
+};
diff --git a/src/tests/profiler/native/classfactory.cpp b/src/tests/profiler/native/classfactory.cpp
index 7d8aa7942e5d9..c1aec6af53b29 100644
--- a/src/tests/profiler/native/classfactory.cpp
+++ b/src/tests/profiler/native/classfactory.cpp
@@ -19,6 +19,7 @@
#include "multiple/multiple.h"
#include "inlining/inlining.h"
#include "moduleload/moduleload.h"
+#include "assemblyprofiler/assemblyprofiler.h"
ClassFactory::ClassFactory(REFCLSID clsid) : refCount(0), clsid(clsid)
{
@@ -134,6 +135,10 @@ HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFI
{
profiler = new ModuleLoad();
}
+ else if (clsid == AssemblyProfiler::GetClsid())
+ {
+ profiler = new AssemblyProfiler();
+ }
else
{
printf("No profiler found in ClassFactory::CreateInstance. Did you add your profiler to the list?\n");