Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 29a2bc5

Browse files
authored
Fix recursive assembly spec parsing with textual PGO (dotnet#78035)
* Add thread local to skip textual PGO data if invoked recursively * Change AssemblyNameParser.ParseVersion to pass NumberFormatInfo.InvariantInfo in its ushort.TryParse invocation. This avoids initializing globalization from within this parsing, which was causing problems in the textual PGO scenario. Fix dotnet#77971
1 parent 005e280 commit 29a2bc5

File tree

3 files changed

+134
-100
lines changed

3 files changed

+134
-100
lines changed

src/coreclr/vm/pgo.cpp

Lines changed: 130 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -820,129 +820,160 @@ HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, BYTE** pAlloca
820820
//
821821
if (s_textFormatPgoData.GetCount() > 0)
822822
{
823-
COUNT_T methodhash = pMD->GetStableHash();
824-
int codehash;
825-
unsigned ilSize;
826-
if (GetVersionResilientILCodeHashCode(pMD, &codehash, &ilSize))
823+
hr = getPgoInstrumentationResultsFromText(pMD, pAllocatedData, ppSchema, pCountSchemaItems, pInstrumentationData, pPgoSource);
824+
}
825+
826+
// If we didn't find any text format data, look for dynamic or static data.
827+
//
828+
if (FAILED(hr))
829+
{
830+
PgoManager *mgr;
831+
if (!pMD->IsDynamicMethod())
827832
{
828-
Header *found = s_textFormatPgoData.Lookup(CodeAndMethodHash(codehash, methodhash));
829-
if (found != NULL)
830-
{
831-
StackSArray<ICorJitInfo::PgoInstrumentationSchema> schemaArray;
833+
mgr = pMD->GetLoaderAllocator()->GetPgoManager();
834+
}
835+
else
836+
{
837+
mgr = pMD->AsDynamicMethodDesc()->GetResolver()->GetDynamicPgoManager();
838+
}
839+
840+
if (mgr != NULL)
841+
{
842+
hr = mgr->getPgoInstrumentationResultsInstance(pMD, pAllocatedData, ppSchema, pCountSchemaItems, pInstrumentationData, pPgoSource);
843+
}
844+
}
845+
846+
return hr;
847+
}
848+
849+
HRESULT PgoManager::getPgoInstrumentationResultsFromText(MethodDesc* pMD, BYTE** pAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32* pCountSchemaItems, BYTE** pInstrumentationData, ICorJitInfo::PgoSource* pPgoSource)
850+
{
851+
int codehash;
852+
unsigned ilSize;
853+
if (!GetVersionResilientILCodeHashCode(pMD, &codehash, &ilSize))
854+
{
855+
return E_NOTIMPL;
856+
}
832857

833-
if (ReadInstrumentationSchemaWithLayoutIntoSArray(found->GetData(), found->countsOffset, found->countsOffset, &schemaArray))
858+
COUNT_T methodhash = pMD->GetStableHash();
859+
Header* found = s_textFormatPgoData.Lookup(CodeAndMethodHash(codehash, methodhash));
860+
if (found == NULL)
861+
{
862+
return E_NOTIMPL;
863+
}
864+
865+
StackSArray<ICorJitInfo::PgoInstrumentationSchema> schemaArray;
866+
867+
if (!ReadInstrumentationSchemaWithLayoutIntoSArray(found->GetData(), found->countsOffset, found->countsOffset, &schemaArray))
868+
{
869+
_ASSERTE(!"Unable to parse schema data");
870+
return E_NOTIMPL;
871+
}
872+
873+
HRESULT hr = E_NOTIMPL;
874+
EX_TRY
875+
{
876+
for (unsigned iSchema = 0; iSchema < schemaArray.GetCount(); iSchema++)
877+
{
878+
ICorJitInfo::PgoInstrumentationSchema* schema = &(schemaArray)[iSchema];
879+
ICorJitInfo::PgoInstrumentationKind kind = schema->InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask;
880+
if ((kind == ICorJitInfo::PgoInstrumentationKind::TypeHandle) || (kind == ICorJitInfo::PgoInstrumentationKind::MethodHandle))
881+
{
882+
for (int iEntry = 0; iEntry < schema->Count; iEntry++)
834883
{
835-
EX_TRY
884+
INT_PTR* handleValueAddress = (INT_PTR*)(found->GetData() + schema->Offset + iEntry * InstrumentationKindToSize(schema->InstrumentationKind));
885+
INT_PTR initialHandleValue = VolatileLoad(handleValueAddress);
886+
887+
// TypeHandles can't reliably be loaded at ReadPGO time
888+
// Instead, translate them before leaving this method.
889+
// The ReadPgo method will place pointers to C style null
890+
// terminated strings in the TypeHandle slots, and this will
891+
// translate any of those into loaded TypeHandles as appropriate
892+
if (((initialHandleValue & 1) == 1) && !ICorJitInfo::IsUnknownHandle(initialHandleValue))
836893
{
837-
// TypeHandles can't reliably be loaded at ReadPGO time
838-
// Instead, translate them before leaving this method.
839-
// The ReadPgo method will place pointers to C style null
840-
// terminated strings in the TypeHandle slots, and this will
841-
// translate any of those into loaded TypeHandles as appropriate
894+
INT_PTR newPtr = 0;
895+
char* string = ((char*)initialHandleValue) - 1;
896+
897+
// Resolving types and methods here can invoke managed code where the
898+
// JIT may recursively ask for PGO data. We do not support textual PGO
899+
// for those cases.
900+
static thread_local bool t_resolvingTypeOrMethod;
842901

843-
for (unsigned iSchema = 0; iSchema < schemaArray.GetCount(); iSchema++)
902+
struct ResolveScope
844903
{
845-
ICorJitInfo::PgoInstrumentationSchema *schema = &(schemaArray)[iSchema];
846-
ICorJitInfo::PgoInstrumentationKind kind = schema->InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask;
847-
if ((kind == ICorJitInfo::PgoInstrumentationKind::TypeHandle) || (kind == ICorJitInfo::PgoInstrumentationKind::MethodHandle))
904+
ResolveScope()
848905
{
849-
for (int iEntry = 0; iEntry < schema->Count; iEntry++)
850-
{
851-
INT_PTR* handleValueAddress = (INT_PTR*)(found->GetData() + schema->Offset + iEntry * InstrumentationKindToSize(schema->InstrumentationKind));
852-
INT_PTR initialHandleValue = VolatileLoad(handleValueAddress);
853-
if (((initialHandleValue & 1) == 1) && !ICorJitInfo::IsUnknownHandle(initialHandleValue))
854-
{
855-
INT_PTR newPtr = 0;
856-
char* string = ((char *)initialHandleValue) - 1;
906+
t_resolvingTypeOrMethod = true;
907+
}
857908

858-
// Don't attempt to load any types or methods until the EE is started
859-
if (g_fEEStarted)
860-
{
861-
if (kind == ICorJitInfo::PgoInstrumentationKind::TypeHandle)
862-
{
863-
StackSString ts(SString::Utf8, string);
864-
TypeHandle th = TypeName::GetTypeManaged(ts.GetUnicode(), NULL, FALSE, FALSE, FALSE, NULL, NULL);
865-
newPtr = (INT_PTR)th.AsPtr();
866-
}
867-
else
868-
{
869-
assert(kind == ICorJitInfo::PgoInstrumentationKind::MethodHandle);
870-
// Format is:
871-
// MethodName|@|fully_qualified_type_name
872-
char* sep = strstr(string, "|@|");
873-
if (sep != nullptr)
874-
{
875-
StackSString typeString(SString::Utf8, sep + 3);
876-
StackSString methodString(SString::Utf8, string, (COUNT_T)(sep - string));
877-
TypeHandle th = TypeName::GetTypeManaged(typeString.GetUnicode(), NULL, FALSE, FALSE, FALSE, NULL, NULL);
878-
if (!th.IsNull())
879-
{
880-
MethodDesc* pMD = MemberLoader::FindMethodByName(th.GetMethodTable(), methodString.GetUTF8());
881-
if (pMD != nullptr && !pMD->IsGenericMethodDefinition())
882-
{
883-
newPtr = (INT_PTR)pMD;
884-
}
885-
}
886-
}
887-
}
888-
}
909+
~ResolveScope()
910+
{
911+
t_resolvingTypeOrMethod = false;
912+
}
913+
};
889914

890-
if (newPtr == 0)
915+
// Don't attempt to load any types or methods until the EE is started
916+
if (g_fEEStarted && !t_resolvingTypeOrMethod)
917+
{
918+
ResolveScope resolve;
919+
920+
if (kind == ICorJitInfo::PgoInstrumentationKind::TypeHandle)
921+
{
922+
StackSString ts(SString::Utf8, string);
923+
TypeHandle th = TypeName::GetTypeManaged(ts.GetUnicode(), NULL, FALSE, FALSE, FALSE, NULL, NULL);
924+
newPtr = (INT_PTR)th.AsPtr();
925+
}
926+
else
927+
{
928+
assert(kind == ICorJitInfo::PgoInstrumentationKind::MethodHandle);
929+
// Format is:
930+
// MethodName|@|fully_qualified_type_name
931+
char* sep = strstr(string, "|@|");
932+
if (sep != nullptr)
933+
{
934+
StackSString typeString(SString::Utf8, sep + 3);
935+
StackSString methodString(SString::Utf8, string, (COUNT_T)(sep - string));
936+
TypeHandle th = TypeName::GetTypeManaged(typeString.GetUnicode(), NULL, FALSE, FALSE, FALSE, NULL, NULL);
937+
938+
if (!th.IsNull())
939+
{
940+
MethodDesc* pMD = MemberLoader::FindMethodByName(th.GetMethodTable(), methodString.GetUTF8());
941+
if (pMD != nullptr && !pMD->IsGenericMethodDefinition())
891942
{
892-
newPtr = HashToPgoUnknownHandle(HashStringA(string));
943+
newPtr = (INT_PTR)pMD;
893944
}
894-
895-
InterlockedCompareExchangeT(handleValueAddress, newPtr, initialHandleValue);
896945
}
897946
}
898947
}
899948
}
900949

901-
*pAllocatedData = new BYTE[schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema)];
902-
memcpy(*pAllocatedData, schemaArray.OpenRawBuffer(), schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema));
903-
schemaArray.CloseRawBuffer();
904-
*ppSchema = (ICorJitInfo::PgoInstrumentationSchema*)*pAllocatedData;
905-
906-
*pCountSchemaItems = schemaArray.GetCount();
907-
*pInstrumentationData = found->GetData();
908-
*pPgoSource = ICorJitInfo::PgoSource::Text;
950+
if (newPtr == 0)
951+
{
952+
newPtr = HashToPgoUnknownHandle(HashStringA(string));
953+
}
909954

910-
hr = S_OK;
911-
}
912-
EX_CATCH
913-
{
914-
hr = E_FAIL;
955+
InterlockedCompareExchangeT(handleValueAddress, newPtr, initialHandleValue);
915956
}
916-
EX_END_CATCH(RethrowTerminalExceptions)
917-
}
918-
else
919-
{
920-
_ASSERTE(!"Unable to parse schema data");
921-
hr = E_NOTIMPL;
922957
}
923958
}
924959
}
925-
}
926960

927-
// If we didn't find any text format data, look for dynamic or static data.
928-
//
929-
if (FAILED(hr))
930-
{
931-
PgoManager *mgr;
932-
if (!pMD->IsDynamicMethod())
933-
{
934-
mgr = pMD->GetLoaderAllocator()->GetPgoManager();
935-
}
936-
else
937-
{
938-
mgr = pMD->AsDynamicMethodDesc()->GetResolver()->GetDynamicPgoManager();
939-
}
961+
*pAllocatedData = new BYTE[schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema)];
962+
memcpy(*pAllocatedData, schemaArray.OpenRawBuffer(), schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema));
963+
schemaArray.CloseRawBuffer();
964+
*ppSchema = (ICorJitInfo::PgoInstrumentationSchema*)*pAllocatedData;
940965

941-
if (mgr != NULL)
942-
{
943-
hr = mgr->getPgoInstrumentationResultsInstance(pMD, pAllocatedData, ppSchema, pCountSchemaItems, pInstrumentationData, pPgoSource);
944-
}
966+
*pCountSchemaItems = schemaArray.GetCount();
967+
*pInstrumentationData = found->GetData();
968+
*pPgoSource = ICorJitInfo::PgoSource::Text;
969+
970+
hr = S_OK;
971+
}
972+
EX_CATCH
973+
{
974+
hr = E_FAIL;
945975
}
976+
EX_END_CATCH(RethrowTerminalExceptions)
946977

947978
return hr;
948979
}

src/coreclr/vm/pgo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class PgoManager
157157

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

161162
static void ReadPgoData();
162163
static void WritePgoData();

src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Diagnostics;
55
using System.Diagnostics.CodeAnalysis;
6+
using System.Globalization;
67
using System.IO;
78
using System.Runtime.CompilerServices;
89
using System.Text;
@@ -205,7 +206,8 @@ private Version ParseVersion(string attributeValue)
205206
if (!char.IsDigit(parts[i][j]))
206207
ThrowInvalidAssemblyName();
207208
}
208-
if (!(ushort.TryParse(parts[i], out versionNumbers[i])))
209+
210+
if (!ushort.TryParse(parts[i], NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out versionNumbers[i]))
209211
{
210212
ThrowInvalidAssemblyName();
211213
}

0 commit comments

Comments
 (0)