Skip to content

Commit

Permalink
-Fix DebuggerDisplay in various DEBUG_EVENT structs
Browse files Browse the repository at this point in the history
-Fix DebugControl.GetStackTrace/GetStackTraceEx not processing framesSize correctly
  • Loading branch information
lordmilko committed Jun 15, 2023
1 parent bcd8a79 commit 6c694eb
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 38 deletions.
21 changes: 18 additions & 3 deletions ClrDebug/Extensions/Extensions.CorDebug.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public static partial class Extensions
/// If this parameter is null, the new process will have the same current drive and directory as the calling process.</param>
/// <param name="lpStartupInfo">[in] Pointer to a Win32 STARTUPINFOW structure that specifies the window station, desktop, standard handles, and appearance of the main window for the launched process.</param>
/// <param name="debuggingFlags">[in] A value of the <see cref="CorDebugCreateProcessFlags"/> enumeration that specifies the debugging options.</param>
/// <param name="closeHandle">A method capable of freeing the <see cref="PROCESS_INFORMATION.hProcess"/> and <see cref="PROCESS_INFORMATION.hThread"/> handles created for the process.<para/>
/// On non-Windows platforms, this value must be provided. On Windows, if no value is provided, the Win32 function CloseHandle() will automatically be used.</param>
/// <returns>[out] A pointer to the address of a <see cref="ICorDebugProcess"/> object that represents the process.</returns>
public static CorDebugProcess CreateProcess(
this CorDebug corDebug,
Expand All @@ -58,7 +60,8 @@ public static CorDebugProcess CreateProcess(
IntPtr? lpEnvironment = null,
string lpCurrentDirectory = null,
STARTUPINFO? lpStartupInfo = null,
CorDebugCreateProcessFlags debuggingFlags = CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS)
CorDebugCreateProcessFlags debuggingFlags = CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS,
Action<IntPtr> closeHandle = null)
{
var processAttribs = lpProcessAttributes ?? new SECURITY_ATTRIBUTES();
var threadAttribs = lpThreadAttributes ?? new SECURITY_ATTRIBUTES();
Expand Down Expand Up @@ -103,11 +106,23 @@ out process
}
else
{
#if NETSTANDARD
closeHandle = closeHandle ?? (h => NativeMethods.CloseHandle(h));
#else
if (closeHandle == null)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
closeHandle = h => NativeMethods.CloseHandle(h);
else
throw new ArgumentException($"On non-Windows platforms, parameter '{nameof(closeHandle)}' must be specified.", nameof(closeHandle));
}
#endif

if (pi.hProcess != IntPtr.Zero)
NativeMethods.CloseHandle(pi.hProcess);
closeHandle(pi.hProcess);

if (pi.hThread != IntPtr.Zero)
NativeMethods.CloseHandle(pi.hThread);
closeHandle(pi.hThread);
}

return process;
Expand Down
42 changes: 18 additions & 24 deletions ClrDebug/Managed/DbgEng/DebugControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2149,14 +2149,15 @@ public HRESULT TryGetNearInstruction(long offset, int delta, out long nearOffset
/// <param name="stackOffset">[in] Specifies the location of the current stack. If StackOffset is set to zero, the current stack pointer is used instead.</param>
/// <param name="instructionOffset">[in] Specifies the location of the instruction of interest for the function that is represented by the stack frame at the top of the stack.<para/>
/// If InstructionOffset is set to zero, the current instruction is used instead.</param>
/// <param name="frameSize">[out, optional] Receives the number of frames that were placed in the array Frames. If FramesFilled is NULL, this information is not returned.</param>
/// <returns>[out] Receives the stack frames. The number of elements this array holds is FrameSize.</returns>
/// <remarks>
/// The stack trace returned to Frames can be printed using <see cref="OutputStackTrace"/>.
/// </remarks>
public DEBUG_STACK_FRAME[] GetStackTrace(long frameOffset, long stackOffset, long instructionOffset)
public DEBUG_STACK_FRAME[] GetStackTrace(long frameOffset, long stackOffset, long instructionOffset, int frameSize)
{
DEBUG_STACK_FRAME[] frames;
TryGetStackTrace(frameOffset, stackOffset, instructionOffset, out frames).ThrowDbgEngNotOK();
TryGetStackTrace(frameOffset, stackOffset, instructionOffset, frameSize, out frames).ThrowDbgEngNotOK();

return frames;
}
Expand All @@ -2169,11 +2170,12 @@ public DEBUG_STACK_FRAME[] GetStackTrace(long frameOffset, long stackOffset, lon
/// <param name="instructionOffset">[in] Specifies the location of the instruction of interest for the function that is represented by the stack frame at the top of the stack.<para/>
/// If InstructionOffset is set to zero, the current instruction is used instead.</param>
/// <param name="frames">[out] Receives the stack frames. The number of elements this array holds is FrameSize.</param>
/// <param name="frameSize">[out, optional] Receives the number of frames that were placed in the array Frames. If FramesFilled is NULL, this information is not returned.</param>
/// <returns>This method may also return other error values. See Return Values for more details.</returns>
/// <remarks>
/// The stack trace returned to Frames can be printed using <see cref="OutputStackTrace"/>.
/// </remarks>
public HRESULT TryGetStackTrace(long frameOffset, long stackOffset, long instructionOffset, out DEBUG_STACK_FRAME[] frames)
public HRESULT TryGetStackTrace(long frameOffset, long stackOffset, long instructionOffset, int frameSize, out DEBUG_STACK_FRAME[] frames)
{
InitDelegate(ref getStackTrace, Vtbl->GetStackTrace);
/*HRESULT GetStackTrace(
Expand All @@ -2183,18 +2185,13 @@ public HRESULT TryGetStackTrace(long frameOffset, long stackOffset, long instruc
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] DEBUG_STACK_FRAME[] Frames,
[In] int FrameSize,
[Out] out int FramesFilled);*/
frames = null;
int frameSize = 0;
frames = new DEBUG_STACK_FRAME[frameSize];
int framesFilled;
HRESULT hr = getStackTrace(Raw, frameOffset, stackOffset, instructionOffset, null, frameSize, out framesFilled);
HRESULT hr = getStackTrace(Raw, frameOffset, stackOffset, instructionOffset, frames, frameSize, out framesFilled);

if (hr != HRESULT.S_FALSE && hr != HRESULT.ERROR_INSUFFICIENT_BUFFER && hr != HRESULT.S_OK)
goto fail;
if (frameSize != framesFilled)
Array.Resize(ref frames, framesFilled);

frameSize = framesFilled;
frames = new DEBUG_STACK_FRAME[frameSize];
hr = getStackTrace(Raw, frameOffset, stackOffset, instructionOffset, frames, frameSize, out framesFilled);
fail:
return hr;
}

Expand Down Expand Up @@ -7671,11 +7668,12 @@ public HRESULT TryResetManagedStatus(DEBUG_MANRESET flags)
/// <param name="stackOffset">[in] Specifies the location of the current stack. If StackOffset is set to zero, the current stack pointer is used instead.</param>
/// <param name="instructionOffset">[in] Specifies the location of the instruction of interest for the function that is represented by the stack frame at the top of the stack.<para/>
/// If InstructionOffset is set to zero, the current instruction is used instead.</param>
/// <param name="framesSize">[out, optional] Receives the number of frames that were placed in the array Frames. If FramesFilled is NULL, this information is not returned.</param>
/// <returns>[out] Receives the stack frames. The number of elements this array holds is FrameSize.</returns>
public DEBUG_STACK_FRAME_EX[] GetStackTraceEx(long frameOffset, long stackOffset, long instructionOffset)
public DEBUG_STACK_FRAME_EX[] GetStackTraceEx(long frameOffset, long stackOffset, long instructionOffset, int framesSize)
{
DEBUG_STACK_FRAME_EX[] frames;
TryGetStackTraceEx(frameOffset, stackOffset, instructionOffset, out frames).ThrowDbgEngNotOK();
TryGetStackTraceEx(frameOffset, stackOffset, instructionOffset, framesSize, out frames).ThrowDbgEngNotOK();

return frames;
}
Expand All @@ -7689,8 +7687,9 @@ public DEBUG_STACK_FRAME_EX[] GetStackTraceEx(long frameOffset, long stackOffset
/// <param name="instructionOffset">[in] Specifies the location of the instruction of interest for the function that is represented by the stack frame at the top of the stack.<para/>
/// If InstructionOffset is set to zero, the current instruction is used instead.</param>
/// <param name="frames">[out] Receives the stack frames. The number of elements this array holds is FrameSize.</param>
/// <param name="framesSize">[out, optional] Receives the number of frames that were placed in the array Frames. If FramesFilled is NULL, this information is not returned.</param>
/// <returns>This method may also return other error values. See Return Values for more details.</returns>
public HRESULT TryGetStackTraceEx(long frameOffset, long stackOffset, long instructionOffset, out DEBUG_STACK_FRAME_EX[] frames)
public HRESULT TryGetStackTraceEx(long frameOffset, long stackOffset, long instructionOffset, int framesSize, out DEBUG_STACK_FRAME_EX[] frames)
{
InitDelegate(ref getStackTraceEx, Vtbl5->GetStackTraceEx);
/*HRESULT GetStackTraceEx(
Expand All @@ -7700,18 +7699,13 @@ public HRESULT TryGetStackTraceEx(long frameOffset, long stackOffset, long instr
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] DEBUG_STACK_FRAME_EX[] Frames,
[In] int FramesSize,
[Out] out int FramesFilled);*/
frames = null;
int framesSize = 0;
frames = new DEBUG_STACK_FRAME_EX[framesSize];
int framesFilled;
HRESULT hr = getStackTraceEx(Raw, frameOffset, stackOffset, instructionOffset, null, framesSize, out framesFilled);
HRESULT hr = getStackTraceEx(Raw, frameOffset, stackOffset, instructionOffset, frames, framesSize, out framesFilled);

if (hr != HRESULT.S_FALSE && hr != HRESULT.ERROR_INSUFFICIENT_BUFFER && hr != HRESULT.S_OK)
goto fail;
if (framesSize != framesFilled)
Array.Resize(ref frames, framesFilled);

framesSize = framesFilled;
frames = new DEBUG_STACK_FRAME_EX[framesSize];
hr = getStackTraceEx(Raw, frameOffset, stackOffset, instructionOffset, frames, framesSize, out framesFilled);
fail:
return hr;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ namespace ClrDebug
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[StructLayout(LayoutKind.Sequential)]
public struct CREATE_PROCESS_DEBUG_INFO
public unsafe struct CREATE_PROCESS_DEBUG_INFO
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal string DebuggerDisplay => $"hFile = {hFile}, hProcess = {hProcess}, hThread = {hThread}, lpBaseOfImage = {lpBaseOfImage}, dwDebugInfoFileOffset = {dwDebugInfoFileOffset}, nDebugInfoSize = {nDebugInfoSize}, lpThreadLocalBase = {lpThreadLocalBase}, lpStartAddress = {lpStartAddress}, lpImageName = {lpImageName}, fUnicode = {fUnicode}";
internal string DebuggerDisplay => $"hFile = {hFile}, hProcess = {hProcess}, hThread = {hThread}, lpBaseOfImage = 0x{((ulong)(void*)lpBaseOfImage):X}, dwDebugInfoFileOffset = {dwDebugInfoFileOffset}, nDebugInfoSize = {nDebugInfoSize}, lpThreadLocalBase = 0x{((ulong)(void*)lpThreadLocalBase):X}, lpStartAddress = 0x{((ulong)(void*)lpStartAddress):X}, lpImageName = 0x{((ulong)(void*)lpImageName):X}, fUnicode = {fUnicode}";

public IntPtr hFile;
public IntPtr hProcess;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ namespace ClrDebug
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[StructLayout(LayoutKind.Sequential)]
public struct CREATE_THREAD_DEBUG_INFO
public unsafe struct CREATE_THREAD_DEBUG_INFO
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal string DebuggerDisplay => $"hThread = {hThread}, lpThreadLocalBase = {lpThreadLocalBase}, lpStartAddress = {lpStartAddress}";
internal string DebuggerDisplay => $"hThread = {hThread}, lpThreadLocalBase = 0x{((ulong)(void*)lpThreadLocalBase):X}, lpStartAddress = 0x{((ulong)(void*)lpStartAddress):X}";

public IntPtr hThread;
public IntPtr lpThreadLocalBase;
Expand Down
4 changes: 2 additions & 2 deletions ClrDebug/Native/Struct/DEBUG_EVENT/LOAD_DLL_DEBUG_INFO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ namespace ClrDebug
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[StructLayout(LayoutKind.Sequential)]
public struct LOAD_DLL_DEBUG_INFO
public unsafe struct LOAD_DLL_DEBUG_INFO
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal string DebuggerDisplay => $"{lpImageName}";
internal string DebuggerDisplay => $"lpBaseOfDll = 0x{((ulong)(void*)lpBaseOfDll):X}";

public IntPtr hFile;
public IntPtr lpBaseOfDll;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ namespace ClrDebug
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[StructLayout(LayoutKind.Sequential)]
public struct OUTPUT_DEBUG_STRING_INFO
public unsafe struct OUTPUT_DEBUG_STRING_INFO
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal string DebuggerDisplay => $"lpDebugStringData = {lpDebugStringData}, fUnicode = {fUnicode}, nDebugStringLength = {nDebugStringLength}";
internal string DebuggerDisplay => $"lpDebugStringData = 0x{((ulong)(void*)lpDebugStringData):X}, fUnicode = {fUnicode}, nDebugStringLength = {nDebugStringLength}";

public IntPtr lpDebugStringData;
public short fUnicode;
Expand Down
4 changes: 2 additions & 2 deletions ClrDebug/Native/Struct/DEBUG_EVENT/UNLOAD_DLL_DEBUG_INFO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ namespace ClrDebug
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[StructLayout(LayoutKind.Sequential)]
public struct UNLOAD_DLL_DEBUG_INFO
public unsafe struct UNLOAD_DLL_DEBUG_INFO
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal string DebuggerDisplay => $"lpBaseOfDll = {lpBaseOfDll}";
internal string DebuggerDisplay => $"lpBaseOfDll = 0x{((ulong)(void*)lpBaseOfDll):X}";

public IntPtr lpBaseOfDll;
}
Expand Down
2 changes: 1 addition & 1 deletion Samples/NetCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ static void Main(string[] args)

/* The disadvantage of using CreateProcessForLaunch is that you can't specify CreateProcessFlags
* like CREATE_NEW_CONSOLE. If the process is allowed to fully start before GetStartupNotificationEvent() is called,
* WaitForSingleObject() will never trigger the event As such it's a good idea to start the process suspended, and only resume
* WaitForSingleObject() will never trigger the event. As such it's a good idea to start the process suspended, and only resume
* it after the notification event is in place */
var process = dbgshim.CreateProcessForLaunch(dbgTargetPath, true);

Expand Down

0 comments on commit 6c694eb

Please sign in to comment.