xref: /reactos/ntoskrnl/mm/ARM3/kdbg.c (revision 3e1f4074)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/kdbg.c
5  * PURPOSE:         ARM Memory Manager Kernel Debugger routines
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  *                  Pierre Schweitzer
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #define MODULE_INVOLVED_IN_ARM3
17 #include <mm/ARM3/miarm.h>
18 
19 /* GLOBALS ********************************************************************/
20 
21 typedef struct _IRP_FIND_CTXT
22 {
23     ULONG_PTR RestartAddress;
24     ULONG_PTR SData;
25     ULONG Criteria;
26 } IRP_FIND_CTXT, *PIRP_FIND_CTXT;
27 
28 extern PVOID MmNonPagedPoolEnd0;
29 extern SIZE_T PoolBigPageTableSize;
30 extern PPOOL_TRACKER_BIG_PAGES PoolBigPageTable;
31 
32 #define POOL_BIG_TABLE_ENTRY_FREE 0x1
33 
34 /* Pool block/header/list access macros */
35 #define POOL_ENTRY(x)       (PPOOL_HEADER)((ULONG_PTR)(x) - sizeof(POOL_HEADER))
36 #define POOL_FREE_BLOCK(x)  (PLIST_ENTRY)((ULONG_PTR)(x)  + sizeof(POOL_HEADER))
37 #define POOL_BLOCK(x, i)    (PPOOL_HEADER)((ULONG_PTR)(x) + ((i) * POOL_BLOCK_SIZE))
38 #define POOL_NEXT_BLOCK(x)  POOL_BLOCK((x), (x)->BlockSize)
39 #define POOL_PREV_BLOCK(x)  POOL_BLOCK((x), -((x)->PreviousSize))
40 
41 VOID MiDumpPoolConsumers(BOOLEAN CalledFromDbg, ULONG Tag, ULONG Mask, ULONG Flags);
42 
43 /* PRIVATE FUNCTIONS **********************************************************/
44 
45 #if DBG && defined(KDBG)
46 
47 BOOLEAN
48 ExpKdbgExtPool(
49     ULONG Argc,
50     PCHAR Argv[])
51 {
52     ULONG_PTR Address = 0, Flags = 0;
53     PVOID PoolPage;
54     PPOOL_HEADER Entry;
55     BOOLEAN ThisOne;
56     PULONG Data;
57 
58     if (Argc > 1)
59     {
60         /* Get address */
61         if (!KdbpGetHexNumber(Argv[1], &Address))
62         {
63             KdbpPrint("Invalid parameter: %s\n", Argv[1]);
64             return TRUE;
65         }
66     }
67 
68     if (Argc > 2)
69     {
70         /* Get flags */
71         if (!KdbpGetHexNumber(Argv[2], &Flags))
72         {
73             KdbpPrint("Invalid parameter: %s\n", Argv[2]);
74             return TRUE;
75         }
76     }
77 
78     /* Check if we got an address */
79     if (Address != 0)
80     {
81         /* Get the base page */
82         PoolPage = PAGE_ALIGN(Address);
83     }
84     else
85     {
86         KdbpPrint("Heap is unimplemented\n");
87         return TRUE;
88     }
89 
90     /* No paging support! */
91     if (!MmIsAddressValid(PoolPage))
92     {
93         KdbpPrint("Address not accessible!\n");
94         return TRUE;
95     }
96 
97     /* Get pool type */
98     if ((Address >= (ULONG_PTR)MmPagedPoolStart) && (Address <= (ULONG_PTR)MmPagedPoolEnd))
99         KdbpPrint("Allocation is from PagedPool region\n");
100     else if ((Address >= (ULONG_PTR)MmNonPagedPoolStart) && (Address <= (ULONG_PTR)MmNonPagedPoolEnd))
101         KdbpPrint("Allocation is from NonPagedPool region\n");
102     else
103     {
104         KdbpPrint("Address 0x%p is not within any pool!\n", (PVOID)Address);
105         return TRUE;
106     }
107 
108     /* Loop all entries of that page */
109     Entry = PoolPage;
110     do
111     {
112         /* Check if the address is within that entry */
113         ThisOne = ((Address >= (ULONG_PTR)Entry) &&
114                    (Address < (ULONG_PTR)(Entry + Entry->BlockSize)));
115 
116         if (!(Flags & 1) || ThisOne)
117         {
118             /* Print the line */
119             KdbpPrint("%c%p size: %4d previous size: %4d  %s  %.4s\n",
120                      ThisOne ? '*' : ' ', Entry, Entry->BlockSize, Entry->PreviousSize,
121                      (Flags & 0x80000000) ? "" : (Entry->PoolType ? "(Allocated)" : "(Free)     "),
122                      (Flags & 0x80000000) ? "" : (PCHAR)&Entry->PoolTag);
123         }
124 
125         if (Flags & 1)
126         {
127             Data = (PULONG)(Entry + 1);
128             KdbpPrint("    %p  %08lx %08lx %08lx %08lx\n"
129                      "    %p  %08lx %08lx %08lx %08lx\n",
130                      &Data[0], Data[0], Data[1], Data[2], Data[3],
131                      &Data[4], Data[4], Data[5], Data[6], Data[7]);
132         }
133 
134         /* Go to next entry */
135         Entry = POOL_BLOCK(Entry, Entry->BlockSize);
136     }
137     while ((Entry->BlockSize != 0) && ((ULONG_PTR)Entry < (ULONG_PTR)PoolPage + PAGE_SIZE));
138 
139     return TRUE;
140 }
141 
142 static
143 VOID
144 ExpKdbgExtPoolUsedGetTag(PCHAR Arg, PULONG Tag, PULONG Mask)
145 {
146     CHAR Tmp[4];
147     SIZE_T Len;
148     USHORT i;
149 
150     /* Get the tag */
151     Len = strlen(Arg);
152     if (Len > 4)
153     {
154         Len = 4;
155     }
156 
157     /* Generate the mask to have wildcards support */
158     for (i = 0; i < Len; ++i)
159     {
160         Tmp[i] = Arg[i];
161         if (Tmp[i] != '?')
162         {
163             *Mask |= (0xFF << i * 8);
164         }
165     }
166 
167     /* Get the tag in the ulong form */
168     *Tag = *((PULONG)Tmp);
169 }
170 
171 BOOLEAN
172 ExpKdbgExtPoolUsed(
173     ULONG Argc,
174     PCHAR Argv[])
175 {
176     ULONG Tag = 0;
177     ULONG Mask = 0;
178     ULONG_PTR Flags = 0;
179 
180     if (Argc > 1)
181     {
182         /* If we have 2+ args, easy: flags then tag */
183         if (Argc > 2)
184         {
185             ExpKdbgExtPoolUsedGetTag(Argv[2], &Tag, &Mask);
186             if (!KdbpGetHexNumber(Argv[1], &Flags))
187             {
188                 KdbpPrint("Invalid parameter: %s\n", Argv[1]);
189             }
190         }
191         else
192         {
193             /* Otherwise, try to find out whether that's flags */
194             if (strlen(Argv[1]) == 1 ||
195                 (strlen(Argv[1]) == 3 && Argv[1][0] == '0' && (Argv[1][1] == 'x' || Argv[1][1] == 'X')))
196             {
197                 /* Fallback: if reading flags failed, assume it's a tag */
198                 if (!KdbpGetHexNumber(Argv[1], &Flags))
199                 {
200                     ExpKdbgExtPoolUsedGetTag(Argv[1], &Tag, &Mask);
201                 }
202             }
203             /* Or tag */
204             else
205             {
206                 ExpKdbgExtPoolUsedGetTag(Argv[1], &Tag, &Mask);
207             }
208         }
209     }
210 
211     /* Call the dumper */
212     MiDumpPoolConsumers(TRUE, Tag, Mask, Flags);
213 
214     return TRUE;
215 }
216 
217 static
218 VOID
219 ExpKdbgExtPoolFindLargePool(
220     ULONG Tag,
221     ULONG Mask,
222     VOID (NTAPI* FoundCallback)(PPOOL_TRACKER_BIG_PAGES, PVOID),
223     PVOID CallbackContext)
224 {
225     ULONG i;
226 
227     KdbpPrint("Scanning large pool allocation table for Tag: %.4s (%p : %p)\n", (PCHAR)&Tag, &PoolBigPageTable[0], &PoolBigPageTable[PoolBigPageTableSize - 1]);
228 
229     for (i = 0; i < PoolBigPageTableSize; i++)
230     {
231         /* Free entry? */
232         if ((ULONG_PTR)PoolBigPageTable[i].Va & POOL_BIG_TABLE_ENTRY_FREE)
233         {
234             continue;
235         }
236 
237         if ((PoolBigPageTable[i].Key & Mask) == (Tag & Mask))
238         {
239             if (FoundCallback != NULL)
240             {
241                 FoundCallback(&PoolBigPageTable[i], CallbackContext);
242             }
243             else
244             {
245                 /* Print the line */
246                 KdbpPrint("%p: tag %.4s, size: %I64x\n",
247                           PoolBigPageTable[i].Va, (PCHAR)&PoolBigPageTable[i].Key,
248                           PoolBigPageTable[i].NumberOfPages << PAGE_SHIFT);
249             }
250         }
251     }
252 }
253 
254 static
255 BOOLEAN
256 ExpKdbgExtValidatePoolHeader(
257     PVOID BaseVa,
258     PPOOL_HEADER Entry,
259     POOL_TYPE BasePoolTye)
260 {
261     /* Block size cannot be NULL or negative and it must cover the page */
262     if (Entry->BlockSize <= 0)
263     {
264         return FALSE;
265     }
266     if (Entry->BlockSize * 8 + (ULONG_PTR)Entry - (ULONG_PTR)BaseVa > PAGE_SIZE)
267     {
268         return FALSE;
269     }
270 
271     /*
272      * PreviousSize cannot be 0 unless on page begin
273      * And it cannot be bigger that our current
274      * position in page
275      */
276     if (Entry->PreviousSize == 0 && BaseVa != Entry)
277     {
278         return FALSE;
279     }
280     if (Entry->PreviousSize * 8 > (ULONG_PTR)Entry - (ULONG_PTR)BaseVa)
281     {
282         return FALSE;
283     }
284 
285     /* Must be paged pool */
286     if (((Entry->PoolType - 1) & BASE_POOL_TYPE_MASK) != BasePoolTye)
287     {
288         return FALSE;
289     }
290 
291     /* Match tag mask */
292     if ((Entry->PoolTag & 0x00808080) != 0)
293     {
294         return FALSE;
295     }
296 
297     return TRUE;
298 }
299 
300 static
301 VOID
302 ExpKdbgExtPoolFindPagedPool(
303     ULONG Tag,
304     ULONG Mask,
305     VOID (NTAPI* FoundCallback)(PPOOL_HEADER, PVOID),
306     PVOID CallbackContext)
307 {
308     ULONG i = 0;
309     PPOOL_HEADER Entry;
310     PVOID BaseVa;
311     PMMPDE PointerPde;
312 
313     KdbpPrint("Searching Paged pool (%p : %p) for Tag: %.4s\n", MmPagedPoolStart, MmPagedPoolEnd, (PCHAR)&Tag);
314 
315     /*
316      * To speed up paged pool search, we will use the allocation bipmap.
317      * This is possible because we live directly in the kernel :-)
318      */
319     i = RtlFindSetBits(MmPagedPoolInfo.PagedPoolAllocationMap, 1, 0);
320     while (i != 0xFFFFFFFF)
321     {
322         BaseVa = (PVOID)((ULONG_PTR)MmPagedPoolStart + (i << PAGE_SHIFT));
323         Entry = BaseVa;
324 
325         /* Validate our address */
326         if ((ULONG_PTR)BaseVa > (ULONG_PTR)MmPagedPoolEnd || (ULONG_PTR)BaseVa + PAGE_SIZE > (ULONG_PTR)MmPagedPoolEnd)
327         {
328             break;
329         }
330 
331         /* Check whether we are beyond expansion */
332         PointerPde = MiAddressToPde(BaseVa);
333         if (PointerPde >= MmPagedPoolInfo.NextPdeForPagedPoolExpansion)
334         {
335             break;
336         }
337 
338         /* Check if allocation is valid */
339         if (MmIsAddressValid(BaseVa))
340         {
341             for (Entry = BaseVa;
342                  (ULONG_PTR)Entry + sizeof(POOL_HEADER) < (ULONG_PTR)BaseVa + PAGE_SIZE;
343                  Entry = (PVOID)((ULONG_PTR)Entry + 8))
344             {
345                 /* Try to find whether we have a pool entry */
346                 if (!ExpKdbgExtValidatePoolHeader(BaseVa, Entry, PagedPool))
347                 {
348                     continue;
349                 }
350 
351                 if ((Entry->PoolTag & Mask) == (Tag & Mask))
352                 {
353                     if (FoundCallback != NULL)
354                     {
355                         FoundCallback(Entry, CallbackContext);
356                     }
357                     else
358                     {
359                         /* Print the line */
360                         KdbpPrint("%p size: %4d previous size: %4d  %s  %.4s\n",
361                                   Entry, Entry->BlockSize, Entry->PreviousSize,
362                                   Entry->PoolType ? "(Allocated)" : "(Free)     ",
363                                   (PCHAR)&Entry->PoolTag);
364                     }
365                 }
366             }
367         }
368 
369         i = RtlFindSetBits(MmPagedPoolInfo.PagedPoolAllocationMap, 1, i + 1);
370     }
371 }
372 
373 static
374 VOID
375 ExpKdbgExtPoolFindNonPagedPool(
376     ULONG Tag,
377     ULONG Mask,
378     VOID (NTAPI* FoundCallback)(PPOOL_HEADER, PVOID),
379     PVOID CallbackContext)
380 {
381     PPOOL_HEADER Entry;
382     PVOID BaseVa;
383 
384     KdbpPrint("Searching NonPaged pool (%p : %p) for Tag: %.4s\n", MmNonPagedPoolStart, MmNonPagedPoolEnd0, (PCHAR)&Tag);
385 
386     /* Brute force search: start browsing the whole non paged pool */
387     for (BaseVa = MmNonPagedPoolStart;
388          (ULONG_PTR)BaseVa + PAGE_SIZE <= (ULONG_PTR)MmNonPagedPoolEnd0;
389          BaseVa = (PVOID)((ULONG_PTR)BaseVa + PAGE_SIZE))
390     {
391         Entry = BaseVa;
392 
393         /* Check whether we are beyond expansion */
394         if (BaseVa >= MmNonPagedPoolExpansionStart)
395         {
396             break;
397         }
398 
399         /* Check if allocation is valid */
400         if (!MmIsAddressValid(BaseVa))
401         {
402             continue;
403         }
404 
405         for (Entry = BaseVa;
406              (ULONG_PTR)Entry + sizeof(POOL_HEADER) < (ULONG_PTR)BaseVa + PAGE_SIZE;
407              Entry = (PVOID)((ULONG_PTR)Entry + 8))
408         {
409             /* Try to find whether we have a pool entry */
410             if (!ExpKdbgExtValidatePoolHeader(BaseVa, Entry, NonPagedPool))
411             {
412                 continue;
413             }
414 
415             if ((Entry->PoolTag & Mask) == (Tag & Mask))
416             {
417                 if (FoundCallback != NULL)
418                 {
419                     FoundCallback(Entry, CallbackContext);
420                 }
421                 else
422                 {
423                     /* Print the line */
424                     KdbpPrint("%p size: %4d previous size: %4d  %s  %.4s\n",
425                               Entry, Entry->BlockSize, Entry->PreviousSize,
426                               Entry->PoolType ? "(Allocated)" : "(Free)     ",
427                               (PCHAR)&Entry->PoolTag);
428                 }
429             }
430         }
431     }
432 }
433 
434 BOOLEAN
435 ExpKdbgExtPoolFind(
436     ULONG Argc,
437     PCHAR Argv[])
438 {
439     ULONG Tag = 0;
440     ULONG Mask = 0;
441     ULONG PoolType = NonPagedPool;
442 
443     if (Argc == 1)
444     {
445         KdbpPrint("Specify a tag string\n");
446         return TRUE;
447     }
448 
449     /* First arg is tag */
450     if (strlen(Argv[1]) != 1 || Argv[1][0] != '*')
451     {
452         ExpKdbgExtPoolUsedGetTag(Argv[1], &Tag, &Mask);
453     }
454 
455     /* Second arg might be pool to search */
456     if (Argc > 2)
457     {
458         PoolType = strtoul(Argv[2], NULL, 0);
459 
460         if (PoolType > 1)
461         {
462             KdbpPrint("Only (non) paged pool are supported\n");
463             return TRUE;
464         }
465     }
466 
467     /* First search for large allocations */
468     ExpKdbgExtPoolFindLargePool(Tag, Mask, NULL, NULL);
469 
470     if (PoolType == NonPagedPool)
471     {
472         ExpKdbgExtPoolFindNonPagedPool(Tag, Mask, NULL, NULL);
473     }
474     else if (PoolType == PagedPool)
475     {
476         ExpKdbgExtPoolFindPagedPool(Tag, Mask, NULL, NULL);
477     }
478 
479     return TRUE;
480 }
481 
482 VOID
483 NTAPI
484 ExpKdbgExtIrpFindPrint(
485     PPOOL_HEADER Entry,
486     PVOID Context)
487 {
488     PIRP Irp;
489     BOOLEAN IsComplete = FALSE;
490     PIRP_FIND_CTXT FindCtxt = Context;
491     PIO_STACK_LOCATION IoStack = NULL;
492     PUNICODE_STRING DriverName = NULL;
493     ULONG_PTR SData = FindCtxt->SData;
494     ULONG Criteria = FindCtxt->Criteria;
495 
496     /* Free entry, ignore */
497     if (Entry->PoolType == 0)
498     {
499         return;
500     }
501 
502     /* Get the IRP */
503     Irp = (PIRP)POOL_FREE_BLOCK(Entry);
504 
505     /* Bail out if not matching restart address */
506     if ((ULONG_PTR)Irp < FindCtxt->RestartAddress)
507     {
508         return;
509     }
510 
511     /* Avoid bogus IRP stack locations */
512     if (Irp->CurrentLocation <= Irp->StackCount + 1)
513     {
514         IoStack = IoGetCurrentIrpStackLocation(Irp);
515 
516         /* Get associated driver */
517         if (IoStack->DeviceObject && IoStack->DeviceObject->DriverObject)
518             DriverName = &IoStack->DeviceObject->DriverObject->DriverName;
519     }
520     else
521     {
522         IsComplete = TRUE;
523     }
524 
525     /* Display if: no data, no criteria or if criteria matches data */
526     if (SData == 0 || Criteria == 0 ||
527         (Criteria & 0x1 && IoStack && SData == (ULONG_PTR)IoStack->DeviceObject) ||
528         (Criteria & 0x2 && SData == (ULONG_PTR)Irp->Tail.Overlay.OriginalFileObject) ||
529         (Criteria & 0x4 && Irp->MdlAddress && SData == (ULONG_PTR)Irp->MdlAddress->Process) ||
530         (Criteria & 0x8 && SData == (ULONG_PTR)Irp->Tail.Overlay.Thread) ||
531         (Criteria & 0x10 && SData == (ULONG_PTR)Irp->UserEvent))
532     {
533         if (!IsComplete)
534         {
535             KdbpPrint("%p Thread %p current stack (%x, %x) belongs to %wZ\n", Irp, Irp->Tail.Overlay.Thread, IoStack->MajorFunction, IoStack->MinorFunction, DriverName);
536         }
537         else
538         {
539             KdbpPrint("%p Thread %p is complete (CurrentLocation %d > StackCount %d)\n", Irp, Irp->Tail.Overlay.Thread, Irp->CurrentLocation, Irp->StackCount + 1);
540         }
541     }
542 }
543 
544 BOOLEAN
545 ExpKdbgExtIrpFind(
546     ULONG Argc,
547     PCHAR Argv[])
548 {
549     ULONG PoolType = NonPagedPool;
550     IRP_FIND_CTXT FindCtxt;
551 
552     /* Pool type */
553     if (Argc > 1)
554     {
555         PoolType = strtoul(Argv[1], NULL, 0);
556 
557         if (PoolType > 1)
558         {
559             KdbpPrint("Only (non) paged pool are supported\n");
560             return TRUE;
561         }
562     }
563 
564     RtlZeroMemory(&FindCtxt, sizeof(IRP_FIND_CTXT));
565 
566     /* Restart address */
567     if (Argc > 2)
568     {
569         if (!KdbpGetHexNumber(Argv[2], &FindCtxt.RestartAddress))
570         {
571             KdbpPrint("Invalid parameter: %s\n", Argv[2]);
572             FindCtxt.RestartAddress = 0;
573         }
574     }
575 
576     if (Argc > 4)
577     {
578         if (!KdbpGetHexNumber(Argv[4], &FindCtxt.SData))
579         {
580             FindCtxt.SData = 0;
581         }
582         else
583         {
584             if (strcmp(Argv[3], "device") == 0)
585             {
586                 FindCtxt.Criteria = 0x1;
587             }
588             else if (strcmp(Argv[3], "fileobject") == 0)
589             {
590                 FindCtxt.Criteria = 0x2;
591             }
592             else if (strcmp(Argv[3], "mdlprocess") == 0)
593             {
594                 FindCtxt.Criteria = 0x4;
595             }
596             else if (strcmp(Argv[3], "thread") == 0)
597             {
598                 FindCtxt.Criteria = 0x8;
599             }
600             else if (strcmp(Argv[3], "userevent") == 0)
601             {
602                 FindCtxt.Criteria = 0x10;
603             }
604             else if (strcmp(Argv[3], "arg") == 0)
605             {
606                 FindCtxt.Criteria = 0x1f;
607             }
608         }
609     }
610 
611     if (PoolType == NonPagedPool)
612     {
613         ExpKdbgExtPoolFindNonPagedPool(TAG_IRP, 0xFFFFFFFF, ExpKdbgExtIrpFindPrint, &FindCtxt);
614     }
615     else if (PoolType == PagedPool)
616     {
617         ExpKdbgExtPoolFindPagedPool(TAG_IRP, 0xFFFFFFFF, ExpKdbgExtIrpFindPrint, &FindCtxt);
618     }
619 
620     return TRUE;
621 }
622 
623 #endif // DBG && KDBG
624 
625 /* EOF */
626