summaryrefslogtreecommitdiff
path: root/src/ToolBox/SOS
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-12-27 16:46:08 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-12-27 16:46:08 +0900
commitdb20f3f1bb8595633a7e16c8900fd401a453a6b5 (patch)
treee5435159cd1bf0519276363a6fe1663d1721bed3 /src/ToolBox/SOS
parent4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (diff)
downloadcoreclr-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')
-rw-r--r--src/ToolBox/SOS/NETCore/SymbolReader.cs97
-rw-r--r--src/ToolBox/SOS/NETCore/project.json2
-rw-r--r--src/ToolBox/SOS/Strike/CMakeLists.txt11
-rw-r--r--src/ToolBox/SOS/Strike/ExpressionNode.cpp20
-rw-r--r--src/ToolBox/SOS/Strike/ExpressionNode.h14
-rw-r--r--src/ToolBox/SOS/Strike/datatarget.cpp4
-rw-r--r--src/ToolBox/SOS/Strike/sildasm.cpp2
-rw-r--r--src/ToolBox/SOS/Strike/sos_stacktrace.h8
-rw-r--r--src/ToolBox/SOS/Strike/sosdocsunix.txt1
-rw-r--r--src/ToolBox/SOS/Strike/stressLogDump.cpp2
-rw-r--r--src/ToolBox/SOS/Strike/strike.cpp65
-rw-r--r--src/ToolBox/SOS/Strike/util.cpp66
-rw-r--r--src/ToolBox/SOS/Strike/util.h16
-rw-r--r--src/ToolBox/SOS/Strike/vm.cpp10
-rw-r--r--src/ToolBox/SOS/lldbplugin/CMakeLists.txt7
-rw-r--r--src/ToolBox/SOS/lldbplugin/services.cpp2
-rw-r--r--src/ToolBox/SOS/tests/OnCrash.do2
-rw-r--r--src/ToolBox/SOS/tests/README.md43
-rw-r--r--src/ToolBox/SOS/tests/Test.cs94
-rw-r--r--src/ToolBox/SOS/tests/dumpil.py26
-rw-r--r--src/ToolBox/SOS/tests/dumpmodule.py26
-rw-r--r--src/ToolBox/SOS/tests/runprocess.py34
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py62
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py62
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py45
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py43
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py43
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py48
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_clrstack.py36
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_clrthreads.py31
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_clru.py46
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_dso.py31
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_dumpclass.py68
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_dumpheap.py31
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_dumpil.py38
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_dumplog.py31
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_dumpmd.py46
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_dumpmodule.py46
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_dumpmt.py68
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_dumpobj.py69
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_dumpstack.py31
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_eeheap.py31
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_eestack.py31
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_gcroot.py61
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_histclear.py31
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_histinit.py31
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_histobj.py61
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_histobjfind.py61
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_histroot.py61
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_ip2md.py53
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_name2ee.py46
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_pe.py28
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_sos.py31
-rw-r--r--src/ToolBox/SOS/tests/t_cmd_soshelp.py31
-rw-r--r--src/ToolBox/SOS/tests/test_libsosplugin.py338
-rw-r--r--src/ToolBox/SOS/tests/testutils.py234
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)