diff options
Diffstat (limited to 'src/ToolBox')
79 files changed, 2347 insertions, 345 deletions
diff --git a/src/ToolBox/.gitmirror b/src/ToolBox/.gitmirror deleted file mode 100644 index f507630f94..0000000000 --- a/src/ToolBox/.gitmirror +++ /dev/null @@ -1 +0,0 @@ -Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/ToolBox/.gitmirrorselective b/src/ToolBox/.gitmirrorselective new file mode 100644 index 0000000000..817429603a --- /dev/null +++ b/src/ToolBox/.gitmirrorselective @@ -0,0 +1 @@ +superpmi
\ No newline at end of file diff --git a/src/ToolBox/SOS/NETCore/SymbolReader.cs b/src/ToolBox/SOS/NETCore/SymbolReader.cs index f4a3ce30d8..492f7cbd1f 100644 --- a/src/ToolBox/SOS/NETCore/SymbolReader.cs +++ b/src/ToolBox/SOS/NETCore/SymbolReader.cs @@ -28,13 +28,16 @@ namespace SOS { public IntPtr points; public int size; + public IntPtr locals; + public int localsSize; + } /// <summary> /// Read memory callback /// </summary> /// <returns>number of bytes read or 0 for error</returns> - internal unsafe delegate int ReadMemoryDelegate(IntPtr address, byte* buffer, int count); + internal unsafe delegate int ReadMemoryDelegate(ulong address, byte* buffer, int count); private sealed class OpenedReader : IDisposable { @@ -58,7 +61,7 @@ namespace SOS /// </summary> private class TargetStream : Stream { - readonly IntPtr _address; + readonly ulong _address; readonly ReadMemoryDelegate _readMemory; public override long Position { get; set; } @@ -67,7 +70,7 @@ namespace SOS public override bool CanRead { get { return true; } } public override bool CanWrite { get { return false; } } - public TargetStream(IntPtr address, int size, ReadMemoryDelegate readMemory) + public TargetStream(ulong address, int size, ReadMemoryDelegate readMemory) : base() { _address = address; @@ -86,7 +89,7 @@ namespace SOS { fixed (byte* p = &buffer[offset]) { - int read = _readMemory(new IntPtr(_address.ToInt64() + Position), p, count); + int read = _readMemory(_address + (ulong)Position, p, count); Position += read; return read; } @@ -126,6 +129,19 @@ namespace SOS } /// <summary> + /// Quick fix for Path.GetFileName which incorrectly handles Windows-style paths on Linux + /// </summary> + /// <param name="pathName"> File path to be processed </param> + /// <returns>Last component of path</returns> + private static string GetFileName(string pathName) + { + int pos = pathName.LastIndexOfAny(new char[] { '/', '\\'}); + if (pos < 0) + return pathName; + return pathName.Substring(pos + 1); + } + + /// <summary> /// Checks availability of debugging information for given assembly. /// </summary> /// <param name="assemblyPath"> @@ -141,18 +157,18 @@ namespace SOS /// <param name="inMemoryPdbAddress">in memory PDB address or zero</param> /// <param name="inMemoryPdbSize">in memory PDB size</param> /// <returns>Symbol reader handle or zero if error</returns> - internal static IntPtr LoadSymbolsForModule(string assemblyPath, bool isFileLayout, IntPtr loadedPeAddress, int loadedPeSize, - IntPtr inMemoryPdbAddress, int inMemoryPdbSize, ReadMemoryDelegate readMemory) + internal static IntPtr LoadSymbolsForModule(string assemblyPath, bool isFileLayout, ulong loadedPeAddress, int loadedPeSize, + ulong inMemoryPdbAddress, int inMemoryPdbSize, ReadMemoryDelegate readMemory) { try { TargetStream peStream = null; - if (assemblyPath == null && loadedPeAddress != IntPtr.Zero) + if (assemblyPath == null && loadedPeAddress != 0) { peStream = new TargetStream(loadedPeAddress, loadedPeSize, readMemory); } TargetStream pdbStream = null; - if (inMemoryPdbAddress != IntPtr.Zero) + if (inMemoryPdbAddress != 0) { pdbStream = new TargetStream(inMemoryPdbAddress, inMemoryPdbSize, readMemory); } @@ -207,7 +223,7 @@ namespace SOS try { - string fileName = Path.GetFileName(filePath); + string fileName = GetFileName(filePath); foreach (MethodDebugInformationHandle methodDebugInformationHandle in reader.MethodDebugInformation) { MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugInformationHandle); @@ -215,7 +231,7 @@ namespace SOS foreach (SequencePoint point in sequencePoints) { string sourceName = reader.GetString(reader.GetDocument(point.Document).Name); - if (point.StartLine == lineNumber && Path.GetFileName(sourceName) == fileName) + if (point.StartLine == lineNumber && GetFileName(sourceName) == fileName) { methodToken = MetadataTokens.GetToken(methodDebugInformationHandle.ToDefinitionHandle()); ilOffset = point.Offset; @@ -378,7 +394,48 @@ namespace SOS } return false; } + internal static bool GetLocalsInfoForMethod(string assemblyPath, int methodToken, out List<string> locals) + { + locals = null; + + OpenedReader openedReader = GetReader(assemblyPath, isFileLayout: true, peStream: null, pdbStream: null); + if (openedReader == null) + return false; + + using (openedReader) + { + try + { + Handle handle = MetadataTokens.Handle(methodToken); + if (handle.Kind != HandleKind.MethodDefinition) + return false; + + locals = new List<string>(); + MethodDebugInformationHandle methodDebugHandle = + ((MethodDefinitionHandle)handle).ToDebugInformationHandle(); + LocalScopeHandleCollection localScopes = openedReader.Reader.GetLocalScopes(methodDebugHandle); + foreach (LocalScopeHandle scopeHandle in localScopes) + { + LocalScope scope = openedReader.Reader.GetLocalScope(scopeHandle); + LocalVariableHandleCollection localVars = scope.GetLocalVariables(); + foreach (LocalVariableHandle varHandle in localVars) + { + LocalVariable localVar = openedReader.Reader.GetLocalVariable(varHandle); + if (localVar.Attributes == LocalVariableAttributes.DebuggerHidden) + continue; + locals.Add(openedReader.Reader.GetString(localVar.Name)); + } + } + } + catch + { + return false; + } + } + return true; + + } /// <summary> /// Returns source name, line numbers and IL offsets for given method token. /// </summary> @@ -392,11 +449,17 @@ namespace SOS try { List<DebugInfo> points = null; + List<string> locals = null; if (!GetDebugInfoForMethod(assemblyPath, methodToken, out points)) { return false; } + + if (!GetLocalsInfoForMethod(assemblyPath, methodToken, out locals)) + { + return false; + } var structSize = Marshal.SizeOf<DebugInfo>(); debugInfo.size = points.Count; @@ -407,6 +470,14 @@ namespace SOS Marshal.StructureToPtr(info, ptr, false); ptr = (IntPtr)(ptr.ToInt64() + structSize); } + debugInfo.localsSize = locals.Count; + debugInfo.locals = Marshal.AllocHGlobal(debugInfo.localsSize * Marshal.SizeOf<IntPtr>()); + IntPtr ptrLocals = debugInfo.locals; + foreach (string s in locals) + { + Marshal.WriteIntPtr(ptrLocals, Marshal.StringToHGlobalUni(s)); + ptrLocals += Marshal.SizeOf<IntPtr>(); + } return true; } catch @@ -446,12 +517,10 @@ namespace SOS foreach (SequencePoint point in sequencePoints) { - if (point.StartLine == 0 || point.StartLine == SequencePoint.HiddenLine) - continue; DebugInfo debugInfo = new DebugInfo(); debugInfo.lineNumber = point.StartLine; - debugInfo.fileName = openedReader.Reader.GetString(openedReader.Reader.GetDocument(point.Document).Name); + debugInfo.fileName = GetFileName(openedReader.Reader.GetString(openedReader.Reader.GetDocument(point.Document).Name)); debugInfo.ilOffset = point.Offset; points.Add(debugInfo); } @@ -609,7 +678,7 @@ namespace SOS { try { - pdbPath = Path.Combine(Path.GetDirectoryName(assemblyPath), Path.GetFileName(pdbPath)); + pdbPath = Path.Combine(Path.GetDirectoryName(assemblyPath), GetFileName(pdbPath)); } catch { diff --git a/src/ToolBox/SOS/NETCore/project.json b/src/ToolBox/SOS/NETCore/project.json index e9e4f0999f..7a3cf4fe1d 100644 --- a/src/ToolBox/SOS/NETCore/project.json +++ b/src/ToolBox/SOS/NETCore/project.json @@ -3,7 +3,7 @@ "Microsoft.NETCore.Platforms": "1.0.1", "System.IO.FileSystem": "4.0.1", "System.Runtime.InteropServices": "4.1.0", - "System.Reflection.Metadata": "1.4.1-beta-24417-02" + "System.Reflection.Metadata": "1.4.1" }, "frameworks": { "netcoreapp1.0": {} diff --git a/src/ToolBox/SOS/Strike/CMakeLists.txt b/src/ToolBox/SOS/Strike/CMakeLists.txt index 5d25865780..8ba0ade1d8 100644 --- a/src/ToolBox/SOS/Strike/CMakeLists.txt +++ b/src/ToolBox/SOS/Strike/CMakeLists.txt @@ -24,7 +24,9 @@ elseif(CLR_CMAKE_PLATFORM_ARCH_I386) add_definitions(-DSOS_TARGET_X86=1) add_definitions(-D_TARGET_X86_=1) add_definitions(-DDBG_TARGET_32BIT) - add_definitions(-DSOS_TARGET_ARM=1) + if(WIN32) + add_definitions(-DSOS_TARGET_ARM=1) + endif(WIN32) elseif(CLR_CMAKE_PLATFORM_ARCH_ARM) add_definitions(-DSOS_TARGET_ARM=1) add_definitions(-D_TARGET_WIN32_=1) @@ -155,8 +157,13 @@ if(CLR_CMAKE_PLATFORM_ARCH_AMD64) elseif(CLR_CMAKE_PLATFORM_ARCH_I386) set(SOS_SOURCES_ARCH disasmX86.cpp - disasmARM.cpp ) + if(WIN32) + list(APPEND + SOS_SOURCES_ARCH + disasmARM.cpp + ) + endif(WIN32) elseif(CLR_CMAKE_PLATFORM_ARCH_ARM) set(SOS_SOURCES_ARCH disasmARM.cpp diff --git a/src/ToolBox/SOS/Strike/ExpressionNode.cpp b/src/ToolBox/SOS/Strike/ExpressionNode.cpp index 6516823aaa..920afbaedc 100644 --- a/src/ToolBox/SOS/Strike/ExpressionNode.cpp +++ b/src/ToolBox/SOS/Strike/ExpressionNode.cpp @@ -129,7 +129,7 @@ VOID ExpressionNode::DFSVisit(ExpressionNodeVisitorCallback pFunc, VOID* pUserDa // Creates a new expression with a given debuggee value and frame -ExpressionNode::ExpressionNode(__in_z WCHAR* pExpression, ICorDebugValue* pValue, ICorDebugILFrame* pFrame) +ExpressionNode::ExpressionNode(__in_z const WCHAR* pExpression, ICorDebugValue* pValue, ICorDebugILFrame* pFrame) { Init(pValue, NULL, pFrame); _snwprintf_s(pAbsoluteExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pExpression); @@ -137,7 +137,7 @@ ExpressionNode::ExpressionNode(__in_z WCHAR* pExpression, ICorDebugValue* pValue } // Creates a new expression that has an error and no value -ExpressionNode::ExpressionNode(__in_z WCHAR* pExpression, __in_z WCHAR* pErrorMessage) +ExpressionNode::ExpressionNode(__in_z const WCHAR* pExpression, __in_z const WCHAR* pErrorMessage) { Init(NULL, NULL, NULL); _snwprintf_s(pAbsoluteExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pExpression); @@ -146,7 +146,7 @@ ExpressionNode::ExpressionNode(__in_z WCHAR* pExpression, __in_z WCHAR* pErrorMe } // Creates a new child expression -ExpressionNode::ExpressionNode(__in_z WCHAR* pParentExpression, ChildKind ck, __in_z WCHAR* pRelativeExpression, ICorDebugValue* pValue, ICorDebugType* pType, ICorDebugILFrame* pFrame, UVCP_CONSTANT pDefaultValue, ULONG cchDefaultValue) +ExpressionNode::ExpressionNode(__in_z const WCHAR* pParentExpression, ChildKind ck, __in_z const WCHAR* pRelativeExpression, ICorDebugValue* pValue, ICorDebugType* pType, ICorDebugILFrame* pFrame, UVCP_CONSTANT pDefaultValue, ULONG cchDefaultValue) { Init(pValue, pType, pFrame); if(ck == ChildKind_BaseClass) @@ -1979,7 +1979,7 @@ HRESULT ExpressionNode::GetCanonicalElementTypeForTypeName(__in_z WCHAR* pTypeNa // Searches the debuggee for any ICorDebugType that matches the given fully qualified name // This will search across all AppDomains and Assemblies -HRESULT ExpressionNode::FindTypeByName(__in_z WCHAR* pTypeName, ICorDebugType** ppType) +HRESULT ExpressionNode::FindTypeByName(__in_z const WCHAR* pTypeName, ICorDebugType** ppType) { HRESULT Status = S_OK; ToRelease<ICorDebugAppDomainEnum> pAppDomainEnum; @@ -2001,7 +2001,7 @@ HRESULT ExpressionNode::FindTypeByName(__in_z WCHAR* pTypeName, ICorDebugType** // Searches the debuggee for any ICorDebugType that matches the given fully qualified name // This will search across all Assemblies in the given AppDomain -HRESULT ExpressionNode::FindTypeByName(ICorDebugAppDomain* pAppDomain, __in_z WCHAR* pTypeName, ICorDebugType** ppType) +HRESULT ExpressionNode::FindTypeByName(ICorDebugAppDomain* pAppDomain, __in_z const WCHAR* pTypeName, ICorDebugType** ppType) { HRESULT Status = S_OK; ToRelease<ICorDebugAssemblyEnum> pAssemblyEnum; @@ -2022,7 +2022,7 @@ HRESULT ExpressionNode::FindTypeByName(ICorDebugAppDomain* pAppDomain, __in_z WC } // Searches the assembly for any ICorDebugType that matches the given fully qualified name -HRESULT ExpressionNode::FindTypeByName(ICorDebugAssembly* pAssembly, __in_z WCHAR* pTypeName, ICorDebugType** ppType) +HRESULT ExpressionNode::FindTypeByName(ICorDebugAssembly* pAssembly, __in_z const WCHAR* pTypeName, ICorDebugType** ppType) { HRESULT Status = S_OK; ToRelease<ICorDebugModuleEnum> pModuleEnum; @@ -2043,7 +2043,7 @@ HRESULT ExpressionNode::FindTypeByName(ICorDebugAssembly* pAssembly, __in_z WCHA } // Searches a given module for any ICorDebugType that matches the given fully qualified type name -HRESULT ExpressionNode::FindTypeByName(ICorDebugModule* pModule, __in_z WCHAR* pTypeName, ICorDebugType** ppType) +HRESULT ExpressionNode::FindTypeByName(ICorDebugModule* pModule, __in_z const WCHAR* pTypeName, ICorDebugType** ppType) { HRESULT Status = S_OK; ToRelease<IUnknown> pMDUnknown; @@ -2054,7 +2054,7 @@ HRESULT ExpressionNode::FindTypeByName(ICorDebugModule* pModule, __in_z WCHAR* p // If the name contains a generic argument list, extract the type name from // before the list WCHAR rootName[mdNameLen]; - WCHAR* pRootName = NULL; + const WCHAR* pRootName = NULL; int typeNameLen = (int) _wcslen(pTypeName); int genericParamListStart = (int) _wcscspn(pTypeName, L"<"); if(genericParamListStart != typeNameLen) @@ -2107,11 +2107,11 @@ HRESULT ExpressionNode::FindTypeByName(ICorDebugModule* pModule, __in_z WCHAR* p } typeParams = new ToRelease<ICorDebugType>[countTypeParams]; - WCHAR* pCurName = pTypeName + genericParamListStart+1; + const WCHAR* pCurName = pTypeName + genericParamListStart+1; for(int i = 0; i < countTypeParams; i++) { WCHAR typeParamName[mdNameLen]; - WCHAR* pNextComma = _wcschr(pCurName, L','); + const WCHAR* pNextComma = _wcschr(pCurName, L','); int len = (pNextComma != NULL) ? (int)(pNextComma - pCurName) : (int)_wcslen(pCurName)-1; if(len > mdNameLen) return E_FAIL; diff --git a/src/ToolBox/SOS/Strike/ExpressionNode.h b/src/ToolBox/SOS/Strike/ExpressionNode.h index 507a8a53d3..48cc036729 100644 --- a/src/ToolBox/SOS/Strike/ExpressionNode.h +++ b/src/ToolBox/SOS/Strike/ExpressionNode.h @@ -134,13 +134,13 @@ private: }; // Creates a new expression with a given debuggee value and frame - ExpressionNode(__in_z WCHAR* pExpression, ICorDebugValue* pValue, ICorDebugILFrame* pFrame); + ExpressionNode(__in_z const WCHAR* pExpression, ICorDebugValue* pValue, ICorDebugILFrame* pFrame); // Creates a new expression that has an error and no value - ExpressionNode(__in_z WCHAR* pExpression, __in_z WCHAR* pErrorMessage); + ExpressionNode(__in_z const WCHAR* pExpression, __in_z const WCHAR* pErrorMessage); // Creates a new child expression - ExpressionNode(__in_z WCHAR* pParentExpression, ChildKind ck, __in_z WCHAR* pRelativeExpression, ICorDebugValue* pValue, ICorDebugType* pType, ICorDebugILFrame* pFrame, UVCP_CONSTANT pDefaultValue = NULL, ULONG cchDefaultValue = 0); + ExpressionNode(__in_z const WCHAR* pParentExpression, ChildKind ck, __in_z const WCHAR* pRelativeExpression, ICorDebugValue* pValue, ICorDebugType* pType, ICorDebugILFrame* pFrame, UVCP_CONSTANT pDefaultValue = NULL, ULONG cchDefaultValue = 0); // Common member initialization for the constructors VOID Init(ICorDebugValue* pValue, ICorDebugType* pTypeCast, ICorDebugILFrame* pFrame); @@ -288,17 +288,17 @@ private: // Searches the debuggee for any ICorDebugType that matches the given fully qualified name // This will search across all AppDomains and Assemblies - static HRESULT FindTypeByName(__in_z WCHAR* pTypeName, ICorDebugType** ppType); + static HRESULT FindTypeByName(__in_z const WCHAR* pTypeName, ICorDebugType** ppType); // Searches the debuggee for any ICorDebugType that matches the given fully qualified name // This will search across all Assemblies in the given AppDomain - static HRESULT FindTypeByName(ICorDebugAppDomain* pAppDomain, __in_z WCHAR* pTypeName, ICorDebugType** ppType); + static HRESULT FindTypeByName(ICorDebugAppDomain* pAppDomain, __in_z const WCHAR* pTypeName, ICorDebugType** ppType); // Searches the assembly for any ICorDebugType that matches the given fully qualified name - static HRESULT FindTypeByName(ICorDebugAssembly* pAssembly, __in_z WCHAR* pTypeName, ICorDebugType** ppType); + static HRESULT FindTypeByName(ICorDebugAssembly* pAssembly, __in_z const WCHAR* pTypeName, ICorDebugType** ppType); // Searches a given module for any ICorDebugType that matches the given fully qualified type name - static HRESULT FindTypeByName(ICorDebugModule* pModule, __in_z WCHAR* pTypeName, ICorDebugType** ppType); + static HRESULT FindTypeByName(ICorDebugModule* pModule, __in_z const WCHAR* pTypeName, ICorDebugType** ppType); // Checks whether the given token is or refers to type System.ValueType or System.Enum static HRESULT IsTokenValueTypeOrEnum(mdToken token, IMetaDataImport* pMetadata, BOOL* pResult); diff --git a/src/ToolBox/SOS/Strike/datatarget.cpp b/src/ToolBox/SOS/Strike/datatarget.cpp index 8f24b7d841..fe90f0e825 100644 --- a/src/ToolBox/SOS/Strike/datatarget.cpp +++ b/src/ToolBox/SOS/Strike/datatarget.cpp @@ -83,9 +83,9 @@ DataTarget::GetPointerSize( { #if defined(SOS_TARGET_AMD64) || defined(SOS_TARGET_ARM64) *size = 8; -#elif defined(SOS_TARGET_ARM) +#elif defined(SOS_TARGET_ARM) || defined(SOS_TARGET_X86) *size = 4; -#elif +#else #error Unsupported architecture #endif diff --git a/src/ToolBox/SOS/Strike/sildasm.cpp b/src/ToolBox/SOS/Strike/sildasm.cpp index 6bd3bb4801..911ff091e5 100644 --- a/src/ToolBox/SOS/Strike/sildasm.cpp +++ b/src/ToolBox/SOS/Strike/sildasm.cpp @@ -789,7 +789,7 @@ PCCOR_SIGNATURE PrettyPrintType( { //if (sizes[i] != 0 || lowerBounds[i] != 0) { - if (lowerBounds[i] == 0 && i < numSizes) + if (i < numSizes && lowerBounds[i] == 0) appendStrNum(out, sizes[i]); else { diff --git a/src/ToolBox/SOS/Strike/sos_stacktrace.h b/src/ToolBox/SOS/Strike/sos_stacktrace.h index 4aba4ea52c..0af241ca3b 100644 --- a/src/ToolBox/SOS/Strike/sos_stacktrace.h +++ b/src/ToolBox/SOS/Strike/sos_stacktrace.h @@ -119,7 +119,7 @@ HRESULT CALLBACK _EFN_StackTrace( // cbString - number of characters available in the string buffer. // // The output will be truncated of cbString is not long enough for the full stack trace. -HRESULT _EFN_GetManagedExcepStack( +HRESULT CALLBACK _EFN_GetManagedExcepStack( PDEBUG_CLIENT client, ULONG64 StackObjAddr, __out_ecount(cbString) PSTR szStackString, @@ -128,7 +128,7 @@ HRESULT _EFN_GetManagedExcepStack( // _EFN_GetManagedExcepStackW - same as _EFN_GetManagedExcepStack, but returns // the stack as a wide string. -HRESULT _EFN_GetManagedExcepStackW( +HRESULT CALLBACK _EFN_GetManagedExcepStackW( PDEBUG_CLIENT client, ULONG64 StackObjAddr, __out_ecount(cchString) PWSTR wszStackString, @@ -141,7 +141,7 @@ HRESULT _EFN_GetManagedExcepStackW( // szName - a buffer to be filled with the full type name // cbName - the number of characters available in the buffer // -HRESULT _EFN_GetManagedObjectName( +HRESULT CALLBACK _EFN_GetManagedObjectName( PDEBUG_CLIENT client, ULONG64 objAddr, __out_ecount(cbName) PSTR szName, @@ -158,7 +158,7 @@ HRESULT _EFN_GetManagedObjectName( // pOffset - the offset from objAddr to the field. This parameter can be NULL. // // At least one of pValue and pOffset must be non-NULL. -HRESULT _EFN_GetManagedObjectFieldInfo( +HRESULT CALLBACK _EFN_GetManagedObjectFieldInfo( PDEBUG_CLIENT client, ULONG64 objAddr, __out_ecount (mdNameLen) PSTR szFieldName, diff --git a/src/ToolBox/SOS/Strike/sosdocsunix.txt b/src/ToolBox/SOS/Strike/sosdocsunix.txt index 52ec86dc4e..5ab2b311cc 100644 --- a/src/ToolBox/SOS/Strike/sosdocsunix.txt +++ b/src/ToolBox/SOS/Strike/sosdocsunix.txt @@ -844,6 +844,7 @@ corruption bug caused by invalid GCEncoding for a particular method. COMMAND: bpmd. bpmd [-nofuturemodule] <module name> <method name> [<il offset>] +bpmd <source file name>:<line number> bpmd -md <MethodDesc> bpmd -list bpmd -clear <pending breakpoint number> diff --git a/src/ToolBox/SOS/Strike/stressLogDump.cpp b/src/ToolBox/SOS/Strike/stressLogDump.cpp index f277f92434..9dfbe1ed5e 100644 --- a/src/ToolBox/SOS/Strike/stressLogDump.cpp +++ b/src/ToolBox/SOS/Strike/stressLogDump.cpp @@ -34,7 +34,7 @@ static const WCHAR* getTime(const FILETIME* time, __out_ecount (buffLen) WCHAR* return badTime; #ifdef FEATURE_PAL - int length = _snwprintf(buff, buffLen, W("%02d:%02d:%02d"), systemTime.wHour, systemTime.wMinute, systemTime.wSecond); + int length = _snwprintf_s(buff, buffLen, _TRUNCATE, W("%02d:%02d:%02d"), systemTime.wHour, systemTime.wMinute, systemTime.wSecond); if (length <= 0) return badTime; #else // FEATURE_PAL diff --git a/src/ToolBox/SOS/Strike/strike.cpp b/src/ToolBox/SOS/Strike/strike.cpp index 731e2f505d..3bb3f50200 100644 --- a/src/ToolBox/SOS/Strike/strike.cpp +++ b/src/ToolBox/SOS/Strike/strike.cpp @@ -4361,8 +4361,10 @@ DECLARE_API(VerifyObj) ExtOut("Unable to build snapshot of the garbage collector state\n"); goto Exit; } - DacpGcHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj); - bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE); + { + DacpGcHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj); + bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE); + } Exit: if (bValid) @@ -5888,6 +5890,56 @@ HRESULT PrintSpecialThreads() } #endif //FEATURE_PAL +HRESULT SwitchToExceptionThread() +{ + HRESULT Status; + + DacpThreadStoreData ThreadStore; + if ((Status = ThreadStore.Request(g_sos)) != S_OK) + { + Print("Failed to request ThreadStore\n"); + return Status; + } + + DacpThreadData Thread; + CLRDATA_ADDRESS CurThread = ThreadStore.firstThread; + while (CurThread) + { + if (IsInterrupt()) + break; + + if ((Status = Thread.Request(g_sos, CurThread)) != S_OK) + { + PrintLn("Failed to request Thread at ", Pointer(CurThread)); + return Status; + } + + TADDR taLTOH; + if (Thread.lastThrownObjectHandle != NULL) + { + if (SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle), &taLTOH, sizeof(taLTOH), NULL)) + { + if (taLTOH != NULL) + { + ULONG id; + if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK) + { + if (g_ExtSystem->SetCurrentThreadId(id) == S_OK) + { + PrintLn("Found managed exception on thread ", ThreadID(Thread.osThreadId)); + break; + } + } + } + } + } + + CurThread = Thread.nextThread; + } + + return Status; +} + struct ThreadStateTable { unsigned int State; @@ -5961,12 +6013,14 @@ DECLARE_API(Threads) BOOL bPrintSpecialThreads = FALSE; BOOL bPrintLiveThreadsOnly = FALSE; + BOOL bSwitchToManagedExceptionThread = FALSE; BOOL dml = FALSE; CMDOption option[] = { // name, vptr, type, hasValue {"-special", &bPrintSpecialThreads, COBOOL, FALSE}, {"-live", &bPrintLiveThreadsOnly, COBOOL, FALSE}, + {"-managedexception", &bSwitchToManagedExceptionThread, COBOOL, FALSE}, #ifndef FEATURE_PAL {"/d", &dml, COBOOL, FALSE}, #endif @@ -5975,6 +6029,11 @@ DECLARE_API(Threads) { return Status; } + + if (bSwitchToManagedExceptionThread) + { + return SwitchToExceptionThread(); + } // We need to support minidumps for this command. BOOL bMiniDump = IsMiniDumpFile(); @@ -9119,7 +9178,7 @@ DECLARE_API (ProcInfo) if (pFntGetProcessTimes && pFntGetProcessTimes (hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime)) { ExtOut("---------------------------------------\n"); ExtOut("Process Times\n"); - static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", + static const char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; SYSTEMTIME SystemTime; FILETIME LocalFileTime; diff --git a/src/ToolBox/SOS/Strike/util.cpp b/src/ToolBox/SOS/Strike/util.cpp index 9eec76e42c..b6336fb143 100644 --- a/src/ToolBox/SOS/Strike/util.cpp +++ b/src/ToolBox/SOS/Strike/util.cpp @@ -254,47 +254,49 @@ HRESULT CreateInstanceCustomImpl( typedef HRESULT (__stdcall IDebugSymbols3::*GetPathFunc)(LPWSTR , ULONG, ULONG*); - // Handle both the image path and the symbol path - GetPathFunc rgGetPathFuncs[] = - { &IDebugSymbols3::GetImagePathWide, &IDebugSymbols3::GetSymbolPathWide }; - - for (int i = 0; i < _countof(rgGetPathFuncs); ++i) { - ULONG pathSize = 0; + // Handle both the image path and the symbol path + GetPathFunc rgGetPathFuncs[] = + { &IDebugSymbols3::GetImagePathWide, &IDebugSymbols3::GetSymbolPathWide }; - // get the path buffer size - if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(NULL, 0, &pathSize) != S_OK) + for (int i = 0; i < _countof(rgGetPathFuncs); ++i) { - continue; - } + ULONG pathSize = 0; - ArrayHolder<WCHAR> imgPath = new WCHAR[pathSize+MAX_LONGPATH+1]; - if (imgPath == NULL) - { - continue; - } + // get the path buffer size + if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(NULL, 0, &pathSize) != S_OK) + { + continue; + } - // actually get the path - if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(imgPath, pathSize, NULL) != S_OK) - { - continue; - } + ArrayHolder<WCHAR> imgPath = new WCHAR[pathSize+MAX_LONGPATH+1]; + if (imgPath == NULL) + { + continue; + } - LPWSTR ctx; - LPCWSTR pathElem = wcstok_s(imgPath, W(";"), &ctx); - while (pathElem != NULL) - { - WCHAR fullName[MAX_LONGPATH]; - wcscpy_s(fullName, _countof(fullName), pathElem); - if (wcscat_s(fullName, W("\\")) == 0 && wcscat_s(fullName, dllName) == 0) + // actually get the path + if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(imgPath, pathSize, NULL) != S_OK) + { + continue; + } + + LPWSTR ctx; + LPCWSTR pathElem = wcstok_s(imgPath, W(";"), &ctx); + while (pathElem != NULL) { - if (SUCCEEDED(CreateInstanceFromPath(clsid, iid, fullName, ppItf))) + WCHAR fullName[MAX_LONGPATH]; + wcscpy_s(fullName, _countof(fullName), pathElem); + if (wcscat_s(fullName, W("\\")) == 0 && wcscat_s(fullName, dllName) == 0) { - return S_OK; + if (SUCCEEDED(CreateInstanceFromPath(clsid, iid, fullName, ppItf))) + { + return S_OK; + } } - } - pathElem = wcstok_s(NULL, W(";"), &ctx); + pathElem = wcstok_s(NULL, W(";"), &ctx); + } } } @@ -6132,7 +6134,7 @@ HRESULT SymbolReader::LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in int ReadMemoryForSymbols(ULONG64 address, char *buffer, int cb) { ULONG read; - if (SafeReadMemory(address, (PVOID)buffer, cb, &read)) + if (SafeReadMemory(TO_TADDR(address), (PVOID)buffer, cb, &read)) { return read; } diff --git a/src/ToolBox/SOS/Strike/util.h b/src/ToolBox/SOS/Strike/util.h index f444c9fcb2..4612acc299 100644 --- a/src/ToolBox/SOS/Strike/util.h +++ b/src/ToolBox/SOS/Strike/util.h @@ -2258,13 +2258,9 @@ void GetMethodName(mdMethodDef methodDef, IMetaDataImport * pImport, CQuickBytes #ifndef _TARGET_WIN64_ #define itoa_s_ptr _itoa_s #define itow_s_ptr _itow_s -#define itoa_ptr _itoa -#define itow_ptr _itow #else #define itoa_s_ptr _i64toa_s #define itow_s_ptr _i64tow_s -#define itoa_ptr _i64toa -#define itow_ptr _i64tow #endif #ifdef FEATURE_PAL @@ -2359,11 +2355,11 @@ static const char *SymbolReaderDllName = "SOS.NETCore"; static const char *SymbolReaderClassName = "SOS.SymbolReader"; typedef int (*ReadMemoryDelegate)(ULONG64, char *, int); -typedef ULONG64 (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate); -typedef void (*DisposeDelegate)(ULONG64); -typedef BOOL (*ResolveSequencePointDelegate)(ULONG64, const char*, unsigned int, unsigned int*, unsigned int*); -typedef BOOL (*GetLocalVariableName)(ULONG64, int, int, BSTR*); -typedef BOOL (*GetLineByILOffsetDelegate)(ULONG64, mdMethodDef, ULONG64, ULONG *, BSTR*); +typedef PVOID (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate); +typedef void (*DisposeDelegate)(PVOID); +typedef BOOL (*ResolveSequencePointDelegate)(PVOID, const char*, unsigned int, unsigned int*, unsigned int*); +typedef BOOL (*GetLocalVariableName)(PVOID, int, int, BSTR*); +typedef BOOL (*GetLineByILOffsetDelegate)(PVOID, mdMethodDef, ULONG64, ULONG *, BSTR*); class SymbolReader { @@ -2371,7 +2367,7 @@ private: #ifndef FEATURE_PAL ISymUnmanagedReader* m_pSymReader; #endif - ULONG64 m_symbolReaderHandle; + PVOID m_symbolReaderHandle; static LoadSymbolsForModuleDelegate loadSymbolsForModuleDelegate; static DisposeDelegate disposeDelegate; diff --git a/src/ToolBox/SOS/Strike/vm.cpp b/src/ToolBox/SOS/Strike/vm.cpp index e7e5701fc6..70e9210dbd 100644 --- a/src/ToolBox/SOS/Strike/vm.cpp +++ b/src/ToolBox/SOS/Strike/vm.cpp @@ -82,7 +82,7 @@ typedef struct _VM_STATS typedef struct PROTECT_MASK { DWORD Bit; - PSTR Name; + PCSTR Name; } PROTECT_MASK, *PPROTECT_MASK; @@ -324,7 +324,7 @@ PrintVmStatsHeader( VOID PrintIndividualStat( - ___in __in_z IN PSTR Name, + ___in __in_z IN PCSTR Name, IN PINDIVIDUAL_STAT Stat ) { @@ -379,7 +379,7 @@ PrintIndividualStat( VOID PrintVmStats( - ___in __in_z IN PSTR Name, + ___in __in_z IN PCSTR Name, IN PVM_STATS Stats ) { @@ -443,7 +443,7 @@ VmStateToString( size_t capacity_Buffer ) { - PSTR result; + PCSTR result; CHAR invalidStr[sizeof("12345678")]; switch( State ) @@ -478,7 +478,7 @@ VmTypeToString( size_t capacity_Buffer ) { - PSTR result; + PCSTR result; CHAR invalidStr[sizeof("12345678")]; switch( Type ) diff --git a/src/ToolBox/SOS/lldbplugin/CMakeLists.txt b/src/ToolBox/SOS/lldbplugin/CMakeLists.txt index 9f90a54056..7f1fa7704f 100644 --- a/src/ToolBox/SOS/lldbplugin/CMakeLists.txt +++ b/src/ToolBox/SOS/lldbplugin/CMakeLists.txt @@ -21,6 +21,10 @@ if(CLR_CMAKE_PLATFORM_ARCH_AMD64) add_definitions(-DDBG_TARGET_AMD64=1) add_definitions(-DDBG_TARGET_WIN64=1) add_definitions(-DBIT64) +elseif(CLR_CMAKE_PLATFORM_ARCH_I386) + add_definitions(-D_TARGET_X86_=1) + add_definitions(-DDBG_TARGET_32BIT=1) + add_definitions(-DDBG_TARGET_X86=1) elseif(CLR_CMAKE_PLATFORM_ARCH_ARM) add_definitions(-D_TARGET_ARM_=1) add_definitions(-DDBG_TARGET_32BIT=1) @@ -33,6 +37,9 @@ endif() set(ENABLE_LLDBPLUGIN ${CLR_CMAKE_PLATFORM_UNIX} CACHE BOOL "Enable building the SOS plugin for LLDB.") set(REQUIRE_LLDBPLUGIN ${CLR_CMAKE_PLATFORM_LINUX} CACHE BOOL "Require building the SOS plugin for LLDB.") +if(SKIP_LLDBPLUGIN) + SET(REQUIRE_LLDBPLUGIN false) +endif() set(LLVM_HOST_DIR "$ENV{LLVM_HOME}") set(WITH_LLDB_LIBS "${LLVM_HOST_DIR}/lib" CACHE PATH "Path to LLDB libraries") set(WITH_LLDB_INCLUDES "${LLVM_HOST_DIR}/include" CACHE PATH "Path to LLDB headers") diff --git a/src/ToolBox/SOS/lldbplugin/services.cpp b/src/ToolBox/SOS/lldbplugin/services.cpp index f6b42139c4..d2d2cf9f8e 100644 --- a/src/ToolBox/SOS/lldbplugin/services.cpp +++ b/src/ToolBox/SOS/lldbplugin/services.cpp @@ -167,6 +167,8 @@ LLDBServices::VirtualUnwind( #ifdef DBG_TARGET_AMD64 DWORD64 spToFind = dtcontext->Rsp; +#elif DBG_TARGET_X86 + DWORD spToFind = dtcontext->Esp; #elif DBG_TARGET_ARM DWORD spToFind = dtcontext->Sp; #endif diff --git a/src/ToolBox/SOS/tests/OnCrash.do b/src/ToolBox/SOS/tests/OnCrash.do deleted file mode 100644 index b1da86154a..0000000000 --- a/src/ToolBox/SOS/tests/OnCrash.do +++ /dev/null @@ -1,2 +0,0 @@ -script open("/tmp/flag_fail", "a").close() -q diff --git a/src/ToolBox/SOS/tests/README.md b/src/ToolBox/SOS/tests/README.md index ed4c8d062c..1b9c1eac29 100644 --- a/src/ToolBox/SOS/tests/README.md +++ b/src/ToolBox/SOS/tests/README.md @@ -1,18 +1,28 @@ Testing libsosplugin ===================================== -**Test assembly** -The test asembly file must follow two rules: -1. the test class must be named `Program` and -2. it must have a static `Main` method. - -**Running tests** -Make sure that python's lldb module is accessible. To run the tests, use the following command: -`python test_libsosplugin.py --clr-args="/path/to/corerun [corerun_options] /path/to/test_assembly.exe"` -`--clr-args` is a command that would normally be used to launch `corerun` -This will run the test suite on the specified assembly. - -**Writing tests** +**Test assembly** +Compile test assembly file using any C# compiler you have, for example: +- `gmcs test.cs` +- `corerun csc.exe /nologo /r:System.Private.CoreLib.dll test.cs` + + +**Running tests** +Make sure that python's lldb module is accessible. To run the tests, use the following command: +`python2 test_libsosplugin.py --corerun=corerun --sosplugin=sosplugin --assembly=assembly --timeout=timeout` +- `lldb` is a path to `lldb` to run +- `clrdir` is a directory with `corerun` and sosplugin +- `assembly` is a compiled test assembly (e.g. Test.exe) +- `timeout` is a deadline for a single test (in seconds) +- `regex` is a regular expression matching tests to run +- `repeat` is a number of passes for each test + + + +Log files for both failed and passed tests are `*.log` and `*.log.2` for standard output and error correspondingly. + + +**Writing tests** Tests start with the `TestSosCommands` class defined in `test_libsosplugin.py`. To add a test to the suite, start with implementing a new method inside this class whose name begins with `test_`. Most new commands will require only one line of code in this method: `self.do_test("scenarioname")`. This command will launch a new `lldb` instance, which in turn will call the `runScenario` method from `scenarioname` module. `scenarioname` is the name of the python module that will be running the scenario inside `lldb` (found in `tests` folder alongside with `test_libsosplugin.py` and named `scenarioname.py`). An example of a scenario looks like this: @@ -25,9 +35,10 @@ An example of a scenario looks like this: process.Continue() return True - `runScenario` method does all the work related to running the scenario: setting breakpoints, running SOS commands and examining their output. It should return a boolean value indicating a success or a failure. + `runScenario` method does all the work related to running the scenario: setting breakpoints, running SOS commands and examining their output. It should return a boolean value indicating a success or a failure. ***Note:*** `testutils.py` defines some useful commands that can be reused in many scenarios. -**Useful links** -[Python scripting in LLDB](http://lldb.llvm.org/python-reference.html) -[Python unittest framework](https://docs.python.org/2.7/library/unittest.html)
\ No newline at end of file + +**Useful links** +[Python scripting in LLDB](http://lldb.llvm.org/python-reference.html) +[Python unittest framework](https://docs.python.org/2.7/library/unittest.html) diff --git a/src/ToolBox/SOS/tests/Test.cs b/src/ToolBox/SOS/tests/Test.cs new file mode 100644 index 0000000000..e4ef76b30a --- /dev/null +++ b/src/ToolBox/SOS/tests/Test.cs @@ -0,0 +1,94 @@ +using System; + +class Test +{ + static void LikelyInlined() + { + Console.WriteLine("I would like to be inlined"); + } + + static void UnlikelyInlined() + { + Console.Write("I"); + Console.Write(" "); + Console.Write("w"); + Console.Write("o"); + Console.Write("u"); + Console.Write("l"); + Console.Write("d"); + Console.Write(" "); + Console.Write("n"); + Console.Write("o"); + Console.Write("t"); + Console.Write(" "); + Console.Write("l"); + Console.Write("i"); + Console.Write("k"); + Console.Write("e"); + Console.Write(" "); + Console.Write("t"); + Console.Write("o"); + Console.Write(" "); + Console.Write("b"); + Console.Write("e"); + Console.Write(" "); + Console.Write("i"); + Console.Write("n"); + Console.Write("l"); + Console.Write("i"); + Console.Write("n"); + Console.Write("e"); + Console.Write("d"); + Console.Write("\n"); + } + + static void ClrU() + { + Console.WriteLine("test dumpclass"); + } + + static void DumpClass() + { + Console.WriteLine("test dumpclass"); + } + + static void DumpIL() + { + Console.WriteLine("test dumpil"); + } + + static void DumpMD() + { + Console.WriteLine("test dumpmd"); + } + + static void DumpModule() + { + Console.WriteLine("test dumpmodule"); + } + + static void DumpObject() + { + Console.WriteLine("test dumpobject"); + } + + static void DumpStackObjects() + { + Console.WriteLine("test dso"); + } + + static void Name2EE() + { + Console.WriteLine("test name2ee"); + } + + + static int Main() + { + DumpIL(); + LikelyInlined(); + UnlikelyInlined(); + + return 0; + } +} diff --git a/src/ToolBox/SOS/tests/dumpil.py b/src/ToolBox/SOS/tests/dumpil.py deleted file mode 100644 index 9539b618bb..0000000000 --- a/src/ToolBox/SOS/tests/dumpil.py +++ /dev/null @@ -1,26 +0,0 @@ -import lldb -import lldbutil -import re -import os -import testutils - -def runScenario(assemblyName, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - testutils.stop_in_main(ci, process, assemblyName) - addr = testutils.exec_and_find(ci, "name2ee " + assemblyName + " Program.Main", "MethodDesc:\s+([0-9a-fA-F]+)") - - result = False - if addr is not None: - ci.HandleCommand("dumpil " + addr, res) - if res.Succeeded(): - result = True - else: - print("DumpIL failed:") - print(res.GetOutput()) - print(res.GetError()) - - process.Continue() - return result diff --git a/src/ToolBox/SOS/tests/dumpmodule.py b/src/ToolBox/SOS/tests/dumpmodule.py deleted file mode 100644 index 04a5764752..0000000000 --- a/src/ToolBox/SOS/tests/dumpmodule.py +++ /dev/null @@ -1,26 +0,0 @@ -import lldb -import lldbutil -import re -import os -import testutils - -def runScenario(assemblyName, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - testutils.stop_in_main(ci, process, assemblyName) - addr = testutils.exec_and_find(ci, "name2ee " + assemblyName + " Program.Main", "Module:\s+([0-9a-fA-F]+)") - - result = False - if addr is not None: - ci.HandleCommand("dumpmodule " + addr, res) - if res.Succeeded(): - result = True - else: - print("DumpModule failed:") - print(res.GetOutput()) - print(res.GetError()) - - process.Continue() - return result
\ No newline at end of file diff --git a/src/ToolBox/SOS/tests/runprocess.py b/src/ToolBox/SOS/tests/runprocess.py deleted file mode 100644 index d9367b3e6c..0000000000 --- a/src/ToolBox/SOS/tests/runprocess.py +++ /dev/null @@ -1,34 +0,0 @@ -import os -import lldb -import sys -import importlib -from test_libsosplugin import fail_flag - -def run(assemblyName, moduleName): - global fail_flag - - print(fail_flag) - # set the flag, if it is not set - if not os.access(fail_flag, os.R_OK): - open(fail_flag, "a").close() - - - debugger = lldb.debugger - - debugger.SetAsync(False) - target = lldb.target - - debugger.HandleCommand("process launch -s") - debugger.HandleCommand("breakpoint set -n LoadLibraryExW") - - target.GetProcess().Continue() - - debugger.HandleCommand("breakpoint delete 1") - #run the scenario - print("starting scenario...") - i = importlib.import_module(moduleName) - scenarioResult = i.runScenario(os.path.basename(assemblyName), debugger, target) - - # clear the failed flag if the exit status is OK - if scenarioResult is True and target.GetProcess().GetExitStatus() == 0: - os.unlink(fail_flag) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py new file mode 100644 index 0000000000..814d114d16 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py @@ -0,0 +1,62 @@ +import lldb +import re +import testutils as test + +# bpmd -clearall + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + # Set breakpoint + + ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) > 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + # Delete the first breakpoint + + ci.HandleCommand("bpmd -clear 1", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + match = re.search('Cleared', out_msg) + # Check for specific output + test.assertTrue(match) + + # Error message is empty + test.assertEqual(err_msg, '') + + process.Continue() + # Process must be exited + test.assertEqual(process.GetState(), lldb.eStateExited) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonNone) + + # + + # Delete all breakpoints, continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py new file mode 100644 index 0000000000..8da9239f57 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py @@ -0,0 +1,62 @@ +import lldb +import re +import testutils as test + +# bpmd -clearall + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + # Set breakpoint + + ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) > 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + # Delete all breakpoints + + ci.HandleCommand("bpmd -clearall", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + match = re.search('All pending breakpoints cleared.', out_msg) + # Check for specific output + test.assertTrue(match) + + # Error message is empty + test.assertEqual(err_msg, '') + + process.Continue() + # Process must be exited + test.assertEqual(process.GetState(), lldb.eStateExited) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonNone) + + # + + # Delete all breakpoints, continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py new file mode 100644 index 0000000000..dfd75432f3 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py @@ -0,0 +1,45 @@ +import lldb +import re +import testutils as test + +# bpmd -md <MethodDesc pointer> + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + md_addr = test.get_methoddesc(debugger, assembly, "Test.UnlikelyInlined") + + ci.HandleCommand("bpmd -md %s" % md_addr, res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) > 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + process.Continue() + # Process must be stopped at UnlinkelyInlined + test.assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + # + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py new file mode 100644 index 0000000000..e407ab3e4e --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py @@ -0,0 +1,43 @@ +import lldb +import re +import testutils as test + +# bpmd <module name> <managed function name> + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) > 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + process.Continue() + # Process must be stopped at UnlinkelyInlined + test.assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + # + + # Delete all breakpoints, continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py new file mode 100644 index 0000000000..91fb1cd125 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py @@ -0,0 +1,43 @@ +import lldb +import re +import testutils as test + +# bpmd <module name> <managed function name> [<il offset>] + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined 66", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) > 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + process.Continue() + # Process must be stopped at UnlinkelyInlined + test.assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + # + + # Delete all breakpoints, continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py new file mode 100644 index 0000000000..64efad7b65 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py @@ -0,0 +1,48 @@ +import lldb +import re +import testutils as test + +# bpmd -nofuturemodule <module name> <managed function name> + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Process must be stopped here while libcoreclr loading. + # This test usually fails on release version of coreclr + # since we depend on 'LoadLibraryExW' symbol present. + test.assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + ci.HandleCommand("bpmd -nofuturemodule " + assembly + " Test.Main", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + test.assertTrue(len(out_msg) == 0) + + # Error message is empty + test.assertTrue(len(err_msg) == 0) + + process.Continue() + # Process must be exited because of -nofuturemodule flag + test.assertEqual(process.GetState(), lldb.eStateExited) + + # The reason of this stop must be a breakpoint + test.assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonNone) + + # + + # Delete all breakpoints, continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_clrstack.py b/src/ToolBox/SOS/tests/t_cmd_clrstack.py new file mode 100644 index 0000000000..28a1a8b950 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_clrstack.py @@ -0,0 +1,36 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("clrstack", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('OS Thread Id', output) + # Specific string must be in the output + test.assertTrue(match) + + match = re.search('Failed to start', output) + # Check if a fail was reported + test.assertFalse(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_clrthreads.py b/src/ToolBox/SOS/tests/t_cmd_clrthreads.py new file mode 100644 index 0000000000..ff731da990 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_clrthreads.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("clrthreads", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("ThreadCount:"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_clru.py b/src/ToolBox/SOS/tests/t_cmd_clru.py new file mode 100644 index 0000000000..81a583e963 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_clru.py @@ -0,0 +1,46 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('JITTED Code Address:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + jit_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(jit_addr)) + + ci.HandleCommand("clru " + jit_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dso.py b/src/ToolBox/SOS/tests/t_cmd_dso.py new file mode 100644 index 0000000000..492204da5e --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dso.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("SP/REG"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpclass.py b/src/ToolBox/SOS/tests/t_cmd_dumpclass.py new file mode 100644 index 0000000000..6a69070eb5 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpclass.py @@ -0,0 +1,68 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + md_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(md_addr)) + + ci.HandleCommand("dumpmd " + md_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('Class:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + class_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(class_addr)) + + ci.HandleCommand("dumpmd " + class_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpheap.py b/src/ToolBox/SOS/tests/t_cmd_dumpheap.py new file mode 100644 index 0000000000..8546de79e1 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpheap.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dumpheap", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("Address"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpil.py b/src/ToolBox/SOS/tests/t_cmd_dumpil.py new file mode 100644 index 0000000000..295cf19e6f --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpil.py @@ -0,0 +1,38 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + md_addr = test.get_methoddesc(debugger, assembly, "Test.DumpIL") + + ci.HandleCommand("dumpil " + md_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + insts = res.GetOutput() + print(insts) + # Function must have some instructions + test.assertTrue(len(insts) > 0) + + match = re.search(r'IL_\w{4}:\sldstr.*test\sdumpil.*' + + r'IL_\w{4}:\scall.*System\.Console::WriteLine.*' + + r'IL_\w{4}:\sret', + insts.replace('\n', ' ')) + # Must have ldstr, call and ret instructions + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumplog.py b/src/ToolBox/SOS/tests/t_cmd_dumplog.py new file mode 100644 index 0000000000..ab33b66d4b --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumplog.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dumplog", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find(" dump "), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpmd.py b/src/ToolBox/SOS/tests/t_cmd_dumpmd.py new file mode 100644 index 0000000000..0340eb5222 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpmd.py @@ -0,0 +1,46 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + md_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(md_addr)) + + ci.HandleCommand("dumpmd " + md_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py b/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py new file mode 100644 index 0000000000..2dd004818a --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py @@ -0,0 +1,46 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('Module:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + md_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(md_addr)) + + ci.HandleCommand("dumpmodule " + md_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpmt.py b/src/ToolBox/SOS/tests/t_cmd_dumpmt.py new file mode 100644 index 0000000000..059060b9b4 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpmt.py @@ -0,0 +1,68 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + md_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(md_addr)) + + ci.HandleCommand("dumpmd " + md_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('MethodTable:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + mt_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(mt_addr)) + + ci.HandleCommand("dumpmt " + mt_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpobj.py b/src/ToolBox/SOS/tests/t_cmd_dumpobj.py new file mode 100644 index 0000000000..93f32cd9ab --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpobj.py @@ -0,0 +1,69 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Get all objects + objects = [] + for line in output.split('\n'): + match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) + # Not all lines list objects + if match: + groups = match.groups() + # Match has exactly two subgroups + test.assertEqual(len(groups), 2) + + obj_addr = groups[1] + # Address must be a hex number + test.assertTrue(test.is_hexnum(obj_addr)) + + objects.append(obj_addr) + + # There must be at least one object + test.assertTrue(len(objects) > 0) + + for obj in objects: + ci.HandleCommand("dumpobj " + obj, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('Name:\s+\S+', output) + test.assertTrue(match) + match = re.search('MethodTable:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('EEClass:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('Size:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('Fields:', output) + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpstack.py b/src/ToolBox/SOS/tests/t_cmd_dumpstack.py new file mode 100644 index 0000000000..3e15f8b1d9 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_dumpstack.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dumpstack", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("Test.Main()"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_eeheap.py b/src/ToolBox/SOS/tests/t_cmd_eeheap.py new file mode 100644 index 0000000000..50d8977422 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_eeheap.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("eeheap", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("Loader Heap"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_eestack.py b/src/ToolBox/SOS/tests/t_cmd_eestack.py new file mode 100644 index 0000000000..bc36592f87 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_eestack.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("eestack", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("Current frame"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_gcroot.py b/src/ToolBox/SOS/tests/t_cmd_gcroot.py new file mode 100644 index 0000000000..e6b727c9a1 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_gcroot.py @@ -0,0 +1,61 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Get all objects + objects = [] + for line in output.split('\n'): + match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) + # Not all lines list objects + if match: + groups = match.groups() + # Match has exactly two subgroups + test.assertEqual(len(groups), 2) + + obj_addr = groups[1] + # Address must be a hex number + test.assertTrue(test.is_hexnum(obj_addr)) + + objects.append(obj_addr) + + # There must be at least one object + test.assertTrue(len(objects) > 0) + + for obj in objects: + ci.HandleCommand("gcroot " + obj, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('Found', output) + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_histclear.py b/src/ToolBox/SOS/tests/t_cmd_histclear.py new file mode 100644 index 0000000000..db29bd85f7 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_histclear.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("histclear", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("Completed successfully."), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_histinit.py b/src/ToolBox/SOS/tests/t_cmd_histinit.py new file mode 100644 index 0000000000..51191283c7 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_histinit.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("histinit", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("STRESS LOG:"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_histobj.py b/src/ToolBox/SOS/tests/t_cmd_histobj.py new file mode 100644 index 0000000000..c88bdac16e --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_histobj.py @@ -0,0 +1,61 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Get all objects + objects = [] + for line in output.split('\n'): + match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) + # Not all lines list objects + if match: + groups = match.groups() + # Match has exactly two subgroups + test.assertEqual(len(groups), 2) + + obj_addr = groups[1] + # Address must be a hex number + test.assertTrue(test.is_hexnum(obj_addr)) + + objects.append(obj_addr) + + # There must be at least one object + test.assertTrue(len(objects) > 0) + + for obj in objects: + ci.HandleCommand("histobj " + obj, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('GCCount', output) + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_histobjfind.py b/src/ToolBox/SOS/tests/t_cmd_histobjfind.py new file mode 100644 index 0000000000..ffe5dbf52d --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_histobjfind.py @@ -0,0 +1,61 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Get all objects + objects = [] + for line in output.split('\n'): + match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) + # Not all lines list objects + if match: + groups = match.groups() + # Match has exactly two subgroups + test.assertEqual(len(groups), 2) + + obj_addr = groups[1] + # Address must be a hex number + test.assertTrue(test.is_hexnum(obj_addr)) + + objects.append(obj_addr) + + # There must be at least one object + test.assertTrue(len(objects) > 0) + + for obj in objects: + ci.HandleCommand("histobjfind " + obj, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('GCCount', output) + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_histroot.py b/src/ToolBox/SOS/tests/t_cmd_histroot.py new file mode 100644 index 0000000000..7b73caafda --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_histroot.py @@ -0,0 +1,61 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Get all objects + objects = [] + for line in output.split('\n'): + match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) + # Not all lines list objects + if match: + groups = match.groups() + # Match has exactly two subgroups + test.assertEqual(len(groups), 2) + + obj_addr = groups[1] + # Address must be a hex number + test.assertTrue(test.is_hexnum(obj_addr)) + + objects.append(obj_addr) + + # There must be at least one object + test.assertTrue(len(objects) > 0) + + for obj in objects: + ci.HandleCommand("histroot " + obj, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('GCCount', output) + test.assertTrue(match) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_ip2md.py b/src/ToolBox/SOS/tests/t_cmd_ip2md.py new file mode 100644 index 0000000000..1384c38f0c --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_ip2md.py @@ -0,0 +1,53 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('JITTED Code Address:\s+([0-9a-fA-F]+)', output) + # Line matched + test.assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + test.assertEqual(len(groups), 1) + + jit_addr = groups[0] + # Address must be a hex number + test.assertTrue(test.is_hexnum(jit_addr)) + + ci.HandleCommand("ip2md " + jit_addr, res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("MethodDesc:"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_name2ee.py b/src/ToolBox/SOS/tests/t_cmd_name2ee.py new file mode 100644 index 0000000000..b617020e36 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_name2ee.py @@ -0,0 +1,46 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("name2ee " + assembly + " Test.Main", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + match = re.search('Module:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('Assembly:\s+\S+', output) + test.assertTrue(match) + match = re.search('Token:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('MethodDesc:\s+[0-9a-fA-F]+', output) + test.assertTrue(match) + match = re.search('Name:\s+\S+', output) + test.assertTrue(match) + + process.Continue() + # Process must exit + test.assertEqual(process.GetState(), lldb.eStateExited) + + # Process must exit with zero code + test.assertEqual(process.GetExitStatus(), 0) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_pe.py b/src/ToolBox/SOS/tests/t_cmd_pe.py new file mode 100644 index 0000000000..0a87014934 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_pe.py @@ -0,0 +1,28 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("dso", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_sos.py b/src/ToolBox/SOS/tests/t_cmd_sos.py new file mode 100644 index 0000000000..b407491d79 --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_sos.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("sos", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("SOS"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/t_cmd_soshelp.py b/src/ToolBox/SOS/tests/t_cmd_soshelp.py new file mode 100644 index 0000000000..8bb51dad5a --- /dev/null +++ b/src/ToolBox/SOS/tests/t_cmd_soshelp.py @@ -0,0 +1,31 @@ +import lldb +import re +import testutils as test + + +def runScenario(assembly, debugger, target): + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + ci = debugger.GetCommandInterpreter() + + # Run debugger, wait until libcoreclr is loaded, + # set breakpoint at Test.Main and stop there + test.stop_in_main(debugger, assembly) + + ci.HandleCommand("soshelp", res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + test.assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + test.assertTrue(len(output) > 0) + + # Specific string must be in the output + test.assertNotEqual(output.find("soshelp <functionname>"), -1) + + # TODO: test other use cases + + # Continue current process and checks its exit code + test.exit_lldb(debugger, assembly) diff --git a/src/ToolBox/SOS/tests/test_libsosplugin.py b/src/ToolBox/SOS/tests/test_libsosplugin.py index e4f59ebbcf..e5a5906264 100644 --- a/src/ToolBox/SOS/tests/test_libsosplugin.py +++ b/src/ToolBox/SOS/tests/test_libsosplugin.py @@ -1,3 +1,4 @@ +from __future__ import print_function import unittest import argparse import re @@ -5,80 +6,279 @@ import tempfile import subprocess import threading import os -import os.path import sys +import inspect + +lldb = '' +clrdir = '' +workdir = '' +corerun = '' +sosplugin = '' +assembly = '' +fail_flag = '' +fail_flag_lldb = '' +summary_file = '' +timeout = 0 +regex = '' +repeat = 0 + + +def runWithTimeout(cmd): + p = None + + def run(): + global p + p = subprocess.Popen(cmd, shell=True) + p.communicate() + + thread = threading.Thread(target=run) + thread.start() + + thread.join(timeout) + if thread.is_alive(): + with open(summary_file, 'a+') as summary: + print('Timeout!', file=summary) + p.kill() + thread.join() + -assemblyName='' -clrArgs='' -fail_flag='/tmp/fail_flag' - -# helper functions - -def prepareScenarioFile(moduleName): - global assemblyName - #create a temporary scenario file - fd, scenarioFileName = tempfile.mkstemp() - scenarioFile = open(scenarioFileName, 'w') - scenarioFile.write('script from runprocess import run\n') - scenarioFile.write('script run("'+assemblyName+'", "'+moduleName+'")\n') - scenarioFile.write('quit\n') - scenarioFile.close() - os.close(fd) - return scenarioFileName - -def runWithTimeout(cmd, timeout): - d = {'process': None} - def run(): - d['process'] = subprocess.Popen(cmd, shell=True) - d['process'].communicate() - - thread = threading.Thread(target=run) - thread.start() - - thread.join(timeout) - if thread.is_alive(): - d['process'].terminate() - thread.join() - -# Test class class TestSosCommands(unittest.TestCase): - def do_test(self, command): - global clrArgs - global fail_flag - filename = prepareScenarioFile(command) - cmd = "lldb --source "+filename+" -b -K \"OnCrash.do\" -- "+clrArgs+" > "+command+".log 2>"+command+".log.2" - runWithTimeout(cmd, 120) - self.assertFalse(os.path.isfile(fail_flag)) - os.unlink(filename) + def do_test(self, command): + open(fail_flag, 'a').close() + try: + os.unlink(fail_flag_lldb) + except: + pass + + cmd = (('%s -b ' % lldb) + + ("-k \"script open('%s', 'a').close()\" " % fail_flag_lldb) + + ("-k 'quit' ") + + ("--no-lldbinit ") + + ("-O \"plugin load %s \" " % sosplugin) + + ("-o \"script import testutils as test\" ") + + ("-o \"script test.fail_flag = '%s'\" " % fail_flag) + + ("-o \"script test.summary_file = '%s'\" " % summary_file) + + ("-o \"script test.run('%s', '%s')\" " % (assembly, command)) + + ("-o \"quit\" ") + + (" -- %s %s > %s.log 2> %s.log.2" % (corerun, assembly, + command, command))) + + runWithTimeout(cmd) + self.assertFalse(os.path.isfile(fail_flag)) + self.assertFalse(os.path.isfile(fail_flag_lldb)) + + try: + os.unlink(fail_flag) + except: + pass + try: + os.unlink(fail_flag_lldb) + except: + pass + + def t_cmd_bpmd_nofuturemodule_module_function(self): + self.do_test('t_cmd_bpmd_nofuturemodule_module_function') + + def t_cmd_bpmd_module_function(self): + self.do_test('t_cmd_bpmd_module_function') + + def t_cmd_bpmd_module_function_iloffset(self): + self.do_test('t_cmd_bpmd_module_function_iloffset') + + def t_cmd_bpmd_methoddesc(self): + self.do_test('t_cmd_bpmd_methoddesc') + + def t_cmd_bpmd_clearall(self): + self.do_test('t_cmd_bpmd_clearall') + + def t_cmd_clrstack(self): + self.do_test('t_cmd_clrstack') + + def t_cmd_clrthreads(self): + self.do_test('t_cmd_clrthreads') + + def t_cmd_clru(self): + self.do_test('t_cmd_clru') + + def t_cmd_dumpclass(self): + self.do_test('t_cmd_dumpclass') + + def t_cmd_dumpheap(self): + self.do_test('t_cmd_dumpheap') + + def t_cmd_dumpil(self): + self.do_test('t_cmd_dumpil') + + def t_cmd_dumplog(self): + self.do_test('t_cmd_dumplog') + + def t_cmd_dumpmd(self): + self.do_test('t_cmd_dumpmd') + + def t_cmd_dumpmodule(self): + self.do_test('t_cmd_dumpmodule') + + def t_cmd_dumpmt(self): + self.do_test('t_cmd_dumpmt') + + def t_cmd_dumpobj(self): + self.do_test('t_cmd_dumpobj') + + def t_cmd_dumpstack(self): + self.do_test('t_cmd_dumpstack') + + def t_cmd_dso(self): + self.do_test('t_cmd_dso') + + def t_cmd_eeheap(self): + self.do_test('t_cmd_eeheap') - def test_dumpmodule(self): - self.do_test("dumpmodule") + def t_cmd_eestack(self): + self.do_test('t_cmd_eestack') + + def t_cmd_gcroot(self): + self.do_test('t_cmd_gcroot') + + def t_cmd_ip2md(self): + self.do_test('t_cmd_ip2md') + + def t_cmd_name2ee(self): + self.do_test('t_cmd_name2ee') + + def t_cmd_pe(self): + self.do_test('t_cmd_pe') + + def t_cmd_histclear(self): + self.do_test('t_cmd_histclear') + + def t_cmd_histinit(self): + self.do_test('t_cmd_histinit') + + def t_cmd_histobj(self): + self.do_test('t_cmd_histobj') + + def t_cmd_histobjfind(self): + self.do_test('t_cmd_histobjfind') + + def t_cmd_histroot(self): + self.do_test('t_cmd_histroot') + + def t_cmd_sos(self): + self.do_test('t_cmd_sos') + + def t_cmd_soshelp(self): + self.do_test('t_cmd_soshelp') + + +def generate_report(): + report = [{'name': 'TOTAL', True: 0, False: 0, 'completed': True}] + fail_messages = [] + + if not os.path.isfile(summary_file): + print('No summary file to process!') + return + + with open(summary_file, 'r') as summary: + for line in summary: + if line.startswith('new_suite: '): + report.append({'name': line.split()[-1], True: 0, False: 0, + 'completed': False, 'timeout': False}) + elif line.startswith('True'): + report[-1][True] += 1 + elif line.startswith('False'): + report[-1][False] += 1 + elif line.startswith('Completed!'): + report[-1]['completed'] = True + elif line.startswith('Timeout!'): + report[-1]['timeout'] = True + elif line.startswith('!!! '): + fail_messages.append(line.rstrip('\n')) + + for suite in report[1:]: + report[0][True] += suite[True] + report[0][False] += suite[False] + report[0]['completed'] &= suite['completed'] + + for line in fail_messages: + print(line) + + print() + print('=' * 79) + print('{:72} {:6}'.format('Test suite', 'Result')) + print('-' * 79) + for suite in report[1:]: + if suite['timeout']: + result = 'Timeout' + elif suite[False]: + result = 'Fail' + elif not suite['completed']: + result = 'Crash' + elif suite[True]: + result = 'Success' + else: + result = 'Please, report' + print('{:68} {:>10}'.format(suite['name'], result)) + print('=' * 79) - def test_dumpil(self): - self.do_test("dumpil") - if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--clr-args', default='') - parser.add_argument('unittest_args', nargs='*') - - args = parser.parse_args() - - clrArgs = args.clr_args - print("ClrArgs: " + clrArgs) - # find assembly name among lldb arguments - assembly_regexp = re.compile("([^\s]+\.exe)") - assemblyMatch = assembly_regexp.search(clrArgs) - if assemblyMatch is not None: - assemblyName = assemblyMatch.group(1) - else: - print("Assembly not recognized") - exit(1) - - print("Assembly name: "+assemblyName) - sys.argv[1:] = args.unittest_args - suite = unittest.TestLoader().loadTestsFromTestCase(TestSosCommands) - unittest.TextTestRunner(verbosity=2).run(suite) - os.unlink(fail_flag)
\ No newline at end of file + parser = argparse.ArgumentParser() + parser.add_argument('--lldb', default='lldb') + parser.add_argument('--clrdir', default='.') + parser.add_argument('--workdir', default='.') + parser.add_argument('--assembly', default='Test.exe') + parser.add_argument('--timeout', default=90) + parser.add_argument('--regex', default='t_cmd_') + parser.add_argument('--repeat', default=1) + parser.add_argument('unittest_args', nargs='*') + + args = parser.parse_args() + + lldb = args.lldb + clrdir = args.clrdir + workdir = args.workdir + assembly = args.assembly + timeout = int(args.timeout) + regex = args.regex + repeat = int(args.repeat) + print("lldb: %s" % lldb) + print("clrdir: %s" % clrdir) + print("workdir: %s" % workdir) + print("assembly: %s" % assembly) + print("timeout: %i" % timeout) + print("regex: %s" % regex) + print("repeat: %i" % repeat) + + corerun = os.path.join(clrdir, 'corerun') + sosplugin = os.path.join(clrdir, 'libsosplugin.so') + if os.name != 'posix': + print('Not implemented: corerun.exe, sosplugin.dll?') + exit(1) + + print("corerun: %s" % corerun) + print("sosplugin: %s" % sosplugin) + + fail_flag = os.path.join(workdir, 'fail_flag') + fail_flag_lldb = os.path.join(workdir, 'fail_flag.lldb') + + print("fail_flag: %s" % fail_flag) + print("fail_flag_lldb: %s" % fail_flag_lldb) + + summary_file = os.path.join(workdir, 'summary') + print("summary_file: %s" % summary_file) + + try: + os.unlink(summary_file) + except: + pass + + sys.argv[1:] = args.unittest_args + suite = unittest.TestSuite() + all_tests = inspect.getmembers(TestSosCommands, predicate=inspect.ismethod) + for (test_name, test_func) in all_tests: + if re.match(regex, test_name): + suite.addTest(TestSosCommands(test_name)) + unittest.TextTestRunner(verbosity=1).run(suite) + + generate_report() diff --git a/src/ToolBox/SOS/tests/testutils.py b/src/ToolBox/SOS/tests/testutils.py index 1ddb6560e6..1f784b48f6 100644 --- a/src/ToolBox/SOS/tests/testutils.py +++ b/src/ToolBox/SOS/tests/testutils.py @@ -1,40 +1,206 @@ +from __future__ import print_function import lldb import re +import inspect +import sys +import os +import importlib + +summary_file = '' +fail_flag = '' + +failed = False + + +def assertCommon(passed, fatal): + global failed + with open(summary_file, 'a+') as summary: + print(bool(passed), file=summary) + if (not passed): + failed = True + print('!!! test failed:', file=summary) + for s in inspect.stack()[2:]: + print("!!! %s:%i" % (s[1], s[2]), file=summary) + print("!!! %s" % s[4][0], file=summary) + if re.match('\W*t_\w+\.py$', s[1]): + break + print('!!! ', file=summary) + + if fatal: + exit(1) + + +def assertTrue(x, fatal=True): + passed = bool(x) + assertCommon(passed, fatal) + + +def assertFalse(x, fatal=True): + passed = not bool(x) + assertCommon(passed, fatal) + + +def assertEqual(x, y, fatal=True): + passed = (x == y) + if not passed: + print(str(x), ' != ', str(y)) + assertCommon(passed, fatal) + + +def assertNotEqual(x, y, fatal=True): + passed = (x != y) + if not passed: + print(str(x), ' == ', str(y)) + assertCommon(passed, fatal) + def checkResult(res): - if not res.Succeeded(): - print(res.GetOutput()) - print(res.GetError()) - exit(1) + if not res.Succeeded(): + print(res.GetOutput()) + print(res.GetError()) + exit(1) + + +def is_hexnum(s): + try: + int(s, 16) + return True + except ValueError: + return False + def exec_and_find(commandInterpreter, cmd, regexp): - res = lldb.SBCommandReturnObject() - commandInterpreter.HandleCommand(cmd, res) - checkResult(res) - - expr = re.compile(regexp) - addr = None - - print(res.GetOutput()) - lines = res.GetOutput().splitlines() - for line in lines: - match = expr.match(line) - if match is not None: - addr = match.group(1) - break - - print("Found addr: " + str(addr)) - return addr - -def stop_in_main(commandInterpreter, process, assemblyName): - res = lldb.SBCommandReturnObject() - commandInterpreter.HandleCommand("bpmd " + assemblyName + " Program.Main", res) - checkResult(res) - print(res.GetOutput()) - print(res.GetError()) - res.Clear() - - - # Use Python API to continue the process. The listening thread should be - # able to receive the state changed events. - process.Continue()
\ No newline at end of file + res = lldb.SBCommandReturnObject() + commandInterpreter.HandleCommand(cmd, res) + checkResult(res) + + expr = re.compile(regexp) + addr = None + + print(res.GetOutput()) + lines = res.GetOutput().splitlines() + for line in lines: + match = expr.match(line) + if match: + addr = match.group(1) + break + + print("Found addr: " + str(addr)) + return addr + + +def stop_in_main(debugger, assembly): + ci = debugger.GetCommandInterpreter() + target = debugger.GetSelectedTarget() + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + + # Process must be stopped here while libcoreclr loading. + # This test usually fails on release version of coreclr + # since we depend on 'LoadLibraryExW' symbol present. + assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + ci.HandleCommand("bpmd " + assembly + " Test.Main", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + assertTrue(res.Succeeded()) + + # Output is not empty + # Should be at least 'Adding pending breakpoints...' + assertTrue(len(out_msg) > 0) + + # Error message is empty + assertTrue(len(err_msg) == 0) + + process.Continue() + # Process must be stopped here if bpmd works at all + assertEqual(process.GetState(), lldb.eStateStopped) + + # The reason of this stop must be a breakpoint + assertEqual(process.GetSelectedThread().GetStopReason(), + lldb.eStopReasonBreakpoint) + + +def exit_lldb(debugger, assembly): + ci = debugger.GetCommandInterpreter() + target = debugger.GetSelectedTarget() + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + + ci.HandleCommand("breakpoint delete --force", res) + out_msg = res.GetOutput() + err_msg = res.GetError() + print(out_msg) + print(err_msg) + # Interpreter must have this command and able to run it + # assertTrue(res.Succeeded()) + + process.Continue() + # Process must exit + assertEqual(process.GetState(), lldb.eStateExited) + + # Process must exit with zero code + assertEqual(process.GetExitStatus(), 0) + + +def get_methoddesc(debugger, assembly, funcname): + ci = debugger.GetCommandInterpreter() + target = debugger.GetSelectedTarget() + process = target.GetProcess() + res = lldb.SBCommandReturnObject() + + ci.HandleCommand("name2ee %s %s" % (assembly, funcname), res) + print(res.GetOutput()) + print(res.GetError()) + # Interpreter must have this command and able to run it + assertTrue(res.Succeeded()) + + output = res.GetOutput() + # Output is not empty + assertTrue(len(output) > 0) + + match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) + # Line matched + assertTrue(match) + + groups = match.groups() + # Match has a single subgroup + assertEqual(len(groups), 1) + + md_addr = groups[0] + # Address must be a hex number + assertTrue(is_hexnum(md_addr)) + + return md_addr + + +def run(assembly, module): + with open(summary_file, 'a+') as summary: + print('new_suite: %s' % module, file=summary) + + debugger = lldb.debugger + + debugger.SetAsync(False) + target = lldb.target + + debugger.HandleCommand("breakpoint set --one-shot --name coreclr_execute_assembly") + debugger.HandleCommand("process launch") + + # run the scenario + print("starting scenario...") + i = importlib.import_module(module) + scenarioResult = i.runScenario(os.path.basename(assembly), debugger, + target) + + if (target.GetProcess().GetExitStatus() == 0) and not failed: + os.unlink(fail_flag) + + with open(summary_file, 'a+') as summary: + print('Completed!', file=summary) diff --git a/src/ToolBox/dirs.proj b/src/ToolBox/dirs.proj index 9ac295d8d5..e16ddd108c 100644 --- a/src/ToolBox/dirs.proj +++ b/src/ToolBox/dirs.proj @@ -79,7 +79,9 @@ </ProjectFile> <ProjectFile Include="urtui\dirs.proj"/> <ProjectFile Include="winmdexp\dirs.proj" /> +<!-- <ProjectFile Include="winverify\dirs.proj" /> +--> <ProjectFile Include="wpf\wpf.proj" Condition="'$(BuildArchitecture)' == 'i386' or '$(BuildArchitecture)' == 'amd64'" /> <ProjectFile Include="PdbTypeMatch\PdbTypeMatch.nativeproj" Condition="'$(BuildArchitecture)' == 'i386' and '$(_BuildType)' == 'ret'" > <ProductGroups>PK</ProductGroups> diff --git a/src/ToolBox/superpmi/superpmi-shared/compileresult.h b/src/ToolBox/superpmi/superpmi-shared/compileresult.h index 8fc3f7a352..87853f4cd3 100644 --- a/src/ToolBox/superpmi/superpmi-shared/compileresult.h +++ b/src/ToolBox/superpmi/superpmi-shared/compileresult.h @@ -203,7 +203,7 @@ public: void recReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd, CorInfoInline inlineResult, const char * reason); void dmpReportInliningDecision(DWORD key, const Agnostic_ReportInliningDecision& value); - CorInfoInline CompileResult::repReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd); + CorInfoInline repReportInliningDecision(CORINFO_METHOD_HANDLE inlinerHnd, CORINFO_METHOD_HANDLE inlineeHnd); void recSetEHcount(unsigned cEH); void dmpSetEHcount(DWORD key, DWORD value); diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h index 671b45b392..e1190d7ab6 100644 --- a/src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h +++ b/src/ToolBox/superpmi/superpmi-shared/icorjitcompilerimpl.h @@ -57,7 +57,11 @@ public: // When the EE loads the System.Numerics.Vectors assembly, it asks the JIT what length (in bytes) of // SIMD vector it supports as an intrinsic type. Zero means that the JIT does not support SIMD // intrinsics, so the EE should use the default size (i.e. the size of the IL implementation). +#if COR_JIT_EE_VERSION > 460 + unsigned getMaxIntrinsicSIMDVectorLength(CORJIT_FLAGS cpuCompileFlags); /* { return 0; } */ +#else unsigned getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags); /* { return 0; } */ +#endif // IL obfuscators sometimes interpose on the EE-JIT interface. This function allows the VM to // tell the JIT to use a particular ICorJitCompiler to implement the methods of this interface, diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h index 6eb862c8b8..b847d9bc50 100644 --- a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h +++ b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h @@ -653,7 +653,7 @@ public: // in the code are. The native compiler will ensure that these places // have a corresponding break point in native code. // - // Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will + // Note that unless CORJIT_FLAG_DEBUG_CODE is specified, this function will // be used only as a hint and the native compiler should not change its // code generation. void getBoundaries( @@ -683,7 +683,7 @@ public: // under debugging, the JIT needs to keep them live over their // entire scope so that they can be inspected. // - // Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will + // Note that unless CORJIT_FLAG_DEBUG_CODE is specified, this function will // be used only as a hint and the native compiler should not change its // code generation. void getVars( diff --git a/src/ToolBox/superpmi/superpmi-shared/logging.cpp b/src/ToolBox/superpmi/superpmi-shared/logging.cpp index 5f7aa48a4f..69c321bb39 100644 --- a/src/ToolBox/superpmi/superpmi-shared/logging.cpp +++ b/src/ToolBox/superpmi/superpmi-shared/logging.cpp @@ -262,9 +262,9 @@ void Logger::LogVprintf(const char *function, const char *file, int line, const char *timeStr = ""; #endif // FEATURE_PAL - const char *logEntryFmtStr = "%s - %s [%s:%d] - %s - %s\r\n"; - size_t logEntryBuffSize = _snprintf(nullptr, 0, logEntryFmtStr, - timeStr, function, file, line, logLevelStr, fullMsg) + 1; + const char logEntryFmtStr[] = "%s - %s [%s:%d] - %s - %s\r\n"; + size_t logEntryBuffSize = sizeof(logEntryFmtStr) + strlen(timeStr) + strlen(function) + + strlen(file) + 10 + strlen(logLevelStr) + strlen(fullMsg); char *logEntry = new char[logEntryBuffSize]; sprintf_s(logEntry, logEntryBuffSize, logEntryFmtStr, diff --git a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h index 774e732620..de0db3a9bb 100644 --- a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h +++ b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h @@ -85,6 +85,7 @@ LWM(GetFunctionFixedEntryPoint, DWORDLONG, Agnostic_CORINFO_CONST_LOOKUP) LWM(GetGSCookie, DWORD, DLDL) LWM(GetHelperFtn, DWORD, DLDL) LWM(GetHelperName, DWORD, DWORD) +LWM(GetHFAType, DWORDLONG, DWORD) LWM(GetInlinedCallFrameVptr, DWORD, DLDL) LWM(GetIntConfigValue, Agnostic_ConfigIntInfo, DWORD) LWM(GetIntrinsicID, DWORDLONG, DD) diff --git a/src/ToolBox/superpmi/superpmi-shared/mclist.cpp b/src/ToolBox/superpmi/superpmi-shared/mclist.cpp index 6a6f8701bf..511893fc96 100644 --- a/src/ToolBox/superpmi/superpmi-shared/mclist.cpp +++ b/src/ToolBox/superpmi/superpmi-shared/mclist.cpp @@ -237,7 +237,7 @@ void MCList::AddMethodToMCL(int methodIndex) DWORD charCount = 0; DWORD bytesWritten = 0; - charCount = sprintf(strMethodIndex, "%d\r\n", methodIndex); + charCount = sprintf_s(strMethodIndex, sizeof(strMethodIndex), "%d\r\n", methodIndex); if (!WriteFile(hMCLFile, strMethodIndex, charCount, &bytesWritten, nullptr) || bytesWritten != charCount) { diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index 2c46065b48..5768d38569 100644 --- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -744,7 +744,7 @@ void MethodContext::recCompileMethod(CORINFO_METHOD_INFO *info, unsigned flags) void MethodContext::dmpCompileMethod(DWORD key, const Agnostic_CompileMethod& value) { printf("CompiledMethod key %u, value ftn-%016llX scp-%016llX ilo-%u ils-%u ms-%u ehc-%u opt-%u rk-%u " - "args{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} " + "args{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} " "locals{cc-%u rc-%016llX rts-%016llX rt-%u(%s) flg-%08X nA-%u cc-%u ci-%u mc-%u mi-%u arg-%016llX cb-%u pSig-%u scp-%016llX tok-%08X} " "flg-%08X", key, @@ -1098,8 +1098,8 @@ void MethodContext::recGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes, DW } void MethodContext::dmpGetJitFlags(DWORD key, DD value) { - CORJIT_FLAGS *flags = (CORJIT_FLAGS*)GetJitFlags->GetBuffer(value.A); - printf("GetJitFlags key %u sizeInBytes-%u corJitFlags-%08X corJitFlags2-%08X", key, value.B, flags->corJitFlags, flags->corJitFlags2); + CORJIT_FLAGS *jitflags = (CORJIT_FLAGS*)GetJitFlags->GetBuffer(value.A); + printf("GetJitFlags key %u sizeInBytes-%u jitFlags-%016llX", key, value.B, jitflags->GetFlagsRaw()); GetJitFlags->Unlock(); } DWORD MethodContext::repGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes) @@ -2842,6 +2842,34 @@ CORINFO_CLASS_HANDLE MethodContext::repGetArgClass(CORINFO_SIG_INFO* sig, CORINF return (CORINFO_CLASS_HANDLE)value.result; } +void MethodContext::recGetHFAType(CORINFO_CLASS_HANDLE clsHnd, CorInfoType result) +{ + if (GetHFAType == nullptr) + GetHFAType = new LightWeightMap<DWORDLONG, DWORD>(); + + GetHFAType->Add((DWORDLONG)clsHnd, (DWORD)result); + DEBUG_REC(dmpGetHFAType((DWORDLONG)clsHnd, (DWORD)result)); + return; +} + +void MethodContext::dmpGetHFAType(DWORDLONG key, DWORD value) +{ + printf("GetHFAType key %016llX, value %u ", key, value); + return; +} + +CorInfoType MethodContext::repGetHFAType(CORINFO_CLASS_HANDLE clsHnd) +{ + DWORD value; + + AssertCodeMsg(GetHFAType != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX", (DWORDLONG)clsHnd); + AssertCodeMsg(GetHFAType->GetIndex((DWORDLONG)clsHnd) != -1, EXCEPTIONCODE_MC, "Didn't find %016llX", (DWORDLONG)clsHnd); + + value = GetHFAType->Get((DWORDLONG)clsHnd); + DEBUG_REP(dmpGetHFAType((DWORDLONG)clsHnd, value)); + return (CorInfoType)value; +} + void MethodContext::recGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, bool result, DWORD exceptionCode) { if (GetMethodInfo == nullptr) diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h index 5869c85b45..0d49666e5c 100644 --- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -513,9 +513,9 @@ public: void dmpGetMethodName(DLD key, DD value); const char *repGetMethodName(CORINFO_METHOD_HANDLE ftn, const char **moduleName); - void MethodContext::recGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes, DWORD result); - void MethodContext::dmpGetJitFlags(DWORD key, DD value); - DWORD MethodContext::repGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes); + void recGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes, DWORD result); + void dmpGetJitFlags(DWORD key, DD value); + DWORD repGetJitFlags(CORJIT_FLAGS *jitFlags, DWORD sizeInBytes); void recGetJitTimeLogFilename(LPCWSTR tempFileName); void dmpGetJitTimeLogFilename(DWORD key, DWORD value); @@ -674,6 +674,10 @@ public: void dmpGetArgClass(const Agnostic_GetArgClass& key, const Agnostic_GetArgClass_Value& value); CORINFO_CLASS_HANDLE repGetArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args, DWORD *exceptionCode); + void recGetHFAType(CORINFO_CLASS_HANDLE clsHnd, CorInfoType result); + void dmpGetHFAType(DWORDLONG key, DWORD value); + CorInfoType repGetHFAType(CORINFO_CLASS_HANDLE clsHnd); + void recGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, bool result, DWORD exceptionCode); void dmpGetMethodInfo(DWORDLONG key, const Agnostic_GetMethodInfo& value); bool repGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO *info, DWORD *exceptionCode); @@ -1012,7 +1016,7 @@ private: // ********************* Please keep this up-to-date to ease adding more *************** -// Highest packet number: 158 +// Highest packet number: 159 // ************************************************************************************* enum mcPackets { @@ -1055,6 +1059,7 @@ enum mcPackets Packet_GetAddrOfCaptureThreadGlobal = 27, Retired1 = 28, Packet_GetArgClass = 139, //retired as 28 on 2013/07/03 + Packet_GetHFAType = 159, Packet_GetArgNext = 29, Retired2 = 30, Packet_GetArgType = 140, //retired as 30 on 2013/07/03 diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp index e3f5ae2764..c5f6d8aac1 100644 --- a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp +++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp @@ -109,7 +109,7 @@ void interceptor_ICJC::getVersionIdentifier(GUID* versionIdentifier /* OUT */) original_ICorJitCompiler->getVersionIdentifier(versionIdentifier); } -unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags) +unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(CORJIT_FLAGS cpuCompileFlags) { return original_ICorJitCompiler->getMaxIntrinsicSIMDVectorLength(cpuCompileFlags); } diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index fb9163629d..1813ed29ff 100644 --- a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1173,7 +1173,7 @@ bool interceptor_ICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd) // in the code are. The native compiler will ensure that these places // have a corresponding break point in native code. // -// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will +// Note that unless CORJIT_FLAG_DEBUG_CODE is specified, this function will // be used only as a hint and the native compiler should not change its // code generation. void interceptor_ICJI::getBoundaries( @@ -1214,7 +1214,7 @@ void interceptor_ICJI::setBoundaries( // under debugging, the JIT needs to keep them live over their // entire scope so that they can be inspected. // -// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will +// Note that unless CORJIT_FLAG_DEBUG_CODE is specified, this function will // be used only as a hint and the native compiler should not change its // code generation. void interceptor_ICJI::getVars( @@ -1388,7 +1388,9 @@ CorInfoType interceptor_ICJI::getHFAType ( ) { mc->cr->AddCall("getHFAType"); - return original_ICorJitInfo->getHFAType(hClass); + CorInfoType temp = original_ICorJitInfo->getHFAType(hClass); + this->mc->recGetHFAType(hClass, temp); + return temp; } /***************************************************************************** diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp index da766cc51d..2e088d438c 100644 --- a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp +++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitcompiler.cpp @@ -55,7 +55,7 @@ void interceptor_ICJC::getVersionIdentifier(GUID* versionIdentifier /* OUT */) original_ICorJitCompiler->getVersionIdentifier(versionIdentifier); } -unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags) +unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(CORJIT_FLAGS cpuCompileFlags) { mcs->AddCall("getMaxIntrinsicSIMDVectorLength"); return original_ICorJitCompiler->getMaxIntrinsicSIMDVectorLength(cpuCompileFlags); diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 77519caa84..448fb1f686 100644 --- a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -961,7 +961,7 @@ bool interceptor_ICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd) // in the code are. The native compiler will ensure that these places // have a corresponding break point in native code. // -// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will +// Note that unless CORJIT_FLAG_DEBUG_CODE is specified, this function will // be used only as a hint and the native compiler should not change its // code generation. void interceptor_ICJI::getBoundaries( @@ -999,7 +999,7 @@ void interceptor_ICJI::setBoundaries( // under debugging, the JIT needs to keep them live over their // entire scope so that they can be inspected. // -// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will +// Note that unless CORJIT_FLAG_DEBUG_CODE is specified, this function will // be used only as a hint and the native compiler should not change its // code generation. void interceptor_ICJI::getVars( diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp index f6fceb2029..ec266e164f 100644 --- a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp +++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitcompiler.cpp @@ -48,7 +48,7 @@ void interceptor_ICJC::getVersionIdentifier(GUID* versionIdentifier /* OUT */) original_ICorJitCompiler->getVersionIdentifier(versionIdentifier); } -unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(DWORD cpuCompileFlags) +unsigned interceptor_ICJC::getMaxIntrinsicSIMDVectorLength(CORJIT_FLAGS cpuCompileFlags) { return original_ICorJitCompiler->getMaxIntrinsicSIMDVectorLength(cpuCompileFlags); } diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index 89b19d8754..4d145f6a41 100644 --- a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -876,7 +876,7 @@ bool interceptor_ICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd) // in the code are. The native compiler will ensure that these places // have a corresponding break point in native code. // -// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will +// Note that unless CORJIT_FLAG_DEBUG_CODE is specified, this function will // be used only as a hint and the native compiler should not change its // code generation. void interceptor_ICJI::getBoundaries( @@ -912,7 +912,7 @@ void interceptor_ICJI::setBoundaries( // under debugging, the JIT needs to keep them live over their // entire scope so that they can be inspected. // -// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will +// Note that unless CORJIT_FLAG_DEBUG_CODE is specified, this function will // be used only as a hint and the native compiler should not change its // code generation. void interceptor_ICJI::getVars( diff --git a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp index 41b0195a6d..b746d3f6f7 100644 --- a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -1018,7 +1018,7 @@ bool MyICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd) // in the code are. The native compiler will ensure that these places // have a corresponding break point in native code. // -// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will +// Note that unless CORJIT_FLAG_DEBUG_CODE is specified, this function will // be used only as a hint and the native compiler should not change its // code generation. void MyICJI::getBoundaries( @@ -1068,7 +1068,7 @@ void MyICJI::setBoundaries( // under debugging, the JIT needs to keep them live over their // entire scope so that they can be inspected. // -// Note that unless CORJIT_FLG_DEBUG_CODE is specified, this function will +// Note that unless CORJIT_FLAG_DEBUG_CODE is specified, this function will // be used only as a hint and the native compiler should not change its // code generation. void MyICJI::getVars( @@ -1192,9 +1192,8 @@ CorInfoType MyICJI::getHFAType ( ) { jitInstance->mc->cr->AddCall("getHFAType"); - LogError("Hit unimplemented getHFAType"); - DebugBreakorAV(75); - return (CorInfoType)0; + CorInfoType value = jitInstance->mc->repGetHFAType(hClass); + return value; } /***************************************************************************** diff --git a/src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp b/src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp index 0a43f02dd9..5cebc97db4 100644 --- a/src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp +++ b/src/ToolBox/superpmi/superpmi/methodstatsemitter.cpp @@ -50,11 +50,11 @@ void MethodStatsEmitter::Emit(int methodNumber, MethodContext *mc, ULONGLONG fir if (mc->dumpMethodMD5HashToBuffer(md5Hash, MD5_HASH_BUFFER_SIZE) != MD5_HASH_BUFFER_SIZE) md5Hash[0] = 0; - charCount += sprintf(rowData + charCount, "%s,", md5Hash); + charCount += sprintf_s(rowData + charCount, _countof(rowData) - charCount, "%s,", md5Hash); } if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'n') != NULL || strchr(statsTypes, 'N') != NULL) { - charCount += sprintf(rowData + charCount, "%d,", methodNumber); + charCount += sprintf_s(rowData + charCount, _countof(rowData) - charCount, "%d,", methodNumber); } if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'i') != NULL || strchr(statsTypes, 'I') != NULL) { @@ -63,7 +63,7 @@ void MethodStatsEmitter::Emit(int methodNumber, MethodContext *mc, ULONGLONG fir unsigned flags = 0; mc->repCompileMethod(&info, &flags); - charCount += sprintf(rowData + charCount, "%d,", info.ILCodeSize); + charCount += sprintf_s(rowData + charCount, _countof(rowData) - charCount, "%d,", info.ILCodeSize); } if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'a') != NULL || strchr(statsTypes, 'A') != NULL) { @@ -76,11 +76,11 @@ void MethodStatsEmitter::Emit(int methodNumber, MethodContext *mc, ULONGLONG fir else codeSize = 0;//this is likely a thin mc - charCount += sprintf(rowData + charCount, "%d,", codeSize); + charCount += sprintf_s(rowData + charCount, _countof(rowData) - charCount, "%d,", codeSize); } if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 't') != NULL || strchr(statsTypes, 'T') != NULL) { - charCount += sprintf(rowData + charCount, "%llu,%llu,", firstTime, secondTime); + charCount += sprintf_s(rowData + charCount, _countof(rowData) - charCount, "%llu,%llu,", firstTime, secondTime); } //get rid of the final ',' and replace it with a '\n' @@ -105,15 +105,15 @@ void MethodStatsEmitter::SetStatsTypes(char *types) DWORD bytesWritten = 0; if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'h') != NULL || strchr(statsTypes, 'H') != NULL) - charCount += sprintf(rowHeader + charCount, "HASH,"); + charCount += sprintf_s(rowHeader + charCount, _countof(rowHeader) - charCount, "HASH,"); if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'n') != NULL || strchr(statsTypes, 'N') != NULL) - charCount += sprintf(rowHeader + charCount, "METHOD_NUMBER,"); + charCount += sprintf_s(rowHeader + charCount, _countof(rowHeader) - charCount, "METHOD_NUMBER,"); if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'i') != NULL || strchr(statsTypes, 'I') != NULL) - charCount += sprintf(rowHeader + charCount, "IL_CODE_SIZE,"); + charCount += sprintf_s(rowHeader + charCount, _countof(rowHeader) - charCount, "IL_CODE_SIZE,"); if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 'a') != NULL || strchr(statsTypes, 'A') != NULL) - charCount += sprintf(rowHeader + charCount, "ASM_CODE_SIZE,"); + charCount += sprintf_s(rowHeader + charCount, _countof(rowHeader) - charCount, "ASM_CODE_SIZE,"); if (strchr(statsTypes, '*') != NULL || strchr(statsTypes, 't') != NULL || strchr(statsTypes, 'T') != NULL) - charCount += sprintf(rowHeader + charCount, "Time1,Time2,"); + charCount += sprintf_s(rowHeader + charCount, _countof(rowHeader) - charCount, "Time1,Time2,"); //get rid of the final ',' and replace it with a '\n' rowHeader[charCount - 1] = '\n'; @@ -123,4 +123,4 @@ void MethodStatsEmitter::SetStatsTypes(char *types) LogError("Failed to write row header '%s'. GetLastError()=%u", rowHeader, GetLastError()); } } -}
\ No newline at end of file +} diff --git a/src/ToolBox/superpmi/superpmi/neardiffer.cpp b/src/ToolBox/superpmi/superpmi/neardiffer.cpp index 5b2e3b1b57..3f2c4db3b8 100644 --- a/src/ToolBox/superpmi/superpmi/neardiffer.cpp +++ b/src/ToolBox/superpmi/superpmi/neardiffer.cpp @@ -154,7 +154,7 @@ void NearDiffer::DumpCodeBlock(unsigned char *block, ULONG blocksize, void *orig const size_t minInstrBytes = 7; size_t instrBytes = max(instrSize, minInstrBytes); - size_t buffSize = _snprintf(nullptr, 0, "%p %s\n", (void*)((size_t)originalAddr+offset), instrMnemonic) + 3 * instrBytes + 1; + size_t buffSize = sizeof("%p %s\n") + 10 + count + 3 * instrBytes + 1; char *buff = new char[buffSize]; int written = 0; written += sprintf_s(buff, buffSize, "%p ", (void*)((size_t)originalAddr+offset)); diff --git a/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp b/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp index 8c5232315e..301db3cfe9 100644 --- a/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp +++ b/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp @@ -138,7 +138,7 @@ bool WriteArrayToMCL(char *mclFilename, int *arr, int count) DWORD charCount = 0; DWORD bytesWritten = 0; - charCount = sprintf(strMethodIndex, "%d\r\n", arr[i]); + charCount = sprintf_s(strMethodIndex, sizeof(strMethodIndex), "%d\r\n", arr[i]); if (!WriteFile(hMCLFile, strMethodIndex, charCount, &bytesWritten, nullptr) || (bytesWritten != charCount)) { @@ -232,7 +232,7 @@ void ProcessChildStdOut(const CommandLine::Options& o, char *stdoutFilename, int if (o.applyDiff) { int temp1 = 0, temp2 = 0, temp3 = 0, temp4 = 0; - int converted = sscanf(buff, g_AsmDiffsSummaryFormatString, &temp1, &temp2, &temp3, &temp4); + int converted = sscanf_s(buff, g_AsmDiffsSummaryFormatString, &temp1, &temp2, &temp3, &temp4); if (converted != 4) { LogError("Couldn't parse status message: \"%s\"", buff); @@ -248,7 +248,7 @@ void ProcessChildStdOut(const CommandLine::Options& o, char *stdoutFilename, int else { int temp1 = 0, temp2 = 0, temp3 = 0; - int converted = sscanf(buff, g_SummaryFormatString, &temp1, &temp2, &temp3); + int converted = sscanf_s(buff, g_SummaryFormatString, &temp1, &temp2, &temp3); if (converted != 3) { LogError("Couldn't parse status message: \"%s\"", buff); diff --git a/src/ToolBox/superpmi/superpmi/superpmi.cpp b/src/ToolBox/superpmi/superpmi/superpmi.cpp index ce352070f8..980792d4a9 100644 --- a/src/ToolBox/superpmi/superpmi/superpmi.cpp +++ b/src/ToolBox/superpmi/superpmi/superpmi.cpp @@ -45,6 +45,8 @@ void SetSuperPmiTargetArchitecture(const char* targetArchitecture) } #elif defined(_TARGET_X86_) SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_X86; +#elif defined(_TARGET_ARM_) + SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_ARM; #endif } diff --git a/src/ToolBox/superpmi/superpmi/superpmi.h b/src/ToolBox/superpmi/superpmi/superpmi.h index d5b7bdaa2b..ce535994a0 100644 --- a/src/ToolBox/superpmi/superpmi/superpmi.h +++ b/src/ToolBox/superpmi/superpmi/superpmi.h @@ -12,7 +12,8 @@ enum SPMI_TARGET_ARCHITECTURE { SPMI_TARGET_ARCHITECTURE_X86, SPMI_TARGET_ARCHITECTURE_AMD64, - SPMI_TARGET_ARCHITECTURE_ARM64 + SPMI_TARGET_ARCHITECTURE_ARM64, + SPMI_TARGET_ARCHITECTURE_ARM }; extern SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture; |