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