| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | //***************************************************************************** |
| 5 | // NativePipeline.h |
| 6 | // |
| 7 | |
| 8 | // |
| 9 | // defines native pipeline abstraction, which includes debug-support |
| 10 | // for event redirection. |
| 11 | //***************************************************************************** |
| 12 | |
| 13 | |
| 14 | #ifndef _NATIVE_PIPELINE_H |
| 15 | #define _NATIVE_PIPELINE_H |
| 16 | |
| 17 | //----------------------------------------------------------------------------- |
| 18 | // Interface for native-debugging pipeline associated with a single process |
| 19 | // that is being debugged. |
| 20 | // |
| 21 | // On windows, this is a wrapper around the win32 debugging API |
| 22 | // (eg, kernel32!WaitForDebugEvent). On most Unix-like platforms, |
| 23 | // it has an alternative implementation. See code:IEventChannel and |
| 24 | // platformspecific.cpp for more information. |
| 25 | // @dbgtodo : All of the APIs that return BOOL should probably be changed to |
| 26 | // return HRESULTS so we don't have to rely on some implicit GetLastError protocol. |
| 27 | //----------------------------------------------------------------------------- |
| 28 | class INativeEventPipeline |
| 29 | { |
| 30 | public: |
| 31 | // Call to delete the pipeline. This can only be called once. |
| 32 | virtual void Delete() = 0; |
| 33 | |
| 34 | |
| 35 | // |
| 36 | // set whether to kill outstanding debuggees when the debugger exits. |
| 37 | // |
| 38 | // Arguments: |
| 39 | // fKillOnExit - When the debugger thread (this thread) exits, outstanding debuggees will be |
| 40 | // terminated (if true), else detached (if false) |
| 41 | // |
| 42 | // Returns: |
| 43 | // True on success, False on failure. |
| 44 | // |
| 45 | // Notes: |
| 46 | // This is a cross-platform wrapper around Kernel32!DebugSetProcessKillOnExit. |
| 47 | // This affects all debuggees handled by this thread. |
| 48 | // This is not supported or necessary for Mac debugging. The only reason we need this on Windows is to |
| 49 | // ask the OS not to terminate the debuggee when the debugger exits. The Mac debugging pipeline |
| 50 | // doesn't automatically kill the debuggee when the debugger exits. |
| 51 | // |
| 52 | |
| 53 | virtual BOOL DebugSetProcessKillOnExit(bool fKillOnExit) = 0; |
| 54 | |
| 55 | // Create |
| 56 | virtual HRESULT CreateProcessUnderDebugger( |
| 57 | MachineInfo machineInfo, |
| 58 | LPCWSTR lpApplicationName, |
| 59 | LPCWSTR lpCommandLine, |
| 60 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
| 61 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
| 62 | BOOL bInheritHandles, |
| 63 | DWORD dwCreationFlags, |
| 64 | LPVOID lpEnvironment, |
| 65 | LPCWSTR lpCurrentDirectory, |
| 66 | LPSTARTUPINFOW lpStartupInfo, |
| 67 | LPPROCESS_INFORMATION lpProcessInformation) = 0; |
| 68 | |
| 69 | // Attach |
| 70 | virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor) = 0; |
| 71 | |
| 72 | // Detach |
| 73 | virtual HRESULT DebugActiveProcessStop(DWORD processId) =0; |
| 74 | |
| 75 | // |
| 76 | // Block and wait for the next debug event from the debuggee process. |
| 77 | // |
| 78 | // Arguments: |
| 79 | // pEvent - buffer for the debug event to be returned |
| 80 | // dwTimeout - number of milliseconds to wait before timing out |
| 81 | // pProcess - the CordbProcess associated with this pipeline; used to look up a thread ID if necessary |
| 82 | // |
| 83 | // Return Value: |
| 84 | // TRUE if a debug event is available |
| 85 | // |
| 86 | // Notes: |
| 87 | // Once a debug event is returned, it is consumed from the pipeline and will not be accessible in the |
| 88 | // future. Caller is responsible for saving the debug event if necessary. |
| 89 | // |
| 90 | |
| 91 | virtual BOOL WaitForDebugEvent(DEBUG_EVENT * pEvent, DWORD dwTimeout, CordbProcess * pProcess) =0; |
| 92 | |
| 93 | // |
| 94 | // This is specific to Windows. When a debug event is sent to the debugger, the debuggee process is |
| 95 | // suspended. The debugger must call this function to resume the debuggee process. |
| 96 | // |
| 97 | // Arguments: |
| 98 | // dwProcessId - process ID of the debuggee |
| 99 | // dwThreadId - thread ID of the thread which has triggered a debug event before |
| 100 | // dwContinueStatus - whether to handle the exception (if any) reported on the specified thread |
| 101 | // |
| 102 | // Return Value: |
| 103 | // TRUE if successful |
| 104 | // |
| 105 | // Notes: |
| 106 | // For Mac debugging, the process isn't actually suspended when a debug event is raised. As such, |
| 107 | // this function is a nop for Mac debugging. See code:Debugger::SendRawEvent. |
| 108 | // |
| 109 | // Of course, this is a semantic difference from Windows. However, in most cases, the LS suspends |
| 110 | // all managed threads by calling code:Debugger::TrapAllRuntimeThreads immediately after raising a |
| 111 | // debug event. The only case where this is not true is code:Debugger::SendCreateProcess, but that |
| 112 | // doesn't seem to be a problem at this point. |
| 113 | // |
| 114 | |
| 115 | virtual BOOL ContinueDebugEvent( |
| 116 | DWORD dwProcessId, |
| 117 | DWORD dwThreadId, |
| 118 | DWORD dwContinueStatus |
| 119 | ) =0; |
| 120 | |
| 121 | // |
| 122 | // Return a handle for the debuggee process. |
| 123 | // |
| 124 | // Return Value: |
| 125 | // handle for the debuggee process (see below) |
| 126 | // |
| 127 | // Notes: |
| 128 | // Handles are a Windows-specific concept. For Mac debugging, the handle returned by this function is |
| 129 | // only valid for waiting on process termination. This is ok for now because the only cases where a |
| 130 | // real process handle is needed are related to interop-debugging, which isn't supported on the Mac. |
| 131 | // |
| 132 | |
| 133 | virtual HANDLE GetProcessHandle() = 0; |
| 134 | |
| 135 | // |
| 136 | // Terminate the debuggee process. |
| 137 | // |
| 138 | // Arguments: |
| 139 | // exitCode - the exit code for the debuggee process |
| 140 | // |
| 141 | // Return Value: |
| 142 | // TRUE if successful |
| 143 | // |
| 144 | // Notes: |
| 145 | // The exit code is ignored for Mac debugging. |
| 146 | // |
| 147 | |
| 148 | virtual BOOL TerminateProcess(UINT32 exitCode) = 0; |
| 149 | |
| 150 | // |
| 151 | // Resume any suspended threads in the currend process. |
| 152 | // This decreases the suspend count of each thread by at most 1. |
| 153 | // Call multiple times until it returns S_FALSE if you want to really ensure |
| 154 | // all threads are running. |
| 155 | // |
| 156 | // Notes: |
| 157 | // On Windows the OS may suspend threads when continuing a 2nd-chance exception. |
| 158 | // Call this to get them resumed again. On other platforms this |
| 159 | // will typically be a no-op, so I provide a default implementation to avoid |
| 160 | // everyone having to override this. |
| 161 | // |
| 162 | // Return Value: |
| 163 | // S_OK if at least one thread was resumed from a suspended state |
| 164 | // S_FALSE if nothing was done |
| 165 | // An error code indicating why we were not able to attempt this |
| 166 | |
| 167 | virtual HRESULT EnsureThreadsRunning() |
| 168 | { |
| 169 | return S_FALSE; |
| 170 | } |
| 171 | |
| 172 | #ifdef FEATURE_PAL |
| 173 | // Used by debugger side (RS) to cleanup the target (LS) named pipes |
| 174 | // and semaphores when the debugger detects the debuggee process exited. |
| 175 | virtual void CleanupTargetProcess() |
| 176 | { |
| 177 | } |
| 178 | #endif |
| 179 | }; |
| 180 | |
| 181 | // |
| 182 | // Helper accessors for manipulating native pipeline. |
| 183 | // These also provide some platform abstractions for DEBUG_EVENT. |
| 184 | // |
| 185 | |
| 186 | // Returns process ID that the debug event is on. |
| 187 | DWORD GetProcessId(const DEBUG_EVENT * pEvent); |
| 188 | |
| 189 | // Returns Thread ID of the thread that fired the debug event. |
| 190 | DWORD GetThreadId(const DEBUG_EVENT * pEvent); |
| 191 | |
| 192 | // |
| 193 | // Determines if this is an exception event. |
| 194 | // |
| 195 | // Arguments: |
| 196 | // pEvent - [required, in]: debug event to inspect |
| 197 | // pfFirstChance - [required, out]: set if this is an 1st-chance exception. |
| 198 | // ppRecord - [required, out]: if this is an exception, pointer into to the exception record. |
| 199 | // this pointer has the same lifetime semantics as the DEBUG_EVENT (it may |
| 200 | // likely be a pointer into the debug-event). |
| 201 | // |
| 202 | // Returns: |
| 203 | // True if this is an exception. Sets outparameters to exception values. |
| 204 | // Else false. |
| 205 | // |
| 206 | // Notes: |
| 207 | // Exceptions are spceial because they need to be sent to the CLR for filtering. |
| 208 | BOOL IsExceptionEvent(const DEBUG_EVENT * pEvent, BOOL * pfFirstChance, const EXCEPTION_RECORD ** ppRecord); |
| 209 | |
| 210 | |
| 211 | //----------------------------------------------------------------------------- |
| 212 | // Allocate and return a pipeline object for this platform |
| 213 | // |
| 214 | // Returns: |
| 215 | // newly allocated pipeline object. Caller must call Dispose() on it. |
| 216 | INativeEventPipeline * NewPipelineForThisPlatform(); |
| 217 | |
| 218 | //----------------------------------------------------------------------------- |
| 219 | // Allocate and return a pipeline object for this platform |
| 220 | // Has debug checks (such as for event redirection) |
| 221 | // |
| 222 | // Returns: |
| 223 | // newly allocated pipeline object. Caller must call Dispose() on it. |
| 224 | INativeEventPipeline * NewPipelineWithDebugChecks(); |
| 225 | |
| 226 | |
| 227 | |
| 228 | #endif // _NATIVE_PIPELINE_H |
| 229 | |
| 230 | |