1682fa8940fbc48fca0f65b1edd1e46c545a086c
[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 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[0]);
64 return TRUE;
65 }
66 }
67
68 if (Argc > 2)
69 {
70 /* Get address */
71 if (!KdbpGetHexNumber(Argv[1], &Flags))
72 {
73 KdbpPrint("Invalid parameter: %s\n", Argv[0]);
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 ULONG 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 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[0]);
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'))
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)(PVOID, 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].Va, 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[0]);
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 */