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