foreach (Process process in Process.All)
Console.WriteLine($"{process.Id}:{process.SystemId} {process.ExecutableName}");
Console.WriteLine($"Current process: {Process.Current.SystemId}");
foreach (Thread thread in Thread.All)
Console.WriteLine($"{thread.Id}:{thread.SystemId}");
Console.WriteLine($"Current thread: {Thread.Current.Id}:{Thread.Current.SystemId}");
foreach (Module module in Module.All)
Console.WriteLine($"0x{module.Offset:X} {module.Name}");
foreach (StackFrame frame in Thread.Current.StackTrace.Frames)
Console.WriteLine(frame.FunctionName);
Thread.All => Process.Current.Threads;
Module.All => Process.Current.Modules;
Thread.Current => Process.Current.CurrentThread;
StackTrace.Current => Thread.Current.StackTrace;
StackFrame.Current => StackTrace.Current.CurrentFrame;
VariableCollection locals = StackTrace.Current.Frames[0].Locals;
foreach (Variable l in locals)
Console.WriteLine(l.GetName());
dynamic myVar = locals["myVar"];
Console.WriteLine(myVar);
Variable globalVariable = Process.Current.GetGlobal("mymodule!globalVariable");
Variable staticClassVariable = Process.Current.GetGlobal("mymodule!Class::staticVariable");
You can also access through Modules in interactive mode:
dynamic globalVariable = Modules.mymodule.globalVariable;
dynamic staticClassVariable = Modules.mymodule.GetVariable("Class::staticVariable");
Variable variable = Process.Current.GetGlobal("mymodule!globalVariable");
Variable field = variable.GetField("field");
Or if you use dynamic:
dynamic variable = Process.Current.GetGlobal("mymodule!globalVariable");
dynamic field = variable.field;
CodeType type = myVariable.GetCodeType();
Variable variable = Process.Current.GetGlobal("mymodule!globalVariable");
int intValue = (int)variable;
ulong ulongValue = (ulong)variable;
Variable variable = Process.Current.GetGlobal("mymodule!globalVariable");
std.wstring s = variable.CastAs<std.wstring>();
Variable p = Process.Current.GetGlobal("mymodule!voidPointerVariable");
Console.WriteLine(p); // This will print address of void* pointer
Variable s = p.CastAs("wchar_t*");
Console.WriteLine(s); // This will print string value pointed by s
Getting array element of variable array
Variable a = Process.Current.GetGlobal("mymodule!intArrayVariable");
int i = (int)a.GetArrayElement(0);
Or by using dynamic:
dynamic a = Modules.mymodule.intArrayVariable;
int i = (int)a[0];
Most of the things will work automatically the same way as for native applications. When debugger (for example DbgEng.dll) doesn't know how to get CLR stack trace correctly, you can find ClrThread and get ClrStackTrace. There are also CLR specific classes that help with debugging CLR applications. Debugging CLR applications is done using ClrMD.
Note: ClrMD at this moment supports Windows PDBs only. If you want to debug CLR application that has Portable PDBs, you can use Pdb2Pdb tool to convert from Portable to Windows PDBs. Another option is to add <DebugType>Full</DebugType>
to your CLR project file.
foreach (Runtime runtime in Process.Current.ClrRuntimes)
Console.WriteLine(runtime.Version);
foreach (AppDomain appDomain in runtime.AppDomains)
Console.WriteLine($"{appDomain.Id}: {appDomain.Name}");
Console.WriteLine($"{runtime.SystemDomain.Id}: {runtime.SystemDomain.Name}");
Console.WriteLine($"{runtime.SharedDomain.Id}: {runtime.SharedDomain.Name}");
foreach (Module module in appDomain.Modules)
Console.WriteLine($"0x{module.Address:X} {module.Name}");
Heap heap = runtime.Heap;
foreach (Variable variable in heap.EnumerateObects())
Console.WriteLine($"({variable.GetCodeType()}) {variable.GetPointerAddress()}");
for (int generation = 0; generation <= 3; generation++)
Console.WriteLine("{generation}: {heap.GetSizeByGeneration(generation)}");
foreach (ClrThread thread in runtime.Threads)
Console.WriteLine("{0}", thread.Id);
ClrThread thread = Thread.Current.FindClrThread();
if (thread != null)
foreach (StackFrame frame in thread.ClrCallStack.Frames)
Console.WriteLine(frame.FunctionNameWithoutModule);
ClrThread thread = Thread.Current.FindClrThread();
ClrException exception = thread?.LastThrownException;
Console.WriteLine(exception?.Message);
ClrThread thread = Thread.Current.FindClrThread();
foreach (Variable variable in thread.EnumerateStackObjects())
Console.WriteLine("({variable.GetCodeType()}) {variable.GetPointerAddress()}");
It works the same way as for native debugging with an addition that if debugger doesn't recognize CLR call stack correctly, you can use ClrThread.ClrStackTrace to get correct one.
Thread thread = Thread.Current;
foreach (StackFrame frame in thread.StackTrace.Frames)
{
Console.WriteLine(frame.FunctionName);
foreach (Variable arg in frame.Arguments)
Console.WriteLine("argument '{arg.GetName()}': ({arg.GetCodeType()}) {arg.GetPointerAddress()}");
foreach (Variable local in frame.Locals)
Console.WriteLine("argument '{local.GetName()}': ({local.GetCodeType()}) {local.GetPointerAddress()}");
}
You can use the same mechanisms used in native debugging to read CLR static variables. If your application has multiple AppDomains, you can get static variable per AppDomain.
Module myModule = Modules.All.Single(m => m.Name = "MyModule");
CodeType codeType = CodeType.Create("MyType", myModule);
Variable sf = codeType.GetStaticField("MyStaticField"); // This will use current AppDomain for CLR applications
Variable sf2 = codeType.GetClrStaticField("MyStaticField", Process.Current.ClrRuntimes[0].AppDomains[1]);
Variable sf3 = myModule.GetVariable("MyType.MyStaticField");
Variable sf4 = Process.Current.GetGlobal("MyModule!MyType.MyStaticField");
You will walk through field, values the same way you do as with native Variables:
// Getting variable fields
Variable variable = Process.Current.GetGlobal("mymodule!MyType.singleton");
Variable field = variable.GetField("field");
// CodeType
CodeType type = myVariable.GetCodeType();
// Arrays
Variable a = Process.Current.GetGlobal("mymodule!MyType.intArrayVariable");
int i = (int)a.GetArrayElement(0);
// Strings
Variable s = Process.Current.GetGlobal("mymodule!MyType.stringVariable");
ClrString str = new ClrString(s);
Console.WriteLine(str.Text);
// Exceptions
Variable e = Process.Current.GetGlobal("mymodule!MyType.exceptionVariable");
ClrException exception = new ClrException(e);
Console.WriteLine(exception.Message);
Previous examples show that scripts don't have namespace and class definition inside. Here you will learn what else can be used in scripts.
using
C# statement must be located at the top of the script. It is supposed to be used as in any other C# file but its scope is for entire script file:
using System;
using System.Linq;
Having multiple script files is preferable as script files should share common code. For example, having this helper script file, helper.csx:
using System;
void HelpMe(string text)
{
Console.WriteLine(text);
}
we can have sample script that is referencing that helper file, script.csx:
#load "helper.csx"
Console.Error.WriteLine("This is sample error");
HelpMe("It works!");
By now, you have huge collection of common code and compiling scripts is not that fast any more, you should create .NET library (dll) and just reference it from the script:
#r "SharpDebug.CommonUserTypes.dll"
using std = SharpDebug.CommonUserTypes.NativeTypes.std;
var variable = Process.Current.GetGlobal("mymodule!globalVariable");
var s = variable.CastAs<std.wstring>();
Please note that CommonUserTypes library is already automatically added as a reference to all scripts.