xref: /reactos/ntoskrnl/ps/debug.c (revision 2b933529)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ps/debug.c
5  * PURPOSE:         Process Manager: Debugging Support (Set/Get Context)
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Thomas Weidenmueller (w3seek@reactos.org)
8  */
9 
10 /* INCLUDES ****************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* PRIVATE FUNCTIONS *********************************************************/
17 
18 #if DBG
19 VOID
20 NTAPI
21 PspDumpThreads(BOOLEAN IncludeSystem)
22 {
23     PLIST_ENTRY CurrentThread, CurrentProcess;
24     PEPROCESS Process;
25     PETHREAD Thread;
26     ULONG nThreads = 0;
27 
28     /* Loop all Active Processes */
29     CurrentProcess = PsActiveProcessHead.Flink;
30     while(CurrentProcess != &PsActiveProcessHead)
31     {
32         /* Get the process */
33         Process = CONTAINING_RECORD(CurrentProcess, EPROCESS, ActiveProcessLinks);
34 
35         /* Skip the Initial Process if requested */
36         if((Process != PsInitialSystemProcess) ||
37            (Process == PsInitialSystemProcess && IncludeSystem))
38         {
39             /* Loop all its threads */
40             CurrentThread = Process->ThreadListHead.Flink;
41             while(CurrentThread != &Process->ThreadListHead)
42             {
43 
44                 /* Get teh Thread */
45                 Thread = CONTAINING_RECORD(CurrentThread, ETHREAD, ThreadListEntry);
46                 nThreads++;
47 
48                 /* Print the Info */
49                 DbgPrint("State %u Affinity %08x Priority %d PID.TID %d.%d Name %.8s Stack: \n",
50                          Thread->Tcb.State,
51                          Thread->Tcb.Affinity,
52                          Thread->Tcb.Priority,
53                          Thread->Cid.UniqueProcess,
54                          Thread->Cid.UniqueThread,
55                          Thread->ThreadsProcess->ImageFileName);
56 
57                 /* Make sure it's not running */
58                 if(Thread->Tcb.State == Ready ||
59                    Thread->Tcb.State == Standby ||
60                    Thread->Tcb.State == Waiting)
61                 {
62 #ifdef _M_IX86
63                     ULONG i = 0;
64                     PULONG Esp = (PULONG)Thread->Tcb.KernelStack;
65                     PULONG Ebp = (PULONG)Esp[4];
66 
67                     /* Print EBP */
68                     DbgPrint("Ebp %p\n", Ebp);
69 
70                     /* Walk it */
71                     while(Ebp != 0 && Ebp >= (PULONG)Thread->Tcb.StackLimit)
72                     {
73                         /* Print what's on the stack */
74                         DbgPrint("%.8X %.8X%s", Ebp[0], Ebp[1], (i % 8) == 7 ? "\n" : "  ");
75                         Ebp = (PULONG)Ebp[0];
76                         i++;
77                     }
78 
79                     /* Print a new line if there's nothing */
80                     if((i % 8) != 0) DbgPrint("\n");
81 #else
82                     DbgPrint("FIXME: Backtrace skipped on non-x86\n");
83 #endif
84                 }
85 
86                 /* Move to the next Thread */
87                 CurrentThread = CurrentThread->Flink;
88             }
89         }
90 
91         /* Move to the next Process */
92         CurrentProcess = CurrentProcess->Flink;
93     }
94 }
95 #endif
96 
97 /* PUBLIC FUNCTIONS **********************************************************/
98 
99 /*
100  * @implemented
101  */
102 NTSTATUS
103 NTAPI
104 PsGetContextThread(IN PETHREAD Thread,
105                    IN OUT PCONTEXT ThreadContext,
106                    IN KPROCESSOR_MODE PreviousMode)
107 {
108     GET_SET_CTX_CONTEXT GetSetContext;
109     ULONG Size = 0, Flags = 0;
110     NTSTATUS Status;
111 
112     /* Enter SEH */
113     _SEH2_TRY
114     {
115         /* Set default length */
116         Size = sizeof(CONTEXT);
117 
118         /* Read the flags */
119         Flags = ProbeForReadUlong(&ThreadContext->ContextFlags);
120 
121 #ifdef _M_IX86
122         /* Check if the caller wanted extended registers */
123         if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
124             CONTEXT_EXTENDED_REGISTERS)
125         {
126             /* Cut them out of the size */
127             Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
128         }
129 #endif
130 
131         /* Check if we came from user mode */
132         if (PreviousMode != KernelMode)
133         {
134             /* Probe the context */
135             ProbeForWrite(ThreadContext, Size, sizeof(ULONG));
136         }
137     }
138     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
139     {
140         /* Return the exception code */
141         _SEH2_YIELD(return _SEH2_GetExceptionCode());
142     }
143     _SEH2_END;
144 
145     /* Initialize the wait event */
146     KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
147 
148     /* Set the flags and previous mode */
149     RtlZeroMemory(&GetSetContext.Context, Size);
150     GetSetContext.Context.ContextFlags = Flags;
151     GetSetContext.Mode = PreviousMode;
152 
153     /* Check if we're running in the same thread */
154     if (Thread == PsGetCurrentThread())
155     {
156         /* Setup APC parameters manually */
157         GetSetContext.Apc.SystemArgument1 = NULL;
158         GetSetContext.Apc.SystemArgument2 = Thread;
159 
160         /* Enter a guarded region to simulate APC_LEVEL */
161         KeEnterGuardedRegion();
162 
163         /* Manually call the APC */
164         PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
165                                         NULL,
166                                         NULL,
167                                         &GetSetContext.Apc.SystemArgument1,
168                                         &GetSetContext.Apc.SystemArgument2);
169 
170         /* Leave the guarded region */
171         KeLeaveGuardedRegion();
172 
173         /* We are done */
174         Status = STATUS_SUCCESS;
175     }
176     else
177     {
178         /* Initialize the APC */
179         KeInitializeApc(&GetSetContext.Apc,
180                         &Thread->Tcb,
181                         OriginalApcEnvironment,
182                         PspGetOrSetContextKernelRoutine,
183                         NULL,
184                         NULL,
185                         KernelMode,
186                         NULL);
187 
188         /* Queue it as a Get APC */
189         if (!KeInsertQueueApc(&GetSetContext.Apc, NULL, Thread, 2))
190         {
191             /* It was already queued, so fail */
192             Status = STATUS_UNSUCCESSFUL;
193         }
194         else
195         {
196             /* Wait for the APC to complete */
197             Status = KeWaitForSingleObject(&GetSetContext.Event,
198                                            0,
199                                            KernelMode,
200                                            FALSE,
201                                            NULL);
202         }
203     }
204 
205     _SEH2_TRY
206     {
207         /* Copy the context */
208         RtlCopyMemory(ThreadContext, &GetSetContext.Context, Size);
209     }
210     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
211     {
212         /* Get the exception code */
213         Status = _SEH2_GetExceptionCode();
214     }
215     _SEH2_END;
216 
217     /* Return status */
218     return Status;
219 }
220 
221 /*
222  * @implemented
223  */
224 NTSTATUS
225 NTAPI
226 PsSetContextThread(IN PETHREAD Thread,
227                    IN OUT PCONTEXT ThreadContext,
228                    IN KPROCESSOR_MODE PreviousMode)
229 {
230     GET_SET_CTX_CONTEXT GetSetContext;
231     ULONG Size = 0, Flags = 0;
232     NTSTATUS Status;
233 
234     /* Enter SEH */
235     _SEH2_TRY
236     {
237         /* Set default length */
238         Size = sizeof(CONTEXT);
239 
240         /* Read the flags */
241         Flags = ProbeForReadUlong(&ThreadContext->ContextFlags);
242 
243 #ifdef _M_IX86
244         /* Check if the caller wanted extended registers */
245         if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
246             CONTEXT_EXTENDED_REGISTERS)
247         {
248             /* Cut them out of the size */
249             Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
250         }
251 #endif
252 
253         /* Check if we came from user mode */
254         if (PreviousMode != KernelMode)
255         {
256             /* Probe the context */
257             ProbeForRead(ThreadContext, Size, sizeof(ULONG));
258         }
259 
260         /* Copy the context */
261         RtlCopyMemory(&GetSetContext.Context, ThreadContext, Size);
262     }
263     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
264     {
265         /* Return the exception code */
266         _SEH2_YIELD(return _SEH2_GetExceptionCode());
267     }
268     _SEH2_END;
269 
270     /* Initialize the wait event */
271     KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
272 
273     /* Set the flags and previous mode */
274     GetSetContext.Context.ContextFlags = Flags;
275     GetSetContext.Mode = PreviousMode;
276 
277     /* Check if we're running in the same thread */
278     if (Thread == PsGetCurrentThread())
279     {
280         /* Setup APC parameters manually */
281         GetSetContext.Apc.SystemArgument1 = UlongToPtr(1);
282         GetSetContext.Apc.SystemArgument2 = Thread;
283 
284         /* Enter a guarded region to simulate APC_LEVEL */
285         KeEnterGuardedRegion();
286 
287         /* Manually call the APC */
288         PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
289                                         NULL,
290                                         NULL,
291                                         &GetSetContext.Apc.SystemArgument1,
292                                         &GetSetContext.Apc.SystemArgument2);
293 
294         /* Leave the guarded region */
295         KeLeaveGuardedRegion();
296 
297         /* We are done */
298         Status = STATUS_SUCCESS;
299     }
300     else
301     {
302         /* Initialize the APC */
303         KeInitializeApc(&GetSetContext.Apc,
304                         &Thread->Tcb,
305                         OriginalApcEnvironment,
306                         PspGetOrSetContextKernelRoutine,
307                         NULL,
308                         NULL,
309                         KernelMode,
310                         NULL);
311 
312         /* Queue it as a Get APC */
313         if (!KeInsertQueueApc(&GetSetContext.Apc, UlongToPtr(1), Thread, 2))
314         {
315             /* It was already queued, so fail */
316             Status = STATUS_UNSUCCESSFUL;
317         }
318         else
319         {
320             /* Wait for the APC to complete */
321             Status = KeWaitForSingleObject(&GetSetContext.Event,
322                                            0,
323                                            KernelMode,
324                                            FALSE,
325                                            NULL);
326         }
327     }
328 
329     /* Return status */
330     return Status;
331 }
332 
333 NTSTATUS
334 NTAPI
335 NtGetContextThread(IN HANDLE ThreadHandle,
336                    IN OUT PCONTEXT ThreadContext)
337 {
338     PETHREAD Thread;
339     NTSTATUS Status;
340     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
341     PAGED_CODE();
342 
343     /* Get the Thread Object */
344     Status = ObReferenceObjectByHandle(ThreadHandle,
345                                        THREAD_GET_CONTEXT,
346                                        PsThreadType,
347                                        PreviousMode,
348                                        (PVOID*)&Thread,
349                                        NULL);
350 
351     if (!NT_SUCCESS(Status)) return Status;
352 
353     /* Make sure it's not a system thread */
354     if (Thread->SystemThread)
355     {
356         /* Fail */
357         Status = STATUS_INVALID_HANDLE;
358     }
359     else
360     {
361         /* Call the kernel API */
362         Status = PsGetContextThread(Thread, ThreadContext, PreviousMode);
363     }
364 
365     /* Dereference it and return */
366     ObDereferenceObject(Thread);
367     return Status;
368 }
369 
370 NTSTATUS
371 NTAPI
372 NtSetContextThread(IN HANDLE ThreadHandle,
373                    IN PCONTEXT ThreadContext)
374 {
375     PETHREAD Thread;
376     NTSTATUS Status;
377     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
378     PAGED_CODE();
379 
380     /* Get the Thread Object */
381     Status = ObReferenceObjectByHandle(ThreadHandle,
382                                        THREAD_SET_CONTEXT,
383                                        PsThreadType,
384                                        PreviousMode,
385                                        (PVOID*)&Thread,
386                                        NULL);
387 
388     if (!NT_SUCCESS(Status)) return Status;
389 
390     /* Make sure it's not a system thread */
391     if (Thread->SystemThread)
392     {
393         /* Fail */
394         Status = STATUS_INVALID_HANDLE;
395     }
396     else
397     {
398         /* Call the kernel API */
399         Status = PsSetContextThread(Thread, ThreadContext, PreviousMode);
400     }
401 
402     /* Dereference it and return */
403     ObDereferenceObject(Thread);
404     return Status;
405 }
406 
407 /* EOF */
408