Skip to content

Commit 132344d

Browse files
committed
Add JIT event signalling and --break arg for attach
1 parent 6b7d3c5 commit 132344d

File tree

3 files changed

+83
-5
lines changed

3 files changed

+83
-5
lines changed

Extensions/dnSpy.Debugger/dnSpy.Debugger/Attach/AppCommandLineArgsHandler.cs

+74-5
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,101 @@ You should have received a copy of the GNU General Public License
2020
using System;
2121
using System.ComponentModel.Composition;
2222
using System.Linq;
23+
using System.Runtime.InteropServices;
2324
using System.Threading;
25+
using System.Threading.Tasks;
2426
using dnSpy.Contracts.App;
27+
using dnSpy.Contracts.Debugger;
2528
using dnSpy.Contracts.Debugger.Attach;
2629

2730
namespace dnSpy.Debugger.Attach {
2831
[Export(typeof(IAppCommandLineArgsHandler))]
2932
sealed class AppCommandLineArgsHandler : IAppCommandLineArgsHandler {
3033
readonly Lazy<AttachableProcessesService> attachableProcessesService;
34+
readonly Lazy<DbgManager> dbgManager;
3135

3236
[ImportingConstructor]
33-
AppCommandLineArgsHandler(Lazy<AttachableProcessesService> attachableProcessesService) =>
37+
AppCommandLineArgsHandler(Lazy<AttachableProcessesService> attachableProcessesService, Lazy<DbgManager> dbgManager) {
3438
this.attachableProcessesService = attachableProcessesService;
39+
this.dbgManager = dbgManager;
40+
}
3541

3642
public double Order => 0;
3743

44+
[DllImport("kernel32.dll")]
45+
private static extern bool SetEvent(IntPtr hEvent);
46+
47+
[DllImport("kernel32.dll")]
48+
private static extern bool CloseHandle(IntPtr hObject);
49+
50+
private async Task HandleEventOnAttach(AttachableProcess process, IntPtr jitDebugStartEventHandle, bool BreakOnStart) {
51+
var mgr = dbgManager.Value;
52+
var tsc = new TaskCompletionSource<object?>();
53+
Func<Task>? SendSignalEvent = jitDebugStartEventHandle != IntPtr.Zero ? async () => {
54+
var hdl = jitDebugStartEventHandle;
55+
jitDebugStartEventHandle = IntPtr.Zero;//we might get called multiple times
56+
if (hdl != IntPtr.Zero) {
57+
await Task.Delay(500); //without the delay we will be 15-40 ticks late on attaching, there is no maximum here other than responsiveness for user. Most of the time without the resume event sent we will not fully attach
58+
SetEvent(hdl);
59+
CloseHandle(hdl);
60+
}
61+
}
62+
: null;
63+
64+
EventHandler<DbgMessageThreadCreatedEventArgs>? threadCreatedHandler = null;
65+
66+
threadCreatedHandler = async (_, e) => {
67+
mgr.MessageThreadCreated -= threadCreatedHandler;
68+
if (BreakOnStart)
69+
e.Pause = true;
70+
tsc.TrySetResult(default);
71+
72+
};
73+
EventHandler? runningDebuggingHandler = null;
74+
runningDebuggingHandler = (_, _) => {
75+
if (!mgr.IsDebugging || mgr.IsRunning != true)
76+
return;
77+
SendSignalEvent?.Invoke();
78+
mgr.IsRunningChanged -= runningDebuggingHandler;
79+
mgr.IsDebuggingChanged -= runningDebuggingHandler;
80+
};
81+
82+
if (jitDebugStartEventHandle != IntPtr.Zero) {
83+
mgr.IsRunningChanged += runningDebuggingHandler;
84+
mgr.IsDebuggingChanged += runningDebuggingHandler;
85+
}
86+
87+
mgr.MessageThreadCreated += threadCreatedHandler; // even if we are not BreakOnStart we will use this to complete the wait event
88+
89+
process.Attach();
90+
91+
/*
92+
We want to do cleanup here for a few just in cases:
93+
- If we don't attach we want to remove the event listeners so we don't randomly pause a future session.
94+
- if the debug manager status events don't go off as expected and we haven't already sent the event handle signal the process is suspended until the signal comes (or we exit) so it acts as a backup to firing that event.
95+
*/
96+
if (await Task.WhenAny(Task.Delay(TimeSpan.FromSeconds(10)), tsc.Task) != tsc.Task) {
97+
SendSignalEvent?.Invoke();
98+
mgr.MessageThreadCreated -= threadCreatedHandler;
99+
mgr.IsRunningChanged -= runningDebuggingHandler;
100+
mgr.IsDebuggingChanged -= runningDebuggingHandler;
101+
}
102+
}
38103
public async void OnNewArgs(IAppCommandLineArgs args) {
104+
AttachableProcess? process = null;
39105
if (args.DebugAttachPid is int pid && pid != 0) {
40106
var processes = await attachableProcessesService.Value.GetAttachableProcessesAsync(null, new[] { pid }, null, CancellationToken.None).ConfigureAwait(false);
41-
var process = processes.FirstOrDefault(p => p.ProcessId == pid);
42-
process?.Attach();
107+
process = processes.FirstOrDefault(p => p.ProcessId == pid);
43108
}
44109
else if (args.DebugAttachProcess is string processName && !string.IsNullOrEmpty(processName)) {
45110
var processes = await attachableProcessesService.Value.GetAttachableProcessesAsync(processName, CancellationToken.None).ConfigureAwait(false);
46-
var process = processes.FirstOrDefault();
47-
process?.Attach();
111+
process = processes.FirstOrDefault();
48112
}
113+
if ((args.DebugBreakOnAttach || args.DebugEvent != 0) && process is not null)
114+
await HandleEventOnAttach(process, new IntPtr(args.DebugEvent), args.DebugBreakOnAttach);
115+
else
116+
process?.Attach();
49117
}
118+
50119
}
51120
}

dnSpy/dnSpy.Contracts.DnSpy/App/IAppCommandLineArgs.cs

+3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ public interface IAppCommandLineArgs {
8181
/// <summary>Attach to this process, unless it's 0</summary>
8282
int DebugAttachPid { get; }
8383

84+
/// <summary>If attaching on startup break right away</summary>
85+
bool DebugBreakOnAttach { get; }
86+
8487
/// <summary>Event handle duplicated into the postmortem debugger process</summary>
8588
uint DebugEvent { get; }
8689

dnSpy/dnSpy/MainApp/AppCommandLineArgs.cs

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ sealed class AppCommandLineArgs : IAppCommandLineArgs {
4646
public string HideToolWindow { get; }
4747
public bool ShowStartupTime { get; }
4848
public int DebugAttachPid { get; }
49+
public bool DebugBreakOnAttach { get; }
4950
public uint DebugEvent { get; }
5051
public ulong JitDebugInfo { get; }
5152
public string DebugAttachProcess { get; }
@@ -76,6 +77,7 @@ public AppCommandLineArgs(string[] args) {
7677
HideToolWindow = string.Empty;
7778
ShowStartupTime = false;
7879
DebugAttachPid = 0;
80+
DebugBreakOnAttach = false;
7981
DebugAttachProcess = string.Empty;
8082
ExtraExtensionDirectory = string.Empty;
8183

@@ -178,6 +180,10 @@ public AppCommandLineArgs(string[] args) {
178180
i++;
179181
break;
180182

183+
case "--break":
184+
DebugBreakOnAttach = true;
185+
break;
186+
181187
case "-e":
182188
if (TryParseUInt32(next, out uint debugEvent))
183189
DebugEvent = debugEvent;

0 commit comments

Comments
 (0)