xref: /reactos/ntoskrnl/ps/debug.c (revision fc82f8e2)
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 ength */
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     GetSetContext.Context.ContextFlags = Flags;
150     GetSetContext.Mode = PreviousMode;
151 
152     /* Check if we're running in the same thread */
153     if (Thread == PsGetCurrentThread())
154     {
155         /* Setup APC parameters manually */
156         GetSetContext.Apc.SystemArgument1 = NULL;
157         GetSetContext.Apc.SystemArgument2 = Thread;
158 
159         /* Enter a guarded region to simulate APC_LEVEL */
160         KeEnterGuardedRegion();
161 
162         /* Manually call the APC */
163         PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
164                                         NULL,
165                                         NULL,
166                                         &GetSetContext.Apc.SystemArgument1,
167                                         &GetSetContext.Apc.SystemArgument2);
168 
169         /* Leave the guarded region */
170         KeLeaveGuardedRegion();
171 
172         /* We are done */
173         Status = STATUS_SUCCESS;
174     }
175     else
176     {
177         /* Initialize the APC */
178         KeInitializeApc(&GetSetContext.Apc,
179                         &Thread->Tcb,
180                         OriginalApcEnvironment,
181                         PspGetOrSetContextKernelRoutine,
182                         NULL,
183                         NULL,
184                         KernelMode,
185                         NULL);
186 
187         /* Queue it as a Get APC */
188         if (!KeInsertQueueApc(&GetSetContext.Apc, NULL, Thread, 2))
189         {
190             /* It was already queued, so fail */
191             Status = STATUS_UNSUCCESSFUL;
192         }
193         else
194         {
195             /* Wait for the APC to complete */
196             Status = KeWaitForSingleObject(&GetSetContext.Event,
197                                            0,
198                                            KernelMode,
199                                            FALSE,
200                                            NULL);
201         }
202     }
203 
204     _SEH2_TRY
205     {
206         /* Copy the context */
207         RtlCopyMemory(ThreadContext, &GetSetContext.Context, Size);
208     }
209     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
210     {
211         /* Get the exception code */
212         Status = _SEH2_GetExceptionCode();
213     }
214     _SEH2_END;
215 
216     /* Return status */
217     return Status;
218 }
219 
220 /*
221  * @implemented
222  */
223 NTSTATUS
224 NTAPI
225 PsSetContextThread(IN PETHREAD Thread,
226                    IN OUT PCONTEXT ThreadContext,
227                    IN KPROCESSOR_MODE PreviousMode)
228 {
229     GET_SET_CTX_CONTEXT GetSetContext;
230     ULONG Size = 0, Flags = 0;
231     NTSTATUS Status;
232 
233     /* Enter SEH */
234     _SEH2_TRY
235     {
236         /* Set default length */
237         Size = sizeof(CONTEXT);
238 
239         /* Read the flags */
240         Flags = ProbeForReadUlong(&ThreadContext->ContextFlags);
241 
242 #ifdef _M_IX86
243         /* Check if the caller wanted extended registers */
244         if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
245             CONTEXT_EXTENDED_REGISTERS)
246         {
247             /* Cut them out of the size */
248             Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
249         }
250 #endif
251 
252         /* Check if we came from user mode */
253         if (PreviousMode != KernelMode)
254         {
255             /* Probe the context */
256             ProbeForRead(ThreadContext, Size, sizeof(ULONG));
257         }
258 
259         /* Copy the context */
260         RtlCopyMemory(&GetSetContext.Context, ThreadContext, Size);
261     }
262     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
263     {
264         /* Return the exception code */
265         _SEH2_YIELD(return _SEH2_GetExceptionCode());
266     }
267     _SEH2_END;
268 
269     /* Initialize the wait event */
270     KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
271 
272     /* Set the flags and previous mode */
273     GetSetContext.Context.ContextFlags = Flags;
274     GetSetContext.Mode = PreviousMode;
275 
276     /* Check if we're running in the same thread */
277     if (Thread == PsGetCurrentThread())
278     {
279         /* Setup APC parameters manually */
280         GetSetContext.Apc.SystemArgument1 = UlongToPtr(1);
281         GetSetContext.Apc.SystemArgument2 = Thread;
282 
283         /* Enter a guarded region to simulate APC_LEVEL */
284         KeEnterGuardedRegion();
285 
286         /* Manually call the APC */
287         PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
288                                         NULL,
289                                         NULL,
290                                         &GetSetContext.Apc.SystemArgument1,
291                                         &GetSetContext.Apc.SystemArgument2);
292 
293         /* Leave the guarded region */
294         KeLeaveGuardedRegion();
295 
296         /* We are done */
297         Status = STATUS_SUCCESS;
298     }
299     else
300     {
301         /* Initialize the APC */
302         KeInitializeApc(&GetSetContext.Apc,
303                         &Thread->Tcb,
304                         OriginalApcEnvironment,
305                         PspGetOrSetContextKernelRoutine,
306                         NULL,
307                         NULL,
308                         KernelMode,
309                         NULL);
310 
311         /* Queue it as a Get APC */
312         if (!KeInsertQueueApc(&GetSetContext.Apc, UlongToPtr(1), Thread, 2))
313         {
314             /* It was already queued, so fail */
315             Status = STATUS_UNSUCCESSFUL;
316         }
317         else
318         {
319             /* Wait for the APC to complete */
320             Status = KeWaitForSingleObject(&GetSetContext.Event,
321                                            0,
322                                            KernelMode,
323                                            FALSE,
324                                            NULL);
325         }
326     }
327 
328     /* Return status */
329     return Status;
330 }
331 
332 NTSTATUS
333 NTAPI
334 NtGetContextThread(IN HANDLE ThreadHandle,
335                    IN OUT PCONTEXT ThreadContext)
336 {
337     PETHREAD Thread;
338     NTSTATUS Status;
339     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
340     PAGED_CODE();
341 
342     /* Get the Thread Object */
343     Status = ObReferenceObjectByHandle(ThreadHandle,
344                                        THREAD_GET_CONTEXT,
345                                        PsThreadType,
346                                        PreviousMode,
347                                        (PVOID*)&Thread,
348                                        NULL);
349 
350     if (!NT_SUCCESS(Status)) return Status;
351 
352     /* Make sure it's not a system thread */
353     if (Thread->SystemThread)
354     {
355         /* Fail */
356         Status = STATUS_INVALID_HANDLE;
357     }
358     else
359     {
360         /* Call the kernel API */
361         Status = PsGetContextThread(Thread, ThreadContext, PreviousMode);
362     }
363 
364     /* Dereference it and return */
365     ObDereferenceObject(Thread);
366     return Status;
367 }
368 
369 NTSTATUS
370 NTAPI
371 NtSetContextThread(IN HANDLE ThreadHandle,
372                    IN PCONTEXT ThreadContext)
373 {
374     PETHREAD Thread;
375     NTSTATUS Status;
376     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
377     PAGED_CODE();
378 
379     /* Get the Thread Object */
380     Status = ObReferenceObjectByHandle(ThreadHandle,
381                                        THREAD_SET_CONTEXT,
382                                        PsThreadType,
383                                        PreviousMode,
384                                        (PVOID*)&Thread,
385                                        NULL);
386 
387     if (!NT_SUCCESS(Status)) return Status;
388 
389     /* Make sure it's not a system thread */
390     if (Thread->SystemThread)
391     {
392         /* Fail */
393         Status = STATUS_INVALID_HANDLE;
394     }
395     else
396     {
397         /* Call the kernel API */
398         Status = PsSetContextThread(Thread, ThreadContext, PreviousMode);
399     }
400 
401     /* Dereference it and return */
402     ObDereferenceObject(Thread);
403     return Status;
404 }
405 
406 /* EOF */
407