[DRWTSN32] Print some extra exception info
[reactos.git] / ntoskrnl / kd64 / kdtrap.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/kd64/kdtrap.c
5 * PURPOSE: KD64 Trap Handlers
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Stefan Ginsberg (stefan.ginsberg@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 //
17 // Retrieves the ComponentId and Level for BREAKPOINT_PRINT
18 // and OutputString and OutputStringLength for BREAKPOINT_PROMPT.
19 //
20 #if defined(_X86_)
21
22 //
23 // EBX/EDI on x86
24 //
25 #define KdpGetParameterThree(Context) ((Context)->Ebx)
26 #define KdpGetParameterFour(Context) ((Context)->Edi)
27
28 #elif defined(_AMD64_)
29
30 //
31 // R8/R9 on AMD64
32 //
33 #define KdpGetParameterThree(Context) ((Context)->R8)
34 #define KdpGetParameterFour(Context) ((Context)->R9)
35
36 #elif defined(_ARM_)
37
38 //
39 // R3/R4 on ARM
40 //
41 #define KdpGetParameterThree(Context) ((Context)->R3)
42 #define KdpGetParameterFour(Context) ((Context)->R4)
43
44 #else
45 #error Unsupported Architecture
46 #endif
47
48 /* FUNCTIONS *****************************************************************/
49
50 BOOLEAN
51 NTAPI
52 KdpReport(IN PKTRAP_FRAME TrapFrame,
53 IN PKEXCEPTION_FRAME ExceptionFrame,
54 IN PEXCEPTION_RECORD ExceptionRecord,
55 IN PCONTEXT ContextRecord,
56 IN KPROCESSOR_MODE PreviousMode,
57 IN BOOLEAN SecondChanceException)
58 {
59 BOOLEAN Enable, Handled;
60 PKPRCB Prcb;
61 NTSTATUS ExceptionCode;
62
63 /*
64 * Determine whether to pass the exception to the debugger.
65 * First, check if this is a "debug exception", meaning breakpoint
66 * (including debug service), single step and assertion failure exceptions.
67 */
68 ExceptionCode = ExceptionRecord->ExceptionCode;
69 if ((ExceptionCode == STATUS_BREAKPOINT) ||
70 (ExceptionCode == STATUS_SINGLE_STEP) ||
71 (ExceptionCode == STATUS_ASSERTION_FAILURE))
72 {
73 /* This is a debug exception; we always pass them to the debugger */
74 }
75 else if (NtGlobalFlag & FLG_STOP_ON_EXCEPTION)
76 {
77 /*
78 * Not a debug exception, but the stop-on-exception flag is set,
79 * meaning the debugger requests that we pass it first chance
80 * exceptions. However, some exceptions are always passed to the
81 * exception handler first, namely exceptions with a code that isn't
82 * an error or warning code, and also exceptions with the special
83 * STATUS_PORT_DISCONNECTED code (an error code).
84 */
85 if ((SecondChanceException == FALSE) &&
86 ((ExceptionCode == STATUS_PORT_DISCONNECTED) ||
87 (NT_SUCCESS(ExceptionCode))))
88 {
89 /* Let the exception handler, if any, try to handle it */
90 return FALSE;
91 }
92 }
93 else if (SecondChanceException == FALSE)
94 {
95 /*
96 * This isn't a debug exception and the stop-on-exception flag isn't set,
97 * so don't bother handling it
98 */
99 return FALSE;
100 }
101
102 /* Enter the debugger */
103 Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
104
105 /*
106 * Get the KPRCB and save the CPU Control State manually instead of
107 * using KiSaveProcessorState, since we already have a valid CONTEXT.
108 */
109 Prcb = KeGetCurrentPrcb();
110 KiSaveProcessorControlState(&Prcb->ProcessorState);
111 KdpMoveMemory(&Prcb->ProcessorState.ContextFrame,
112 ContextRecord,
113 sizeof(CONTEXT));
114
115 /* Report the new state */
116 Handled = KdpReportExceptionStateChange(ExceptionRecord,
117 &Prcb->ProcessorState.
118 ContextFrame,
119 SecondChanceException);
120
121 /* Now restore the processor state, manually again. */
122 KdpMoveMemory(ContextRecord,
123 &Prcb->ProcessorState.ContextFrame,
124 sizeof(CONTEXT));
125 KiRestoreProcessorControlState(&Prcb->ProcessorState);
126
127 /* Exit the debugger and clear the CTRL-C state */
128 KdExitDebugger(Enable);
129 KdpControlCPressed = FALSE;
130 return Handled;
131 }
132
133 BOOLEAN
134 NTAPI
135 KdpTrap(IN PKTRAP_FRAME TrapFrame,
136 IN PKEXCEPTION_FRAME ExceptionFrame,
137 IN PEXCEPTION_RECORD ExceptionRecord,
138 IN PCONTEXT ContextRecord,
139 IN KPROCESSOR_MODE PreviousMode,
140 IN BOOLEAN SecondChanceException)
141 {
142 BOOLEAN Unload;
143 ULONG_PTR ProgramCounter;
144 BOOLEAN Handled;
145 NTSTATUS ReturnStatus;
146 USHORT ReturnLength;
147
148 /*
149 * Check if we got a STATUS_BREAKPOINT with a SubID for Print, Prompt or
150 * Load/Unload symbols. Make sure it isn't a software breakpoints as those
151 * are handled by KdpReport.
152 */
153 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
154 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
155 {
156 /* Save Program Counter */
157 ProgramCounter = KeGetContextPc(ContextRecord);
158
159 /* Check what kind of operation was requested from us */
160 Unload = FALSE;
161 switch (ExceptionRecord->ExceptionInformation[0])
162 {
163 /* DbgPrint */
164 case BREAKPOINT_PRINT:
165
166 /* Call the worker routine */
167 ReturnStatus = KdpPrint((ULONG)KdpGetParameterThree(ContextRecord),
168 (ULONG)KdpGetParameterFour(ContextRecord),
169 (LPSTR)ExceptionRecord->ExceptionInformation[1],
170 (USHORT)ExceptionRecord->ExceptionInformation[2],
171 PreviousMode,
172 TrapFrame,
173 ExceptionFrame,
174 &Handled);
175
176 /* Update the return value for the caller */
177 KeSetContextReturnRegister(ContextRecord,
178 ReturnStatus);
179 break;
180
181 /* DbgPrompt */
182 case BREAKPOINT_PROMPT:
183
184 /* Call the worker routine */
185 ReturnLength = KdpPrompt((LPSTR)ExceptionRecord->ExceptionInformation[1],
186 (USHORT)ExceptionRecord->ExceptionInformation[2],
187 (LPSTR)KdpGetParameterThree(ContextRecord),
188 (USHORT)KdpGetParameterFour(ContextRecord),
189 PreviousMode,
190 TrapFrame,
191 ExceptionFrame);
192 Handled = TRUE;
193
194 /* Update the return value for the caller */
195 KeSetContextReturnRegister(ContextRecord, ReturnLength);
196 break;
197
198 /* DbgUnLoadImageSymbols */
199 case BREAKPOINT_UNLOAD_SYMBOLS:
200
201 /* Drop into the load case below, with the unload parameter */
202 Unload = TRUE;
203
204 /* DbgLoadImageSymbols */
205 case BREAKPOINT_LOAD_SYMBOLS:
206
207 /* Call the worker routine */
208 KdpSymbol((PSTRING)ExceptionRecord->
209 ExceptionInformation[1],
210 (PKD_SYMBOLS_INFO)ExceptionRecord->
211 ExceptionInformation[2],
212 Unload,
213 PreviousMode,
214 ContextRecord,
215 TrapFrame,
216 ExceptionFrame);
217 Handled = TRUE;
218 break;
219
220 /* DbgCommandString */
221 case BREAKPOINT_COMMAND_STRING:
222
223 /* Call the worker routine */
224 KdpCommandString((PSTRING)ExceptionRecord->
225 ExceptionInformation[1],
226 (PSTRING)ExceptionRecord->
227 ExceptionInformation[2],
228 PreviousMode,
229 ContextRecord,
230 TrapFrame,
231 ExceptionFrame);
232 Handled = TRUE;
233 break;
234
235 /* Anything else, do nothing */
236 default:
237
238 /* Invalid debug service! Don't handle this! */
239 Handled = FALSE;
240 break;
241 }
242
243 /*
244 * If the PC was not updated, we'll increment it ourselves so execution
245 * continues past the breakpoint.
246 */
247 if (ProgramCounter == KeGetContextPc(ContextRecord))
248 {
249 /* Update it */
250 KeSetContextPc(ContextRecord,
251 ProgramCounter + KD_BREAKPOINT_SIZE);
252 }
253 }
254 else
255 {
256 /* Call the worker routine */
257 Handled = KdpReport(TrapFrame,
258 ExceptionFrame,
259 ExceptionRecord,
260 ContextRecord,
261 PreviousMode,
262 SecondChanceException);
263 }
264
265 /* Return TRUE or FALSE to caller */
266 return Handled;
267 }
268
269 BOOLEAN
270 NTAPI
271 KdpStub(IN PKTRAP_FRAME TrapFrame,
272 IN PKEXCEPTION_FRAME ExceptionFrame,
273 IN PEXCEPTION_RECORD ExceptionRecord,
274 IN PCONTEXT ContextRecord,
275 IN KPROCESSOR_MODE PreviousMode,
276 IN BOOLEAN SecondChanceException)
277 {
278 ULONG_PTR ExceptionCommand;
279
280 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
281 ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
282 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
283 (ExceptionRecord->NumberParameters > 0) &&
284 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
285 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
286 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
287 (ExceptionCommand == BREAKPOINT_PRINT)))
288 {
289 /* This we can handle: simply bump the Program Counter */
290 KeSetContextPc(ContextRecord,
291 KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE);
292 return TRUE;
293 }
294 else if (KdPitchDebugger)
295 {
296 /* There's no debugger, fail. */
297 return FALSE;
298 }
299 else if ((KdAutoEnableOnEvent) &&
300 (KdPreviouslyEnabled) &&
301 !(KdDebuggerEnabled) &&
302 (NT_SUCCESS(KdEnableDebugger())) &&
303 (KdDebuggerEnabled))
304 {
305 /* Debugging was Auto-Enabled. We can now send this to KD. */
306 return KdpTrap(TrapFrame,
307 ExceptionFrame,
308 ExceptionRecord,
309 ContextRecord,
310 PreviousMode,
311 SecondChanceException);
312 }
313 else
314 {
315 /* FIXME: All we can do in this case is trace this exception */
316 return FALSE;
317 }
318 }
319
320 BOOLEAN
321 NTAPI
322 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
323 IN PCONTEXT Context,
324 IN KPROCESSOR_MODE PreviousMode)
325 {
326 /*
327 * Determine if this is a valid debug service call and make sure that
328 * it isn't a software breakpoint
329 */
330 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
331 (ExceptionRecord->NumberParameters > 0) &&
332 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
333 {
334 /* Then we have to handle it */
335 return TRUE;
336 }
337 else
338 {
339 /* We don't have to handle it */
340 return FALSE;
341 }
342 }