diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2016-12-27 16:46:08 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2016-12-27 16:46:08 +0900 |
commit | db20f3f1bb8595633a7e16c8900fd401a453a6b5 (patch) | |
tree | e5435159cd1bf0519276363a6fe1663d1721bed3 /src/ToolBox/SOS | |
parent | 4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (diff) | |
download | coreclr-db20f3f1bb8595633a7e16c8900fd401a453a6b5.tar.gz coreclr-db20f3f1bb8595633a7e16c8900fd401a453a6b5.tar.bz2 coreclr-db20f3f1bb8595633a7e16c8900fd401a453a6b5.zip |
Imported Upstream version 1.0.0.9127upstream/1.0.0.9127
Diffstat (limited to 'src/ToolBox/SOS')
56 files changed, 2257 insertions, 299 deletions
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) |