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

Fix recursive assembly spec parsing with textual PGO #78035

Merged
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
229 changes: 130 additions & 99 deletions src/coreclr/vm/pgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,129 +820,160 @@ HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, BYTE** pAlloca
//
if (s_textFormatPgoData.GetCount() > 0)
{
COUNT_T methodhash = pMD->GetStableHash();
int codehash;
unsigned ilSize;
if (GetVersionResilientILCodeHashCode(pMD, &codehash, &ilSize))
hr = getPgoInstrumentationResultsFromText(pMD, pAllocatedData, ppSchema, pCountSchemaItems, pInstrumentationData, pPgoSource);
}

// If we didn't find any text format data, look for dynamic or static data.
//
if (FAILED(hr))
{
PgoManager *mgr;
if (!pMD->IsDynamicMethod())
{
Header *found = s_textFormatPgoData.Lookup(CodeAndMethodHash(codehash, methodhash));
if (found != NULL)
{
StackSArray<ICorJitInfo::PgoInstrumentationSchema> schemaArray;
mgr = pMD->GetLoaderAllocator()->GetPgoManager();
}
else
{
mgr = pMD->AsDynamicMethodDesc()->GetResolver()->GetDynamicPgoManager();
}

if (mgr != NULL)
{
hr = mgr->getPgoInstrumentationResultsInstance(pMD, pAllocatedData, ppSchema, pCountSchemaItems, pInstrumentationData, pPgoSource);
}
}

return hr;
}

HRESULT PgoManager::getPgoInstrumentationResultsFromText(MethodDesc* pMD, BYTE** pAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32* pCountSchemaItems, BYTE** pInstrumentationData, ICorJitInfo::PgoSource* pPgoSource)
{
int codehash;
unsigned ilSize;
if (!GetVersionResilientILCodeHashCode(pMD, &codehash, &ilSize))
{
return E_NOTIMPL;
}

if (ReadInstrumentationSchemaWithLayoutIntoSArray(found->GetData(), found->countsOffset, found->countsOffset, &schemaArray))
COUNT_T methodhash = pMD->GetStableHash();
Header* found = s_textFormatPgoData.Lookup(CodeAndMethodHash(codehash, methodhash));
if (found == NULL)
{
return E_NOTIMPL;
}

StackSArray<ICorJitInfo::PgoInstrumentationSchema> schemaArray;

if (!ReadInstrumentationSchemaWithLayoutIntoSArray(found->GetData(), found->countsOffset, found->countsOffset, &schemaArray))
{
_ASSERTE(!"Unable to parse schema data");
return E_NOTIMPL;
}

HRESULT hr = E_NOTIMPL;
EX_TRY
{
for (unsigned iSchema = 0; iSchema < schemaArray.GetCount(); iSchema++)
{
ICorJitInfo::PgoInstrumentationSchema* schema = &(schemaArray)[iSchema];
ICorJitInfo::PgoInstrumentationKind kind = schema->InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask;
if ((kind == ICorJitInfo::PgoInstrumentationKind::TypeHandle) || (kind == ICorJitInfo::PgoInstrumentationKind::MethodHandle))
{
for (int iEntry = 0; iEntry < schema->Count; iEntry++)
{
EX_TRY
INT_PTR* handleValueAddress = (INT_PTR*)(found->GetData() + schema->Offset + iEntry * InstrumentationKindToSize(schema->InstrumentationKind));
INT_PTR initialHandleValue = VolatileLoad(handleValueAddress);

// TypeHandles can't reliably be loaded at ReadPGO time
// Instead, translate them before leaving this method.
// The ReadPgo method will place pointers to C style null
// terminated strings in the TypeHandle slots, and this will
// translate any of those into loaded TypeHandles as appropriate
if (((initialHandleValue & 1) == 1) && !ICorJitInfo::IsUnknownHandle(initialHandleValue))
{
// TypeHandles can't reliably be loaded at ReadPGO time
// Instead, translate them before leaving this method.
// The ReadPgo method will place pointers to C style null
// terminated strings in the TypeHandle slots, and this will
// translate any of those into loaded TypeHandles as appropriate
INT_PTR newPtr = 0;
char* string = ((char*)initialHandleValue) - 1;

// Resolving types and methods here can invoke managed code where the
// JIT may recursively ask for PGO data. We do not support textual PGO
// for those cases.
static thread_local bool t_resolvingTypeOrMethod;

for (unsigned iSchema = 0; iSchema < schemaArray.GetCount(); iSchema++)
struct ResolveScope
{
ICorJitInfo::PgoInstrumentationSchema *schema = &(schemaArray)[iSchema];
ICorJitInfo::PgoInstrumentationKind kind = schema->InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask;
if ((kind == ICorJitInfo::PgoInstrumentationKind::TypeHandle) || (kind == ICorJitInfo::PgoInstrumentationKind::MethodHandle))
ResolveScope()
{
for (int iEntry = 0; iEntry < schema->Count; iEntry++)
{
INT_PTR* handleValueAddress = (INT_PTR*)(found->GetData() + schema->Offset + iEntry * InstrumentationKindToSize(schema->InstrumentationKind));
INT_PTR initialHandleValue = VolatileLoad(handleValueAddress);
if (((initialHandleValue & 1) == 1) && !ICorJitInfo::IsUnknownHandle(initialHandleValue))
{
INT_PTR newPtr = 0;
char* string = ((char *)initialHandleValue) - 1;
t_resolvingTypeOrMethod = true;
}

// Don't attempt to load any types or methods until the EE is started
if (g_fEEStarted)
{
if (kind == ICorJitInfo::PgoInstrumentationKind::TypeHandle)
{
StackSString ts(SString::Utf8, string);
TypeHandle th = TypeName::GetTypeManaged(ts.GetUnicode(), NULL, FALSE, FALSE, FALSE, NULL, NULL);
newPtr = (INT_PTR)th.AsPtr();
}
else
{
assert(kind == ICorJitInfo::PgoInstrumentationKind::MethodHandle);
// Format is:
// MethodName|@|fully_qualified_type_name
char* sep = strstr(string, "|@|");
if (sep != nullptr)
{
StackSString typeString(SString::Utf8, sep + 3);
StackSString methodString(SString::Utf8, string, (COUNT_T)(sep - string));
TypeHandle th = TypeName::GetTypeManaged(typeString.GetUnicode(), NULL, FALSE, FALSE, FALSE, NULL, NULL);
if (!th.IsNull())
{
MethodDesc* pMD = MemberLoader::FindMethodByName(th.GetMethodTable(), methodString.GetUTF8());
if (pMD != nullptr && !pMD->IsGenericMethodDefinition())
{
newPtr = (INT_PTR)pMD;
}
}
}
}
}
~ResolveScope()
{
t_resolvingTypeOrMethod = false;
}
};

if (newPtr == 0)
// Don't attempt to load any types or methods until the EE is started
if (g_fEEStarted && !t_resolvingTypeOrMethod)
{
ResolveScope resolve;

if (kind == ICorJitInfo::PgoInstrumentationKind::TypeHandle)
{
StackSString ts(SString::Utf8, string);
TypeHandle th = TypeName::GetTypeManaged(ts.GetUnicode(), NULL, FALSE, FALSE, FALSE, NULL, NULL);
newPtr = (INT_PTR)th.AsPtr();
}
else
{
assert(kind == ICorJitInfo::PgoInstrumentationKind::MethodHandle);
// Format is:
// MethodName|@|fully_qualified_type_name
char* sep = strstr(string, "|@|");
if (sep != nullptr)
{
StackSString typeString(SString::Utf8, sep + 3);
StackSString methodString(SString::Utf8, string, (COUNT_T)(sep - string));
TypeHandle th = TypeName::GetTypeManaged(typeString.GetUnicode(), NULL, FALSE, FALSE, FALSE, NULL, NULL);

if (!th.IsNull())
{
MethodDesc* pMD = MemberLoader::FindMethodByName(th.GetMethodTable(), methodString.GetUTF8());
if (pMD != nullptr && !pMD->IsGenericMethodDefinition())
{
newPtr = HashToPgoUnknownHandle(HashStringA(string));
newPtr = (INT_PTR)pMD;
}

InterlockedCompareExchangeT(handleValueAddress, newPtr, initialHandleValue);
}
}
}
}

*pAllocatedData = new BYTE[schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema)];
memcpy(*pAllocatedData, schemaArray.OpenRawBuffer(), schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema));
schemaArray.CloseRawBuffer();
*ppSchema = (ICorJitInfo::PgoInstrumentationSchema*)*pAllocatedData;

*pCountSchemaItems = schemaArray.GetCount();
*pInstrumentationData = found->GetData();
*pPgoSource = ICorJitInfo::PgoSource::Text;
if (newPtr == 0)
{
newPtr = HashToPgoUnknownHandle(HashStringA(string));
}

hr = S_OK;
}
EX_CATCH
{
hr = E_FAIL;
InterlockedCompareExchangeT(handleValueAddress, newPtr, initialHandleValue);
}
EX_END_CATCH(RethrowTerminalExceptions)
}
else
{
_ASSERTE(!"Unable to parse schema data");
hr = E_NOTIMPL;
}
}
}
}

// If we didn't find any text format data, look for dynamic or static data.
//
if (FAILED(hr))
{
PgoManager *mgr;
if (!pMD->IsDynamicMethod())
{
mgr = pMD->GetLoaderAllocator()->GetPgoManager();
}
else
{
mgr = pMD->AsDynamicMethodDesc()->GetResolver()->GetDynamicPgoManager();
}
*pAllocatedData = new BYTE[schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema)];
memcpy(*pAllocatedData, schemaArray.OpenRawBuffer(), schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema));
schemaArray.CloseRawBuffer();
*ppSchema = (ICorJitInfo::PgoInstrumentationSchema*)*pAllocatedData;

if (mgr != NULL)
{
hr = mgr->getPgoInstrumentationResultsInstance(pMD, pAllocatedData, ppSchema, pCountSchemaItems, pInstrumentationData, pPgoSource);
}
*pCountSchemaItems = schemaArray.GetCount();
*pInstrumentationData = found->GetData();
*pPgoSource = ICorJitInfo::PgoSource::Text;

hr = S_OK;
}
EX_CATCH
{
hr = E_FAIL;
}
EX_END_CATCH(RethrowTerminalExceptions)

return hr;
}
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/pgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ class PgoManager

private:
static HRESULT ComputeOffsetOfActualInstrumentationData(const ICorJitInfo::PgoInstrumentationSchema* pSchema, UINT32 countSchemaItems, size_t headerInitialSize, UINT *offsetOfActualInstrumentationData);
static HRESULT getPgoInstrumentationResultsFromText(MethodDesc* pMD, BYTE** pAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32 *pCountSchemaItems, BYTE**pInstrumentationData, ICorJitInfo::PgoSource *pPgoSource);

static void ReadPgoData();
static void WritePgoData();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
Expand Down Expand Up @@ -205,7 +206,8 @@ private Version ParseVersion(string attributeValue)
if (!char.IsDigit(parts[i][j]))
ThrowInvalidAssemblyName();
}
if (!(ushort.TryParse(parts[i], out versionNumbers[i])))

if (!ushort.TryParse(parts[i], NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out versionNumbers[i]))
{
ThrowInvalidAssemblyName();
}
Expand Down