Synchronize up to trunk's revision r57784.
[reactos.git] / win32ss / gdi / ntgdi / gdidbg.c
1 /*
2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: subsystems/win32/win32k/objects/gdidbg.c
5 * PURPOSE: Special debugging functions for GDI
6 * PROGRAMMERS: Timo Kreuzer
7 */
8
9 /** INCLUDES ******************************************************************/
10
11 #if DBG
12 #include <win32k.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 extern ULONG gulFirstFree;
17 extern ULONG gulFirstUnused;
18
19 ULONG gulLogUnique = 0;
20
21 /* Note: the following values need to be sorted */
22 DBG_CHANNEL DbgChannels[DbgChCount]={
23 {L"EngBlt", DbgChEngBlt},
24 {L"EngBrush", DbgChEngBrush},
25 {L"EngClip", DbgChEngClip},
26 {L"EngCursor", DbgChEngCursor},
27 {L"EngDev", DbgChEngDev},
28 {L"EngErr", DbgChEngErr},
29 {L"EngEvent", DbgChEngEvent},
30 {L"EngGrad", DbgChEngGrad},
31 {L"EngLDev", DbgChEngLDev},
32 {L"EngLine", DbgChEngLine},
33 {L"EngMapping", DbgChEngMapping},
34 {L"EngPDev", DbgChEngPDev},
35 {L"EngSurface", DbgChEngSurface},
36 {L"EngWnd", DbgChEngWnd},
37 {L"EngXlate", DbgChEngXlate},
38 {L"GdiBitmap", DbgChGdiBitmap},
39 {L"GdiBlt", DbgChGdiBlt},
40 {L"GdiBrush", DbgChGdiBrush},
41 {L"GdiClipRgn", DbgChGdiClipRgn},
42 {L"GdiCoord", DbgChGdiCoord},
43 {L"GdiDC", DbgChGdiDC},
44 {L"GdiDCAttr", DbgChGdiDCAttr},
45 {L"GdiDCState", DbgChGdiDCState},
46 {L"GdiDev", DbgChGdiDev},
47 {L"GdiDib", DbgChGdiDib},
48 {L"GdiFont", DbgChGdiFont},
49 {L"GdiLine", DbgChGdiLine},
50 {L"GdiObj", DbgChGdiObj},
51 {L"GdiPalette", DbgChGdiPalette},
52 {L"GdiPath", DbgChGdiPath},
53 {L"GdiPen", DbgChGdiPen},
54 {L"GdiPool", DbgChGdiPool},
55 {L"GdiRgn", DbgChGdiRgn},
56 {L"GdiText", DbgChGdiText},
57 {L"GdiXFormObj", DbgChGdiXFormObj},
58 {L"UserAccel", DbgChUserAccel},
59 {L"UserCallback", DbgChUserCallback},
60 {L"UserCallProc", DbgChUserCallProc},
61 {L"UserCaret", DbgChUserCaret},
62 {L"UserClass", DbgChUserClass},
63 {L"UserClipbrd", DbgChUserClipbrd},
64 {L"UserCsr", DbgChUserCsr},
65 {L"UserDce", DbgChUserDce},
66 {L"UserDefwnd", DbgChUserDefwnd},
67 {L"UserDesktop", DbgChUserDesktop},
68 {L"UserDisplay",DbgChUserDisplay},
69 {L"UserEvent", DbgChUserEvent},
70 {L"UserFocus", DbgChUserFocus},
71 {L"UserHook", DbgChUserHook},
72 {L"UserHotkey", DbgChUserHotkey},
73 {L"UserIcon", DbgChUserIcon},
74 {L"UserInput", DbgChUserInput},
75 {L"UserKbd", DbgChUserKbd},
76 {L"UserKbdLayout", DbgChUserKbdLayout},
77 {L"UserMenu", DbgChUserMenu},
78 {L"UserMetric", DbgChUserMetric},
79 {L"UserMisc", DbgChUserMisc},
80 {L"UserMonitor", DbgChUserMonitor},
81 {L"UserMsg", DbgChUserMsg},
82 {L"UserMsgQ", DbgChUserMsgQ},
83 {L"UserObj", DbgChUserObj},
84 {L"UserPainting", DbgChUserPainting},
85 {L"UserProcess", DbgChUserProcess},
86 {L"UserProp", DbgChUserProp},
87 {L"UserScrollbar", DbgChUserScrollbar},
88 {L"UserSysparams", DbgChUserSysparams},
89 {L"UserTimer", DbgChUserTimer},
90 {L"UserThread", DbgChUserThread},
91 {L"UserWinpos", DbgChUserWinpos},
92 {L"UserWinsta", DbgChUserWinsta},
93 {L"UserWnd", DbgChUserWnd}
94 };
95
96 #ifdef GDI_DEBUG
97 #if 0
98 static
99 BOOL
100 CompareBacktraces(ULONG idx1, ULONG idx2)
101 {
102 ULONG iLevel;
103
104 /* Loop all stack levels */
105 for (iLevel = 0; iLevel < GDI_STACK_LEVELS; iLevel++)
106 {
107 if (GDIHandleAllocator[idx1][iLevel]
108 != GDIHandleAllocator[idx2][iLevel])
109 // if (GDIHandleShareLocker[idx1][iLevel]
110 // != GDIHandleShareLocker[idx2][iLevel])
111 {
112 return FALSE;
113 }
114 }
115
116 return TRUE;
117 }
118
119 VOID
120 NTAPI
121 DbgDumpGdiHandleTableWithBT(void)
122 {
123 static int leak_reported = 0;
124 int i, j, idx, nTraces = 0;
125 KIRQL OldIrql;
126
127 if (leak_reported)
128 {
129 DPRINT1("GDI handle abusers already reported!\n");
130 return;
131 }
132
133 leak_reported = 1;
134 DPRINT1("Reporting GDI handle abusers:\n");
135
136 /* We've got serious business to do */
137 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
138
139 /* Step through GDI handle table and find out who our culprit is... */
140 for (idx = RESERVE_ENTRIES_COUNT; idx < GDI_HANDLE_COUNT; idx++)
141 {
142 /* If the handle is free, continue */
143 if (!IS_HANDLE_VALID(idx)) continue;
144
145 /* Step through all previous backtraces */
146 for (j = 0; j < nTraces; j++)
147 {
148 /* Check if the backtrace matches */
149 if (CompareBacktraces(idx, AllocatorTable[j].idx))
150 {
151 /* It matches, increment count and break out */
152 AllocatorTable[j].count++;
153 break;
154 }
155 }
156
157 /* Did we find a new backtrace? */
158 if (j == nTraces)
159 {
160 /* Break out, if we reached the maximum */
161 if (nTraces == MAX_BACKTRACES) break;
162
163 /* Initialize this entry */
164 AllocatorTable[j].idx = idx;
165 AllocatorTable[j].count = 1;
166 nTraces++;
167 }
168 }
169
170 /* bubble sort time! weeeeee!! */
171 for (i = 0; i < nTraces-1; i++)
172 {
173 if (AllocatorTable[i].count < AllocatorTable[i+1].count)
174 {
175 struct DbgOpenGDIHandle temp;
176
177 temp = AllocatorTable[i+1];
178 AllocatorTable[i+1] = AllocatorTable[i];
179 j = i;
180 while (j > 0 && AllocatorTable[j-1].count < temp.count)
181 j--;
182 AllocatorTable[j] = temp;
183 }
184 }
185
186 /* Print the worst offenders... */
187 DbgPrint("Worst GDI Handle leak offenders (out of %i unique locations):\n", nTraces);
188 for (i = 0; i < nTraces && AllocatorTable[i].count > 1; i++)
189 {
190 /* Print out the allocation count */
191 DbgPrint(" %i allocs, type = 0x%lx:\n",
192 AllocatorTable[i].count,
193 GdiHandleTable->Entries[AllocatorTable[i].idx].Type);
194
195 /* Dump the frames */
196 KeRosDumpStackFrames(GDIHandleAllocator[AllocatorTable[i].idx], GDI_STACK_LEVELS);
197 //KeRosDumpStackFrames(GDIHandleShareLocker[AllocatorTable[i].idx], GDI_STACK_LEVELS);
198
199 /* Print new line for better readability */
200 DbgPrint("\n");
201 }
202
203 if (i < nTraces)
204 DbgPrint("(List terminated - the remaining entries have 1 allocation only)\n");
205
206 KeLowerIrql(OldIrql);
207
208 ASSERT(FALSE);
209 }
210 #endif
211
212 ULONG
213 NTAPI
214 DbgCaptureStackBackTace(PVOID* pFrames, ULONG nFramesToCapture)
215 {
216 ULONG nFrameCount;
217
218 memset(pFrames, 0x00, (nFramesToCapture + 1) * sizeof(PVOID));
219
220 nFrameCount = RtlWalkFrameChain(pFrames, nFramesToCapture, 0);
221
222 if (nFrameCount < nFramesToCapture)
223 {
224 nFrameCount += RtlWalkFrameChain(pFrames + nFrameCount,
225 nFramesToCapture - nFrameCount,
226 1);
227 }
228
229 return nFrameCount;
230 }
231
232 BOOL
233 NTAPI
234 DbgGdiHTIntegrityCheck()
235 {
236 ULONG i, nDeleted = 0, nFree = 0, nUsed = 0;
237 PGDI_TABLE_ENTRY pEntry;
238 BOOL r = 1;
239
240 KeEnterCriticalRegion();
241
242 /* FIXME: Check reserved entries */
243
244 /* Now go through the deleted objects */
245 i = gulFirstFree & 0xffff;
246 while (i)
247 {
248 pEntry = &GdiHandleTable->Entries[i];
249 if (i >= GDI_HANDLE_COUNT)
250 {
251 DPRINT1("nDeleted=%lu\n", nDeleted);
252 ASSERT(FALSE);
253 }
254
255 nDeleted++;
256
257 /* Check the entry */
258 if ((pEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
259 {
260 r = 0;
261 DPRINT1("Deleted Entry has a type != 0\n");
262 }
263 if ((ULONG_PTR)pEntry->KernelData >= GDI_HANDLE_COUNT)
264 {
265 r = 0;
266 DPRINT1("Deleted entries KernelPointer too big\n");
267 }
268 if (pEntry->UserData != NULL)
269 {
270 r = 0;
271 DPRINT1("Deleted entry has UserData != 0\n");
272 }
273 if (pEntry->ProcessId != 0)
274 {
275 r = 0;
276 DPRINT1("Deleted entry has ProcessId != 0\n");
277 }
278
279 i = (ULONG_PTR)pEntry->KernelData & 0xffff;
280 };
281
282 for (i = gulFirstUnused;
283 i < GDI_HANDLE_COUNT;
284 i++)
285 {
286 pEntry = &GdiHandleTable->Entries[i];
287
288 if ((pEntry->Type) != 0)
289 {
290 r = 0;
291 DPRINT1("Free Entry has a type != 0\n");
292 }
293 if ((ULONG_PTR)pEntry->KernelData != 0)
294 {
295 r = 0;
296 DPRINT1("Free entries KernelPointer != 0\n");
297 }
298 if (pEntry->UserData != NULL)
299 {
300 r = 0;
301 DPRINT1("Free entry has UserData != 0\n");
302 }
303 if (pEntry->ProcessId != 0)
304 {
305 r = 0;
306 DPRINT1("Free entry has ProcessId != 0\n");
307 }
308 nFree++;
309 }
310
311 for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
312 {
313 HGDIOBJ Handle;
314 ULONG Type;
315
316 pEntry = &GdiHandleTable->Entries[i];
317 Type = pEntry->Type;
318 Handle = (HGDIOBJ)((Type << GDI_ENTRY_UPPER_SHIFT) + i);
319
320 if (Type & GDI_ENTRY_BASETYPE_MASK)
321 {
322 if (pEntry->KernelData == NULL)
323 {
324 r = 0;
325 DPRINT1("Used entry has KernelData == 0\n");
326 }
327 if (pEntry->KernelData <= MmHighestUserAddress)
328 {
329 r = 0;
330 DPRINT1("Used entry invalid KernelData\n");
331 }
332 if (((POBJ)(pEntry->KernelData))->hHmgr != Handle)
333 {
334 r = 0;
335 DPRINT1("Used entry %lu, has invalid hHmg %p (expected: %p)\n",
336 i, ((POBJ)(pEntry->KernelData))->hHmgr, Handle);
337 }
338 nUsed++;
339 }
340 }
341
342 if (RESERVE_ENTRIES_COUNT + nDeleted + nFree + nUsed != GDI_HANDLE_COUNT)
343 {
344 r = 0;
345 DPRINT1("Number of all entries incorrect: RESERVE_ENTRIES_COUNT = %lu, nDeleted = %lu, nFree = %lu, nUsed = %lu\n",
346 RESERVE_ENTRIES_COUNT, nDeleted, nFree, nUsed);
347 }
348
349 KeLeaveCriticalRegion();
350
351 return r;
352 }
353
354 #endif /* GDI_DEBUG */
355
356 VOID
357 NTAPI
358 DbgDumpLockedGdiHandles()
359 {
360 #if 0
361 ULONG i;
362
363 for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
364 {
365 PENTRY pentry = &gpentHmgr[i];
366
367 if (pentry->Objt)
368 {
369 POBJ pobj = pentry->einfo.pobj;
370 if (pobj->cExclusiveLock > 0)
371 {
372 DPRINT1("Locked object: %lx, type = %lx. allocated from:\n",
373 i, pentry->Objt);
374 DBG_DUMP_EVENT_LIST(&pobj->slhLog);
375 }
376 }
377 }
378 #endif
379 }
380
381 VOID
382 NTAPI
383 DbgLogEvent(PSLIST_HEADER pslh, LOG_EVENT_TYPE nEventType, LPARAM lParam)
384 {
385 PLOGENTRY pLogEntry;
386
387 /* Log a maximum of 100 events */
388 if (QueryDepthSList(pslh) >= 1000) return;
389
390 /* Allocate a logentry */
391 pLogEntry = EngAllocMem(0, sizeof(LOGENTRY), 'golG');
392 if (!pLogEntry) return;
393
394 /* Set type */
395 pLogEntry->nEventType = nEventType;
396 pLogEntry->ulUnique = InterlockedIncrement((LONG*)&gulLogUnique);
397 pLogEntry->dwProcessId = HandleToUlong(PsGetCurrentProcessId());
398 pLogEntry->dwThreadId = HandleToUlong(PsGetCurrentThreadId());
399 pLogEntry->lParam = lParam;
400
401 /* Capture a backtrace */
402 DbgCaptureStackBackTace(pLogEntry->apvBackTrace, 20);
403
404 switch (nEventType)
405 {
406 case EVENT_ALLOCATE:
407 case EVENT_CREATE_HANDLE:
408 case EVENT_REFERENCE:
409 case EVENT_DEREFERENCE:
410 case EVENT_LOCK:
411 case EVENT_UNLOCK:
412 case EVENT_DELETE:
413 case EVENT_FREE:
414 case EVENT_SET_OWNER:
415 default:
416 break;
417 }
418
419 /* Push it on the list */
420 InterlockedPushEntrySList(pslh, &pLogEntry->sleLink);
421 }
422
423 #define REL_ADDR(va) ((ULONG_PTR)va - (ULONG_PTR)&__ImageBase)
424
425 VOID
426 NTAPI
427 DbgPrintEvent(PLOGENTRY pLogEntry)
428 {
429 PSTR pstr;
430
431 switch (pLogEntry->nEventType)
432 {
433 case EVENT_ALLOCATE: pstr = "Allocate"; break;
434 case EVENT_CREATE_HANDLE: pstr = "CreatHdl"; break;
435 case EVENT_REFERENCE: pstr = "Ref"; break;
436 case EVENT_DEREFERENCE: pstr = "Deref"; break;
437 case EVENT_LOCK: pstr = "Lock"; break;
438 case EVENT_UNLOCK: pstr = "Unlock"; break;
439 case EVENT_DELETE: pstr = "Delete"; break;
440 case EVENT_FREE: pstr = "Free"; break;
441 case EVENT_SET_OWNER: pstr = "SetOwner"; break;
442 default: pstr = "Unknown"; break;
443 }
444
445 DbgPrint("[%lu] %03x:%03x %.8s val=%p <%lx,%lx,%lx,%lx>\n",
446 pLogEntry->ulUnique,
447 pLogEntry->dwProcessId,
448 pLogEntry->dwThreadId,
449 pstr,
450 (PVOID)pLogEntry->lParam,
451 REL_ADDR(pLogEntry->apvBackTrace[2]),
452 REL_ADDR(pLogEntry->apvBackTrace[3]),
453 REL_ADDR(pLogEntry->apvBackTrace[4]),
454 REL_ADDR(pLogEntry->apvBackTrace[5]));
455 }
456
457 VOID
458 NTAPI
459 DbgDumpEventList(PSLIST_HEADER pslh)
460 {
461 PSLIST_ENTRY psle;
462 PLOGENTRY pLogEntry;
463
464 while ((psle = InterlockedPopEntrySList(pslh)))
465 {
466 pLogEntry = CONTAINING_RECORD(psle, LOGENTRY, sleLink);
467 DbgPrintEvent(pLogEntry);
468 }
469 }
470
471 VOID
472 NTAPI
473 DbgCleanupEventList(PSLIST_HEADER pslh)
474 {
475 PSLIST_ENTRY psle;
476 PLOGENTRY pLogEntry;
477
478 while ((psle = InterlockedPopEntrySList(pslh)))
479 {
480 pLogEntry = CONTAINING_RECORD(psle, LOGENTRY, sleLink);
481 EngFreeMem(pLogEntry);
482 }
483 }
484
485 void
486 NTAPI
487 GdiDbgPreServiceHook(ULONG ulSyscallId, PULONG_PTR pulArguments)
488 {
489 PTHREADINFO pti = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
490 if (pti && pti->cExclusiveLocks != 0)
491 {
492 DbgPrint("FATAL: Win32DbgPreServiceHook(0x%lx): There are %lu exclusive locks!\n",
493 ulSyscallId, pti->cExclusiveLocks);
494 DbgDumpLockedGdiHandles();
495 ASSERT(FALSE);
496 }
497
498 }
499
500 ULONG_PTR
501 NTAPI
502 GdiDbgPostServiceHook(ULONG ulSyscallId, ULONG_PTR ulResult)
503 {
504 PTHREADINFO pti = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
505 if (pti && pti->cExclusiveLocks != 0)
506 {
507 DbgPrint("FATAL: Win32DbgPostServiceHook(0x%lx): There are %lu exclusive locks!\n",
508 ulSyscallId, pti->cExclusiveLocks);
509 DbgDumpLockedGdiHandles();
510 ASSERT(FALSE);
511 }
512 return ulResult;
513 }
514
515 NTSTATUS NTAPI
516 QueryEnvironmentVariable(PUNICODE_STRING Name,
517 PUNICODE_STRING Value)
518 {
519 NTSTATUS Status;
520 PWSTR wcs;
521 UNICODE_STRING var;
522 PWSTR val;
523 PPEB Peb;
524 PWSTR Environment;
525
526 /* Ugly HACK for ReactOS system threads */
527 if(!NtCurrentTeb())
528 {
529 return(STATUS_VARIABLE_NOT_FOUND);
530 }
531
532 Peb = NtCurrentPeb();
533
534 if (Peb == NULL)
535 {
536 return(STATUS_VARIABLE_NOT_FOUND);
537 }
538
539 Environment = Peb->ProcessParameters->Environment;
540
541 if (Environment == NULL)
542 {
543 return(STATUS_VARIABLE_NOT_FOUND);
544 }
545
546 Value->Length = 0;
547
548 wcs = Environment;
549 while (*wcs)
550 {
551 var.Buffer = wcs++;
552 wcs = wcschr(wcs, L'=');
553 if (wcs == NULL)
554 {
555 wcs = var.Buffer + wcslen(var.Buffer);
556 }
557 if (*wcs)
558 {
559 var.Length = var.MaximumLength = (wcs - var.Buffer) * sizeof(WCHAR);
560 val = ++wcs;
561 wcs += wcslen(wcs);
562
563 if (RtlEqualUnicodeString(&var, Name, TRUE))
564 {
565 Value->Length = (wcs - val) * sizeof(WCHAR);
566 if (Value->Length <= Value->MaximumLength)
567 {
568 memcpy(Value->Buffer, val,
569 min(Value->Length + sizeof(WCHAR), Value->MaximumLength));
570 Status = STATUS_SUCCESS;
571 }
572 else
573 {
574 Status = STATUS_BUFFER_TOO_SMALL;
575 }
576
577 return(Status);
578 }
579 }
580 wcs++;
581 }
582
583 return(STATUS_VARIABLE_NOT_FOUND);
584 }
585
586 static int
587 DbgCompareChannels(const void * a, const void * b)
588 {
589 return wcscmp((WCHAR*)a, ((DBG_CHANNEL*)b)->Name);
590 }
591
592 static BOOL
593 DbgAddDebugChannel(PPROCESSINFO ppi, WCHAR* channel, WCHAR* level, WCHAR op)
594 {
595 DBG_CHANNEL *ChannelEntry;
596 UINT iLevel, iChannel;
597
598 ChannelEntry = (DBG_CHANNEL*)bsearch(channel,
599 DbgChannels,
600 DbgChCount,
601 sizeof(DBG_CHANNEL),
602 DbgCompareChannels);
603 if(ChannelEntry == NULL)
604 {
605 return FALSE;
606 }
607
608 iChannel = ChannelEntry->Id;
609 ASSERT(iChannel >= 0 && iChannel < DbgChCount);
610
611 if(level == NULL || *level == L'\0' ||wcslen(level) == 0 )
612 iLevel = MAX_LEVEL;
613 else if(wcsncmp(level, L"err", 3) == 0)
614 iLevel = ERR_LEVEL;
615 else if(wcsncmp(level, L"fixme", 5) == 0)
616 iLevel = FIXME_LEVEL;
617 else if(wcsncmp(level, L"warn", 4) == 0)
618 iLevel = WARN_LEVEL;
619 else if (wcsncmp(level, L"trace", 4) == 0)
620 iLevel = TRACE_LEVEL;
621 else
622 return FALSE;
623
624 if(op==L'+')
625 {
626 DBG_ENABLE_CHANNEL(ppi, iChannel, iLevel);
627 }
628 else
629 {
630 DBG_DISABLE_CHANNEL(ppi, iChannel, iLevel);
631 }
632
633 return TRUE;
634 }
635
636 static BOOL
637 DbgParseDebugChannels(PPROCESSINFO ppi, PUNICODE_STRING Value)
638 {
639 WCHAR *str, *separator, *c, op;
640
641 str = Value->Buffer;
642
643 do
644 {
645 separator = wcschr(str, L',');
646 if(separator != NULL)
647 *separator = L'\0';
648
649 c = wcschr(str, L'+');
650 if(c == NULL)
651 c = wcschr(str, L'-');
652
653 if(c != NULL)
654 {
655 op = *c;
656 *c = L'\0';
657 c++;
658
659 DbgAddDebugChannel(ppi, c, str, op);
660 }
661
662 str = separator + 1;
663 }while(separator != NULL);
664
665 return TRUE;
666 }
667
668 BOOL DbgInitDebugChannels()
669 {
670 WCHAR valBuffer[100];
671 UNICODE_STRING Value;
672 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"DEBUGCHANNEL");
673 NTSTATUS Status;
674 PPROCESSINFO ppi;
675 BOOL ret;
676
677 /* Initialize all channels to ERROR */
678 ppi = PsGetCurrentProcessWin32Process();
679 RtlFillMemory( ppi->DbgChannelLevel,
680 sizeof(ppi->DbgChannelLevel),
681 ERR_LEVEL);
682
683 /* Find DEBUGCHANNEL env var */
684 Value.Buffer = valBuffer;
685 Value.Length = 0;
686 Value.MaximumLength = sizeof(valBuffer);
687 Status = QueryEnvironmentVariable(&Name, &Value);
688
689 /* It does not exist */
690 if(Status == STATUS_VARIABLE_NOT_FOUND)
691 {
692 /* There is nothing more to do */
693 return TRUE;
694 }
695
696 /* If the buffer in the stack is not enough allocate it */
697 if(Status == STATUS_BUFFER_TOO_SMALL)
698 {
699 Value.Buffer = ExAllocatePool(PagedPool, Value.MaximumLength);
700 if(Value.Buffer == NULL)
701 {
702 return FALSE;
703 }
704
705 /* Get the env var again */
706 Status = QueryEnvironmentVariable(&Name, &Value);
707 }
708
709 /* Check for error */
710 if(!NT_SUCCESS(Status))
711 {
712 if(Value.Buffer != valBuffer)
713 {
714 ExFreePool(Value.Buffer);
715 }
716
717 return FALSE;
718 }
719
720 /* Parse the variable */
721 ret = DbgParseDebugChannels(ppi, &Value);
722
723 /* Clean up */
724 if(Value.Buffer != valBuffer)
725 {
726 ExFreePool(Value.Buffer);
727 }
728
729 return ret;
730 }
731
732
733 #endif // DBG
734
735 /* EOF */