Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Generational aware analysis based on environment variable.
  • Loading branch information
cshung committed Aug 14, 2020
commit 6869fe137512d053202109eaa1ea95586fe8ebaa
2 changes: 1 addition & 1 deletion src/coreclr/src/gc/env/gcenv.ee.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class GCToEEInterface
static uint32_t GetTotalNumSizedRefHandles();

static bool AnalyzeSurvivorsRequested(int condemnedGeneration);
static void AnalyzeSurvivorsFinished(int condemnedGeneration);
static void AnalyzeSurvivorsFinished(int condemnedGeneration, uint64_t promoted_bytes, void (*reportGenerationBounds)());

static void VerifySyncTableEntry();
static void UpdateGCEventStatus(int publicLevel, int publicKeywords, int privateLevel, int privateKeywords);
Expand Down
19 changes: 18 additions & 1 deletion src/coreclr/src/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20909,9 +20909,26 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
if (gc_t_join.joined())
#endif //MULTIPLE_HEAPS
{
uint64_t promoted_bytes = 0;
#ifdef HEAP_ANALYZE
heap_analyze_enabled = FALSE;
GCToEEInterface::AnalyzeSurvivorsFinished(condemned_gen_number);
#ifdef MULTIPLE_HEAPS
for (int i = 0; i < n_heaps; i++)
{
promoted_bytes += g_promoted[i * 16];
}
#else
promoted_bytes = g_promoted;
#endif //MULTIPLE_HEAPS

GCToEEInterface::AnalyzeSurvivorsFinished(condemned_gen_number, promoted_bytes, [](){
g_theGCHeap->DiagDescrGenerations([](void*, int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved)
{
uint64_t range = static_cast<uint64_t>(rangeEnd - rangeStart);
uint64_t rangeReserved = static_cast<uint64_t>(rangeEndReserved - rangeStart);
FIRE_EVENT(GCGenerationRange, generation, rangeStart, range, rangeReserved);
}, nullptr);
});
#endif // HEAP_ANALYZE
GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);

Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/src/gc/gcconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ class GCConfigStringHolder
INT_CONFIG (GCHeapHardLimitLOHPercent, "GCHeapHardLimitLOHPercent", "System.GC.HeapHardLimitLOHPercent", 0, "Specifies the GC heap LOH usage as a percentage of the total memory") \
INT_CONFIG (GCHeapHardLimitPOHPercent, "GCHeapHardLimitPOHPercent", "System.GC.HeapHardLimitPOHPercent", 0, "Specifies the GC heap POH usage as a percentage of the total memory") \
INT_CONFIG (GCEnabledInstructionSets, "GCEnabledInstructionSets", NULL, -1, "Specifies whether GC can use AVX2 or AVX512F - 0 for neither, 1 for AVX2, 3 for AVX512F")\
INT_CONFIG (GCGenAnalysisGen, "GCGenAnalysisGen", NULL, 999, "Specifies which generation to analysis") \
INT_CONFIG (GCGenAnalysisBytes, "GCGenAnalysisBytes", NULL, 0, "Specifies how much promoted bytes to trigger generation analysis") \

// This class is responsible for retreiving configuration information
// for how the GC should operate.
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/src/gc/gcenv.ee.standalone.inl
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,10 @@ inline bool GCToEEInterface::AnalyzeSurvivorsRequested(int condemnedGeneration)
return g_theGCToCLR->AnalyzeSurvivorsRequested(condemnedGeneration);
}

inline void GCToEEInterface::AnalyzeSurvivorsFinished(int condemnedGeneration)
inline void GCToEEInterface::AnalyzeSurvivorsFinished(int condemnedGeneration, uint64_t promoted_bytes, void (*reportGenerationBounds)())
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->AnalyzeSurvivorsFinished(condemnedGeneration);
g_theGCToCLR->AnalyzeSurvivorsFinished(condemnedGeneration, promoted_bytes, reportGenerationBounds);
}

inline void GCToEEInterface::VerifySyncTableEntry()
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/gc/gcinterface.ee.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ class IGCToCLR {
bool AnalyzeSurvivorsRequested(int condemnedGeneration) = 0;

virtual
void AnalyzeSurvivorsFinished(int condemnedGeneration) = 0;
void AnalyzeSurvivorsFinished(int condemnedGeneration, uint64_t promoted_bytes, void (*reportGenerationBounds)()) = 0;

virtual
void VerifySyncTableEntry() = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/gc/sample/gcenv.ee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ inline bool GCToEEInterface::AnalyzeSurvivorsRequested(int condemnedGeneration)
return false;
}

inline void GCToEEInterface::AnalyzeSurvivorsFinished(int condemnedGeneration)
inline void GCToEEInterface::AnalyzeSurvivorsFinished(int condemnedGeneration, uint64_t promoted_bytes, void (*reportGenerationBounds)())
{

}
12 changes: 12 additions & 0 deletions src/coreclr/src/vm/ClrEtwAll.man
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
message="$(string.RuntimePublisher.MethodDiagnosticKeywordMessage)" symbol="CLR_METHODDIAGNOSTIC_KEYWORD" />
<keyword name="TypeDiagnosticKeyword" mask="0x8000000000"
message="$(string.RuntimePublisher.TypeDiagnosticKeywordMessage)" symbol="CLR_TYPEDIAGNOSTIC_KEYWORD" />
<keyword name="GenAwareKeyword" mask="0x10000000000"
message="$(string.RuntimePublisher.GenAwareKeywordMessage)" symbol="CLR_GEN_AWARE_KEYWORD" />
</keywords>
<!--Tasks-->
<tasks>
Expand Down Expand Up @@ -3795,6 +3797,13 @@
task="AssemblyLoader"
symbol="KnownPathProbed" message="$(string.RuntimePublisher.KnownPathProbedEventMessage)"/>

<event value="297" version="0" level="win:Informational"
keywords ="GenAwareKeyword"
symbol="GenAwareBegin" message="$(string.RuntimePublisher.GenAwareBeginEventMessage)"/>

<event value="298" version="0" level="win:Informational"
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a corresponding change to TraceEvent? If so it might be good to link it from the PR.

keywords ="GenAwareKeyword"
symbol="GenAwareEnd" message="$(string.RuntimePublisher.GenAwareEndEventMessage)"/>
</events>
</provider>

Expand Down Expand Up @@ -7548,6 +7557,9 @@
<string id="RuntimePublisher.CompilationDiagnosticKeywordMessage" value="CompilationDiagnostic" />
<string id="RuntimePublisher.MethodDiagnosticKeywordMessage" value="MethodDiagnostic" />
<string id="RuntimePublisher.TypeDiagnosticKeywordMessage" value="TypeDiagnostic" />
<string id="RuntimePublisher.GenAwareKeywordMessage" value="GenAwareKeyword" />
<string id="RuntimePublisher.GenAwareBeginEventMessage" value="NONE" />
<string id="RuntimePublisher.GenAwareEndEventMessage" value="NONE" />
<string id="RundownPublisher.LoaderKeywordMessage" value="Loader" />
<string id="RundownPublisher.JitKeywordMessage" value="Jit" />
<string id="RundownPublisher.JittedMethodILToNativeMapRundownKeywordMessage" value="JittedMethodILToNativeMapRundown" />
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/src/vm/eventpipe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ void EventPipe::EnableViaEnvironmentVariables()
{
outputPath = configOutputPath;
}
auto configuration = XplatEventLoggerConfiguration();

LPWSTR configToParse = eventpipeConfig;
int providerCnt = 0;

Expand All @@ -183,6 +183,7 @@ void EventPipe::EnableViaEnvironmentVariables()
}
else
{
auto configuration = XplatEventLoggerConfiguration();
// Count how many providers there are to parse
static WCHAR comma = W(',');
while (*configToParse != '\0')
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/src/vm/eventpipesession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ EventPipeSession::EventPipeSession(

GetSystemTimeAsFileTime(&m_sessionStartTime);
QueryPerformanceCounter(&m_sessionStartTimeStamp);
this->m_paused = false;
}

EventPipeSession::~EventPipeSession()
Expand Down Expand Up @@ -315,6 +316,16 @@ bool EventPipeSession::WriteAllBuffersToFile(bool *pEventsWritten)
return !m_pFile->HasErrors();
}

void EventPipeSession::Pause()
{
this->m_paused = true;
}

void EventPipeSession::Resume()
{
this->m_paused = false;
}

bool EventPipeSession::WriteEvent(
Thread *pThread,
EventPipeEvent &event,
Expand All @@ -332,6 +343,11 @@ bool EventPipeSession::WriteEvent(
}
CONTRACTL_END;

if (this->m_paused)
{
return true;
}

// Filter events specific to "this" session based on precomputed flag on provider/events.
if (event.IsEnabled(GetMask()))
{
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/src/vm/eventpipesession.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ class EventPipeSession

void DisableIpcStreamingThread();

bool m_paused;

public:
EventPipeSession(
uint32_t index,
Expand All @@ -111,6 +113,15 @@ class EventPipeSession

~EventPipeSession();

void Pause();

void Resume();

bool Paused()
{
return this->m_paused;
}

uint64_t GetMask() const
{
LIMITED_METHOD_CONTRACT;
Expand Down
7 changes: 6 additions & 1 deletion src/coreclr/src/vm/eventtrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1463,7 +1463,12 @@ void BulkStaticsLogger::WriteEntry(AppDomain *domain, Object **address, Object *
m_domain = domain;
}

ULONGLONG th = (ULONGLONG)obj->GetTypeHandle().AsTAddr();
TypeHandle typeHandle = obj->GetGCSafeTypeHandleIfPossible();
if (typeHandle == NULL)
{
return;
}
ULONGLONG th = (ULONGLONG)typeHandle.AsTAddr();
ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(m_typeLogger, th, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);

// We should have at least 512 characters remaining in the buffer here.
Expand Down
71 changes: 70 additions & 1 deletion src/coreclr/src/vm/gcenv.ee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@

#include "gcrefmap.h"

int event_pipe_state = 0;
EventPipeSession* pEventPipeSession = nullptr;
uint64_t sessionId = (uint64_t)-1;
bool gcGenAnalysis = false;
int64_t gcGenAnalysisGen = 999;
int64_t gcGenAnalysisBytes = 0;

void GCToEEInterface::SuspendEE(SUSPEND_REASON reason)
{
WRAPPER_NO_CONTRACT;
Expand All @@ -22,6 +29,43 @@ void GCToEEInterface::SuspendEE(SUSPEND_REASON reason)
_ASSERTE(reason == SUSPEND_FOR_GC || reason == SUSPEND_FOR_GC_PREP);

g_pDebugInterface->SuspendForGarbageCollectionStarted();

if (GetIntConfigValue("GCGenAnalysisGen", nullptr, &gcGenAnalysisGen))
{
if (GetIntConfigValue("GCGenAnalysisBytes", nullptr, &gcGenAnalysisBytes))
{
gcGenAnalysis = true;
}
}
if (gcGenAnalysis && event_pipe_state == 0)
{
event_pipe_state = 1;
LPCWSTR outputPath = nullptr;
outputPath = W("trace.nettrace");
Copy link
Member

Choose a reason for hiding this comment

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

Should this rather use regular event stream instead of ad-hoc file? Do we have ad-hoc file like this anywhere else?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should this rather use regular event stream instead of ad-hoc file?

The regular event pipe worked by having a client connecting to it, that gives us a socket where the events can write to. In this case, we do not have a client, therefore there is no socket, the only alternative is a file.

Do we have ad-hoc file like this anywhere else?

We have a precedent of writing to a file here.

void EventPipe::EnableViaEnvironmentVariables()
{
STANDARD_VM_CONTRACT;
if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EnableEventPipe) != 0)
{
CLRConfigStringHolder eventpipeConfig(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeConfig));
CLRConfigStringHolder configOutputPath(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeOutputPath));
uint32_t eventpipeCircularBufferMB = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeCircularMB);
LPCWSTR outputPath = nullptr;
if (configOutputPath == NULL)
{
outputPath = W("trace.nettrace");
}

NewHolder<EventPipeProviderConfiguration> pProviders = nullptr;
int providerCnt = 1;
pProviders = new EventPipeProviderConfiguration[providerCnt];
const uint64_t GenAwareKeyword = 0x10000000000; // This keyword is necessary for the start and stop events
const uint64_t GCHeapAndTypeNamesKeyword = 0x00001000000; // This keyword is necessary for the type names
const uint64_t GCHeapSurvivalAndMovementKeyword = 0x00000400000; // This keyword is necessary for the generation range data.
const uint64_t GCHeapDumpKeyword = 0x00000100000; // This keyword is necessary for enabling walking the heap
const uint64_t TypeKeyword = 0x00000080000; // This keyword is necessary for enabling BulkType events
const uint64_t keyword = GenAwareKeyword|GCHeapAndTypeNamesKeyword|GCHeapSurvivalAndMovementKeyword|GCHeapDumpKeyword|TypeKeyword;
pProviders[0] = EventPipeProviderConfiguration(W("Microsoft-Windows-DotNETRuntime"), keyword, 5, nullptr);
sessionId = EventPipe::Enable(
outputPath,
1024,
pProviders,
providerCnt,
EventPipeSessionType::File,
EventPipeSerializationFormat::NetTraceV4,
false,
nullptr
);
pEventPipeSession= EventPipe::GetSession(sessionId);
pEventPipeSession->Pause();
EventPipe::StartStreaming(sessionId);
}

ThreadSuspend::SuspendEE((ThreadSuspend::SUSPEND_REASON)reason);

Expand All @@ -31,10 +75,19 @@ void GCToEEInterface::SuspendEE(SUSPEND_REASON reason)
void GCToEEInterface::RestartEE(bool bFinishedGC)
{
WRAPPER_NO_CONTRACT;
CONTRACT_VIOLATION(ThrowsViolation);

g_pDebugInterface->ResumeForGarbageCollectionStarted();

ThreadSuspend::RestartEE(bFinishedGC, TRUE);

if (event_pipe_state == 2)
{
event_pipe_state = 3;
EventPipe::Disable(sessionId);
// Writing an empty file to indicate completion
fclose(fopen("trace.nettrace.completed","w+"));
}
}

VOID GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2)
Expand Down Expand Up @@ -1582,7 +1635,7 @@ bool GCToEEInterface::AnalyzeSurvivorsRequested(int condemnedGeneration)
return false;
}

void GCToEEInterface::AnalyzeSurvivorsFinished(int condemnedGeneration)
void GCToEEInterface::AnalyzeSurvivorsFinished(int condemnedGeneration, uint64_t promoted_bytes, void (*reportGenerationBounds)())
{
LIMITED_METHOD_CONTRACT;

Expand All @@ -1596,6 +1649,22 @@ void GCToEEInterface::AnalyzeSurvivorsFinished(int condemnedGeneration)
DACNotify::DoGCNotification(gea);
}
}

if (event_pipe_state == 1)
{
if (condemnedGeneration == gcGenAnalysisGen && promoted_bytes > (uint64_t)gcGenAnalysisBytes)
{
event_pipe_state = 2;
pEventPipeSession->Resume();
FireEtwGenAwareBegin();
s_forcedGCInProgress = true;
GCProfileWalkHeap();
s_forcedGCInProgress = false;
reportGenerationBounds();
FireEtwGenAwareEnd();
pEventPipeSession->Pause();
}
}
}

void GCToEEInterface::VerifySyncTableEntry()
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/vm/gcenv.ee.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class GCToEEInterface : public IGCToCLR {
uint32_t GetTotalNumSizedRefHandles();

bool AnalyzeSurvivorsRequested(int condemnedGeneration);
void AnalyzeSurvivorsFinished(int condemnedGeneration);
void AnalyzeSurvivorsFinished(int condemnedGeneration, uint64_t promoted_bytes, void (*reportGenerationBounds)());

void VerifySyncTableEntry();

Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/src/vm/gcenv.ee.standalone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

#include "gctoclreventsink.h"
#include "configuration.h"
#include "eventpipe.h"
#include "eventpipesession.h"
extern bool s_forcedGCInProgress;

// the method table for the WeakReference class
extern MethodTable* pWeakReferenceMT;
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/src/vm/gcenv.ee.static.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

#include "gctoclreventsink.h"
#include "configuration.h"
#include "eventpipe.h"
#include "eventpipesession.h"
extern bool s_forcedGCInProgress;

// the method table for the WeakReference class
extern MethodTable* pWeakReferenceMT;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/vm/proftoeeinterfaceimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,7 @@ bool HeapWalkHelper(Object * pBO, void * pvContext)
OBJECTREF * arrObjRef = NULL;
size_t cNumRefs = 0;
bool bOnStack = false;
MethodTable * pMT = pBO->GetMethodTable();
MethodTable * pMT = pBO->GetGCSafeMethodTable();
Copy link
Member

Choose a reason for hiding this comment

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

GetGCSafeMethodTable [](start = 40, length = 20)

shouldn't this also be GetGCSafeTypeHandleIfPossible?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This case is interesting and I am not sure what is the right thing to do here. We cannot simply skip the execution of this method because it requires a return value. Also, right after the call, the code handles collectible right away.


ProfilerWalkHeapContext * pProfilerWalkHeapContext = (ProfilerWalkHeapContext *) pvContext;

Expand Down