xref: /reactos/ntoskrnl/ps/debug.c (revision bbabe248)
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                         ULONG EbpContent[2];
74                         ULONG MemoryCopied;
75                         NTSTATUS Status;
76 
77                         /* Get stack frame content */
78                         Status = KdpCopyMemoryChunks((ULONG64)(ULONG_PTR)Ebp,
79                                                      EbpContent,
80                                                      sizeof(EbpContent),
81                                                      sizeof(EbpContent),
82                                                      MMDBG_COPY_UNSAFE,
83                                                      &MemoryCopied);
84                         if (!NT_SUCCESS(Status) || (MemoryCopied < sizeof(EbpContent)))
85                         {
86                             break;
87                         }
88 
89                         DbgPrint("%.8X %.8X%s", EbpContent[0], EbpContent[1], (i % 8) == 7 ? "\n" : "  ");
90                         Ebp = (PULONG)EbpContent[0];
91                         i++;
92                     }
93 
94                     /* Print a new line if there's nothing */
95                     if((i % 8) != 0) DbgPrint("\n");
96 #else
97                     DbgPrint("FIXME: Backtrace skipped on non-x86\n");
98 #endif
99                 }
100 
101                 /* Move to the next Thread */
102                 CurrentThread = CurrentThread->Flink;
103             }
104         }
105 
106         /* Move to the next Process */
107         CurrentProcess = CurrentProcess->Flink;
108     }
109 }
110 #endif
111 
112 /* PUBLIC FUNCTIONS **********************************************************/
113 
114 /*
115  * @implemented
116  */
117 NTSTATUS
118 NTAPI
119 PsGetContextThread(IN PETHREAD Thread,
120                    IN OUT PCONTEXT ThreadContext,
121                    IN KPROCESSOR_MODE PreviousMode)
122 {
123     GET_SET_CTX_CONTEXT GetSetContext;
124     ULONG Size = 0, Flags = 0;
125     NTSTATUS Status;
126 
127     /* Enter SEH */
128     _SEH2_TRY
129     {
130         /* Set default length */
131         Size = sizeof(CONTEXT);
132 
133         /* Read the flags */
134         Flags = ProbeForReadUlong(&ThreadContext->ContextFlags);
135 
136 #ifdef _M_IX86
137         /* Check if the caller wanted extended registers */
138         if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
139             CONTEXT_EXTENDED_REGISTERS)
140         {
141             /* Cut them out of the size */
142             Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
143         }
144 #endif
145 
146         /* Check if we came from user mode */
147         if (PreviousMode != KernelMode)
148         {
149             /* Probe the context */
150             ProbeForWrite(ThreadContext, Size, sizeof(ULONG));
151         }
152     }
153     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
154     {
155         /* Return the exception code */
156         _SEH2_YIELD(return _SEH2_GetExceptionCode());
157     }
158     _SEH2_END;
159 
160     /* Initialize the wait event */
161     KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
162 
163     /* Set the flags and previous mode */
164     RtlZeroMemory(&GetSetContext.Context, Size);
165     GetSetContext.Context.ContextFlags = Flags;
166     GetSetContext.Mode = PreviousMode;
167 
168     /* Check if we're running in the same thread */
169     if (Thread == PsGetCurrentThread())
170     {
171         /* Setup APC parameters manually */
172         GetSetContext.Apc.SystemArgument1 = NULL;
173         GetSetContext.Apc.SystemArgument2 = Thread;
174 
175         /* Enter a guarded region to simulate APC_LEVEL */
176         KeEnterGuardedRegion();
177 
178         /* Manually call the APC */
179         PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
180                                         NULL,
181                                         NULL,
182                                         &GetSetContext.Apc.SystemArgument1,
183                                         &GetSetContext.Apc.SystemArgument2);
184 
185         /* Leave the guarded region */
186         KeLeaveGuardedRegion();
187 
188         /* We are done */
189         Status = STATUS_SUCCESS;
190     }
191     else
192     {
193         /* Initialize the APC */
194         KeInitializeApc(&GetSetContext.Apc,
195                         &Thread->Tcb,
196                         OriginalApcEnvironment,
197                         PspGetOrSetContextKernelRoutine,
198                         NULL,
199                         NULL,
200                         KernelMode,
201                         NULL);
202 
203         /* Queue it as a Get APC */
204         if (!KeInsertQueueApc(&GetSetContext.Apc, NULL, Thread, 2))
205         {
206             /* It was already queued, so fail */
207             Status = STATUS_UNSUCCESSFUL;
208         }
209         else
210         {
211             /* Wait for the APC to complete */
212             Status = KeWaitForSingleObject(&GetSetContext.Event,
213                                            0,
214                                            KernelMode,
215                                            FALSE,
216                                            NULL);
217         }
218     }
219 
220     _SEH2_TRY
221     {
222         /* Copy the context */
223         RtlCopyMemory(ThreadContext, &GetSetContext.Context, Size);
224     }
225     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
226     {
227         /* Get the exception code */
228         Status = _SEH2_GetExceptionCode();
229     }
230     _SEH2_END;
231 
232     /* Return status */
233     return Status;
234 }
235 
236 /*
237  * @implemented
238  */
239 NTSTATUS
240 NTAPI
241 PsSetContextThread(IN PETHREAD Thread,
242                    IN OUT PCONTEXT ThreadContext,
243                    IN KPROCESSOR_MODE PreviousMode)
244 {
245     GET_SET_CTX_CONTEXT GetSetContext;
246     ULONG Size = 0, Flags = 0;
247     NTSTATUS Status;
248 
249     /* Enter SEH */
250     _SEH2_TRY
251     {
252         /* Set default length */
253         Size = sizeof(CONTEXT);
254 
255         /* Read the flags */
256         Flags = ProbeForReadUlong(&ThreadContext->ContextFlags);
257 
258 #ifdef _M_IX86
259         /* Check if the caller wanted extended registers */
260         if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
261             CONTEXT_EXTENDED_REGISTERS)
262         {
263             /* Cut them out of the size */
264             Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
265         }
266 #endif
267 
268         /* Check if we came from user mode */
269         if (PreviousMode != KernelMode)
270         {
271             /* Probe the context */
272             ProbeForRead(ThreadContext, Size, sizeof(ULONG));
273         }
274 
275         /* Copy the context */
276         RtlCopyMemory(&GetSetContext.Context, ThreadContext, Size);
277     }
278     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
279     {
280         /* Return the exception code */
281         _SEH2_YIELD(return _SEH2_GetExceptionCode());
282     }
283     _SEH2_END;
284 
285     /* Initialize the wait event */
286     KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
287 
288     /* Set the flags and previous mode */
289     GetSetContext.Context.ContextFlags = Flags;
290     GetSetContext.Mode = PreviousMode;
291 
292     /* Check if we're running in the same thread */
293     if (Thread == PsGetCurrentThread())
294     {
295         /* Setup APC parameters manually */
296         GetSetContext.Apc.SystemArgument1 = UlongToPtr(1);
297         GetSetContext.Apc.SystemArgument2 = Thread;
298 
299         /* Enter a guarded region to simulate APC_LEVEL */
300         KeEnterGuardedRegion();
301 
302         /* Manually call the APC */
303         PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
304                                         NULL,
305                                         NULL,
306                                         &GetSetContext.Apc.SystemArgument1,
307                                         &GetSetContext.Apc.SystemArgument2);
308 
309         /* Leave the guarded region */
310         KeLeaveGuardedRegion();
311 
312         /* We are done */
313         Status = STATUS_SUCCESS;
314     }
315     else
316     {
317         /* Initialize the APC */
318         KeInitializeApc(&GetSetContext.Apc,
319                         &Thread->Tcb,
320                         OriginalApcEnvironment,
321                         PspGetOrSetContextKernelRoutine,
322                         NULL,
323                         NULL,
324                         KernelMode,
325                         NULL);
326 
327         /* Queue it as a Get APC */
328         if (!KeInsertQueueApc(&GetSetContext.Apc, UlongToPtr(1), Thread, 2))
329         {
330             /* It was already queued, so fail */
331             Status = STATUS_UNSUCCESSFUL;
332         }
333         else
334         {
335             /* Wait for the APC to complete */
336             Status = KeWaitForSingleObject(&GetSetContext.Event,
337                                            0,
338                                            KernelMode,
339                                            FALSE,
340                                            NULL);
341         }
342     }
343 
344     /* Return status */
345     return Status;
346 }
347 
348 NTSTATUS
349 NTAPI
350 NtGetContextThread(IN HANDLE ThreadHandle,
351                    IN OUT PCONTEXT ThreadContext)
352 {
353     PETHREAD Thread;
354     NTSTATUS Status;
355     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
356     PAGED_CODE();
357 
358     /* Get the Thread Object */
359     Status = ObReferenceObjectByHandle(ThreadHandle,
360                                        THREAD_GET_CONTEXT,
361                                        PsThreadType,
362                                        PreviousMode,
363                                        (PVOID*)&Thread,
364                                        NULL);
365 
366     if (!NT_SUCCESS(Status)) return Status;
367 
368     /* Make sure it's not a system thread */
369     if (Thread->SystemThread)
370     {
371         /* Fail */
372         Status = STATUS_INVALID_HANDLE;
373     }
374     else
375     {
376         /* Call the kernel API */
377         Status = PsGetContextThread(Thread, ThreadContext, PreviousMode);
378     }
379 
380     /* Dereference it and return */
381     ObDereferenceObject(Thread);
382     return Status;
383 }
384 
385 NTSTATUS
386 NTAPI
387 NtSetContextThread(IN HANDLE ThreadHandle,
388                    IN PCONTEXT ThreadContext)
389 {
390     PETHREAD Thread;
391     NTSTATUS Status;
392     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
393     PAGED_CODE();
394 
395     /* Get the Thread Object */
396     Status = ObReferenceObjectByHandle(ThreadHandle,
397                                        THREAD_SET_CONTEXT,
398                                        PsThreadType,
399                                        PreviousMode,
400                                        (PVOID*)&Thread,
401                                        NULL);
402 
403     if (!NT_SUCCESS(Status)) return Status;
404 
405     /* Make sure it's not a system thread */
406     if (Thread->SystemThread)
407     {
408         /* Fail */
409         Status = STATUS_INVALID_HANDLE;
410     }
411     else
412     {
413         /* Call the kernel API */
414         Status = PsSetContextThread(Thread, ThreadContext, PreviousMode);
415     }
416 
417     /* Dereference it and return */
418     ObDereferenceObject(Thread);
419     return Status;
420 }
421 
422 /* EOF */
423