summaryrefslogtreecommitdiff
path: root/src/debug/di/eventchannel.h
blob: 232f6e0a10cb6dd6a75997ce44171253647500f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
//*****************************************************************************
// EventChannel.h
// 

//
// This file contains the old-style event channel interface.  
//*****************************************************************************


#ifndef _EVENT_CHANNEL_H_
#define _EVENT_CHANNEL_H_

//---------------------------------------------------------------------------------------
//
// This is the abstract base class for the old-style "IPC" event channel.  (Despite the name, these events are 
// no longer transmitted in an IPC shared memory block.)  The event channel owns the DebuggerIPCControlBlock.
//
// Assumptions:
//    This class is NOT thread-safe.  Caller is assumed to have taken the appropriate measures for 
//    synchronization.
//    
// Notes:
//    In Whidbey, both LS-to-RS and RS-to-LS communication are done by IPC shared memory block.  We allocate
//    a DebuggerIPCControlBlock (DCB) on the IPC shared memory block.  The DCB contains both a send buffer
//    and a receive buffer (from the perspective of the LS, e.g. the send buffer is for LS-to-RS communication).
//    
//    In the new architecture, LS-to-RS communication is mostly done by raising an exception on the LS and 
//    calling code:INativeEventPipeline::WaitForDebugEvent on the RS.  This communication is handled by
//    code:INativeEventPipeline.  RS-to-LS communication is mostly done by calling into the code:IDacDbiInterface,
//    which on Windows is just a structured way to do ReadProcessMemory().
//    
//    There are still cases where we are sending IPC events in not-yet-DACized code.  There are two main 
//    categories:
//    
//    1) There are three types of events which the RS can send to the LS:
//       a) asynchronous: the RS can just send the event and continue
//       b) synchronous, but no reply: the RS must wait for an acknowledgement, but there is no reply
//       c) synchronous, reply required: the RS must wait for an acknowledgement before it can get the reply
//       
//       For (c), the RS sends a synchronous IPC event to the LS and wait for a reply.  The reply is returned 
//       in the same buffer space used to send the event, i.e. in the receive buffer.
//         - RS: code:CordbRCEventThread::SendIPCEvent
//         - LS: code:DebuggerRCThread::SendIPCReply
//       
//    2) In the case where the information from the LS has a variable size (and so we are not sure if it will
//       fit in one event), the RS sends an asynchronous IPC event to the LS and wait for one or more 
//       events from the LS.  The events from the LS are actually sent using the native pipeline.  This is
//       somewhat tricky because we need to make sure the event from the native pipeline is passed along to 
//       the thread which is waiting for the IPC events from the LS.  (For more information, see how we use
//       code:CordbProcess::m_leftSideEventAvailable and code:CordbProcess::m_leftSideEventRead).  Currently, 
//       the only place where we use send IPC events this way is in the inspection code used to check the 
//       results from the DAC against the results from the IPC events.
//         - RS: code:Cordb::WaitForIPCEventFromProcess
//         - LS: code:DebuggerRCThread::SendIPCEvent
//
//    In a sense, you can think of the LS and the RS sharing 3 channels: one for debug events (see 
//    code:INativeEventPipeline), one for DDI calls (see code:IDacDbiInterface and code:DDMarshal::SendDDMessage), 
//    and one for "IPC" events.  This is the interface for the "IPC" events.
//    

class IEventChannel
{
public:

    //
    // Inititalize the event channel.
    //
    // Arguments:
    //    hTargetProc - the handle of the debuggee process
    //
    // Return Value:
    //    S_OK if successful
    //
    // Notes:
    //    For Mac debugging, the handle is not necessary.
    //

    virtual HRESULT Init(HANDLE hTargetProc) = 0;

    //
    // Called when the debugger is detaching.  Depending on the implementation, this may be necessary to
    // make sure the debuggee state is reset in case another debugger attaches to it.
    //
    // Notes:
    //    This is currently a nop on for Mac debugging.
    //

    virtual void Detach() = 0;

    //
    // Delete the event channel and clean up all the resources it owns.  This function can only be called once.
    //

    virtual void Delete() = 0;

    //
    // Update a single field with a value stored in the RS copy of the DCB. We can't update the entire LS DCB
    // because in some cases, the LS and RS are simultaneously initializing the DCB. If we initialize a field on
    // the RS and write back the whole thing, we may overwrite something the LS has initialized in the interim. 
    // 
    // Arguments:
    //    rsFieldAddr - the address of the field in the RS copy of the DCB that we want to write back to
    //                  the LS DCB. We use this to compute the offset of the field from the beginning of the
    //                  DCB and then add this offset to the starting address of the LS DCB to get the LS
    //                  address of the field we are updating
    //    size        - the size of the field we're updating.
    // 
    // Return Value:
    //    S_OK if successful, otherwise whatever failure HR returned by the actual write operation
    //    

    virtual HRESULT UpdateLeftSideDCBField(void * rsFieldAddr, SIZE_T size) = 0;

    //
    // Update the entire RS copy of the debugger control block by reading the LS copy. The RS copy is treated as
    // a throw-away temporary buffer, rather than a true cache. That is, we make no assumptions about the
    // validity of the information over time. Thus, before using any of the values, we need to update it. We
    // update everything for simplicity; any perf hit we take by doing this instead of updating the individual
    // fields we want at any given point isn't significant, particularly if we are updating multiple fields.
    // 
    // Return Value: 
    //    S_OK if successful, otherwise whatever failure HR returned by the actual read operation
    //     

    virtual HRESULT UpdateRightSideDCB() = 0;

    //
    // Get the pointer to the RS DCB.  The LS copy isn't updated until UpdateLeftSideDCBField() is called.
    // Note that the DCB is owned by the event channel.
    //
    // Return Value:
    //    Return a pointer to the RS DCB.  The memory is owned by the event channel.
    //

    virtual DebuggerIPCControlBlock * GetDCB() = 0;

    //
    // Check whether we need to wait for an acknowledgement from the LS after sending an IPC event.
    // If so, wait for GetRightSideEventAckHandle().
    //
    // Arguments:
    //    pEvent - the IPC event which has just been sent to the LS
    //    
    // Return Value:
    //    TRUE if an acknowledgement is required (see the comment for this class for more information)
    //

    virtual BOOL NeedToWaitForAck(DebuggerIPCEvent * pEvent) = 0;

    //
    // Get a handle to wait on after sending an IPC event to the LS.  The caller should call NeedToWaitForAck()
    // first to see if it is necessary to wait for an acknowledgement.
    //
    // Return Value:
    //    a handle to a Win32 event which will be signaled when the LS acknowledges the receipt of the IPC event
    //
    // Assumptions:
    //    NeedToWaitForAck() returns true after sending an IPC event to the LS
    //

    virtual HANDLE GetRightSideEventAckHandle() = 0;

    //
    // After sending an event to the LS and determining that we need to wait for the LS's acknowledgement,
    // if any failure occurs, the LS may not have reset the Win32 event which is signaled when an event is
    // available on the RS (i.e. what's called the Right-Side-Event-Available (RSEA) event).  This function
    // should be called if any failure occurs to make sure our state is consistent. 
    //

    virtual void   ClearEventForLeftSide() = 0;

    //
    // Send an IPC event to the LS.  The caller should call NeedToWaitForAck() to check if it needs to wait
    // for an acknowledgement, and wait on GetRightSideEventAckHandle() if necessary.
    //
    // Arguments:
    //    pEvent    - the IPC event to be sent over to the LS
    //    eventSize - the size of the IPC event; cannot be bigger than CorDBIPC_BUFFER_SIZE
    //
    // Return Value:
    //    S_OK if successful
    //
    // Notes:
    //    This function returns a failure HR for recoverable errors.  It throws on unrecoverable errors.
    //

    virtual HRESULT SendEventToLeftSide(DebuggerIPCEvent * pEvent, SIZE_T eventSize) = 0;

    //
    // Get the reply from the LS for a previously sent IPC event.  The caller must have waited on 
    // GetRightSdieEventAckHandle().
    //
    // Arguments:
    //    pReplyEvent - buffer for the replyl event
    //    eventSize   - size of the buffer
    //
    // Return Value:
    //    S_OK if successful
    //

    virtual HRESULT GetReplyFromLeftSide(DebuggerIPCEvent * pReplyEvent, SIZE_T eventSize) = 0;

    //
    // This function and GetEventFromLeftSide() are for the second category of IPC events described in the
    // class header above, i.e. for events which take more than one IPC event to reply.  The event actually
    // doesn't come from the IPC channel.  Instead, it comes from the native pipeline.  We need to save the
    // event from the native pipeline and then wake up the thread which is waiting for this event.  Then the
    // thread can call GetEventFromLeftSide() to receive this event.
    //
    // Arguments:
    //    pEventFromLeftSide - IPC event from the LS
    //
    // Return Value:
    //    S_OK if successful, E_FAIL if an event has already been saved
    //
    // Assumptions:
    //    At any given time there should only be one event saved.  The caller is responsible for the
    //    synchronization. 
    //

    virtual HRESULT SaveEventFromLeftSide(DebuggerIPCEvent * pEventFromLeftSide) = 0;

    //
    // See the function header for SaveEventFromLeftSide.
    //
    // Arguments:
    //    pLocalManagedEvent - buffer to be filled with the IPC event from the LS
    //
    // Return Value:
    //    S_OK if successful
    //
    // Assumptions:
    //    At any given time there should only be one event saved.  The caller is responsible for the
    //    synchronization. 
    //

    virtual HRESULT GetEventFromLeftSide(DebuggerIPCEvent * pLocalManagedEvent) = 0;
};

//-----------------------------------------------------------------------------
//
// Allocate and return an old-style event channel object for this target platform.
//
// Arguments:
//    pLeftSideDCB       - target address of the DCB on the LS
//    pMutableDataTarget - data target for reading from and writing to the target process's address space
//    dwProcessId        - used for Mac debugging; specifies the target process ID
//    machineInfo        - used for Mac debugging; specifies the machine and the port number of the proxy
//    ppEventChannel     - out parament; returns the newly created event channel
//
// Return Value:
//    S_OK if successful
//

HRESULT NewEventChannelForThisPlatform(CORDB_ADDRESS pLeftSideDCB, 
                                       ICorDebugMutableDataTarget * pMutableDataTarget,
                                       DWORD dwProcessId,
                                       MachineInfo machineInfo,
                                       IEventChannel ** ppEventChannel);

#endif // _EVENT_CHANNEL_H_