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

Fire AssemblyUnloadStarted event #87890

Merged
merged 9 commits into from
Jul 20, 2023
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
2 changes: 2 additions & 0 deletions src/coreclr/vm/loaderallocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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++;
Expand Down
35 changes: 35 additions & 0 deletions src/tests/profiler/assembly/ALCTest.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
19 changes: 19 additions & 0 deletions src/tests/profiler/assembly/ALCTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworkIdentifier>.NETCoreApp</TargetFrameworkIdentifier>
<OutputType>exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<!-- The test launches a secondary process and process launch creates
an infinite event loop in the SocketAsyncEngine on Linux. Since
runincontext loads even framework assemblies into the unloadable
context, locals in this loop prevent unloading -->
<UnloadabilityIncompatible>true</UnloadabilityIncompatible>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
<ProjectReference Include="../common/profiler_common.csproj" />
<CMakeProjectReference Include="$(MSBuildThisFileDirectory)/../native/CMakeLists.txt" />
</ItemGroup>
</Project>
16 changes: 16 additions & 0 deletions src/tests/profiler/assembly/TestFile.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
14 changes: 14 additions & 0 deletions src/tests/profiler/assembly/TestFile.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworkIdentifier>.NETCoreApp</TargetFrameworkIdentifier>
<OutputType>exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
<ProjectReference Include="../common/profiler_common.csproj" />
<CMakeProjectReference Include="$(MSBuildThisFileDirectory)/../native/CMakeLists.txt" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions src/tests/profiler/native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.14.5)
project(Profiler)

set(SOURCES
assemblyprofiler/assemblyprofiler.cpp
eltprofiler/slowpatheltprofiler.cpp
eventpipeprofiler/eventpipereadingprofiler.cpp
eventpipeprofiler/eventpipewritingprofiler.cpp
Expand Down
50 changes: 50 additions & 0 deletions src/tests/profiler/native/assemblyprofiler/assemblyprofiler.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
25 changes: 25 additions & 0 deletions src/tests/profiler/native/assemblyprofiler/assemblyprofiler.h
Original file line number Diff line number Diff line change
@@ -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<int> _assemblyUnloadStartedCount;
std::atomic<int> _assemblyUnloadFinishedCount;
};
5 changes: 5 additions & 0 deletions src/tests/profiler/native/classfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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");
Expand Down