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