-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
394 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,377 @@ | ||
using System.Diagnostics; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace Ultra.Sampler; | ||
|
||
public class Sampler | ||
{ | ||
public unsafe static void Sample() | ||
{ | ||
var result = Native.task_for_pid(Native.mach_task_self(), Process.GetCurrentProcess().Id, out int task); | ||
if (result != 0) | ||
{ | ||
Console.WriteLine($"task_for_pid failed with error code {result}"); | ||
return; | ||
} | ||
|
||
uint* taskList; | ||
|
||
result = Native.task_threads(task, &taskList, out uint taskCount); | ||
if (result != 0) | ||
{ | ||
Console.WriteLine($"task_threads failed with error code {result}"); | ||
return; | ||
} | ||
|
||
result = Native.pthread_threadid_np(0, out var currentThreadId); | ||
Console.WriteLine($"Current Thread ID: {currentThreadId}"); | ||
|
||
byte* nameBuffer = stackalloc byte[256]; | ||
|
||
for (var i = 0; i < taskCount; i++) | ||
{ | ||
var threadPort = taskList[i]; | ||
int threadInfoCount = Native.THREAD_IDENTIFIER_INFO_COUNT; | ||
result = Native.thread_info(threadPort, Native.THREAD_IDENTIFIER_INFO, out var threadInfo, ref threadInfoCount); | ||
if (result != 0) | ||
{ | ||
Console.WriteLine($"thread_info failed with error code {result}"); | ||
return; | ||
} | ||
|
||
var thread_t = Native.pthread_from_mach_thread_np(threadPort); | ||
|
||
if (thread_t != 0) | ||
{ | ||
result = Native.pthread_getname_np(thread_t, nameBuffer, 256); | ||
if (result != 0) | ||
{ | ||
Console.WriteLine($"pthread_getname_np failed with error code {result}"); | ||
return; | ||
} | ||
Console.WriteLine($"Thread ID: {threadInfo.thread_id} Name: {Marshal.PtrToStringAnsi((IntPtr)nameBuffer)}"); | ||
} | ||
else | ||
{ | ||
Console.WriteLine($"Thread ID: {threadInfo.thread_id}"); | ||
} | ||
|
||
if (threadInfo.thread_id == currentThreadId) continue; | ||
|
||
result = Native.thread_suspend(taskList[i]); | ||
if (result != 0) | ||
{ | ||
Console.WriteLine($"thread_suspend failed with error code {result}"); | ||
return; | ||
} | ||
|
||
arm_thread_state64_t armThreadState = new arm_thread_state64_t(); | ||
int armThreadStateCount = Native.ARM_THREAD_STATE64_COUNT; | ||
|
||
result = Native.thread_get_state(threadPort, Native.ARM_THREAD_STATE64, (nint)(void*)&armThreadState, ref armThreadStateCount); | ||
if (result != 0) | ||
{ | ||
Console.WriteLine($"thread_get_state failed with error code {result}"); | ||
return; | ||
} | ||
|
||
//Console.WriteLine($"sp: 0x{armThreadState.__sp:X8}, fp: 0x{armThreadState.__fp:X8}, lr: 0x{armThreadState.__lr:X8}"); | ||
WalkCallStack(armThreadState.__sp, armThreadState.__fp, armThreadState.__lr); | ||
|
||
|
||
result = Native.thread_resume(threadPort); | ||
if (result != 0) | ||
{ | ||
Console.WriteLine($"thread_resume failed with error code {result}"); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
private static unsafe void WalkCallStack(ulong sp, ulong fp, ulong lr) | ||
{ | ||
while (fp != 0) | ||
{ | ||
// TODO: log lr | ||
sp = fp + 16; | ||
lr = *(ulong*)(fp + 8); | ||
fp = *(ulong*)fp; | ||
} | ||
} | ||
} | ||
|
||
internal static class Native | ||
{ | ||
internal const int EPERM = 1; | ||
|
||
internal const int KERN_INVALID_ADDRESS = 1; | ||
|
||
internal const int PROT_READ = 0x01; | ||
|
||
internal const int PT_ATTACH = 10; // TODO: deprecated | ||
internal const int PT_DETACH = 11; | ||
|
||
internal const int TASK_DYLD_INFO = 17; | ||
internal const int THREAD_IDENTIFIER_INFO = 4; | ||
internal const int x86_THREAD_STATE64 = 4; | ||
internal const int ARM_THREAD_STATE64 = 6; | ||
internal const int VM_REGION_BASIC_INFO_64 = 9; | ||
|
||
internal static readonly unsafe int TASK_DYLD_INFO_COUNT = sizeof(task_dyld_info) / sizeof(uint); | ||
|
||
internal static readonly unsafe int | ||
THREAD_IDENTIFIER_INFO_COUNT = sizeof(thread_identifier_info) / sizeof(uint); | ||
|
||
internal static readonly unsafe int x86_THREAD_STATE64_COUNT = sizeof(x86_thread_state64_t) / sizeof(uint); | ||
internal static readonly unsafe int ARM_THREAD_STATE64_COUNT = sizeof(arm_thread_state64_t) / sizeof(uint); | ||
|
||
internal static readonly unsafe int VM_REGION_BASIC_INFO_COUNT_64 = | ||
sizeof(vm_region_basic_info_64) / sizeof(int); | ||
|
||
private const string LibSystem = "libSystem.dylib"; | ||
|
||
[DllImport(LibSystem, SetLastError = true)] | ||
internal static extern int kill(int pid, int sig); | ||
|
||
[DllImport(LibSystem)] | ||
public static extern int pthread_threadid_np(nint thread, out ulong threadId); | ||
|
||
[DllImport(LibSystem)] | ||
public static extern unsafe int pthread_getname_np(nint thread, byte* name, nint len); | ||
|
||
[DllImport(LibSystem)] | ||
public static extern int thread_suspend(uint target_act); | ||
|
||
[DllImport(LibSystem)] | ||
public static extern int thread_resume(uint target_act); | ||
|
||
[DllImport(LibSystem)] | ||
public static extern nint pthread_from_mach_thread_np(uint target_act); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern int mach_task_self(); | ||
|
||
[DllImport(LibSystem, SetLastError = true)] | ||
internal static extern int ptrace(int request, int pid, IntPtr addr = default, int data = default); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern int task_for_pid(int parent, int pid, out int task); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern int task_info(int target_task, uint flavor, out /*int*/task_dyld_info task_info, | ||
ref /*uint*/int task_info_count); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern unsafe int task_threads(int target_task, uint** act_list, out uint act_list_count); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern int thread_info(uint target_act, uint flavor, | ||
out /*int*/thread_identifier_info thread_info, ref /*uint*/int thread_info_count); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern int thread_get_state(uint target_act, int flavor, /*uint**/nint old_state, ref /*uint*/int old_state_count); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern unsafe int vm_read_overwrite(int target_task, /*UIntPtr*/ulong address, /*UIntPtr*/ | ||
long size, /*UIntPtr*/void* data, out /*UIntPtr*/long data_size); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern int mach_vm_region(int target_task, ref /*UIntPtr*/ulong address, | ||
out /*UIntPtr*/ulong size, int flavor, out /*int*/vm_region_basic_info_64 info, ref /*uint*/int info_count, | ||
out int object_name); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern int mach_vm_deallocate(int target_task, /*UIntPtr*/ulong address, /*UIntPtr*/ulong size); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern int mach_port_deallocate( /*uint*/ int task, uint name); | ||
|
||
[DllImport(LibSystem)] | ||
internal static extern int waitpid(int pid, IntPtr status, int options); | ||
|
||
internal readonly struct dyld_all_image_infos | ||
{ | ||
internal readonly uint version; | ||
internal readonly uint infoArrayCount; | ||
internal readonly ulong infoArray; | ||
|
||
// We don't need the rest of this struct so we do not define the rest of the fields. | ||
} | ||
|
||
internal readonly struct dyld_image_info | ||
{ | ||
internal readonly ulong imageLoadAddress; | ||
internal readonly ulong imageFilePath; | ||
internal readonly ulong imageFileModDate; | ||
} | ||
|
||
internal readonly struct task_dyld_info | ||
{ | ||
internal readonly ulong all_image_info_addr; | ||
internal readonly ulong all_image_info_size; | ||
internal readonly int all_image_info_format; | ||
} | ||
|
||
internal readonly struct thread_identifier_info | ||
{ | ||
internal readonly ulong thread_id; | ||
internal readonly ulong thread_handle; | ||
internal readonly ulong dispatch_qaddr; | ||
} | ||
|
||
internal readonly struct vm_region_basic_info_64 | ||
{ | ||
internal readonly int protection; | ||
internal readonly int max_protection; | ||
internal readonly uint inheritance; | ||
internal readonly uint shared; | ||
internal readonly uint reserved; | ||
internal readonly ulong offset; | ||
internal readonly int behavior; | ||
internal readonly ushort user_wired_count; | ||
} | ||
} | ||
|
||
[StructLayout(LayoutKind.Explicit)] | ||
internal struct thread_state_t | ||
{ | ||
[FieldOffset(0)] public x86_thread_state64_t x64; | ||
|
||
[FieldOffset(0)] public arm_thread_state64_t arm; | ||
} | ||
|
||
|
||
[StructLayout(LayoutKind.Sequential, Pack = 1)] | ||
internal struct arm_thread_state64_t | ||
{ | ||
public unsafe fixed ulong __x[29]; | ||
public ulong __fp; | ||
public ulong __lr; | ||
public ulong __sp; | ||
public ulong __pc; | ||
public uint __cpsr; | ||
public uint __pad; // or __opaque_flags when ptrauth is enabled | ||
|
||
/*public unsafe bool CopyContext(Span<byte> context) | ||
{ | ||
if (context.Length < Arm64Context.Size) | ||
return false; | ||
ref Arm64Context contextRef = ref Unsafe.As<byte, Arm64Context>(ref MemoryMarshal.GetReference(context)); | ||
contextRef.ContextFlags = Arm64Context.ContextControl | Arm64Context.ContextInteger; | ||
contextRef.Cpsr = __cpsr; | ||
contextRef.X0 = __x[0]; | ||
contextRef.X1 = __x[1]; | ||
contextRef.X2 = __x[2]; | ||
contextRef.X3 = __x[3]; | ||
contextRef.X4 = __x[4]; | ||
contextRef.X5 = __x[5]; | ||
contextRef.X6 = __x[6]; | ||
contextRef.X7 = __x[7]; | ||
contextRef.X8 = __x[8]; | ||
contextRef.X9 = __x[9]; | ||
contextRef.X10 = __x[10]; | ||
contextRef.X11 = __x[11]; | ||
contextRef.X12 = __x[12]; | ||
contextRef.X13 = __x[13]; | ||
contextRef.X14 = __x[14]; | ||
contextRef.X15 = __x[15]; | ||
contextRef.X16 = __x[16]; | ||
contextRef.X17 = __x[17]; | ||
contextRef.X18 = __x[18]; | ||
contextRef.X19 = __x[19]; | ||
contextRef.X20 = __x[20]; | ||
contextRef.X21 = __x[21]; | ||
contextRef.X22 = __x[22]; | ||
contextRef.X23 = __x[23]; | ||
contextRef.X24 = __x[24]; | ||
contextRef.X25 = __x[25]; | ||
contextRef.X26 = __x[26]; | ||
contextRef.X27 = __x[27]; | ||
contextRef.X28 = __x[28]; | ||
// no ptrauth | ||
contextRef.Fp = __fp; | ||
contextRef.Lr = __lr; | ||
contextRef.Sp = __sp; | ||
contextRef.Pc = __pc; | ||
return true; | ||
}*/ | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential, Pack = 1)] | ||
internal readonly struct x86_thread_state64_t | ||
{ | ||
public readonly ulong __rax; | ||
public readonly ulong __rbx; | ||
public readonly ulong __rcx; | ||
public readonly ulong __rdx; | ||
public readonly ulong __rdi; | ||
public readonly ulong __rsi; | ||
public readonly ulong __rbp; | ||
public readonly ulong __rsp; | ||
public readonly ulong __r8; | ||
public readonly ulong __r9; | ||
public readonly ulong __r10; | ||
public readonly ulong __r11; | ||
public readonly ulong __r12; | ||
public readonly ulong __r13; | ||
public readonly ulong __r14; | ||
public readonly ulong __r15; | ||
public readonly ulong __rip; | ||
public readonly ulong __rflags; | ||
public readonly ulong __cs; | ||
public readonly ulong __fs; | ||
public readonly ulong __gs; | ||
|
||
/*public bool CopyContext(Span<byte> context) | ||
{ | ||
if (context.Length < AMD64Context.Size) | ||
return false; | ||
ref AMD64Context contextRef = ref Unsafe.As<byte, AMD64Context>(ref MemoryMarshal.GetReference(context)); | ||
contextRef.ContextFlags = | ||
AMD64Context.ContextControl | AMD64Context.ContextInteger | AMD64Context.ContextSegments; | ||
contextRef.Rax = __rax; | ||
contextRef.Rbx = __rbx; | ||
contextRef.Rcx = __rcx; | ||
contextRef.Rdx = __rdx; | ||
contextRef.Rdi = __rdi; | ||
contextRef.Rsi = __rsi; | ||
contextRef.Rbp = __rbp; | ||
contextRef.Rsp = __rsp; | ||
contextRef.R8 = __r8; | ||
contextRef.R9 = __r9; | ||
contextRef.R10 = __r10; | ||
contextRef.R11 = __r11; | ||
contextRef.R12 = __r12; | ||
contextRef.R13 = __r13; | ||
contextRef.R14 = __r14; | ||
contextRef.R15 = __r15; | ||
contextRef.Rip = __rip; | ||
contextRef.EFlags = (int) __rflags; | ||
contextRef.Cs = (ushort) __cs; | ||
contextRef.Ss = 0; | ||
contextRef.Ds = 0; | ||
contextRef.Es = 0; | ||
contextRef.Fs = (ushort) __fs; | ||
contextRef.Gs = (ushort) __gs; | ||
return true; | ||
} | ||
*/ | ||
} | ||
|
||
internal struct MemoryRegion //: IRegion | ||
{ | ||
public ulong BeginAddress { get; set; } | ||
public ulong EndAddress { get; set; } | ||
|
||
public int Permission { get; set; } | ||
|
||
public bool IsReadable => (Permission & Native.PROT_READ) != 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
<IsPackable>false</IsPackable> | ||
</PropertyGroup> | ||
|
||
</Project> |
Oops, something went wrong.